Home | History | Annotate | Download | only in files
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/files/file.h"
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <stdint.h>
     10 #include <sys/stat.h>
     11 #include <unistd.h>
     12 
     13 #include "base/logging.h"
     14 #include "base/metrics/histogram_functions.h"
     15 #include "base/posix/eintr_wrapper.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/threading/thread_restrictions.h"
     18 #include "build/build_config.h"
     19 
     20 #if defined(OS_ANDROID)
     21 #include "base/os_compat_android.h"
     22 #endif
     23 
     24 namespace base {
     25 
     26 // Make sure our Whence mappings match the system headers.
     27 static_assert(File::FROM_BEGIN == SEEK_SET && File::FROM_CURRENT == SEEK_CUR &&
     28                   File::FROM_END == SEEK_END,
     29               "whence mapping must match the system headers");
     30 
     31 namespace {
     32 
     33 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) || \
     34   defined(OS_FUCHSIA) || (defined(OS_ANDROID) && __ANDROID_API__ < 21)
     35 int CallFstat(int fd, stat_wrapper_t *sb) {
     36   AssertBlockingAllowed();
     37   return fstat(fd, sb);
     38 }
     39 #else
     40 int CallFstat(int fd, stat_wrapper_t *sb) {
     41   AssertBlockingAllowed();
     42   return fstat64(fd, sb);
     43 }
     44 #endif
     45 
     46 // NaCl doesn't provide the following system calls, so either simulate them or
     47 // wrap them in order to minimize the number of #ifdef's in this file.
     48 #if !defined(OS_NACL) && !defined(OS_AIX)
     49 bool IsOpenAppend(PlatformFile file) {
     50   return (fcntl(file, F_GETFL) & O_APPEND) != 0;
     51 }
     52 
     53 int CallFtruncate(PlatformFile file, int64_t length) {
     54   return HANDLE_EINTR(ftruncate(file, length));
     55 }
     56 
     57 int CallFutimes(PlatformFile file, const struct timeval times[2]) {
     58 #ifdef __USE_XOPEN2K8
     59   // futimens should be available, but futimes might not be
     60   // http://pubs.opengroup.org/onlinepubs/9699919799/
     61 
     62   timespec ts_times[2];
     63   ts_times[0].tv_sec  = times[0].tv_sec;
     64   ts_times[0].tv_nsec = times[0].tv_usec * 1000;
     65   ts_times[1].tv_sec  = times[1].tv_sec;
     66   ts_times[1].tv_nsec = times[1].tv_usec * 1000;
     67 
     68   return futimens(file, ts_times);
     69 #else
     70   return futimes(file, times);
     71 #endif
     72 }
     73 
     74 #if !defined(OS_FUCHSIA)
     75 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
     76   struct flock lock;
     77   lock.l_type = do_lock ? F_WRLCK : F_UNLCK;
     78   lock.l_whence = SEEK_SET;
     79   lock.l_start = 0;
     80   lock.l_len = 0;  // Lock entire file.
     81   if (HANDLE_EINTR(fcntl(file, F_SETLK, &lock)) == -1)
     82     return File::GetLastFileError();
     83   return File::FILE_OK;
     84 }
     85 #endif
     86 
     87 #else   // defined(OS_NACL) && !defined(OS_AIX)
     88 
     89 bool IsOpenAppend(PlatformFile file) {
     90   // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
     91   // standard and always appends if the file is opened with O_APPEND, just
     92   // return false here.
     93   return false;
     94 }
     95 
     96 int CallFtruncate(PlatformFile file, int64_t length) {
     97   NOTIMPLEMENTED();  // NaCl doesn't implement ftruncate.
     98   return 0;
     99 }
    100 
    101 int CallFutimes(PlatformFile file, const struct timeval times[2]) {
    102   NOTIMPLEMENTED();  // NaCl doesn't implement futimes.
    103   return 0;
    104 }
    105 
    106 File::Error CallFcntlFlock(PlatformFile file, bool do_lock) {
    107   NOTIMPLEMENTED();  // NaCl doesn't implement flock struct.
    108   return File::FILE_ERROR_INVALID_OPERATION;
    109 }
    110 #endif  // defined(OS_NACL)
    111 
    112 }  // namespace
    113 
    114 void File::Info::FromStat(const stat_wrapper_t& stat_info) {
    115   is_directory = S_ISDIR(stat_info.st_mode);
    116   is_symbolic_link = S_ISLNK(stat_info.st_mode);
    117   size = stat_info.st_size;
    118 
    119 #if defined(OS_LINUX)
    120   time_t last_modified_sec = stat_info.st_mtim.tv_sec;
    121   int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
    122   time_t last_accessed_sec = stat_info.st_atim.tv_sec;
    123   int64_t last_accessed_nsec = stat_info.st_atim.tv_nsec;
    124   time_t creation_time_sec = stat_info.st_ctim.tv_sec;
    125   int64_t creation_time_nsec = stat_info.st_ctim.tv_nsec;
    126 #elif defined(OS_ANDROID)
    127   time_t last_modified_sec = stat_info.st_mtime;
    128   int64_t last_modified_nsec = stat_info.st_mtime_nsec;
    129   time_t last_accessed_sec = stat_info.st_atime;
    130   int64_t last_accessed_nsec = stat_info.st_atime_nsec;
    131   time_t creation_time_sec = stat_info.st_ctime;
    132   int64_t creation_time_nsec = stat_info.st_ctime_nsec;
    133 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
    134   time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
    135   int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
    136   time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
    137   int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
    138   time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
    139   int64_t creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
    140 #else
    141   time_t last_modified_sec = stat_info.st_mtime;
    142   int64_t last_modified_nsec = 0;
    143   time_t last_accessed_sec = stat_info.st_atime;
    144   int64_t last_accessed_nsec = 0;
    145   time_t creation_time_sec = stat_info.st_ctime;
    146   int64_t creation_time_nsec = 0;
    147 #endif
    148 
    149   last_modified =
    150       Time::FromTimeT(last_modified_sec) +
    151       TimeDelta::FromMicroseconds(last_modified_nsec /
    152                                   Time::kNanosecondsPerMicrosecond);
    153 
    154   last_accessed =
    155       Time::FromTimeT(last_accessed_sec) +
    156       TimeDelta::FromMicroseconds(last_accessed_nsec /
    157                                   Time::kNanosecondsPerMicrosecond);
    158 
    159   creation_time =
    160       Time::FromTimeT(creation_time_sec) +
    161       TimeDelta::FromMicroseconds(creation_time_nsec /
    162                                   Time::kNanosecondsPerMicrosecond);
    163 }
    164 
    165 bool File::IsValid() const {
    166   return file_.is_valid();
    167 }
    168 
    169 PlatformFile File::GetPlatformFile() const {
    170   return file_.get();
    171 }
    172 
    173 PlatformFile File::TakePlatformFile() {
    174   return file_.release();
    175 }
    176 
    177 void File::Close() {
    178   if (!IsValid())
    179     return;
    180 
    181   SCOPED_FILE_TRACE("Close");
    182   AssertBlockingAllowed();
    183   file_.reset();
    184 }
    185 
    186 int64_t File::Seek(Whence whence, int64_t offset) {
    187   AssertBlockingAllowed();
    188   DCHECK(IsValid());
    189 
    190   SCOPED_FILE_TRACE_WITH_SIZE("Seek", offset);
    191 
    192 // Additionally check __BIONIC__ since older versions of Android don't define
    193 // _FILE_OFFSET_BITS.
    194 #if _FILE_OFFSET_BITS != 64 || defined(__BIONIC__)
    195   static_assert(sizeof(int64_t) == sizeof(off64_t), "off64_t must be 64 bits");
    196   return lseek64(file_.get(), static_cast<off64_t>(offset),
    197                  static_cast<int>(whence));
    198 #else
    199   static_assert(sizeof(int64_t) == sizeof(off_t), "off_t must be 64 bits");
    200   return lseek(file_.get(), static_cast<off_t>(offset),
    201                static_cast<int>(whence));
    202 #endif
    203 }
    204 
    205 int File::Read(int64_t offset, char* data, int size) {
    206   AssertBlockingAllowed();
    207   DCHECK(IsValid());
    208   if (size < 0)
    209     return -1;
    210 
    211   SCOPED_FILE_TRACE_WITH_SIZE("Read", size);
    212 
    213   int bytes_read = 0;
    214   int rv;
    215   do {
    216     rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,
    217                             size - bytes_read, offset + bytes_read));
    218     if (rv <= 0)
    219       break;
    220 
    221     bytes_read += rv;
    222   } while (bytes_read < size);
    223 
    224   return bytes_read ? bytes_read : rv;
    225 }
    226 
    227 int File::ReadAtCurrentPos(char* data, int size) {
    228   AssertBlockingAllowed();
    229   DCHECK(IsValid());
    230   if (size < 0)
    231     return -1;
    232 
    233   SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPos", size);
    234 
    235   int bytes_read = 0;
    236   int rv;
    237   do {
    238     rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
    239     if (rv <= 0)
    240       break;
    241 
    242     bytes_read += rv;
    243   } while (bytes_read < size);
    244 
    245   return bytes_read ? bytes_read : rv;
    246 }
    247 
    248 int File::ReadNoBestEffort(int64_t offset, char* data, int size) {
    249   AssertBlockingAllowed();
    250   DCHECK(IsValid());
    251   SCOPED_FILE_TRACE_WITH_SIZE("ReadNoBestEffort", size);
    252   return HANDLE_EINTR(pread(file_.get(), data, size, offset));
    253 }
    254 
    255 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
    256   AssertBlockingAllowed();
    257   DCHECK(IsValid());
    258   if (size < 0)
    259     return -1;
    260 
    261   SCOPED_FILE_TRACE_WITH_SIZE("ReadAtCurrentPosNoBestEffort", size);
    262   return HANDLE_EINTR(read(file_.get(), data, size));
    263 }
    264 
    265 int File::Write(int64_t offset, const char* data, int size) {
    266   AssertBlockingAllowed();
    267 
    268   if (IsOpenAppend(file_.get()))
    269     return WriteAtCurrentPos(data, size);
    270 
    271   DCHECK(IsValid());
    272   if (size < 0)
    273     return -1;
    274 
    275   SCOPED_FILE_TRACE_WITH_SIZE("Write", size);
    276 
    277   int bytes_written = 0;
    278   int rv;
    279   do {
    280 #if _FILE_OFFSET_BITS != 64 || defined(__BIONIC__)
    281     // In case __USE_FILE_OFFSET64 is not used, we need to call pwrite64()
    282     // instead of pwrite().
    283     static_assert(sizeof(int64_t) == sizeof(off64_t),
    284                   "off64_t must be 64 bits");
    285     rv = HANDLE_EINTR(pwrite64(file_.get(), data + bytes_written,
    286                                size - bytes_written, offset + bytes_written));
    287 #else
    288     rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
    289                              size - bytes_written, offset + bytes_written));
    290 #endif
    291     if (rv <= 0)
    292       break;
    293 
    294     bytes_written += rv;
    295   } while (bytes_written < size);
    296 
    297   return bytes_written ? bytes_written : rv;
    298 }
    299 
    300 int File::WriteAtCurrentPos(const char* data, int size) {
    301   AssertBlockingAllowed();
    302   DCHECK(IsValid());
    303   if (size < 0)
    304     return -1;
    305 
    306   SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPos", size);
    307 
    308   int bytes_written = 0;
    309   int rv;
    310   do {
    311     rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,
    312                             size - bytes_written));
    313     if (rv <= 0)
    314       break;
    315 
    316     bytes_written += rv;
    317   } while (bytes_written < size);
    318 
    319   return bytes_written ? bytes_written : rv;
    320 }
    321 
    322 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
    323   AssertBlockingAllowed();
    324   DCHECK(IsValid());
    325   if (size < 0)
    326     return -1;
    327 
    328   SCOPED_FILE_TRACE_WITH_SIZE("WriteAtCurrentPosNoBestEffort", size);
    329   return HANDLE_EINTR(write(file_.get(), data, size));
    330 }
    331 
    332 int64_t File::GetLength() {
    333   DCHECK(IsValid());
    334 
    335   SCOPED_FILE_TRACE("GetLength");
    336 
    337   stat_wrapper_t file_info;
    338   if (CallFstat(file_.get(), &file_info))
    339     return -1;
    340 
    341   return file_info.st_size;
    342 }
    343 
    344 bool File::SetLength(int64_t length) {
    345   AssertBlockingAllowed();
    346   DCHECK(IsValid());
    347 
    348   SCOPED_FILE_TRACE_WITH_SIZE("SetLength", length);
    349   return !CallFtruncate(file_.get(), length);
    350 }
    351 
    352 bool File::SetTimes(Time last_access_time, Time last_modified_time) {
    353   AssertBlockingAllowed();
    354   DCHECK(IsValid());
    355 
    356   SCOPED_FILE_TRACE("SetTimes");
    357 
    358   timeval times[2];
    359   times[0] = last_access_time.ToTimeVal();
    360   times[1] = last_modified_time.ToTimeVal();
    361 
    362   return !CallFutimes(file_.get(), times);
    363 }
    364 
    365 bool File::GetInfo(Info* info) {
    366   DCHECK(IsValid());
    367 
    368   SCOPED_FILE_TRACE("GetInfo");
    369 
    370   stat_wrapper_t file_info;
    371   if (CallFstat(file_.get(), &file_info))
    372     return false;
    373 
    374   info->FromStat(file_info);
    375   return true;
    376 }
    377 
    378 #if !defined(OS_FUCHSIA)
    379 File::Error File::Lock() {
    380   SCOPED_FILE_TRACE("Lock");
    381   return CallFcntlFlock(file_.get(), true);
    382 }
    383 
    384 File::Error File::Unlock() {
    385   SCOPED_FILE_TRACE("Unlock");
    386   return CallFcntlFlock(file_.get(), false);
    387 }
    388 #endif
    389 
    390 File File::Duplicate() const {
    391   if (!IsValid())
    392     return File();
    393 
    394   SCOPED_FILE_TRACE("Duplicate");
    395 
    396   PlatformFile other_fd = HANDLE_EINTR(dup(GetPlatformFile()));
    397   if (other_fd == -1)
    398     return File(File::GetLastFileError());
    399 
    400   return File(other_fd, async());
    401 }
    402 
    403 // Static.
    404 File::Error File::OSErrorToFileError(int saved_errno) {
    405   switch (saved_errno) {
    406     case EACCES:
    407     case EISDIR:
    408     case EROFS:
    409     case EPERM:
    410       return FILE_ERROR_ACCESS_DENIED;
    411     case EBUSY:
    412 #if !defined(OS_NACL)  // ETXTBSY not defined by NaCl.
    413     case ETXTBSY:
    414 #endif
    415       return FILE_ERROR_IN_USE;
    416     case EEXIST:
    417       return FILE_ERROR_EXISTS;
    418     case EIO:
    419       return FILE_ERROR_IO;
    420     case ENOENT:
    421       return FILE_ERROR_NOT_FOUND;
    422     case ENFILE:  // fallthrough
    423     case EMFILE:
    424       return FILE_ERROR_TOO_MANY_OPENED;
    425     case ENOMEM:
    426       return FILE_ERROR_NO_MEMORY;
    427     case ENOSPC:
    428       return FILE_ERROR_NO_SPACE;
    429     case ENOTDIR:
    430       return FILE_ERROR_NOT_A_DIRECTORY;
    431     default:
    432 #if !defined(OS_NACL)  // NaCl build has no metrics code.
    433       UmaHistogramSparse("PlatformFile.UnknownErrors.Posix", saved_errno);
    434 #endif
    435       // This function should only be called for errors.
    436       DCHECK_NE(0, saved_errno);
    437       return FILE_ERROR_FAILED;
    438   }
    439 }
    440 
    441 // NaCl doesn't implement system calls to open files directly.
    442 #if !defined(OS_NACL)
    443 // TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
    444 void File::DoInitialize(const FilePath& path, uint32_t flags) {
    445   AssertBlockingAllowed();
    446   DCHECK(!IsValid());
    447 
    448   int open_flags = 0;
    449   if (flags & FLAG_CREATE)
    450     open_flags = O_CREAT | O_EXCL;
    451 
    452   created_ = false;
    453 
    454   if (flags & FLAG_CREATE_ALWAYS) {
    455     DCHECK(!open_flags);
    456     DCHECK(flags & FLAG_WRITE);
    457     open_flags = O_CREAT | O_TRUNC;
    458   }
    459 
    460   if (flags & FLAG_OPEN_TRUNCATED) {
    461     DCHECK(!open_flags);
    462     DCHECK(flags & FLAG_WRITE);
    463     open_flags = O_TRUNC;
    464   }
    465 
    466   if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
    467     NOTREACHED();
    468     errno = EOPNOTSUPP;
    469     error_details_ = FILE_ERROR_FAILED;
    470     return;
    471   }
    472 
    473   if (flags & FLAG_WRITE && flags & FLAG_READ) {
    474     open_flags |= O_RDWR;
    475   } else if (flags & FLAG_WRITE) {
    476     open_flags |= O_WRONLY;
    477   } else if (!(flags & FLAG_READ) &&
    478              !(flags & FLAG_WRITE_ATTRIBUTES) &&
    479              !(flags & FLAG_APPEND) &&
    480              !(flags & FLAG_OPEN_ALWAYS)) {
    481     NOTREACHED();
    482   }
    483 
    484   if (flags & FLAG_TERMINAL_DEVICE)
    485     open_flags |= O_NOCTTY | O_NDELAY;
    486 
    487   if (flags & FLAG_APPEND && flags & FLAG_READ)
    488     open_flags |= O_APPEND | O_RDWR;
    489   else if (flags & FLAG_APPEND)
    490     open_flags |= O_APPEND | O_WRONLY;
    491 
    492   static_assert(O_RDONLY == 0, "O_RDONLY must equal zero");
    493 
    494   int mode = S_IRUSR | S_IWUSR;
    495 #if defined(OS_CHROMEOS)
    496   mode |= S_IRGRP | S_IROTH;
    497 #endif
    498 
    499   int descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
    500 
    501   if (flags & FLAG_OPEN_ALWAYS) {
    502     if (descriptor < 0) {
    503       open_flags |= O_CREAT;
    504       if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
    505         open_flags |= O_EXCL;   // together with O_CREAT implies O_NOFOLLOW
    506 
    507       descriptor = HANDLE_EINTR(open(path.value().c_str(), open_flags, mode));
    508       if (descriptor >= 0)
    509         created_ = true;
    510     }
    511   }
    512 
    513   if (descriptor < 0) {
    514     error_details_ = File::GetLastFileError();
    515     return;
    516   }
    517 
    518   if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
    519     created_ = true;
    520 
    521   if (flags & FLAG_DELETE_ON_CLOSE)
    522     unlink(path.value().c_str());
    523 
    524   async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
    525   error_details_ = FILE_OK;
    526   file_.reset(descriptor);
    527 }
    528 #endif  // !defined(OS_NACL)
    529 
    530 bool File::Flush() {
    531   AssertBlockingAllowed();
    532   DCHECK(IsValid());
    533   SCOPED_FILE_TRACE("Flush");
    534 
    535 #if defined(OS_NACL)
    536   NOTIMPLEMENTED();  // NaCl doesn't implement fsync.
    537   return true;
    538 #elif defined(OS_LINUX) || defined(OS_ANDROID)
    539   return !HANDLE_EINTR(fdatasync(file_.get()));
    540 #else
    541   return !HANDLE_EINTR(fsync(file_.get()));
    542 #endif
    543 }
    544 
    545 void File::SetPlatformFile(PlatformFile file) {
    546   DCHECK(!file_.is_valid());
    547   file_.reset(file);
    548 }
    549 
    550 // static
    551 File::Error File::GetLastFileError() {
    552   return base::File::OSErrorToFileError(errno);
    553 }
    554 
    555 }  // namespace base
    556