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 <sys/mman.h> | 9 #include <sys/mman.h> |
10 #include <sys/stat.h> | 10 #include <sys/stat.h> |
(...skipping 12 matching lines...) Expand all Loading... | |
23 | 23 |
24 #if defined(OS_MACOSX) | 24 #if defined(OS_MACOSX) |
25 #include "base/mac/foundation_util.h" | 25 #include "base/mac/foundation_util.h" |
26 #endif // OS_MACOSX | 26 #endif // OS_MACOSX |
27 | 27 |
28 #if defined(OS_ANDROID) | 28 #if defined(OS_ANDROID) |
29 #include "base/os_compat_android.h" | 29 #include "base/os_compat_android.h" |
30 #include "third_party/ashmem/ashmem.h" | 30 #include "third_party/ashmem/ashmem.h" |
31 #endif | 31 #endif |
32 | 32 |
33 using file_util::ScopedFD; | |
34 using file_util::ScopedFILE; | |
35 | |
33 namespace base { | 36 namespace base { |
34 | 37 |
35 namespace { | 38 namespace { |
36 | 39 |
37 LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER; | 40 LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER; |
38 | 41 |
39 } | 42 } |
40 | 43 |
41 SharedMemory::SharedMemory() | 44 SharedMemory::SharedMemory() |
42 : mapped_file_(-1), | 45 : mapped_file_(-1), |
46 readonly_mapped_file_(-1), | |
43 inode_(0), | 47 inode_(0), |
44 mapped_size_(0), | 48 mapped_size_(0), |
45 memory_(NULL), | 49 memory_(NULL), |
46 read_only_(false), | 50 read_only_(false), |
47 requested_size_(0) { | 51 requested_size_(0) { |
48 } | 52 } |
49 | 53 |
50 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) | 54 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) |
51 : mapped_file_(handle.fd), | 55 : mapped_file_(handle.fd), |
56 readonly_mapped_file_(-1), | |
52 inode_(0), | 57 inode_(0), |
53 mapped_size_(0), | 58 mapped_size_(0), |
54 memory_(NULL), | 59 memory_(NULL), |
55 read_only_(read_only), | 60 read_only_(read_only), |
56 requested_size_(0) { | 61 requested_size_(0) { |
57 struct stat st; | 62 struct stat st; |
58 if (fstat(handle.fd, &st) == 0) { | 63 if (fstat(handle.fd, &st) == 0) { |
59 // If fstat fails, then the file descriptor is invalid and we'll learn this | 64 // If fstat fails, then the file descriptor is invalid and we'll learn this |
60 // fact when Map() fails. | 65 // fact when Map() fails. |
61 inode_ = st.st_ino; | 66 inode_ = st.st_ino; |
62 } | 67 } |
63 } | 68 } |
64 | 69 |
65 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, | 70 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, |
66 ProcessHandle process) | 71 ProcessHandle process) |
67 : mapped_file_(handle.fd), | 72 : mapped_file_(handle.fd), |
73 readonly_mapped_file_(-1), | |
68 inode_(0), | 74 inode_(0), |
69 mapped_size_(0), | 75 mapped_size_(0), |
70 memory_(NULL), | 76 memory_(NULL), |
71 read_only_(read_only), | 77 read_only_(read_only), |
72 requested_size_(0) { | 78 requested_size_(0) { |
73 // We don't handle this case yet (note the ignored parameter); let's die if | 79 // We don't handle this case yet (note the ignored parameter); let's die if |
74 // someone comes calling. | 80 // someone comes calling. |
75 NOTREACHED(); | 81 NOTREACHED(); |
76 } | 82 } |
77 | 83 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
117 if (options.size == 0) return false; | 123 if (options.size == 0) return false; |
118 | 124 |
119 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) | 125 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) |
120 return false; | 126 return false; |
121 | 127 |
122 // This function theoretically can block on the disk, but realistically | 128 // This function theoretically can block on the disk, but realistically |
123 // the temporary files we create will just go into the buffer cache | 129 // the temporary files we create will just go into the buffer cache |
124 // and be deleted before they ever make it out to disk. | 130 // and be deleted before they ever make it out to disk. |
125 base::ThreadRestrictions::ScopedAllowIO allow_io; | 131 base::ThreadRestrictions::ScopedAllowIO allow_io; |
126 | 132 |
127 FILE *fp; | 133 ScopedFILE fp; |
128 bool fix_size = true; | 134 bool fix_size = true; |
135 int readonly_fd_storage = -1; | |
136 ScopedFD readonly_fd(&readonly_fd_storage); | |
129 | 137 |
130 FilePath path; | 138 FilePath path; |
131 if (options.name == NULL || options.name->empty()) { | 139 if (options.name == NULL || options.name->empty()) { |
132 // It doesn't make sense to have a open-existing private piece of shmem | 140 // It doesn't make sense to have a open-existing private piece of shmem |
133 DCHECK(!options.open_existing); | 141 DCHECK(!options.open_existing); |
134 // Q: Why not use the shm_open() etc. APIs? | 142 // Q: Why not use the shm_open() etc. APIs? |
135 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU | 143 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU |
136 fp = file_util::CreateAndOpenTemporaryShmemFile(&path, options.executable); | 144 fp.reset( |
145 file_util::CreateAndOpenTemporaryShmemFile(&path, options.executable)); | |
137 | 146 |
138 // Deleting the file prevents anyone else from mapping it in (making it | |
139 // private), and prevents the need for cleanup (once the last fd is closed, | |
140 // it is truly freed). | |
141 if (fp) { | 147 if (fp) { |
148 // Also open as readonly so that we can ShareReadOnlyToProcess. | |
149 *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); | |
150 if (*readonly_fd < 0) { | |
151 DPLOG(ERROR) << "open(\"" << path.value().c_str() | |
152 << "\", O_RDONLY) failed"; | |
153 fp.reset(); | |
154 } | |
155 // Deleting the file prevents anyone else from mapping it in (making it | |
156 // private), and prevents the need for cleanup (once the last fd is | |
157 // closed, | |
Mark Mentovai
2013/11/20 14:21:41
Reflow this comment, this word doesn’t need to be
Jeffrey Yasskin
2013/11/20 18:02:09
Done.
| |
158 // it is truly freed). | |
142 if (unlink(path.value().c_str())) | 159 if (unlink(path.value().c_str())) |
143 PLOG(WARNING) << "unlink"; | 160 PLOG(WARNING) << "unlink"; |
144 } | 161 } |
145 } else { | 162 } else { |
146 if (!FilePathForMemoryName(*options.name, &path)) | 163 if (!FilePathForMemoryName(*options.name, &path)) |
147 return false; | 164 return false; |
148 | 165 |
149 // Make sure that the file is opened without any permission | 166 // Make sure that the file is opened without any permission |
150 // to other users on the system. | 167 // to other users on the system. |
151 const mode_t kOwnerOnly = S_IRUSR | S_IWUSR; | 168 const mode_t kOwnerOnly = S_IRUSR | S_IWUSR; |
(...skipping 23 matching lines...) Expand all Loading... | |
175 sb.st_uid != effective_uid)) { | 192 sb.st_uid != effective_uid)) { |
176 LOG(ERROR) << | 193 LOG(ERROR) << |
177 "Invalid owner when opening existing shared memory file."; | 194 "Invalid owner when opening existing shared memory file."; |
178 HANDLE_EINTR(close(fd)); | 195 HANDLE_EINTR(close(fd)); |
179 return false; | 196 return false; |
180 } | 197 } |
181 | 198 |
182 // An existing file was opened, so its size should not be fixed. | 199 // An existing file was opened, so its size should not be fixed. |
183 fix_size = false; | 200 fix_size = false; |
184 } | 201 } |
185 fp = NULL; | 202 |
203 // Also open as readonly so that we can ShareReadOnlyToProcess. | |
204 *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); | |
205 if (*readonly_fd < 0) { | |
206 DPLOG(ERROR) << "open(\"" << path.value().c_str() | |
207 << "\", O_RDONLY) failed"; | |
208 HANDLE_EINTR(close(fd)); | |
209 fd = -1; | |
210 } | |
186 if (fd >= 0) { | 211 if (fd >= 0) { |
187 // "a+" is always appropriate: if it's a new file, a+ is similar to w+. | 212 // "a+" is always appropriate: if it's a new file, a+ is similar to w+. |
188 fp = fdopen(fd, "a+"); | 213 fp.reset(fdopen(fd, "a+")); |
189 } | 214 } |
190 } | 215 } |
191 if (fp && fix_size) { | 216 if (fp && fix_size) { |
192 // Get current size. | 217 // Get current size. |
193 struct stat stat; | 218 struct stat stat; |
194 if (fstat(fileno(fp), &stat) != 0) { | 219 if (fstat(fileno(fp.get()), &stat) != 0) |
195 file_util::CloseFile(fp); | |
196 return false; | 220 return false; |
197 } | |
198 const size_t current_size = stat.st_size; | 221 const size_t current_size = stat.st_size; |
199 if (current_size != options.size) { | 222 if (current_size != options.size) { |
200 if (HANDLE_EINTR(ftruncate(fileno(fp), options.size)) != 0) { | 223 if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0) |
201 file_util::CloseFile(fp); | |
202 return false; | 224 return false; |
203 } | |
204 } | 225 } |
205 requested_size_ = options.size; | 226 requested_size_ = options.size; |
206 } | 227 } |
207 if (fp == NULL) { | 228 if (fp == NULL) { |
208 #if !defined(OS_MACOSX) | 229 #if !defined(OS_MACOSX) |
209 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; | 230 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; |
210 FilePath dir = path.DirName(); | 231 FilePath dir = path.DirName(); |
211 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { | 232 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { |
212 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); | 233 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); |
213 if (dir.value() == "/dev/shm") { | 234 if (dir.value() == "/dev/shm") { |
214 LOG(FATAL) << "This is frequently caused by incorrect permissions on " | 235 LOG(FATAL) << "This is frequently caused by incorrect permissions on " |
215 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; | 236 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; |
216 } | 237 } |
217 } | 238 } |
218 #else | 239 #else |
219 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; | 240 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; |
220 #endif | 241 #endif |
221 return false; | 242 return false; |
222 } | 243 } |
223 | 244 |
224 return PrepareMapFile(fp); | 245 return PrepareMapFile(fp.Pass(), readonly_fd.Pass()); |
225 } | 246 } |
226 | 247 |
227 // Our current implementation of shmem is with mmap()ing of files. | 248 // Our current implementation of shmem is with mmap()ing of files. |
228 // These files need to be deleted explicitly. | 249 // These files need to be deleted explicitly. |
229 // In practice this call is only needed for unit tests. | 250 // In practice this call is only needed for unit tests. |
230 bool SharedMemory::Delete(const std::string& name) { | 251 bool SharedMemory::Delete(const std::string& name) { |
231 FilePath path; | 252 FilePath path; |
232 if (!FilePathForMemoryName(name, &path)) | 253 if (!FilePathForMemoryName(name, &path)) |
233 return false; | 254 return false; |
234 | 255 |
235 if (PathExists(path)) | 256 if (PathExists(path)) |
236 return base::DeleteFile(path, false); | 257 return base::DeleteFile(path, false); |
237 | 258 |
238 // Doesn't exist, so success. | 259 // Doesn't exist, so success. |
239 return true; | 260 return true; |
240 } | 261 } |
241 | 262 |
242 bool SharedMemory::Open(const std::string& name, bool read_only) { | 263 bool SharedMemory::Open(const std::string& name, bool read_only) { |
243 FilePath path; | 264 FilePath path; |
244 if (!FilePathForMemoryName(name, &path)) | 265 if (!FilePathForMemoryName(name, &path)) |
245 return false; | 266 return false; |
246 | 267 |
247 read_only_ = read_only; | 268 read_only_ = read_only; |
248 | 269 |
249 const char *mode = read_only ? "r" : "r+"; | 270 const char *mode = read_only ? "r" : "r+"; |
250 FILE *fp = file_util::OpenFile(path, mode); | 271 ScopedFILE fp(file_util::OpenFile(path, mode)); |
251 return PrepareMapFile(fp); | 272 int readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); |
273 if (readonly_fd < 0) { | |
274 DPLOG(ERROR) << "open(\"" << path.value().c_str() << "\", O_RDONLY) failed"; | |
275 } | |
276 return PrepareMapFile(fp.Pass(), ScopedFD(&readonly_fd)); | |
Mark Mentovai
2013/11/20 14:21:41
Move the ScopedFD up (like you did above) to bette
Jeffrey Yasskin
2013/11/20 18:02:09
Done.
| |
252 } | 277 } |
253 | 278 |
254 #endif // !defined(OS_ANDROID) | 279 #endif // !defined(OS_ANDROID) |
255 | 280 |
256 bool SharedMemory::MapAt(off_t offset, size_t bytes) { | 281 bool SharedMemory::MapAt(off_t offset, size_t bytes) { |
257 if (mapped_file_ == -1) | 282 if (mapped_file_ == -1) |
258 return false; | 283 return false; |
259 | 284 |
260 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) | 285 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) |
261 return false; | 286 return false; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
302 } | 327 } |
303 | 328 |
304 void SharedMemory::Close() { | 329 void SharedMemory::Close() { |
305 Unmap(); | 330 Unmap(); |
306 | 331 |
307 if (mapped_file_ > 0) { | 332 if (mapped_file_ > 0) { |
308 if (HANDLE_EINTR(close(mapped_file_)) < 0) | 333 if (HANDLE_EINTR(close(mapped_file_)) < 0) |
309 PLOG(ERROR) << "close"; | 334 PLOG(ERROR) << "close"; |
310 mapped_file_ = -1; | 335 mapped_file_ = -1; |
311 } | 336 } |
337 if (readonly_mapped_file_ > 0) { | |
338 if (HANDLE_EINTR(close(readonly_mapped_file_)) < 0) | |
Mark Mentovai
2013/11/20 14:21:41
I have determined that it’s never correct to HANDL
Jeffrey Yasskin
2013/11/20 18:02:09
Oh, ok; done. It was very slightly easier to unwra
| |
339 PLOG(ERROR) << "close"; | |
340 readonly_mapped_file_ = -1; | |
341 } | |
312 } | 342 } |
313 | 343 |
314 void SharedMemory::Lock() { | 344 void SharedMemory::Lock() { |
315 g_thread_lock_.Get().Acquire(); | 345 g_thread_lock_.Get().Acquire(); |
316 LockOrUnlockCommon(F_LOCK); | 346 LockOrUnlockCommon(F_LOCK); |
317 } | 347 } |
318 | 348 |
319 void SharedMemory::Unlock() { | 349 void SharedMemory::Unlock() { |
320 LockOrUnlockCommon(F_ULOCK); | 350 LockOrUnlockCommon(F_ULOCK); |
321 g_thread_lock_.Get().Release(); | 351 g_thread_lock_.Get().Release(); |
322 } | 352 } |
323 | 353 |
324 #if !defined(OS_ANDROID) | 354 #if !defined(OS_ANDROID) |
325 bool SharedMemory::PrepareMapFile(FILE *fp) { | 355 bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { |
326 DCHECK_EQ(-1, mapped_file_); | 356 DCHECK_EQ(-1, mapped_file_); |
327 if (fp == NULL) return false; | 357 DCHECK_EQ(-1, readonly_mapped_file_); |
358 if (fp == NULL || *readonly_fd < 0) return false; | |
328 | 359 |
329 // This function theoretically can block on the disk, but realistically | 360 // This function theoretically can block on the disk, but realistically |
330 // the temporary files we create will just go into the buffer cache | 361 // the temporary files we create will just go into the buffer cache |
331 // and be deleted before they ever make it out to disk. | 362 // and be deleted before they ever make it out to disk. |
332 base::ThreadRestrictions::ScopedAllowIO allow_io; | 363 base::ThreadRestrictions::ScopedAllowIO allow_io; |
333 | 364 |
334 file_util::ScopedFILE file_closer(fp); | 365 struct stat st; |
366 struct stat readonly_st; | |
367 if (fstat(fileno(fp.get()), &st)) | |
368 NOTREACHED(); | |
369 if (fstat(*readonly_fd, &readonly_st)) | |
370 NOTREACHED(); | |
371 if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { | |
372 LOG(ERROR) << "writable and read-only inodes don't match; bailing"; | |
373 return false; | |
374 } | |
335 | 375 |
336 mapped_file_ = dup(fileno(fp)); | 376 mapped_file_ = dup(fileno(fp.get())); |
337 if (mapped_file_ == -1) { | 377 if (mapped_file_ == -1) { |
338 if (errno == EMFILE) { | 378 if (errno == EMFILE) { |
339 LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; | 379 LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; |
340 return false; | 380 return false; |
341 } else { | 381 } else { |
342 NOTREACHED() << "Call to dup failed, errno=" << errno; | 382 NOTREACHED() << "Call to dup failed, errno=" << errno; |
343 } | 383 } |
344 } | 384 } |
345 | |
346 struct stat st; | |
347 if (fstat(mapped_file_, &st)) | |
348 NOTREACHED(); | |
349 inode_ = st.st_ino; | 385 inode_ = st.st_ino; |
386 readonly_mapped_file_ = *readonly_fd.release(); | |
350 | 387 |
351 return true; | 388 return true; |
352 } | 389 } |
353 #endif | 390 #endif |
354 | 391 |
355 // For the given shmem named |mem_name|, return a filename to mmap() | 392 // For the given shmem named |mem_name|, return a filename to mmap() |
356 // (and possibly create). Modifies |filename|. Return false on | 393 // (and possibly create). Modifies |filename|. Return false on |
357 // error, or true of we are happy. | 394 // error, or true of we are happy. |
358 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, | 395 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, |
359 FilePath* path) { | 396 FilePath* path) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
392 NOTREACHED() << "lockf() failed." | 429 NOTREACHED() << "lockf() failed." |
393 << " function:" << function | 430 << " function:" << function |
394 << " fd:" << mapped_file_ | 431 << " fd:" << mapped_file_ |
395 << " errno:" << errno | 432 << " errno:" << errno |
396 << " msg:" << safe_strerror(errno); | 433 << " msg:" << safe_strerror(errno); |
397 } | 434 } |
398 } | 435 } |
399 } | 436 } |
400 | 437 |
401 bool SharedMemory::ShareToProcessCommon(ProcessHandle process, | 438 bool SharedMemory::ShareToProcessCommon(ProcessHandle process, |
402 SharedMemoryHandle *new_handle, | 439 SharedMemoryHandle* new_handle, |
403 bool close_self) { | 440 bool close_self, |
404 const int new_fd = dup(mapped_file_); | 441 ShareMode share_mode) { |
442 int handle_to_dup = -1; | |
443 switch(share_mode) { | |
444 case SHARE_CURRENT_MODE: | |
445 handle_to_dup = mapped_file_; | |
446 break; | |
447 case SHARE_READONLY: | |
448 // We could imagine re-opening the file from /dev/fd, but that can't make | |
449 // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10 | |
450 CHECK(readonly_mapped_file_ >= 0); | |
451 handle_to_dup = readonly_mapped_file_; | |
452 break; | |
453 } | |
454 | |
455 const int new_fd = dup(handle_to_dup); | |
405 if (new_fd < 0) { | 456 if (new_fd < 0) { |
406 DPLOG(ERROR) << "dup() failed."; | 457 DPLOG(ERROR) << "dup() failed."; |
407 return false; | 458 return false; |
408 } | 459 } |
409 | 460 |
410 new_handle->fd = new_fd; | 461 new_handle->fd = new_fd; |
411 new_handle->auto_close = true; | 462 new_handle->auto_close = true; |
412 | 463 |
413 if (close_self) | 464 if (close_self) |
414 Close(); | 465 Close(); |
415 | 466 |
416 return true; | 467 return true; |
417 } | 468 } |
418 | 469 |
419 } // namespace base | 470 } // namespace base |
OLD | NEW |