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 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 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 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 65 SharedMemory::~SharedMemory() { 66 Close(); 67 } 68 69 // static 70 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { 71 return handle.fd >= 0; 72 } 73 74 // static 75 SharedMemoryHandle SharedMemory::NULLHandle() { 76 return SharedMemoryHandle(); 77 } 78 79 // static 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 86 bool SharedMemory::CreateAndMapAnonymous(uint32 size) { 87 return CreateAnonymous(size) && Map(size); 88 } 89 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(). 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. 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 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 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 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 225 SharedMemoryHandle SharedMemory::handle() const { 226 return FileDescriptor(mapped_file_, false); 227 } 228 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 239 void SharedMemory::Lock() { 240 #if !defined(ANDROID) 241 LockOrUnlockCommon(F_LOCK); 242 #endif 243 } 244 245 void SharedMemory::Unlock() { 246 #if !defined(ANDROID) 247 LockOrUnlockCommon(F_ULOCK); 248 #endif 249 } 250 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. 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 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 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