brettw@chromium.org | 11f89b0 | 2010-08-18 08:05:28 +0900 | [diff] [blame] | 1 | // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
license.bot | f003cfe | 2008-08-24 09:55:55 +0900 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 4 | |
| 5 | #include "base/shared_memory.h" |
| 6 | |
avi@google.com | 8e4b653 | 2008-12-12 04:51:24 +0900 | [diff] [blame] | 7 | #include <errno.h> |
tc@google.com | 7e1fb1d | 2008-08-13 07:40:21 +0900 | [diff] [blame] | 8 | #include <fcntl.h> |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 9 | #include <sys/mman.h> |
agl@chromium.org | 31de02e | 2009-02-20 11:00:04 +0900 | [diff] [blame] | 10 | #include <sys/stat.h> |
| 11 | #include <unistd.h> |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 12 | |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 13 | #include "base/file_util.h" |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 14 | #include "base/logging.h" |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 15 | #include "base/threading/platform_thread.h" |
tschmelcher@chromium.org | 90a3f8a | 2009-10-14 03:27:40 +0900 | [diff] [blame] | 16 | #include "base/safe_strerror_posix.h" |
brettw@chromium.org | 5b5f5e0 | 2011-01-01 10:01:06 +0900 | [diff] [blame] | 17 | #include "base/threading/thread_restrictions.h" |
brettw@chromium.org | 11f89b0 | 2010-08-18 08:05:28 +0900 | [diff] [blame] | 18 | #include "base/utf_string_conversions.h" |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 19 | |
brettw@google.com | c60d989 | 2008-11-14 12:25:15 +0900 | [diff] [blame] | 20 | namespace base { |
| 21 | |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 22 | namespace { |
avi@google.com | 9e0cc15 | 2008-08-14 04:59:07 +0900 | [diff] [blame] | 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"; |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 26 | } |
| 27 | |
| 28 | SharedMemory::SharedMemory() |
| 29 | : mapped_file_(-1), |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 30 | mapped_size_(0), |
agl@chromium.org | 31de02e | 2009-02-20 11:00:04 +0900 | [diff] [blame] | 31 | inode_(0), |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 32 | memory_(NULL), |
| 33 | read_only_(false), |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 34 | created_size_(0) { |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 35 | } |
| 36 | |
| 37 | SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) |
agl@chromium.org | 42bbb99 | 2009-02-12 03:59:20 +0900 | [diff] [blame] | 38 | : mapped_file_(handle.fd), |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 39 | mapped_size_(0), |
agl@chromium.org | 31de02e | 2009-02-20 11:00:04 +0900 | [diff] [blame] | 40 | inode_(0), |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 41 | memory_(NULL), |
| 42 | read_only_(read_only), |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 43 | created_size_(0) { |
agl@chromium.org | 31de02e | 2009-02-20 11:00:04 +0900 | [diff] [blame] | 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 | } |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, |
| 53 | ProcessHandle process) |
agl@chromium.org | 42bbb99 | 2009-02-12 03:59:20 +0900 | [diff] [blame] | 54 | : mapped_file_(handle.fd), |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 55 | mapped_size_(0), |
| 56 | inode_(0), |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 57 | memory_(NULL), |
| 58 | read_only_(read_only), |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 59 | created_size_(0) { |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 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(); |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 67 | } |
| 68 | |
agl@chromium.org | 42bbb99 | 2009-02-12 03:59:20 +0900 | [diff] [blame] | 69 | // static |
| 70 | bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { |
| 71 | return handle.fd >= 0; |
| 72 | } |
| 73 | |
phajdan.jr@chromium.org | fa1476b | 2009-03-17 01:45:36 +0900 | [diff] [blame] | 74 | // static |
| 75 | SharedMemoryHandle SharedMemory::NULLHandle() { |
| 76 | return SharedMemoryHandle(); |
| 77 | } |
| 78 | |
hclam@chromium.org | 32300e2 | 2009-05-19 02:46:31 +0900 | [diff] [blame] | 79 | // static |
| 80 | void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { |
| 81 | DCHECK(handle.fd >= 0); |
piman@google.com | 36c0625 | 2011-02-24 06:37:41 +0900 | [diff] [blame^] | 82 | if (HANDLE_EINTR(close(handle.fd)) < 0) |
| 83 | PLOG(ERROR) << "close"; |
hclam@chromium.org | 32300e2 | 2009-05-19 02:46:31 +0900 | [diff] [blame] | 84 | } |
| 85 | |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 86 | bool SharedMemory::CreateAndMapAnonymous(uint32 size) { |
| 87 | return CreateAnonymous(size) && Map(size); |
| 88 | } |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 89 | |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 90 | bool SharedMemory::CreateAnonymous(uint32 size) { |
| 91 | return CreateNamed("", false, size); |
| 92 | } |
agl@chromium.org | 69e0a49 | 2008-11-18 09:21:05 +0900 | [diff] [blame] | 93 | |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 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(mapped_file_ == -1); |
| 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 (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 " |
markus@chromium.org | dacd545 | 2010-11-02 07:36:36 +0900 | [diff] [blame] | 160 | << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | #else |
| 164 | PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; |
| 165 | #endif |
agl@chromium.org | 8c15834 | 2008-11-18 09:21:37 +0900 | [diff] [blame] | 166 | return false; |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 167 | } |
agl@chromium.org | 8c15834 | 2008-11-18 09:21:37 +0900 | [diff] [blame] | 168 | |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 169 | return PrepareMapFile(fp); |
avi@google.com | 9e0cc15 | 2008-08-14 04:59:07 +0900 | [diff] [blame] | 170 | } |
| 171 | |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 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. |
evan@chromium.org | 5675699 | 2010-09-30 05:32:22 +0900 | [diff] [blame] | 175 | bool SharedMemory::Delete(const std::string& name) { |
evan@chromium.org | bd3a0d6 | 2009-09-17 02:32:11 +0900 | [diff] [blame] | 176 | FilePath path; |
| 177 | if (!FilePathForMemoryName(name, &path)) |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 178 | return false; |
| 179 | |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 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 | |
evan@chromium.org | 5675699 | 2010-09-30 05:32:22 +0900 | [diff] [blame] | 188 | bool SharedMemory::Open(const std::string& name, bool read_only) { |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 189 | FilePath path; |
| 190 | if (!FilePathForMemoryName(name, &path)) |
| 191 | return false; |
| 192 | |
avi@google.com | 9e0cc15 | 2008-08-14 04:59:07 +0900 | [diff] [blame] | 193 | read_only_ = read_only; |
agl@chromium.org | 69e0a49 | 2008-11-18 09:21:05 +0900 | [diff] [blame] | 194 | |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 195 | const char *mode = read_only ? "r" : "r+"; |
| 196 | FILE *fp = file_util::OpenFile(path, mode); |
| 197 | return PrepareMapFile(fp); |
avi@google.com | 9e0cc15 | 2008-08-14 04:59:07 +0900 | [diff] [blame] | 198 | } |
| 199 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 200 | bool SharedMemory::Map(uint32 bytes) { |
| 201 | if (mapped_file_ == -1) |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 202 | return false; |
agl@chromium.org | 69e0a49 | 2008-11-18 09:21:05 +0900 | [diff] [blame] | 203 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 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; |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 222 | return true; |
| 223 | } |
| 224 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 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) { |
piman@google.com | 36c0625 | 2011-02-24 06:37:41 +0900 | [diff] [blame^] | 233 | if (HANDLE_EINTR(close(mapped_file_)) < 0) |
| 234 | PLOG(ERROR) << "close"; |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 235 | mapped_file_ = -1; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | void SharedMemory::Lock() { |
| 240 | LockOrUnlockCommon(F_LOCK); |
| 241 | } |
| 242 | |
| 243 | void SharedMemory::Unlock() { |
| 244 | LockOrUnlockCommon(F_ULOCK); |
| 245 | } |
| 246 | |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 247 | bool SharedMemory::PrepareMapFile(FILE *fp) { |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 248 | DCHECK(mapped_file_ == -1); |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 249 | if (fp == NULL) return false; |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 250 | |
evan@chromium.org | 7c9cd8b | 2010-10-23 14:19:20 +0900 | [diff] [blame] | 251 | // This function theoretically can block on the disk, but realistically |
| 252 | // the temporary files we create will just go into the buffer cache |
| 253 | // and be deleted before they ever make it out to disk. |
| 254 | base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 255 | |
dmaclach@chromium.org | bb6b763 | 2010-10-28 03:16:06 +0900 | [diff] [blame] | 256 | file_util::ScopedFILE file_closer(fp); |
jrg@chromium.org | d505c3a | 2009-02-04 09:58:39 +0900 | [diff] [blame] | 257 | |
| 258 | mapped_file_ = dup(fileno(fp)); |
stuartmorgan@chromium.org | 8a8e966 | 2009-06-13 06:15:33 +0900 | [diff] [blame] | 259 | if (mapped_file_ == -1) { |
| 260 | if (errno == EMFILE) { |
| 261 | LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; |
| 262 | return false; |
| 263 | } else { |
| 264 | NOTREACHED() << "Call to dup failed, errno=" << errno; |
| 265 | } |
| 266 | } |
agl@chromium.org | 31de02e | 2009-02-20 11:00:04 +0900 | [diff] [blame] | 267 | |
| 268 | struct stat st; |
| 269 | if (fstat(mapped_file_, &st)) |
| 270 | NOTREACHED(); |
| 271 | inode_ = st.st_ino; |
| 272 | |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 273 | return true; |
| 274 | } |
| 275 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 276 | // For the given shmem named |mem_name|, return a filename to mmap() |
| 277 | // (and possibly create). Modifies |filename|. Return false on |
| 278 | // error, or true of we are happy. |
| 279 | bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, |
| 280 | FilePath* path) { |
| 281 | // mem_name will be used for a filename; make sure it doesn't |
| 282 | // contain anything which will confuse us. |
| 283 | DCHECK(mem_name.find('/') == std::string::npos); |
| 284 | DCHECK(mem_name.find('\0') == std::string::npos); |
| 285 | |
| 286 | FilePath temp_dir; |
| 287 | if (!file_util::GetShmemTempDir(&temp_dir)) |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 288 | return false; |
agl@chromium.org | 69e0a49 | 2008-11-18 09:21:05 +0900 | [diff] [blame] | 289 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 290 | *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name); |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 291 | return true; |
| 292 | } |
| 293 | |
jrg@chromium.org | 81e2260 | 2009-02-06 04:04:34 +0900 | [diff] [blame] | 294 | void SharedMemory::LockOrUnlockCommon(int function) { |
| 295 | DCHECK(mapped_file_ >= 0); |
| 296 | while (lockf(mapped_file_, function, 0) < 0) { |
| 297 | if (errno == EINTR) { |
| 298 | continue; |
| 299 | } else if (errno == ENOLCK) { |
| 300 | // temporary kernel resource exaustion |
brettw@chromium.org | 6139182 | 2011-01-01 05:02:16 +0900 | [diff] [blame] | 301 | base::PlatformThread::Sleep(500); |
jrg@chromium.org | 81e2260 | 2009-02-06 04:04:34 +0900 | [diff] [blame] | 302 | continue; |
| 303 | } else { |
| 304 | NOTREACHED() << "lockf() failed." |
| 305 | << " function:" << function |
| 306 | << " fd:" << mapped_file_ |
| 307 | << " errno:" << errno |
tschmelcher@chromium.org | 90a3f8a | 2009-10-14 03:27:40 +0900 | [diff] [blame] | 308 | << " msg:" << safe_strerror(errno); |
jrg@chromium.org | 81e2260 | 2009-02-06 04:04:34 +0900 | [diff] [blame] | 309 | } |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 310 | } |
| 311 | } |
| 312 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 313 | bool SharedMemory::ShareToProcessCommon(ProcessHandle process, |
| 314 | SharedMemoryHandle *new_handle, |
| 315 | bool close_self) { |
| 316 | const int new_fd = dup(mapped_file_); |
| 317 | DCHECK(new_fd >= 0); |
| 318 | new_handle->fd = new_fd; |
| 319 | new_handle->auto_close = true; |
avi@google.com | 3a20898 | 2008-08-13 06:06:40 +0900 | [diff] [blame] | 320 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 321 | if (close_self) |
| 322 | Close(); |
license.bot | f003cfe | 2008-08-24 09:55:55 +0900 | [diff] [blame] | 323 | |
erg@google.com | 37c078e | 2011-01-11 09:50:59 +0900 | [diff] [blame] | 324 | return true; |
agl@chromium.org | 42bbb99 | 2009-02-12 03:59:20 +0900 | [diff] [blame] | 325 | } |
| 326 | |
brettw@google.com | c60d989 | 2008-11-14 12:25:15 +0900 | [diff] [blame] | 327 | } // namespace base |