1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/shared_memory.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/mman.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12
13 #include "base/file_util.h"
14 #include "base/logging.h"
15 #include "base/threading/platform_thread.h"
16 #include "base/safe_strerror_posix.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "base/utf_string_conversions.h"
19
20 namespace base {
21
22 namespace {
23 // Paranoia. Semaphores and shared memory segments should live in different
24 // namespaces, but who knows what's out there.
25 const char kSemaphoreSuffix[] = "-sem";
26 }
27
SharedMemory()28 SharedMemory::SharedMemory()
29 : mapped_file_(-1),
30 mapped_size_(0),
31 inode_(0),
32 memory_(NULL),
33 read_only_(false),
34 created_size_(0) {
35 }
36
SharedMemory(SharedMemoryHandle handle,bool read_only)37 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
38 : mapped_file_(handle.fd),
39 mapped_size_(0),
40 inode_(0),
41 memory_(NULL),
42 read_only_(read_only),
43 created_size_(0) {
44 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 }
50 }
51
SharedMemory(SharedMemoryHandle handle,bool read_only,ProcessHandle process)52 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
53 ProcessHandle process)
54 : mapped_file_(handle.fd),
55 mapped_size_(0),
56 inode_(0),
57 memory_(NULL),
58 read_only_(read_only),
59 created_size_(0) {
60 // We don't handle this case yet (note the ignored parameter); let's die if
61 // someone comes calling.
62 NOTREACHED();
63 }
64
~SharedMemory()65 SharedMemory::~SharedMemory() {
66 Close();
67 }
68
69 // static
IsHandleValid(const SharedMemoryHandle & handle)70 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
71 return handle.fd >= 0;
72 }
73
74 // static
NULLHandle()75 SharedMemoryHandle SharedMemory::NULLHandle() {
76 return SharedMemoryHandle();
77 }
78
79 // static
CloseHandle(const SharedMemoryHandle & handle)80 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
81 DCHECK(handle.fd >= 0);
82 if (HANDLE_EINTR(close(handle.fd)) < 0)
83 PLOG(ERROR) << "close";
84 }
85
CreateAndMapAnonymous(uint32 size)86 bool SharedMemory::CreateAndMapAnonymous(uint32 size) {
87 return CreateAnonymous(size) && Map(size);
88 }
89
CreateAnonymous(uint32 size)90 bool SharedMemory::CreateAnonymous(uint32 size) {
91 return CreateNamed("", false, size);
92 }
93
94 // 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().
CreateNamed(const std::string & name,bool open_existing,uint32 size)100 bool SharedMemory::CreateNamed(const std::string& name,
101 bool open_existing, uint32 size) {
102 DCHECK_EQ(-1, mapped_file_);
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 (HANDLE_EINTR(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 "
160 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
161 }
162 }
163 #else
164 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
165 #endif
166 return false;
167 }
168
169 return PrepareMapFile(fp);
170 }
171
172 // 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.
Delete(const std::string & name)175 bool SharedMemory::Delete(const std::string& name) {
176 FilePath path;
177 if (!FilePathForMemoryName(name, &path))
178 return false;
179
180 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
Open(const std::string & name,bool read_only)188 bool SharedMemory::Open(const std::string& name, bool read_only) {
189 FilePath path;
190 if (!FilePathForMemoryName(name, &path))
191 return false;
192
193 read_only_ = read_only;
194
195 const char *mode = read_only ? "r" : "r+";
196 FILE *fp = file_util::OpenFile(path, mode);
197 return PrepareMapFile(fp);
198 }
199
Map(uint32 bytes)200 bool SharedMemory::Map(uint32 bytes) {
201 if (mapped_file_ == -1)
202 return false;
203
204 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
Unmap()215 bool SharedMemory::Unmap() {
216 if (memory_ == NULL)
217 return false;
218
219 munmap(memory_, mapped_size_);
220 memory_ = NULL;
221 mapped_size_ = 0;
222 return true;
223 }
224
handle() const225 SharedMemoryHandle SharedMemory::handle() const {
226 return FileDescriptor(mapped_file_, false);
227 }
228
Close()229 void SharedMemory::Close() {
230 Unmap();
231
232 if (mapped_file_ > 0) {
233 if (HANDLE_EINTR(close(mapped_file_)) < 0)
234 PLOG(ERROR) << "close";
235 mapped_file_ = -1;
236 }
237 }
238
Lock()239 void SharedMemory::Lock() {
240 #if !defined(ANDROID)
241 LockOrUnlockCommon(F_LOCK);
242 #endif
243 }
244
Unlock()245 void SharedMemory::Unlock() {
246 #if !defined(ANDROID)
247 LockOrUnlockCommon(F_ULOCK);
248 #endif
249 }
250
PrepareMapFile(FILE * fp)251 bool SharedMemory::PrepareMapFile(FILE *fp) {
252 DCHECK_EQ(-1, mapped_file_);
253 if (fp == NULL) return false;
254
255 // This function theoretically can block on the disk, but realistically
256 // the temporary files we create will just go into the buffer cache
257 // and be deleted before they ever make it out to disk.
258 base::ThreadRestrictions::ScopedAllowIO allow_io;
259
260 file_util::ScopedFILE file_closer(fp);
261
262 mapped_file_ = dup(fileno(fp));
263 if (mapped_file_ == -1) {
264 if (errno == EMFILE) {
265 LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
266 return false;
267 } else {
268 NOTREACHED() << "Call to dup failed, errno=" << errno;
269 }
270 }
271
272 struct stat st;
273 if (fstat(mapped_file_, &st))
274 NOTREACHED();
275 inode_ = st.st_ino;
276
277 return true;
278 }
279
280 // For the given shmem named |mem_name|, return a filename to mmap()
281 // (and possibly create). Modifies |filename|. Return false on
282 // error, or true of we are happy.
FilePathForMemoryName(const std::string & mem_name,FilePath * path)283 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
284 FilePath* path) {
285 // mem_name will be used for a filename; make sure it doesn't
286 // contain anything which will confuse us.
287 DCHECK_EQ(std::string::npos, mem_name.find('/'));
288 DCHECK_EQ(std::string::npos, mem_name.find('\0'));
289
290 FilePath temp_dir;
291 if (!file_util::GetShmemTempDir(&temp_dir))
292 return false;
293
294 *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name);
295 return true;
296 }
297
LockOrUnlockCommon(int function)298 void SharedMemory::LockOrUnlockCommon(int function) {
299
300 DCHECK_GE(mapped_file_, 0);
301 #if !defined(ANDROID)
302 while (lockf(mapped_file_, function, 0) < 0) {
303 if (errno == EINTR) {
304 continue;
305 } else if (errno == ENOLCK) {
306 // temporary kernel resource exaustion
307 base::PlatformThread::Sleep(500);
308 continue;
309 } else {
310 NOTREACHED() << "lockf() failed."
311 << " function:" << function
312 << " fd:" << mapped_file_
313 << " errno:" << errno
314 << " msg:" << safe_strerror(errno);
315 }
316 }
317 #endif
318 }
319
ShareToProcessCommon(ProcessHandle process,SharedMemoryHandle * new_handle,bool close_self)320 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
321 SharedMemoryHandle *new_handle,
322 bool close_self) {
323 const int new_fd = dup(mapped_file_);
324 DCHECK_GE(new_fd, 0);
325 new_handle->fd = new_fd;
326 new_handle->auto_close = true;
327
328 if (close_self)
329 Close();
330
331 return true;
332 }
333
334 } // namespace base
335