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