Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2006-2008 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/platform_thread.h"
     16 #include "base/safe_strerror_posix.h"
     17 #include "base/string_util.h"
     18 
     19 namespace base {
     20 
     21 namespace {
     22 // Paranoia. Semaphores and shared memory segments should live in different
     23 // namespaces, but who knows what's out there.
     24 const char kSemaphoreSuffix[] = "-sem";
     25 }
     26 
     27 SharedMemory::SharedMemory()
     28     : mapped_file_(-1),
     29       inode_(0),
     30       memory_(NULL),
     31       read_only_(false),
     32       max_size_(0) {
     33 }
     34 
     35 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
     36     : mapped_file_(handle.fd),
     37       inode_(0),
     38       memory_(NULL),
     39       read_only_(read_only),
     40       max_size_(0) {
     41   struct stat st;
     42   if (fstat(handle.fd, &st) == 0) {
     43     // If fstat fails, then the file descriptor is invalid and we'll learn this
     44     // fact when Map() fails.
     45     inode_ = st.st_ino;
     46   }
     47 }
     48 
     49 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
     50                            ProcessHandle process)
     51     : mapped_file_(handle.fd),
     52       memory_(NULL),
     53       read_only_(read_only),
     54       max_size_(0) {
     55   // We don't handle this case yet (note the ignored parameter); let's die if
     56   // someone comes calling.
     57   NOTREACHED();
     58 }
     59 
     60 SharedMemory::~SharedMemory() {
     61   Close();
     62 }
     63 
     64 // static
     65 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
     66   return handle.fd >= 0;
     67 }
     68 
     69 // static
     70 SharedMemoryHandle SharedMemory::NULLHandle() {
     71   return SharedMemoryHandle();
     72 }
     73 
     74 // static
     75 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
     76   DCHECK(handle.fd >= 0);
     77   close(handle.fd);
     78 }
     79 
     80 bool SharedMemory::Create(const std::wstring &name, bool read_only,
     81                           bool open_existing, size_t size) {
     82   read_only_ = read_only;
     83 
     84   int posix_flags = 0;
     85   posix_flags |= read_only ? O_RDONLY : O_RDWR;
     86   if (!open_existing || mapped_file_ <= 0)
     87     posix_flags |= O_CREAT;
     88 
     89   if (!CreateOrOpen(name, posix_flags, size))
     90     return false;
     91 
     92   max_size_ = size;
     93   return true;
     94 }
     95 
     96 // Our current implementation of shmem is with mmap()ing of files.
     97 // These files need to be deleted explicitly.
     98 // In practice this call is only needed for unit tests.
     99 bool SharedMemory::Delete(const std::wstring& name) {
    100   FilePath path;
    101   if (!FilePathForMemoryName(name, &path))
    102     return false;
    103 
    104   if (file_util::PathExists(path)) {
    105     return file_util::Delete(path, false);
    106   }
    107 
    108   // Doesn't exist, so success.
    109   return true;
    110 }
    111 
    112 bool SharedMemory::Open(const std::wstring &name, bool read_only) {
    113   read_only_ = read_only;
    114 
    115   int posix_flags = 0;
    116   posix_flags |= read_only ? O_RDONLY : O_RDWR;
    117 
    118   return CreateOrOpen(name, posix_flags, 0);
    119 }
    120 
    121 // For the given shmem named |memname|, return a filename to mmap()
    122 // (and possibly create).  Modifies |filename|.  Return false on
    123 // error, or true of we are happy.
    124 bool SharedMemory::FilePathForMemoryName(const std::wstring& memname,
    125                                          FilePath* path) {
    126   // mem_name will be used for a filename; make sure it doesn't
    127   // contain anything which will confuse us.
    128   DCHECK(memname.find_first_of(L"/") == std::string::npos);
    129   DCHECK(memname.find_first_of(L"\0") == std::string::npos);
    130 
    131   FilePath temp_dir;
    132   if (file_util::GetShmemTempDir(&temp_dir) == false)
    133     return false;
    134 
    135   *path = temp_dir.AppendASCII("com.google.chrome.shmem." +
    136                                WideToASCII(memname));
    137   return true;
    138 }
    139 
    140 // Chromium mostly only use the unique/private shmem as specified by
    141 // "name == L"". The exception is in the StatsTable.
    142 // TODO(jrg): there is no way to "clean up" all unused named shmem if
    143 // we restart from a crash.  (That isn't a new problem, but it is a problem.)
    144 // In case we want to delete it later, it may be useful to save the value
    145 // of mem_filename after FilePathForMemoryName().
    146 bool SharedMemory::CreateOrOpen(const std::wstring &name,
    147                                 int posix_flags, size_t size) {
    148   DCHECK(mapped_file_ == -1);
    149 
    150   file_util::ScopedFILE file_closer;
    151   FILE *fp;
    152 
    153   FilePath path;
    154   if (name == L"") {
    155     // It doesn't make sense to have a read-only private piece of shmem
    156     DCHECK(posix_flags & (O_RDWR | O_WRONLY));
    157 
    158     // Q: Why not use the shm_open() etc. APIs?
    159     // A: Because they're limited to 4mb on OS X.  FFFFFFFUUUUUUUUUUU
    160     fp = file_util::CreateAndOpenTemporaryShmemFile(&path);
    161 
    162     // Deleting the file prevents anyone else from mapping it in
    163     // (making it private), and prevents the need for cleanup (once
    164     // the last fd is closed, it is truly freed).
    165     file_util::Delete(path, false);
    166   } else {
    167     if (!FilePathForMemoryName(name, &path))
    168       return false;
    169 
    170     std::string mode;
    171     switch (posix_flags) {
    172       case (O_RDWR | O_CREAT):
    173         // Careful: "w+" will truncate if it already exists.
    174         mode = "a+";
    175         break;
    176       case O_RDWR:
    177         mode = "r+";
    178         break;
    179       case O_RDONLY:
    180         mode = "r";
    181         break;
    182       default:
    183         NOTIMPLEMENTED();
    184         break;
    185     }
    186 
    187     fp = file_util::OpenFile(path, mode.c_str());
    188   }
    189 
    190   if (fp == NULL) {
    191     if (posix_flags & O_CREAT) {
    192       PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
    193 #if !defined(OS_MACOSX)
    194       LOG(ERROR) << "This is frequently caused by incorrect permissions on "
    195                  << "/dev/shm.  Try 'sudo chmod 777 /dev/shm' to fix.";
    196 #endif
    197     }
    198     return false;
    199   }
    200 
    201   file_closer.reset(fp);  // close when we go out of scope
    202 
    203   // Make sure the (new) file is the right size.
    204   if (size && (posix_flags & (O_RDWR | O_CREAT))) {
    205     // Get current size.
    206     struct stat stat;
    207     if (fstat(fileno(fp), &stat) != 0)
    208       return false;
    209     const size_t current_size = stat.st_size;
    210     if (current_size != size) {
    211       if (ftruncate(fileno(fp), size) != 0)
    212         return false;
    213       if (fseeko(fp, size, SEEK_SET) != 0)
    214         return false;
    215     }
    216   }
    217 
    218   mapped_file_ = dup(fileno(fp));
    219   if (mapped_file_ == -1) {
    220     if (errno == EMFILE) {
    221       LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
    222       return false;
    223     } else {
    224       NOTREACHED() << "Call to dup failed, errno=" << errno;
    225     }
    226   }
    227 
    228   struct stat st;
    229   if (fstat(mapped_file_, &st))
    230     NOTREACHED();
    231   inode_ = st.st_ino;
    232 
    233   return true;
    234 }
    235 
    236 bool SharedMemory::Map(size_t bytes) {
    237   if (mapped_file_ == -1)
    238     return false;
    239 
    240   memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
    241                  MAP_SHARED, mapped_file_, 0);
    242 
    243   if (memory_)
    244     max_size_ = bytes;
    245 
    246   bool mmap_succeeded = (memory_ != (void*)-1);
    247   DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
    248   return mmap_succeeded;
    249 }
    250 
    251 bool SharedMemory::Unmap() {
    252   if (memory_ == NULL)
    253     return false;
    254 
    255   munmap(memory_, max_size_);
    256   memory_ = NULL;
    257   max_size_ = 0;
    258   return true;
    259 }
    260 
    261 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
    262                                         SharedMemoryHandle *new_handle,
    263                                         bool close_self) {
    264   const int new_fd = dup(mapped_file_);
    265   DCHECK(new_fd >= -1);
    266   new_handle->fd = new_fd;
    267   new_handle->auto_close = true;
    268 
    269   if (close_self)
    270     Close();
    271 
    272   return true;
    273 }
    274 
    275 
    276 void SharedMemory::Close() {
    277   Unmap();
    278 
    279   if (mapped_file_ > 0) {
    280     close(mapped_file_);
    281     mapped_file_ = -1;
    282   }
    283 }
    284 
    285 void SharedMemory::LockOrUnlockCommon(int function) {
    286   DCHECK(mapped_file_ >= 0);
    287   while (lockf(mapped_file_, function, 0) < 0) {
    288     if (errno == EINTR) {
    289       continue;
    290     } else if (errno == ENOLCK) {
    291       // temporary kernel resource exaustion
    292       PlatformThread::Sleep(500);
    293       continue;
    294     } else {
    295       NOTREACHED() << "lockf() failed."
    296                    << " function:" << function
    297                    << " fd:" << mapped_file_
    298                    << " errno:" << errno
    299                    << " msg:" << safe_strerror(errno);
    300     }
    301   }
    302 }
    303 
    304 void SharedMemory::Lock() {
    305   LockOrUnlockCommon(F_LOCK);
    306 }
    307 
    308 void SharedMemory::Unlock() {
    309   LockOrUnlockCommon(F_ULOCK);
    310 }
    311 
    312 SharedMemoryHandle SharedMemory::handle() const {
    313   return FileDescriptor(mapped_file_, false);
    314 }
    315 
    316 }  // namespace base
    317