Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(351)

Side by Side Diff: base/memory/shared_memory_posix.cc

Issue 27265002: Implement SharedMemory::NewAnonymousReadOnly(contents). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Mark's comments Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/memory/shared_memory_nacl.cc ('k') | base/memory/shared_memory_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
78 SharedMemory::~SharedMemory() { 84 SharedMemory::~SharedMemory() {
79 Close(); 85 Close();
80 } 86 }
81 87
82 // static 88 // static
83 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { 89 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
84 return handle.fd >= 0; 90 return handle.fd >= 0;
85 } 91 }
86 92
87 // static 93 // static
88 SharedMemoryHandle SharedMemory::NULLHandle() { 94 SharedMemoryHandle SharedMemory::NULLHandle() {
89 return SharedMemoryHandle(); 95 return SharedMemoryHandle();
90 } 96 }
91 97
92 // static 98 // static
93 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { 99 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
94 DCHECK_GE(handle.fd, 0); 100 DCHECK_GE(handle.fd, 0);
95 if (HANDLE_EINTR(close(handle.fd)) < 0) 101 if (close(handle.fd) < 0)
96 DPLOG(ERROR) << "close"; 102 DPLOG(ERROR) << "close";
97 } 103 }
98 104
99 // static 105 // static
100 size_t SharedMemory::GetHandleLimit() { 106 size_t SharedMemory::GetHandleLimit() {
101 return base::GetMaxFds(); 107 return base::GetMaxFds();
102 } 108 }
103 109
104 bool SharedMemory::CreateAndMapAnonymous(size_t size) { 110 bool SharedMemory::CreateAndMapAnonymous(size_t size) {
105 return CreateAnonymous(size) && Map(size); 111 return CreateAnonymous(size) && Map(size);
(...skipping 11 matching lines...) Expand all
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() << "\", O_RDONLY) failed";
152 fp.reset();
153 }
154 // Deleting the file prevents anyone else from mapping it in (making it
155 // private), and prevents the need for cleanup (once the last fd is
156 // closed, it is truly freed).
142 if (unlink(path.value().c_str())) 157 if (unlink(path.value().c_str()))
143 PLOG(WARNING) << "unlink"; 158 PLOG(WARNING) << "unlink";
144 } 159 }
145 } else { 160 } else {
146 if (!FilePathForMemoryName(*options.name, &path)) 161 if (!FilePathForMemoryName(*options.name, &path))
147 return false; 162 return false;
148 163
149 // Make sure that the file is opened without any permission 164 // Make sure that the file is opened without any permission
150 // to other users on the system. 165 // to other users on the system.
151 const mode_t kOwnerOnly = S_IRUSR | S_IWUSR; 166 const mode_t kOwnerOnly = S_IRUSR | S_IWUSR;
(...skipping 16 matching lines...) Expand all
168 // If uid != euid, then a more complex permission model is used and this 183 // If uid != euid, then a more complex permission model is used and this
169 // API is not appropriate. 184 // API is not appropriate.
170 const uid_t real_uid = getuid(); 185 const uid_t real_uid = getuid();
171 const uid_t effective_uid = geteuid(); 186 const uid_t effective_uid = geteuid();
172 struct stat sb; 187 struct stat sb;
173 if (fd >= 0 && 188 if (fd >= 0 &&
174 (fstat(fd, &sb) != 0 || sb.st_uid != real_uid || 189 (fstat(fd, &sb) != 0 || sb.st_uid != real_uid ||
175 sb.st_uid != effective_uid)) { 190 sb.st_uid != effective_uid)) {
176 LOG(ERROR) << 191 LOG(ERROR) <<
177 "Invalid owner when opening existing shared memory file."; 192 "Invalid owner when opening existing shared memory file.";
178 HANDLE_EINTR(close(fd)); 193 close(fd);
179 return false; 194 return false;
180 } 195 }
181 196
182 // An existing file was opened, so its size should not be fixed. 197 // An existing file was opened, so its size should not be fixed.
183 fix_size = false; 198 fix_size = false;
184 } 199 }
185 fp = NULL; 200
201 // Also open as readonly so that we can ShareReadOnlyToProcess.
202 *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
203 if (*readonly_fd < 0) {
204 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
205 close(fd);
206 fd = -1;
207 }
186 if (fd >= 0) { 208 if (fd >= 0) {
187 // "a+" is always appropriate: if it's a new file, a+ is similar to w+. 209 // "a+" is always appropriate: if it's a new file, a+ is similar to w+.
188 fp = fdopen(fd, "a+"); 210 fp.reset(fdopen(fd, "a+"));
189 } 211 }
190 } 212 }
191 if (fp && fix_size) { 213 if (fp && fix_size) {
192 // Get current size. 214 // Get current size.
193 struct stat stat; 215 struct stat stat;
194 if (fstat(fileno(fp), &stat) != 0) { 216 if (fstat(fileno(fp.get()), &stat) != 0)
195 file_util::CloseFile(fp);
196 return false; 217 return false;
197 }
198 const size_t current_size = stat.st_size; 218 const size_t current_size = stat.st_size;
199 if (current_size != options.size) { 219 if (current_size != options.size) {
200 if (HANDLE_EINTR(ftruncate(fileno(fp), options.size)) != 0) { 220 if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0)
201 file_util::CloseFile(fp);
202 return false; 221 return false;
203 }
204 } 222 }
205 requested_size_ = options.size; 223 requested_size_ = options.size;
206 } 224 }
207 if (fp == NULL) { 225 if (fp == NULL) {
208 #if !defined(OS_MACOSX) 226 #if !defined(OS_MACOSX)
209 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; 227 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
210 FilePath dir = path.DirName(); 228 FilePath dir = path.DirName();
211 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { 229 if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
212 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); 230 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
213 if (dir.value() == "/dev/shm") { 231 if (dir.value() == "/dev/shm") {
214 LOG(FATAL) << "This is frequently caused by incorrect permissions on " 232 LOG(FATAL) << "This is frequently caused by incorrect permissions on "
215 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; 233 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
216 } 234 }
217 } 235 }
218 #else 236 #else
219 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; 237 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
220 #endif 238 #endif
221 return false; 239 return false;
222 } 240 }
223 241
224 return PrepareMapFile(fp); 242 return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
225 } 243 }
226 244
227 // Our current implementation of shmem is with mmap()ing of files. 245 // Our current implementation of shmem is with mmap()ing of files.
228 // These files need to be deleted explicitly. 246 // These files need to be deleted explicitly.
229 // In practice this call is only needed for unit tests. 247 // In practice this call is only needed for unit tests.
230 bool SharedMemory::Delete(const std::string& name) { 248 bool SharedMemory::Delete(const std::string& name) {
231 FilePath path; 249 FilePath path;
232 if (!FilePathForMemoryName(name, &path)) 250 if (!FilePathForMemoryName(name, &path))
233 return false; 251 return false;
234 252
235 if (PathExists(path)) 253 if (PathExists(path))
236 return base::DeleteFile(path, false); 254 return base::DeleteFile(path, false);
237 255
238 // Doesn't exist, so success. 256 // Doesn't exist, so success.
239 return true; 257 return true;
240 } 258 }
241 259
242 bool SharedMemory::Open(const std::string& name, bool read_only) { 260 bool SharedMemory::Open(const std::string& name, bool read_only) {
243 FilePath path; 261 FilePath path;
244 if (!FilePathForMemoryName(name, &path)) 262 if (!FilePathForMemoryName(name, &path))
245 return false; 263 return false;
246 264
247 read_only_ = read_only; 265 read_only_ = read_only;
248 266
249 const char *mode = read_only ? "r" : "r+"; 267 const char *mode = read_only ? "r" : "r+";
250 FILE *fp = file_util::OpenFile(path, mode); 268 ScopedFILE fp(file_util::OpenFile(path, mode));
251 return PrepareMapFile(fp); 269 int readonly_fd_storage = -1;
270 ScopedFD readonly_fd(&readonly_fd_storage);
271 *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
272 if (*readonly_fd < 0) {
273 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
274 }
275 return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
252 } 276 }
253 277
254 #endif // !defined(OS_ANDROID) 278 #endif // !defined(OS_ANDROID)
255 279
256 bool SharedMemory::MapAt(off_t offset, size_t bytes) { 280 bool SharedMemory::MapAt(off_t offset, size_t bytes) {
257 if (mapped_file_ == -1) 281 if (mapped_file_ == -1)
258 return false; 282 return false;
259 283
260 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) 284 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
261 return false; 285 return false;
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 } 322 }
299 323
300 SharedMemoryHandle SharedMemory::handle() const { 324 SharedMemoryHandle SharedMemory::handle() const {
301 return FileDescriptor(mapped_file_, false); 325 return FileDescriptor(mapped_file_, false);
302 } 326 }
303 327
304 void SharedMemory::Close() { 328 void SharedMemory::Close() {
305 Unmap(); 329 Unmap();
306 330
307 if (mapped_file_ > 0) { 331 if (mapped_file_ > 0) {
308 if (HANDLE_EINTR(close(mapped_file_)) < 0) 332 if (close(mapped_file_) < 0)
309 PLOG(ERROR) << "close"; 333 PLOG(ERROR) << "close";
310 mapped_file_ = -1; 334 mapped_file_ = -1;
311 } 335 }
336 if (readonly_mapped_file_ > 0) {
337 if (close(readonly_mapped_file_) < 0)
338 PLOG(ERROR) << "close";
339 readonly_mapped_file_ = -1;
340 }
312 } 341 }
313 342
314 void SharedMemory::Lock() { 343 void SharedMemory::Lock() {
315 g_thread_lock_.Get().Acquire(); 344 g_thread_lock_.Get().Acquire();
316 LockOrUnlockCommon(F_LOCK); 345 LockOrUnlockCommon(F_LOCK);
317 } 346 }
318 347
319 void SharedMemory::Unlock() { 348 void SharedMemory::Unlock() {
320 LockOrUnlockCommon(F_ULOCK); 349 LockOrUnlockCommon(F_ULOCK);
321 g_thread_lock_.Get().Release(); 350 g_thread_lock_.Get().Release();
322 } 351 }
323 352
324 #if !defined(OS_ANDROID) 353 #if !defined(OS_ANDROID)
325 bool SharedMemory::PrepareMapFile(FILE *fp) { 354 bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
326 DCHECK_EQ(-1, mapped_file_); 355 DCHECK_EQ(-1, mapped_file_);
327 if (fp == NULL) return false; 356 DCHECK_EQ(-1, readonly_mapped_file_);
357 if (fp == NULL || *readonly_fd < 0) return false;
328 358
329 // This function theoretically can block on the disk, but realistically 359 // This function theoretically can block on the disk, but realistically
330 // the temporary files we create will just go into the buffer cache 360 // the temporary files we create will just go into the buffer cache
331 // and be deleted before they ever make it out to disk. 361 // and be deleted before they ever make it out to disk.
332 base::ThreadRestrictions::ScopedAllowIO allow_io; 362 base::ThreadRestrictions::ScopedAllowIO allow_io;
333 363
334 file_util::ScopedFILE file_closer(fp); 364 struct stat st = {};
365 struct stat readonly_st = {};
366 if (fstat(fileno(fp.get()), &st))
367 NOTREACHED();
368 if (fstat(*readonly_fd, &readonly_st))
369 NOTREACHED();
370 if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
371 LOG(ERROR) << "writable and read-only inodes don't match; bailing";
372 return false;
373 }
335 374
336 mapped_file_ = dup(fileno(fp)); 375 mapped_file_ = dup(fileno(fp.get()));
337 if (mapped_file_ == -1) { 376 if (mapped_file_ == -1) {
338 if (errno == EMFILE) { 377 if (errno == EMFILE) {
339 LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; 378 LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
340 return false; 379 return false;
341 } else { 380 } else {
342 NOTREACHED() << "Call to dup failed, errno=" << errno; 381 NOTREACHED() << "Call to dup failed, errno=" << errno;
343 } 382 }
344 } 383 }
345
346 struct stat st;
347 if (fstat(mapped_file_, &st))
348 NOTREACHED();
349 inode_ = st.st_ino; 384 inode_ = st.st_ino;
385 readonly_mapped_file_ = *readonly_fd.release();
350 386
351 return true; 387 return true;
352 } 388 }
353 #endif 389 #endif
354 390
355 // For the given shmem named |mem_name|, return a filename to mmap() 391 // For the given shmem named |mem_name|, return a filename to mmap()
356 // (and possibly create). Modifies |filename|. Return false on 392 // (and possibly create). Modifies |filename|. Return false on
357 // error, or true of we are happy. 393 // error, or true of we are happy.
358 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, 394 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
359 FilePath* path) { 395 FilePath* path) {
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 NOTREACHED() << "lockf() failed." 428 NOTREACHED() << "lockf() failed."
393 << " function:" << function 429 << " function:" << function
394 << " fd:" << mapped_file_ 430 << " fd:" << mapped_file_
395 << " errno:" << errno 431 << " errno:" << errno
396 << " msg:" << safe_strerror(errno); 432 << " msg:" << safe_strerror(errno);
397 } 433 }
398 } 434 }
399 } 435 }
400 436
401 bool SharedMemory::ShareToProcessCommon(ProcessHandle process, 437 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
402 SharedMemoryHandle *new_handle, 438 SharedMemoryHandle* new_handle,
403 bool close_self) { 439 bool close_self,
404 const int new_fd = dup(mapped_file_); 440 ShareMode share_mode) {
441 int handle_to_dup = -1;
442 switch(share_mode) {
443 case SHARE_CURRENT_MODE:
444 handle_to_dup = mapped_file_;
445 break;
446 case SHARE_READONLY:
447 // We could imagine re-opening the file from /dev/fd, but that can't make
448 // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10
449 CHECK(readonly_mapped_file_ >= 0);
450 handle_to_dup = readonly_mapped_file_;
451 break;
452 }
453
454 const int new_fd = dup(handle_to_dup);
405 if (new_fd < 0) { 455 if (new_fd < 0) {
406 DPLOG(ERROR) << "dup() failed."; 456 DPLOG(ERROR) << "dup() failed.";
407 return false; 457 return false;
408 } 458 }
409 459
410 new_handle->fd = new_fd; 460 new_handle->fd = new_fd;
411 new_handle->auto_close = true; 461 new_handle->auto_close = true;
412 462
413 if (close_self) 463 if (close_self)
414 Close(); 464 Close();
415 465
416 return true; 466 return true;
417 } 467 }
418 468
419 } // namespace base 469 } // namespace base
OLDNEW
« no previous file with comments | « base/memory/shared_memory_nacl.cc ('k') | base/memory/shared_memory_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698