OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/memory/shared_memory.h" | 5 #include "base/memory/shared_memory.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <fcntl.h> | 8 #include <fcntl.h> |
9 #include <stddef.h> | 9 #include <stddef.h> |
10 #include <sys/mman.h> | 10 #include <sys/mman.h> |
11 #include <sys/stat.h> | 11 #include <sys/stat.h> |
12 #include <unistd.h> | 12 #include <unistd.h> |
13 | 13 |
14 #include "base/files/file_util.h" | 14 #include "base/files/file_util.h" |
15 #include "base/files/scoped_file.h" | 15 #include "base/files/scoped_file.h" |
16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/memory/shared_memory_helper.h" |
17 #include "base/posix/eintr_wrapper.h" | 18 #include "base/posix/eintr_wrapper.h" |
18 #include "base/posix/safe_strerror.h" | 19 #include "base/posix/safe_strerror.h" |
19 #include "base/process/process_metrics.h" | 20 #include "base/process/process_metrics.h" |
20 #include "base/profiler/scoped_tracker.h" | |
21 #include "base/scoped_generic.h" | 21 #include "base/scoped_generic.h" |
22 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
| 23 #include "base/threading/thread_restrictions.h" |
23 #include "build/build_config.h" | 24 #include "build/build_config.h" |
24 | 25 |
25 #if defined(OS_ANDROID) | 26 #if defined(OS_ANDROID) |
26 #include "base/os_compat_android.h" | 27 #include "base/os_compat_android.h" |
27 #include "third_party/ashmem/ashmem.h" | 28 #include "third_party/ashmem/ashmem.h" |
28 #endif | 29 #endif |
29 | 30 |
30 namespace base { | 31 namespace base { |
31 | 32 |
32 namespace { | |
33 | |
34 struct ScopedPathUnlinkerTraits { | |
35 static FilePath* InvalidValue() { return nullptr; } | |
36 | |
37 static void Free(FilePath* path) { | |
38 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 | |
39 // is fixed. | |
40 tracked_objects::ScopedTracker tracking_profile( | |
41 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
42 "466437 SharedMemory::Create::Unlink")); | |
43 if (unlink(path->value().c_str())) | |
44 PLOG(WARNING) << "unlink"; | |
45 } | |
46 }; | |
47 | |
48 // Unlinks the FilePath when the object is destroyed. | |
49 typedef ScopedGeneric<FilePath*, ScopedPathUnlinkerTraits> ScopedPathUnlinker; | |
50 | |
51 #if !defined(OS_ANDROID) | |
52 // Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated | |
53 // with the fdopened FILE. |readonly_fd| is populated with the opened fd if | |
54 // options.share_read_only is true. |path| is populated with the location of | |
55 // the file before it was unlinked. | |
56 // Returns false if there's an unhandled failure. | |
57 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options, | |
58 ScopedFILE* fp, | |
59 ScopedFD* readonly_fd, | |
60 FilePath* path) { | |
61 // It doesn't make sense to have a open-existing private piece of shmem | |
62 DCHECK(!options.open_existing_deprecated); | |
63 // Q: Why not use the shm_open() etc. APIs? | |
64 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU | |
65 FilePath directory; | |
66 ScopedPathUnlinker path_unlinker; | |
67 if (GetShmemTempDir(options.executable, &directory)) { | |
68 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 | |
69 // is fixed. | |
70 tracked_objects::ScopedTracker tracking_profile( | |
71 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
72 "466437 SharedMemory::Create::OpenTemporaryFile")); | |
73 fp->reset(base::CreateAndOpenTemporaryFileInDir(directory, path)); | |
74 | |
75 // Deleting the file prevents anyone else from mapping it in (making it | |
76 // private), and prevents the need for cleanup (once the last fd is | |
77 // closed, it is truly freed). | |
78 if (*fp) | |
79 path_unlinker.reset(path); | |
80 } | |
81 | |
82 if (*fp) { | |
83 if (options.share_read_only) { | |
84 // TODO(erikchen): Remove ScopedTracker below once | |
85 // http://crbug.com/466437 is fixed. | |
86 tracked_objects::ScopedTracker tracking_profile( | |
87 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
88 "466437 SharedMemory::Create::OpenReadonly")); | |
89 // Also open as readonly so that we can ShareReadOnlyToProcess. | |
90 readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY))); | |
91 if (!readonly_fd->is_valid()) { | |
92 DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed"; | |
93 fp->reset(); | |
94 return false; | |
95 } | |
96 } | |
97 } | |
98 return true; | |
99 } | |
100 #endif // !defined(OS_ANDROID) | |
101 } | |
102 | |
103 SharedMemory::SharedMemory() | 33 SharedMemory::SharedMemory() |
104 : mapped_file_(-1), | 34 : mapped_file_(-1), |
105 readonly_mapped_file_(-1), | 35 readonly_mapped_file_(-1), |
106 mapped_size_(0), | 36 mapped_size_(0), |
107 memory_(NULL), | 37 memory_(NULL), |
108 read_only_(false), | 38 read_only_(false), |
109 requested_size_(0) { | 39 requested_size_(0) { |
110 } | 40 } |
111 | 41 |
112 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) | 42 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
178 return true; | 108 return true; |
179 } | 109 } |
180 | 110 |
181 // Chromium mostly only uses the unique/private shmem as specified by | 111 // Chromium mostly only uses the unique/private shmem as specified by |
182 // "name == L"". The exception is in the StatsTable. | 112 // "name == L"". The exception is in the StatsTable. |
183 // TODO(jrg): there is no way to "clean up" all unused named shmem if | 113 // TODO(jrg): there is no way to "clean up" all unused named shmem if |
184 // we restart from a crash. (That isn't a new problem, but it is a problem.) | 114 // we restart from a crash. (That isn't a new problem, but it is a problem.) |
185 // In case we want to delete it later, it may be useful to save the value | 115 // In case we want to delete it later, it may be useful to save the value |
186 // of mem_filename after FilePathForMemoryName(). | 116 // of mem_filename after FilePathForMemoryName(). |
187 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { | 117 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { |
188 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 | |
189 // is fixed. | |
190 tracked_objects::ScopedTracker tracking_profile1( | |
191 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
192 "466437 SharedMemory::Create::Start")); | |
193 DCHECK_EQ(-1, mapped_file_); | 118 DCHECK_EQ(-1, mapped_file_); |
194 if (options.size == 0) return false; | 119 if (options.size == 0) return false; |
195 | 120 |
196 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) | 121 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) |
197 return false; | 122 return false; |
198 | 123 |
199 // This function theoretically can block on the disk, but realistically | 124 // This function theoretically can block on the disk, but realistically |
200 // the temporary files we create will just go into the buffer cache | 125 // the temporary files we create will just go into the buffer cache |
201 // and be deleted before they ever make it out to disk. | 126 // and be deleted before they ever make it out to disk. |
202 base::ThreadRestrictions::ScopedAllowIO allow_io; | 127 base::ThreadRestrictions::ScopedAllowIO allow_io; |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
285 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { | 210 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { |
286 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); | 211 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); |
287 if (dir.value() == "/dev/shm") { | 212 if (dir.value() == "/dev/shm") { |
288 LOG(FATAL) << "This is frequently caused by incorrect permissions on " | 213 LOG(FATAL) << "This is frequently caused by incorrect permissions on " |
289 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; | 214 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; |
290 } | 215 } |
291 } | 216 } |
292 return false; | 217 return false; |
293 } | 218 } |
294 | 219 |
295 return PrepareMapFile(std::move(fp), std::move(readonly_fd)); | 220 return PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file_, |
| 221 &readonly_mapped_file_); |
296 } | 222 } |
297 | 223 |
298 // Our current implementation of shmem is with mmap()ing of files. | 224 // Our current implementation of shmem is with mmap()ing of files. |
299 // These files need to be deleted explicitly. | 225 // These files need to be deleted explicitly. |
300 // In practice this call is only needed for unit tests. | 226 // In practice this call is only needed for unit tests. |
301 bool SharedMemory::Delete(const std::string& name) { | 227 bool SharedMemory::Delete(const std::string& name) { |
302 FilePath path; | 228 FilePath path; |
303 if (!FilePathForMemoryName(name, &path)) | 229 if (!FilePathForMemoryName(name, &path)) |
304 return false; | 230 return false; |
305 | 231 |
(...skipping 11 matching lines...) Expand all Loading... |
317 | 243 |
318 read_only_ = read_only; | 244 read_only_ = read_only; |
319 | 245 |
320 const char *mode = read_only ? "r" : "r+"; | 246 const char *mode = read_only ? "r" : "r+"; |
321 ScopedFILE fp(base::OpenFile(path, mode)); | 247 ScopedFILE fp(base::OpenFile(path, mode)); |
322 ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); | 248 ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); |
323 if (!readonly_fd.is_valid()) { | 249 if (!readonly_fd.is_valid()) { |
324 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; | 250 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; |
325 return false; | 251 return false; |
326 } | 252 } |
327 return PrepareMapFile(std::move(fp), std::move(readonly_fd)); | 253 return PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file_, |
| 254 &readonly_mapped_file_); |
328 } | 255 } |
329 #endif // !defined(OS_ANDROID) | 256 #endif // !defined(OS_ANDROID) |
330 | 257 |
331 bool SharedMemory::MapAt(off_t offset, size_t bytes) { | 258 bool SharedMemory::MapAt(off_t offset, size_t bytes) { |
332 if (mapped_file_ == -1) | 259 if (mapped_file_ == -1) |
333 return false; | 260 return false; |
334 | 261 |
335 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) | 262 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) |
336 return false; | 263 return false; |
337 | 264 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 mapped_file_ = -1; | 321 mapped_file_ = -1; |
395 } | 322 } |
396 if (readonly_mapped_file_ > 0) { | 323 if (readonly_mapped_file_ > 0) { |
397 if (IGNORE_EINTR(close(readonly_mapped_file_)) < 0) | 324 if (IGNORE_EINTR(close(readonly_mapped_file_)) < 0) |
398 PLOG(ERROR) << "close"; | 325 PLOG(ERROR) << "close"; |
399 readonly_mapped_file_ = -1; | 326 readonly_mapped_file_ = -1; |
400 } | 327 } |
401 } | 328 } |
402 | 329 |
403 #if !defined(OS_ANDROID) | 330 #if !defined(OS_ANDROID) |
404 bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { | |
405 DCHECK_EQ(-1, mapped_file_); | |
406 DCHECK_EQ(-1, readonly_mapped_file_); | |
407 if (fp == NULL) | |
408 return false; | |
409 | |
410 // This function theoretically can block on the disk, but realistically | |
411 // the temporary files we create will just go into the buffer cache | |
412 // and be deleted before they ever make it out to disk. | |
413 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
414 | |
415 struct stat st = {}; | |
416 if (fstat(fileno(fp.get()), &st)) | |
417 NOTREACHED(); | |
418 if (readonly_fd.is_valid()) { | |
419 struct stat readonly_st = {}; | |
420 if (fstat(readonly_fd.get(), &readonly_st)) | |
421 NOTREACHED(); | |
422 if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { | |
423 LOG(ERROR) << "writable and read-only inodes don't match; bailing"; | |
424 return false; | |
425 } | |
426 } | |
427 | |
428 mapped_file_ = HANDLE_EINTR(dup(fileno(fp.get()))); | |
429 if (mapped_file_ == -1) { | |
430 if (errno == EMFILE) { | |
431 LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; | |
432 return false; | |
433 } else { | |
434 NOTREACHED() << "Call to dup failed, errno=" << errno; | |
435 } | |
436 } | |
437 readonly_mapped_file_ = readonly_fd.release(); | |
438 | |
439 return true; | |
440 } | |
441 | |
442 // For the given shmem named |mem_name|, return a filename to mmap() | 331 // For the given shmem named |mem_name|, return a filename to mmap() |
443 // (and possibly create). Modifies |filename|. Return false on | 332 // (and possibly create). Modifies |filename|. Return false on |
444 // error, or true of we are happy. | 333 // error, or true of we are happy. |
445 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, | 334 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, |
446 FilePath* path) { | 335 FilePath* path) { |
447 // mem_name will be used for a filename; make sure it doesn't | 336 // mem_name will be used for a filename; make sure it doesn't |
448 // contain anything which will confuse us. | 337 // contain anything which will confuse us. |
449 DCHECK_EQ(std::string::npos, mem_name.find('/')); | 338 DCHECK_EQ(std::string::npos, mem_name.find('/')); |
450 DCHECK_EQ(std::string::npos, mem_name.find('\0')); | 339 DCHECK_EQ(std::string::npos, mem_name.find('\0')); |
451 | 340 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 | 384 |
496 if (close_self) { | 385 if (close_self) { |
497 Unmap(); | 386 Unmap(); |
498 Close(); | 387 Close(); |
499 } | 388 } |
500 | 389 |
501 return true; | 390 return true; |
502 } | 391 } |
503 | 392 |
504 } // namespace base | 393 } // namespace base |
OLD | NEW |