• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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