xref: /aosp_15_r20/external/cronet/base/files/file_posix.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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