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