shared_memory_posix.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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
20namespace base {
21
22namespace {
23// Paranoia. Semaphores and shared memory segments should live in different
24// namespaces, but who knows what's out there.
25const char kSemaphoreSuffix[] = "-sem";
26}
27
28SharedMemory::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
37SharedMemory::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
52SharedMemory::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
65SharedMemory::~SharedMemory() {
66  Close();
67}
68
69// static
70bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
71  return handle.fd >= 0;
72}
73
74// static
75SharedMemoryHandle SharedMemory::NULLHandle() {
76  return SharedMemoryHandle();
77}
78
79// static
80void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
81  DCHECK(handle.fd >= 0);
82  if (HANDLE_EINTR(close(handle.fd)) < 0)
83    PLOG(ERROR) << "close";
84}
85
86bool SharedMemory::CreateAndMapAnonymous(uint32 size) {
87  return CreateAnonymous(size) && Map(size);
88}
89
90bool 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().
100bool 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.
175bool 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
188bool 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
200bool 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
215bool 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
225SharedMemoryHandle SharedMemory::handle() const {
226  return FileDescriptor(mapped_file_, false);
227}
228
229void 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
239void SharedMemory::Lock() {
240#if !defined(ANDROID)
241  LockOrUnlockCommon(F_LOCK);
242#endif
243}
244
245void SharedMemory::Unlock() {
246#if !defined(ANDROID)
247  LockOrUnlockCommon(F_ULOCK);
248#endif
249}
250
251bool 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.
283bool 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
298void SharedMemory::LockOrUnlockCommon(int function) {
299<<<<<<< HEAD
300  DCHECK(mapped_file_ >= 0);
301#if !defined(ANDROID)
302=======
303  DCHECK_GE(mapped_file_, 0);
304>>>>>>> chromium.org at r12.0.742.93
305  while (lockf(mapped_file_, function, 0) < 0) {
306    if (errno == EINTR) {
307      continue;
308    } else if (errno == ENOLCK) {
309      // temporary kernel resource exaustion
310      base::PlatformThread::Sleep(500);
311      continue;
312    } else {
313      NOTREACHED() << "lockf() failed."
314                   << " function:" << function
315                   << " fd:" << mapped_file_
316                   << " errno:" << errno
317                   << " msg:" << safe_strerror(errno);
318    }
319  }
320#endif
321}
322
323bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
324                                        SharedMemoryHandle *new_handle,
325                                        bool close_self) {
326  const int new_fd = dup(mapped_file_);
327  DCHECK_GE(new_fd, 0);
328  new_handle->fd = new_fd;
329  new_handle->auto_close = true;
330
331  if (close_self)
332    Close();
333
334  return true;
335}
336
337}  // namespace base
338