blob: cafda5a62ed852bb6e03d1ec6765279daf5b364b [file] [log] [blame]
brettw@chromium.org11f89b02010-08-18 08:05:28 +09001// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botf003cfe2008-08-24 09:55:55 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
avi@google.com3a208982008-08-13 06:06:40 +09004
5#include "base/shared_memory.h"
6
avi@google.com8e4b6532008-12-12 04:51:24 +09007#include <errno.h>
tc@google.com7e1fb1d2008-08-13 07:40:21 +09008#include <fcntl.h>
avi@google.com3a208982008-08-13 06:06:40 +09009#include <sys/mman.h>
agl@chromium.org31de02e2009-02-20 11:00:04 +090010#include <sys/stat.h>
11#include <unistd.h>
avi@google.com3a208982008-08-13 06:06:40 +090012
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +090013#include "base/file_util.h"
avi@google.com3a208982008-08-13 06:06:40 +090014#include "base/logging.h"
brettw@chromium.org61391822011-01-01 05:02:16 +090015#include "base/threading/platform_thread.h"
tschmelcher@chromium.org90a3f8a2009-10-14 03:27:40 +090016#include "base/safe_strerror_posix.h"
brettw@chromium.org5b5f5e02011-01-01 10:01:06 +090017#include "base/threading/thread_restrictions.h"
brettw@chromium.org11f89b02010-08-18 08:05:28 +090018#include "base/utf_string_conversions.h"
avi@google.com3a208982008-08-13 06:06:40 +090019
brettw@google.comc60d9892008-11-14 12:25:15 +090020namespace base {
21
avi@google.com3a208982008-08-13 06:06:40 +090022namespace {
avi@google.com9e0cc152008-08-14 04:59:07 +090023// Paranoia. Semaphores and shared memory segments should live in different
24// namespaces, but who knows what's out there.
25const char kSemaphoreSuffix[] = "-sem";
avi@google.com3a208982008-08-13 06:06:40 +090026}
27
28SharedMemory::SharedMemory()
29 : mapped_file_(-1),
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090030 mapped_size_(0),
agl@chromium.org31de02e2009-02-20 11:00:04 +090031 inode_(0),
avi@google.com3a208982008-08-13 06:06:40 +090032 memory_(NULL),
33 read_only_(false),
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090034 created_size_(0) {
avi@google.com3a208982008-08-13 06:06:40 +090035}
36
37SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
agl@chromium.org42bbb992009-02-12 03:59:20 +090038 : mapped_file_(handle.fd),
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090039 mapped_size_(0),
agl@chromium.org31de02e2009-02-20 11:00:04 +090040 inode_(0),
avi@google.com3a208982008-08-13 06:06:40 +090041 memory_(NULL),
42 read_only_(read_only),
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090043 created_size_(0) {
agl@chromium.org31de02e2009-02-20 11:00:04 +090044 struct stat st;
45 if (fstat(handle.fd, &st) == 0) {
46 // If fstat fails, then the file descriptor is invalid and we'll learn this
47 // fact when Map() fails.
48 inode_ = st.st_ino;
49 }
avi@google.com3a208982008-08-13 06:06:40 +090050}
51
52SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
53 ProcessHandle process)
agl@chromium.org42bbb992009-02-12 03:59:20 +090054 : mapped_file_(handle.fd),
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090055 mapped_size_(0),
56 inode_(0),
avi@google.com3a208982008-08-13 06:06:40 +090057 memory_(NULL),
58 read_only_(read_only),
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090059 created_size_(0) {
avi@google.com3a208982008-08-13 06:06:40 +090060 // We don't handle this case yet (note the ignored parameter); let's die if
61 // someone comes calling.
62 NOTREACHED();
63}
64
65SharedMemory::~SharedMemory() {
66 Close();
avi@google.com3a208982008-08-13 06:06:40 +090067}
68
agl@chromium.org42bbb992009-02-12 03:59:20 +090069// static
70bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
71 return handle.fd >= 0;
72}
73
phajdan.jr@chromium.orgfa1476b2009-03-17 01:45:36 +090074// static
75SharedMemoryHandle SharedMemory::NULLHandle() {
76 return SharedMemoryHandle();
77}
78
hclam@chromium.org32300e22009-05-19 02:46:31 +090079// static
80void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
81 DCHECK(handle.fd >= 0);
piman@google.com36c06252011-02-24 06:37:41 +090082 if (HANDLE_EINTR(close(handle.fd)) < 0)
83 PLOG(ERROR) << "close";
hclam@chromium.org32300e22009-05-19 02:46:31 +090084}
85
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090086bool SharedMemory::CreateAndMapAnonymous(uint32 size) {
87 return CreateAnonymous(size) && Map(size);
88}
avi@google.com3a208982008-08-13 06:06:40 +090089
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090090bool SharedMemory::CreateAnonymous(uint32 size) {
91 return CreateNamed("", false, size);
92}
agl@chromium.org69e0a492008-11-18 09:21:05 +090093
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +090094// Chromium mostly only uses the unique/private shmem as specified by
95// "name == L"". The exception is in the StatsTable.
96// TODO(jrg): there is no way to "clean up" all unused named shmem if
97// we restart from a crash. (That isn't a new problem, but it is a problem.)
98// In case we want to delete it later, it may be useful to save the value
99// of mem_filename after FilePathForMemoryName().
100bool SharedMemory::CreateNamed(const std::string& name,
101 bool open_existing, uint32 size) {
102 DCHECK(mapped_file_ == -1);
103 if (size == 0) return false;
104
105 // This function theoretically can block on the disk, but realistically
106 // the temporary files we create will just go into the buffer cache
107 // and be deleted before they ever make it out to disk.
108 base::ThreadRestrictions::ScopedAllowIO allow_io;
109
110 FILE *fp;
111 bool fix_size = true;
112
113 FilePath path;
114 if (name.empty()) {
115 // It doesn't make sense to have a open-existing private piece of shmem
116 DCHECK(!open_existing);
117 // Q: Why not use the shm_open() etc. APIs?
118 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
119 fp = file_util::CreateAndOpenTemporaryShmemFile(&path);
120
121 // Deleting the file prevents anyone else from mapping it in
122 // (making it private), and prevents the need for cleanup (once
123 // the last fd is closed, it is truly freed).
124 if (fp)
125 file_util::Delete(path, false);
126
127 } else {
128 if (!FilePathForMemoryName(name, &path))
129 return false;
130
131 fp = file_util::OpenFile(path, "w+x");
132 if (fp == NULL && open_existing) {
133 // "w+" will truncate if it already exists.
134 fp = file_util::OpenFile(path, "a+");
135 fix_size = false;
136 }
137 }
138 if (fp && fix_size) {
139 // Get current size.
140 struct stat stat;
141 if (fstat(fileno(fp), &stat) != 0)
142 return false;
143 const uint32 current_size = stat.st_size;
144 if (current_size != size) {
145 if (ftruncate(fileno(fp), size) != 0)
146 return false;
147 if (fseeko(fp, size, SEEK_SET) != 0)
148 return false;
149 }
150 created_size_ = size;
151 }
152 if (fp == NULL) {
153#if !defined(OS_MACOSX)
154 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
155 FilePath dir = path.DirName();
156 if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
157 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
158 if (dir.value() == "/dev/shm") {
159 LOG(FATAL) << "This is frequently caused by incorrect permissions on "
markus@chromium.orgdacd5452010-11-02 07:36:36 +0900160 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900161 }
162 }
163#else
164 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
165#endif
agl@chromium.org8c158342008-11-18 09:21:37 +0900166 return false;
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900167 }
agl@chromium.org8c158342008-11-18 09:21:37 +0900168
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900169 return PrepareMapFile(fp);
avi@google.com9e0cc152008-08-14 04:59:07 +0900170}
171
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900172// Our current implementation of shmem is with mmap()ing of files.
173// These files need to be deleted explicitly.
174// In practice this call is only needed for unit tests.
evan@chromium.org56756992010-09-30 05:32:22 +0900175bool SharedMemory::Delete(const std::string& name) {
evan@chromium.orgbd3a0d62009-09-17 02:32:11 +0900176 FilePath path;
177 if (!FilePathForMemoryName(name, &path))
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900178 return false;
179
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900180 if (file_util::PathExists(path)) {
181 return file_util::Delete(path, false);
182 }
183
184 // Doesn't exist, so success.
185 return true;
186}
187
evan@chromium.org56756992010-09-30 05:32:22 +0900188bool SharedMemory::Open(const std::string& name, bool read_only) {
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900189 FilePath path;
190 if (!FilePathForMemoryName(name, &path))
191 return false;
192
avi@google.com9e0cc152008-08-14 04:59:07 +0900193 read_only_ = read_only;
agl@chromium.org69e0a492008-11-18 09:21:05 +0900194
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900195 const char *mode = read_only ? "r" : "r+";
196 FILE *fp = file_util::OpenFile(path, mode);
197 return PrepareMapFile(fp);
avi@google.com9e0cc152008-08-14 04:59:07 +0900198}
199
erg@google.com37c078e2011-01-11 09:50:59 +0900200bool SharedMemory::Map(uint32 bytes) {
201 if (mapped_file_ == -1)
avi@google.com3a208982008-08-13 06:06:40 +0900202 return false;
agl@chromium.org69e0a492008-11-18 09:21:05 +0900203
erg@google.com37c078e2011-01-11 09:50:59 +0900204 memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
205 MAP_SHARED, mapped_file_, 0);
206
207 if (memory_)
208 mapped_size_ = bytes;
209
210 bool mmap_succeeded = (memory_ != (void*)-1);
211 DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
212 return mmap_succeeded;
213}
214
215bool SharedMemory::Unmap() {
216 if (memory_ == NULL)
217 return false;
218
219 munmap(memory_, mapped_size_);
220 memory_ = NULL;
221 mapped_size_ = 0;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900222 return true;
223}
224
erg@google.com37c078e2011-01-11 09:50:59 +0900225SharedMemoryHandle SharedMemory::handle() const {
226 return FileDescriptor(mapped_file_, false);
227}
228
229void SharedMemory::Close() {
230 Unmap();
231
232 if (mapped_file_ > 0) {
piman@google.com36c06252011-02-24 06:37:41 +0900233 if (HANDLE_EINTR(close(mapped_file_)) < 0)
234 PLOG(ERROR) << "close";
erg@google.com37c078e2011-01-11 09:50:59 +0900235 mapped_file_ = -1;
236 }
237}
238
239void SharedMemory::Lock() {
240 LockOrUnlockCommon(F_LOCK);
241}
242
243void SharedMemory::Unlock() {
244 LockOrUnlockCommon(F_ULOCK);
245}
246
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900247bool SharedMemory::PrepareMapFile(FILE *fp) {
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900248 DCHECK(mapped_file_ == -1);
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900249 if (fp == NULL) return false;
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900250
evan@chromium.org7c9cd8b2010-10-23 14:19:20 +0900251 // This function theoretically can block on the disk, but realistically
252 // the temporary files we create will just go into the buffer cache
253 // and be deleted before they ever make it out to disk.
254 base::ThreadRestrictions::ScopedAllowIO allow_io;
255
dmaclach@chromium.orgbb6b7632010-10-28 03:16:06 +0900256 file_util::ScopedFILE file_closer(fp);
jrg@chromium.orgd505c3a2009-02-04 09:58:39 +0900257
258 mapped_file_ = dup(fileno(fp));
stuartmorgan@chromium.org8a8e9662009-06-13 06:15:33 +0900259 if (mapped_file_ == -1) {
260 if (errno == EMFILE) {
261 LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
262 return false;
263 } else {
264 NOTREACHED() << "Call to dup failed, errno=" << errno;
265 }
266 }
agl@chromium.org31de02e2009-02-20 11:00:04 +0900267
268 struct stat st;
269 if (fstat(mapped_file_, &st))
270 NOTREACHED();
271 inode_ = st.st_ino;
272
avi@google.com3a208982008-08-13 06:06:40 +0900273 return true;
274}
275
erg@google.com37c078e2011-01-11 09:50:59 +0900276// For the given shmem named |mem_name|, return a filename to mmap()
277// (and possibly create). Modifies |filename|. Return false on
278// error, or true of we are happy.
279bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
280 FilePath* path) {
281 // mem_name will be used for a filename; make sure it doesn't
282 // contain anything which will confuse us.
283 DCHECK(mem_name.find('/') == std::string::npos);
284 DCHECK(mem_name.find('\0') == std::string::npos);
285
286 FilePath temp_dir;
287 if (!file_util::GetShmemTempDir(&temp_dir))
avi@google.com3a208982008-08-13 06:06:40 +0900288 return false;
agl@chromium.org69e0a492008-11-18 09:21:05 +0900289
erg@google.com37c078e2011-01-11 09:50:59 +0900290 *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name);
avi@google.com3a208982008-08-13 06:06:40 +0900291 return true;
292}
293
jrg@chromium.org81e22602009-02-06 04:04:34 +0900294void SharedMemory::LockOrUnlockCommon(int function) {
295 DCHECK(mapped_file_ >= 0);
296 while (lockf(mapped_file_, function, 0) < 0) {
297 if (errno == EINTR) {
298 continue;
299 } else if (errno == ENOLCK) {
300 // temporary kernel resource exaustion
brettw@chromium.org61391822011-01-01 05:02:16 +0900301 base::PlatformThread::Sleep(500);
jrg@chromium.org81e22602009-02-06 04:04:34 +0900302 continue;
303 } else {
304 NOTREACHED() << "lockf() failed."
305 << " function:" << function
306 << " fd:" << mapped_file_
307 << " errno:" << errno
tschmelcher@chromium.org90a3f8a2009-10-14 03:27:40 +0900308 << " msg:" << safe_strerror(errno);
jrg@chromium.org81e22602009-02-06 04:04:34 +0900309 }
avi@google.com3a208982008-08-13 06:06:40 +0900310 }
311}
312
erg@google.com37c078e2011-01-11 09:50:59 +0900313bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
314 SharedMemoryHandle *new_handle,
315 bool close_self) {
316 const int new_fd = dup(mapped_file_);
317 DCHECK(new_fd >= 0);
318 new_handle->fd = new_fd;
319 new_handle->auto_close = true;
avi@google.com3a208982008-08-13 06:06:40 +0900320
erg@google.com37c078e2011-01-11 09:50:59 +0900321 if (close_self)
322 Close();
license.botf003cfe2008-08-24 09:55:55 +0900323
erg@google.com37c078e2011-01-11 09:50:59 +0900324 return true;
agl@chromium.org42bbb992009-02-12 03:59:20 +0900325}
326
brettw@google.comc60d9892008-11-14 12:25:15 +0900327} // namespace base