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 |
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 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() << "\", 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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |