1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/files/file.h"
6
7 // The only 32-bit platform that uses this file is Android. On Android APIs
8 // >= 21, this standard define is the right way to express that you want a
9 // 64-bit offset in struct stat, and the stat64 struct and functions aren't
10 // useful.
11 #define _FILE_OFFSET_BITS 64
12
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <stdint.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19 static_assert(sizeof(base::stat_wrapper_t::st_size) >= 8);
20
21 #include <atomic>
22 #include <optional>
23
24 #include "base/check_op.h"
25 #include "base/feature_list.h"
26 #include "base/metrics/field_trial_params.h"
27 #include "base/notimplemented.h"
28 #include "base/notreached.h"
29 #include "base/numerics/safe_conversions.h"
30 #include "base/posix/eintr_wrapper.h"
31 #include "base/strings/utf_string_conversions.h"
32 #include "base/threading/scoped_blocking_call.h"
33 #include "build/build_config.h"
34
35 #if BUILDFLAG(IS_ANDROID)
36 #include "base/os_compat_android.h"
37 #endif
38
39 namespace base {
40
41 // Make sure our Whence mappings match the system headers.
42 static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
43 File::FROM_END == SEEK_END,
44 "whence mapping must match the system headers");
45
46 namespace {
47
48 #if BUILDFLAG(IS_APPLE)
49 // When enabled, `F_FULLFSYNC` is not used in `File::Flush`. Instead,
50 // `F_BARRIERFSYNC` or `flush()` is used (depending on the
51 // "MacEfficientFileFlushUseBarrier" param). The feature exists to measure the
52 // cost of `F_FULLFSYNC` compared to other solutions (not ready to enable by
53 // default as-is). See
54 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html
55 BASE_FEATURE(kMacEfficientFileFlush,
56 "MacEfficientFileFlush",
57 base::FEATURE_DISABLED_BY_DEFAULT);
58
59 const FeatureParam<bool> kMacEfficientFileFlushUseBarrier{
60 &kMacEfficientFileFlush, "MacEfficientFileFlushUseBarrier", true};
61
62 enum class MacFileFlushMechanism {
63 kFlush,
64 kFullFsync,
65 kBarrierFsync,
66 };
67
68 std::atomic<MacFileFlushMechanism> g_mac_file_flush_mechanism{
69 MacFileFlushMechanism::kFullFsync};
70 #endif // BUILDFLAG(IS_APPLE)
71
72 // NaCl doesn't provide the following system calls, so either simulate them or
73 // wrap them in order to minimize the number of #ifdef's in this file.
74 #if !BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)
IsOpenAppend(PlatformFile file)75 bool IsOpenAppend(PlatformFile file) {
76 return (fcntl(file, F_GETFL) & O_APPEND) != 0;
77 }
78
CallFtruncate(PlatformFile file,int64_t length)79 int CallFtruncate(PlatformFile file, int64_t length) {
80 #if BUILDFLAG(IS_BSD) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_FUCHSIA)
81 static_assert(sizeof(off_t) >= sizeof(int64_t),
82 "off_t is not a 64-bit integer");
83 return HANDLE_EINTR(ftruncate(file, length));
84 #else
85 return HANDLE_EINTR(ftruncate64(file, length));
86 #endif
87 }
88
CallFutimes(PlatformFile file,const struct timeval times[2])89 int CallFutimes(PlatformFile file, const struct timeval times[2]) {
90 #ifdef __USE_XOPEN2K8
91 // futimens should be available, but futimes might not be
92 // http://pubs.opengroup.org/onlinepubs/9699919799/
93
94 timespec ts_times[2];
95 ts_times[0].tv_sec = times[0].tv_sec;
96 ts_times[0].tv_nsec = times[0].tv_usec * 1000;
97 ts_times[1].tv_sec = times[1].tv_sec;
98 ts_times[1].tv_nsec = times[1].tv_usec * 1000;
99
100 return futimens(file, ts_times);
101 #else
102 return futimes(file, times);
103 #endif
104 }
105
106 #if !BUILDFLAG(IS_FUCHSIA)
FcntlFlockType(std::optional<File::LockMode> mode)107 short FcntlFlockType(std::optional<File::LockMode> mode) {
108 if (!mode.has_value())
109 return F_UNLCK;
110 switch (mode.value()) {
111 case File::LockMode::kShared:
112 return F_RDLCK;
113 case File::LockMode::kExclusive:
114 return F_WRLCK;
115 }
116 NOTREACHED();
117 }
118
CallFcntlFlock(PlatformFile file,std::optional<File::LockMode> mode)119 File::Error CallFcntlFlock(PlatformFile file,
120 std::optional<File::LockMode> mode) {
121 struct flock lock;
122 lock.l_type = FcntlFlockType(std::move(mode));
123 lock.l_whence = SEEK_SET;
124 lock.l_start = 0;
125 lock.l_len = 0; // Lock entire file.
126 if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
127 return File::GetLastFileError();
128 return File::FILE_OK;
129 }
130 #endif
131
132 #else // BUILDFLAG(IS_NACL) && !BUILDFLAG(IS_AIX)
133
IsOpenAppend(PlatformFile file)134 bool IsOpenAppend(PlatformFile file) {
135 // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
136 // standard and always appends if the file is opened with O_APPEND, just
137 // return false here.
138 return false;
139 }
140
CallFtruncate(PlatformFile file,int64_t length)141 int CallFtruncate(PlatformFile file, int64_t length) {
142 NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
143 return 0;
144 }
145
CallFutimes(PlatformFile file,const struct timeval times[2])146 int CallFutimes(PlatformFile file, const struct timeval times[2]) {
147 NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
148 return 0;
149 }
150
CallFcntlFlock(PlatformFile file,std::optional<File::LockMode> mode)151 File::Error CallFcntlFlock(PlatformFile file,
152 std::optional<File::LockMode> mode) {
153 NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
154 return File::FILE_ERROR_INVALID_OPERATION;
155 }
156 #endif // BUILDFLAG(IS_NACL)
157
158 } // namespace
159
FromStat(const stat_wrapper_t & stat_info)160 void File::Info::FromStat(const stat_wrapper_t& stat_info) {
161 is_directory = S_ISDIR(stat_info.st_mode);
162 is_symbolic_link = S_ISLNK(stat_info.st_mode);
163 size = stat_info.st_size;
164
165 // Get last modification time, last access time, and creation time from
166 // |stat_info|.
167 // Note: st_ctime is actually last status change time when the inode was last
168 // updated, which happens on any metadata change. It is not the file's
169 // creation time. However, other than on Mac & iOS where the actual file
170 // creation time is included as st_birthtime, the rest of POSIX platforms have
171 // no portable way to get the creation time.
172 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_FUCHSIA)
173 time_t last_modified_sec = stat_info.st_mtim.tv_sec;
174 int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
175 time_t last_accessed_sec = stat_info.st_atim.tv_sec;
176 int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
177 time_t creation_time_sec = stat_info.st_ctim.tv_sec;
178 int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
179 #elif BUILDFLAG(IS_ANDROID)
180 time_t last_modified_sec = stat_info.st_mtime;
181 int64_t last_modified_nsec = stat_info.st_mtime_nsec;
182 time_t last_accessed_sec = stat_info.st_atime;
183 int64_t last_accessed_nsec = stat_info.st_atime_nsec;
184 time_t creation_time_sec = stat_info.st_ctime;
185 int64_t creation_time_nsec = stat_info.st_ctime_nsec;
186 #elif BUILDFLAG(IS_APPLE)
187 time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
188 int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
189 time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
190 int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
191 time_t creation_time_sec = stat_info.st_birthtimespec.tv_sec;
192 int64_t creation_time_nsec = stat_info.st_birthtimespec.tv_nsec;
193 #elif BUILDFLAG(IS_BSD)
194 time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
195 int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
196 time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
197 int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
198 time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
199 int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
200 #else
201 time_t last_modified_sec = stat_info.st_mtime;
202 int64_t last_modified_nsec = 0;
203 time_t last_accessed_sec = stat_info.st_atime;
204 int64_t last_accessed_nsec = 0;
205 time_t creation_time_sec = stat_info.st_ctime;
206 int64_t creation_time_nsec = 0;
207 #endif
208
209 last_modified =
210 Time::FromTimeT(last_modified_sec) +
211 Microseconds(last_modified_nsec / Time::kNanosecondsPerMicrosecond);
212
213 last_accessed =
214 Time::FromTimeT(last_accessed_sec) +
215 Microseconds(last_accessed_nsec / Time::kNanosecondsPerMicrosecond);
216
217 creation_time =
218 Time::FromTimeT(creation_time_sec) +
219 Microseconds(creation_time_nsec / Time::kNanosecondsPerMicrosecond);
220 }
221
IsValid() const222 bool File::IsValid() const {
223 return file_.is_valid();
224 }
225
GetPlatformFile() const226 PlatformFile File::GetPlatformFile() const {
227 return file_.get();
228 }
229
TakePlatformFile()230 PlatformFile File::TakePlatformFile() {
231 return file_.release();
232 }
233
Close()234 void File::Close() {
235 if (!IsValid())
236 return;
237
238 SCOPED_FILE_TRACE("Close");
239 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
240 file_.reset();
241 }
242
Seek(Whence whence,int64_t offset)243 int64_t File::Seek(Whence whence, int64_t offset) {
244 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
245 DCHECK(IsValid());
246
247 SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
248
249 #if BUILDFLAG(IS_ANDROID)
250 static_assert(sizeof(int64_t) == sizeof(off64_t), "off64_t must be 64 bits");
251 return lseek64(file_.get(), static_cast<off64_t>(offset),
252 static_cast<int>(whence));
253 #else
254 static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
255 return lseek(file_.get(), static_cast<off_t>(offset),
256 static_cast<int>(whence));
257 #endif
258 }
259
Read(int64_t offset,char * data,int size)260 int File::Read(int64_t offset, char* data, int size) {
261 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
262 DCHECK(IsValid());
263 if (size < 0 || !IsValueInRangeForNumericType<off_t>(offset + size - 1))
264 return -1;
265
266 SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
267
268 int bytes_read = 0;
269 long rv;
270 do {
271 rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,
272 static_cast<size_t>(size - bytes_read),
273 static_cast<off_t>(offset + bytes_read)));
274 if (rv <= 0)
275 break;
276
277 bytes_read += rv;
278 } while (bytes_read < size);
279
280 return bytes_read ? bytes_read : checked_cast<int>(rv);
281 }
282
ReadAtCurrentPos(char * data,int size)283 int File::ReadAtCurrentPos(char* data, int size) {
284 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
285 DCHECK(IsValid());
286 if (size < 0)
287 return -1;
288
289 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
290
291 int bytes_read = 0;
292 long rv;
293 do {
294 rv = HANDLE_EINTR(read(file_.get(), data + bytes_read,
295 static_cast<size_t>(size - bytes_read)));
296 if (rv <= 0)
297 break;
298
299 bytes_read += rv;
300 } while (bytes_read < size);
301
302 return bytes_read ? bytes_read : checked_cast<int>(rv);
303 }
304
ReadNoBestEffort(int64_t offset,char * data,int size)305 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
306 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
307 DCHECK(IsValid());
308 if (size < 0 || !IsValueInRangeForNumericType<off_t>(offset))
309 return -1;
310
311 SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size);
312 return checked_cast<int>(
313 HANDLE_EINTR(pread(file_.get(), data, static_cast<size_t>(size),
314 static_cast<off_t>(offset))));
315 }
316
ReadAtCurrentPosNoBestEffort(char * data,int size)317 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
318 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
319 DCHECK(IsValid());
320 if (size < 0)
321 return -1;
322
323 SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size);
324 return checked_cast<int>(
325 HANDLE_EINTR(read(file_.get(), data, static_cast<size_t>(size))));
326 }
327
Write(int64_t offset,const char * data,int size)328 int File::Write(int64_t offset, const char* data, int size) {
329 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
330
331 if (IsOpenAppend(file_.get()))
332 return WriteAtCurrentPos(data, size);
333
334 DCHECK(IsValid());
335 if (size < 0)
336 return -1;
337
338 SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
339
340 int bytes_written = 0;
341 long rv;
342 do {
343 #if BUILDFLAG(IS_ANDROID)
344 // In case __USE_FILE_OFFSET64 is not used, we need to call pwrite64()
345 // instead of pwrite().
346 static_assert(sizeof(int64_t) == sizeof(off64_t),
347 "off64_t must be 64 bits");
348 rv = HANDLE_EINTR(pwrite64(file_.get(), data + bytes_written,
349 static_cast<size_t>(size - bytes_written),
350 offset + bytes_written));
351 #else
352 rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
353 static_cast<size_t>(size - bytes_written),
354 offset + bytes_written));
355 #endif
356 if (rv <= 0)
357 break;
358
359 bytes_written += rv;
360 } while (bytes_written < size);
361
362 return bytes_written ? bytes_written : checked_cast<int>(rv);
363 }
364
WriteAtCurrentPos(const char * data,int size)365 int File::WriteAtCurrentPos(const char* data, int size) {
366 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
367 DCHECK(IsValid());
368 if (size < 0)
369 return -1;
370
371 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
372
373 int bytes_written = 0;
374 long rv;
375 do {
376 rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,
377 static_cast<size_t>(size - bytes_written)));
378 if (rv <= 0)
379 break;
380
381 bytes_written += rv;
382 } while (bytes_written < size);
383
384 return bytes_written ? bytes_written : checked_cast<int>(rv);
385 }
386
WriteAtCurrentPosNoBestEffort(const char * data,int size)387 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
388 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
389 DCHECK(IsValid());
390 if (size < 0)
391 return -1;
392
393 SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size);
394 return checked_cast<int>(
395 HANDLE_EINTR(write(file_.get(), data, static_cast<size_t>(size))));
396 }
397
GetLength() const398 int64_t File::GetLength() const {
399 DCHECK(IsValid());
400
401 SCOPED_FILE_TRACE("GetLength");
402
403 stat_wrapper_t file_info;
404 if (Fstat(file_.get(), &file_info))
405 return -1;
406
407 return file_info.st_size;
408 }
409
SetLength(int64_t length)410 bool File::SetLength(int64_t length) {
411 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
412 DCHECK(IsValid());
413
414 SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
415 return !CallFtruncate(file_.get(), length);
416 }
417
SetTimes(Time last_access_time,Time last_modified_time)418 bool File::SetTimes(Time last_access_time, Time last_modified_time) {
419 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
420 DCHECK(IsValid());
421
422 SCOPED_FILE_TRACE("SetTimes");
423
424 timeval times[2];
425 times[0] = last_access_time.ToTimeVal();
426 times[1] = last_modified_time.ToTimeVal();
427
428 return !CallFutimes(file_.get(), times);
429 }
430
GetInfo(Info * info)431 bool File::GetInfo(Info* info) {
432 DCHECK(IsValid());
433
434 SCOPED_FILE_TRACE("GetInfo");
435
436 stat_wrapper_t file_info;
437 if (Fstat(file_.get(), &file_info))
438 return false;
439
440 info->FromStat(file_info);
441 return true;
442 }
443
444 #if !BUILDFLAG(IS_FUCHSIA)
Lock(File::LockMode mode)445 File::Error File::Lock(File::LockMode mode) {
446 SCOPED_FILE_TRACE("Lock");
447 return CallFcntlFlock(file_.get(), mode);
448 }
449
Unlock()450 File::Error File::Unlock() {
451 SCOPED_FILE_TRACE("Unlock");
452 return CallFcntlFlock(file_.get(), std::optional<File::LockMode>());
453 }
454 #endif
455
Duplicate() const456 File File::Duplicate() const {
457 if (!IsValid())
458 return File();
459
460 SCOPED_FILE_TRACE("Duplicate");
461
462 ScopedPlatformFile other_fd(HANDLE_EINTR(dup(GetPlatformFile())));
463 if (!other_fd.is_valid())
464 return File(File::GetLastFileError());
465
466 return File(std::move(other_fd), async());
467 }
468
469 #if BUILDFLAG(IS_APPLE)
InitializeFeatures()470 void File::InitializeFeatures() {
471 if (FeatureList::IsEnabled(kMacEfficientFileFlush)) {
472 // "relaxed" because there is no dependency between these memory operations
473 // and other memory operations.
474 if (kMacEfficientFileFlushUseBarrier.Get()) {
475 g_mac_file_flush_mechanism.store(MacFileFlushMechanism::kBarrierFsync,
476 std::memory_order_relaxed);
477 } else {
478 g_mac_file_flush_mechanism.store(MacFileFlushMechanism::kFlush,
479 std::memory_order_relaxed);
480 }
481 }
482 }
483 #endif // BUILDFLAG(IS_APPLE)
484
485 // Static.
OSErrorToFileError(int saved_errno)486 File::Error File::OSErrorToFileError(int saved_errno) {
487 switch (saved_errno) {
488 case EACCES:
489 case EISDIR:
490 case EROFS:
491 case EPERM:
492 return FILE_ERROR_ACCESS_DENIED;
493 case EBUSY:
494 #if !BUILDFLAG(IS_NACL) // ETXTBSY not defined by NaCl.
495 case ETXTBSY:
496 #endif
497 return FILE_ERROR_IN_USE;
498 case EEXIST:
499 return FILE_ERROR_EXISTS;
500 case EIO:
501 return FILE_ERROR_IO;
502 case ENOENT:
503 return FILE_ERROR_NOT_FOUND;
504 case ENFILE: // fallthrough
505 case EMFILE:
506 return FILE_ERROR_TOO_MANY_OPENED;
507 case ENOMEM:
508 return FILE_ERROR_NO_MEMORY;
509 case ENOSPC:
510 return FILE_ERROR_NO_SPACE;
511 case ENOTDIR:
512 return FILE_ERROR_NOT_A_DIRECTORY;
513 default:
514 // This function should only be called for errors.
515 DCHECK_NE(0, saved_errno);
516 return FILE_ERROR_FAILED;
517 }
518 }
519
520 // NaCl doesn't implement system calls to open files directly.
521 #if !BUILDFLAG(IS_NACL)
522 // TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
DoInitialize(const FilePath & path,uint32_t flags)523 void File::DoInitialize(const FilePath& path, uint32_t flags) {
524 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
525 DCHECK(!IsValid());
526
527 int open_flags = 0;
528 if (flags & FLAG_CREATE)
529 open_flags = O_CREAT | O_EXCL;
530
531 created_ = false;
532
533 if (flags & FLAG_CREATE_ALWAYS) {
534 DCHECK(!open_flags);
535 DCHECK(flags & FLAG_WRITE);
536 open_flags = O_CREAT | O_TRUNC;
537 }
538
539 if (flags & FLAG_OPEN_TRUNCATED) {
540 DCHECK(!open_flags);
541 DCHECK(flags & FLAG_WRITE);
542 open_flags = O_TRUNC;
543 }
544
545 if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
546 NOTREACHED();
547 errno = EOPNOTSUPP;
548 error_details_ = FILE_ERROR_FAILED;
549 return;
550 }
551
552 if (flags & FLAG_WRITE && flags & FLAG_READ) {
553 open_flags |= O_RDWR;
554 } else if (flags & FLAG_WRITE) {
555 open_flags |= O_WRONLY;
556 } else if (!(flags & FLAG_READ) && !(flags & FLAG_WRITE_ATTRIBUTES) &&
557 !(flags & FLAG_APPEND) && !(flags & FLAG_OPEN_ALWAYS)) {
558 // Note: For FLAG_WRITE_ATTRIBUTES and no other read/write flags, we'll
559 // open the file in O_RDONLY mode (== 0, see static_assert below), so that
560 // we get a fd that can be used for SetTimes().
561 NOTREACHED();
562 }
563
564 if (flags & FLAG_TERMINAL_DEVICE)
565 open_flags |= O_NOCTTY | O_NDELAY;
566
567 if (flags & FLAG_APPEND && flags & FLAG_READ)
568 open_flags |= O_APPEND | O_RDWR;
569 else if (flags & FLAG_APPEND)
570 open_flags |= O_APPEND | O_WRONLY;
571
572 static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
573
574 mode_t mode = S_IRUSR | S_IWUSR;
575 #if BUILDFLAG(IS_CHROMEOS)
576 mode |= S_IRGRP | S_IROTH;
577 #endif
578
579 int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
580
581 if (flags & FLAG_OPEN_ALWAYS) {
582 if (descriptor < 0) {
583 open_flags |= O_CREAT;
584 descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
585 if (descriptor >= 0)
586 created_ = true;
587 }
588 }
589
590 if (descriptor < 0) {
591 error_details_ = File::GetLastFileError();
592 return;
593 }
594
595 if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
596 created_ = true;
597
598 if (flags & FLAG_DELETE_ON_CLOSE)
599 unlink(path.value().c_str());
600
601 async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
602 error_details_ = FILE_OK;
603 file_.reset(descriptor);
604 }
605 #endif // !BUILDFLAG(IS_NACL)
606
Flush()607 bool File::Flush() {
608 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
609 DCHECK(IsValid());
610 SCOPED_FILE_TRACE("Flush");
611
612 #if BUILDFLAG(IS_NACL)
613 NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
614 return true;
615 #elif BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || \
616 BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX)
617 return !HANDLE_EINTR(fdatasync(file_.get()));
618 #elif BUILDFLAG(IS_APPLE)
619 // On macOS and iOS, fsync() is guaranteed to send the file's data to the
620 // underlying storage device, but may return before the device actually writes
621 // the data to the medium. When used by database systems, this may result in
622 // unexpected data loss. Depending on experiment state, this function may use
623 // F_BARRIERFSYNC or F_FULLFSYNC to provide stronger guarantees than fsync().
624 //
625 // See documentation:
626 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html
627 //
628 // "relaxed" because there is no dependency between this memory operation and
629 // other memory operations.
630 switch (g_mac_file_flush_mechanism.load(std::memory_order_relaxed)) {
631 case MacFileFlushMechanism::kBarrierFsync: {
632 if (!HANDLE_EINTR(fcntl(file_.get(), F_BARRIERFSYNC))) {
633 return true;
634 }
635 // Fall back to `fsync()` in case of failure.
636 break;
637 }
638 case MacFileFlushMechanism::kFullFsync: {
639 if (!HANDLE_EINTR(fcntl(file_.get(), F_FULLFSYNC))) {
640 return true;
641 }
642 // Fall back to `fsync()` in case of failure.
643 break;
644 }
645 case MacFileFlushMechanism::kFlush: {
646 // Fall back to `fsync()`.
647 break;
648 }
649 }
650
651 // `fsync()` if `F_BARRIERFSYNC` or `F_FULLFSYNC` failed, or if the mechanism
652 // is `kFlush`. Some file systems do not support `F_FULLFSYNC` /
653 // `F_BARRIERFSYNC` but we cannot use the error code as a definitive indicator
654 // that it's the case, so we'll keep trying `F_FULLFSYNC` / `F_BARRIERFSYNC`
655 // for every call to this method when it's the case. See the CL description at
656 // https://crrev.com/c/1400159 for details.
657 return !HANDLE_EINTR(fsync(file_.get()));
658 #else
659 return !HANDLE_EINTR(fsync(file_.get()));
660 #endif
661 }
662
SetPlatformFile(PlatformFile file)663 void File::SetPlatformFile(PlatformFile file) {
664 DCHECK(!file_.is_valid());
665 file_.reset(file);
666 }
667
668 // static
GetLastFileError()669 File::Error File::GetLastFileError() {
670 return base::File::OSErrorToFileError(errno);
671 }
672
Stat(const char * path,stat_wrapper_t * sb)673 int File::Stat(const char* path, stat_wrapper_t* sb) {
674 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
675 return stat(path, sb);
676 }
Fstat(int fd,stat_wrapper_t * sb)677 int File::Fstat(int fd, stat_wrapper_t* sb) {
678 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
679 return fstat(fd, sb);
680 }
Lstat(const char * path,stat_wrapper_t * sb)681 int File::Lstat(const char* path, stat_wrapper_t* sb) {
682 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
683 return lstat(path, sb);
684 }
685
686 } // namespace base
687