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