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