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 <sys/stat.h>
     10 #include <unistd.h>
     11 
     12 #include "base/files/file_path.h"
     13 #include "base/logging.h"
     14 #include "base/metrics/sparse_histogram.h"
     15 // TODO(rvargas): remove this (needed for kInvalidPlatformFileValue).
     16 #include "base/platform_file.h"
     17 #include "base/posix/eintr_wrapper.h"
     18 #include "base/strings/utf_string_conversions.h"
     19 #include "base/threading/thread_restrictions.h"
     20 
     21 #if defined(OS_ANDROID)
     22 #include "base/os_compat_android.h"
     23 #endif
     24 
     25 namespace base {
     26 
     27 // Make sure our Whence mappings match the system headers.
     28 COMPILE_ASSERT(File::FROM_BEGIN   == SEEK_SET &&
     29                File::FROM_CURRENT == SEEK_CUR &&
     30                File::FROM_END     == SEEK_END, whence_matches_system);
     31 
     32 namespace {
     33 
     34 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
     35 typedef struct stat stat_wrapper_t;
     36 static int CallFstat(int fd, stat_wrapper_t *sb) {
     37   base::ThreadRestrictions::AssertIOAllowed();
     38   return fstat(fd, sb);
     39 }
     40 #else
     41 typedef struct stat64 stat_wrapper_t;
     42 static int CallFstat(int fd, stat_wrapper_t *sb) {
     43   base::ThreadRestrictions::AssertIOAllowed();
     44   return fstat64(fd, sb);
     45 }
     46 #endif
     47 
     48 // NaCl doesn't provide the following system calls, so either simulate them or
     49 // wrap them in order to minimize the number of #ifdef's in this file.
     50 #if !defined(OS_NACL)
     51 static bool IsOpenAppend(PlatformFile file) {
     52   return (fcntl(file, F_GETFL) & O_APPEND) != 0;
     53 }
     54 
     55 static int CallFtruncate(PlatformFile file, int64 length) {
     56   return HANDLE_EINTR(ftruncate(file, length));
     57 }
     58 
     59 static int CallFsync(PlatformFile file) {
     60   return HANDLE_EINTR(fsync(file));
     61 }
     62 
     63 static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
     64 #ifdef __USE_XOPEN2K8
     65   // futimens should be available, but futimes might not be
     66   // http://pubs.opengroup.org/onlinepubs/9699919799/
     67 
     68   timespec ts_times[2];
     69   ts_times[0].tv_sec  = times[0].tv_sec;
     70   ts_times[0].tv_nsec = times[0].tv_usec * 1000;
     71   ts_times[1].tv_sec  = times[1].tv_sec;
     72   ts_times[1].tv_nsec = times[1].tv_usec * 1000;
     73 
     74   return futimens(file, ts_times);
     75 #else
     76   return futimes(file, times);
     77 #endif
     78 }
     79 
     80 static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) {
     81   struct flock lock;
     82   lock.l_type = F_WRLCK;
     83   lock.l_whence = SEEK_SET;
     84   lock.l_start = 0;
     85   lock.l_len = 0;  // Lock entire file.
     86   if (HANDLE_EINTR(fcntl(file, do_lock ? F_SETLK : F_UNLCK, &lock)) == -1)
     87     return File::OSErrorToFileError(errno);
     88   return File::FILE_OK;
     89 }
     90 #else  // defined(OS_NACL)
     91 
     92 static bool IsOpenAppend(PlatformFile file) {
     93   // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
     94   // standard and always appends if the file is opened with O_APPEND, just
     95   // return false here.
     96   return false;
     97 }
     98 
     99 static int CallFtruncate(PlatformFile file, int64 length) {
    100   NOTIMPLEMENTED();  // NaCl doesn't implement ftruncate.
    101   return 0;
    102 }
    103 
    104 static int CallFsync(PlatformFile file) {
    105   NOTIMPLEMENTED();  // NaCl doesn't implement fsync.
    106   return 0;
    107 }
    108 
    109 static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
    110   NOTIMPLEMENTED();  // NaCl doesn't implement futimes.
    111   return 0;
    112 }
    113 
    114 static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) {
    115   NOTIMPLEMENTED();  // NaCl doesn't implement flock struct.
    116   return File::FILE_ERROR_INVALID_OPERATION;
    117 }
    118 #endif  // defined(OS_NACL)
    119 
    120 }  // namespace
    121 
    122 // NaCl doesn't implement system calls to open files directly.
    123 #if !defined(OS_NACL)
    124 // TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
    125 void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
    126   base::ThreadRestrictions::AssertIOAllowed();
    127   DCHECK(!IsValid());
    128   DCHECK(!(flags & FLAG_ASYNC));
    129 
    130   int open_flags = 0;
    131   if (flags & FLAG_CREATE)
    132     open_flags = O_CREAT | O_EXCL;
    133 
    134   created_ = false;
    135 
    136   if (flags & FLAG_CREATE_ALWAYS) {
    137     DCHECK(!open_flags);
    138     open_flags = O_CREAT | O_TRUNC;
    139   }
    140 
    141   if (flags & FLAG_OPEN_TRUNCATED) {
    142     DCHECK(!open_flags);
    143     DCHECK(flags & FLAG_WRITE);
    144     open_flags = O_TRUNC;
    145   }
    146 
    147   if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
    148     NOTREACHED();
    149     errno = EOPNOTSUPP;
    150     error_ = FILE_ERROR_FAILED;
    151     return;
    152   }
    153 
    154   if (flags & FLAG_WRITE && flags & FLAG_READ) {
    155     open_flags |= O_RDWR;
    156   } else if (flags & FLAG_WRITE) {
    157     open_flags |= O_WRONLY;
    158   } else if (!(flags & FLAG_READ) &&
    159              !(flags & FLAG_WRITE_ATTRIBUTES) &&
    160              !(flags & FLAG_APPEND) &&
    161              !(flags & FLAG_OPEN_ALWAYS)) {
    162     NOTREACHED();
    163   }
    164 
    165   if (flags & FLAG_TERMINAL_DEVICE)
    166     open_flags |= O_NOCTTY | O_NDELAY;
    167 
    168   if (flags & FLAG_APPEND && flags & FLAG_READ)
    169     open_flags |= O_APPEND | O_RDWR;
    170   else if (flags & FLAG_APPEND)
    171     open_flags |= O_APPEND | O_WRONLY;
    172 
    173   COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero);
    174 
    175   int mode = S_IRUSR | S_IWUSR;
    176 #if defined(OS_CHROMEOS)
    177   mode |= S_IRGRP | S_IROTH;
    178 #endif
    179 
    180   int descriptor = HANDLE_EINTR(open(name.value().c_str(), open_flags, mode));
    181 
    182   if (flags & FLAG_OPEN_ALWAYS) {
    183     if (descriptor < 0) {
    184       open_flags |= O_CREAT;
    185       if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_EXCLUSIVE_WRITE)
    186         open_flags |= O_EXCL;   // together with O_CREAT implies O_NOFOLLOW
    187 
    188       descriptor = HANDLE_EINTR(open(name.value().c_str(), open_flags, mode));
    189       if (descriptor >= 0)
    190         created_ = true;
    191     }
    192   }
    193 
    194   if (descriptor >= 0 && (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)))
    195     created_ = true;
    196 
    197   if ((descriptor >= 0) && (flags & FLAG_DELETE_ON_CLOSE))
    198     unlink(name.value().c_str());
    199 
    200   if (descriptor >= 0)
    201     error_ = FILE_OK;
    202   else
    203     error_ = File::OSErrorToFileError(errno);
    204 
    205   file_ = descriptor;
    206 }
    207 #endif  // !defined(OS_NACL)
    208 
    209 bool File::IsValid() const {
    210   return file_ >= 0;
    211 }
    212 
    213 PlatformFile File::TakePlatformFile() {
    214   PlatformFile file = file_;
    215   file_ = kInvalidPlatformFileValue;
    216   return file;
    217 }
    218 
    219 void File::Close() {
    220   base::ThreadRestrictions::AssertIOAllowed();
    221   if (!IsValid())
    222     return;
    223 
    224   if (!IGNORE_EINTR(close(file_)))
    225     file_ = kInvalidPlatformFileValue;
    226 }
    227 
    228 int64 File::Seek(Whence whence, int64 offset) {
    229   base::ThreadRestrictions::AssertIOAllowed();
    230   DCHECK(IsValid());
    231   if (file_ < 0 || offset < 0)
    232     return -1;
    233 
    234   return lseek(file_, static_cast<off_t>(offset), static_cast<int>(whence));
    235 }
    236 
    237 int File::Read(int64 offset, char* data, int size) {
    238   base::ThreadRestrictions::AssertIOAllowed();
    239   DCHECK(IsValid());
    240   if (size < 0)
    241     return -1;
    242 
    243   int bytes_read = 0;
    244   int rv;
    245   do {
    246     rv = HANDLE_EINTR(pread(file_, data + bytes_read,
    247                             size - bytes_read, offset + bytes_read));
    248     if (rv <= 0)
    249       break;
    250 
    251     bytes_read += rv;
    252   } while (bytes_read < size);
    253 
    254   return bytes_read ? bytes_read : rv;
    255 }
    256 
    257 int File::ReadAtCurrentPos(char* data, int size) {
    258   base::ThreadRestrictions::AssertIOAllowed();
    259   DCHECK(IsValid());
    260   if (size < 0)
    261     return -1;
    262 
    263   int bytes_read = 0;
    264   int rv;
    265   do {
    266     rv = HANDLE_EINTR(read(file_, data, size));
    267     if (rv <= 0)
    268       break;
    269 
    270     bytes_read += rv;
    271   } while (bytes_read < size);
    272 
    273   return bytes_read ? bytes_read : rv;
    274 }
    275 
    276 int File::ReadNoBestEffort(int64 offset, char* data, int size) {
    277   base::ThreadRestrictions::AssertIOAllowed();
    278   DCHECK(IsValid());
    279 
    280   return HANDLE_EINTR(pread(file_, data, size, offset));
    281 }
    282 
    283 int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
    284   base::ThreadRestrictions::AssertIOAllowed();
    285   DCHECK(IsValid());
    286   if (size < 0)
    287     return -1;
    288 
    289   return HANDLE_EINTR(read(file_, data, size));
    290 }
    291 
    292 int File::Write(int64 offset, const char* data, int size) {
    293   base::ThreadRestrictions::AssertIOAllowed();
    294 
    295   if (IsOpenAppend(file_))
    296     return WriteAtCurrentPos(data, size);
    297 
    298   DCHECK(IsValid());
    299   if (size < 0)
    300     return -1;
    301 
    302   int bytes_written = 0;
    303   int rv;
    304   do {
    305     rv = HANDLE_EINTR(pwrite(file_, data + bytes_written,
    306                              size - bytes_written, offset + bytes_written));
    307     if (rv <= 0)
    308       break;
    309 
    310     bytes_written += rv;
    311   } while (bytes_written < size);
    312 
    313   return bytes_written ? bytes_written : rv;
    314 }
    315 
    316 int File::WriteAtCurrentPos(const char* data, int size) {
    317   base::ThreadRestrictions::AssertIOAllowed();
    318   DCHECK(IsValid());
    319   if (size < 0)
    320     return -1;
    321 
    322   int bytes_written = 0;
    323   int rv;
    324   do {
    325     rv = HANDLE_EINTR(write(file_, data, size));
    326     if (rv <= 0)
    327       break;
    328 
    329     bytes_written += rv;
    330   } while (bytes_written < size);
    331 
    332   return bytes_written ? bytes_written : rv;
    333 }
    334 
    335 int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
    336   base::ThreadRestrictions::AssertIOAllowed();
    337   DCHECK(IsValid());
    338   if (size < 0)
    339     return -1;
    340 
    341   return HANDLE_EINTR(write(file_, data, size));
    342 }
    343 
    344 bool File::Truncate(int64 length) {
    345   base::ThreadRestrictions::AssertIOAllowed();
    346   DCHECK(IsValid());
    347   return !CallFtruncate(file_, length);
    348 }
    349 
    350 bool File::Flush() {
    351   base::ThreadRestrictions::AssertIOAllowed();
    352   DCHECK(IsValid());
    353   return !CallFsync(file_);
    354 }
    355 
    356 bool File::SetTimes(Time last_access_time, Time last_modified_time) {
    357   base::ThreadRestrictions::AssertIOAllowed();
    358   DCHECK(IsValid());
    359 
    360   timeval times[2];
    361   times[0] = last_access_time.ToTimeVal();
    362   times[1] = last_modified_time.ToTimeVal();
    363 
    364   return !CallFutimes(file_, times);
    365 }
    366 
    367 bool File::GetInfo(Info* info) {
    368   DCHECK(IsValid());
    369 
    370   stat_wrapper_t file_info;
    371   if (CallFstat(file_, &file_info))
    372     return false;
    373 
    374   info->is_directory = S_ISDIR(file_info.st_mode);
    375   info->is_symbolic_link = S_ISLNK(file_info.st_mode);
    376   info->size = file_info.st_size;
    377 
    378 #if defined(OS_LINUX)
    379   const time_t last_modified_sec = file_info.st_mtim.tv_sec;
    380   const int64 last_modified_nsec = file_info.st_mtim.tv_nsec;
    381   const time_t last_accessed_sec = file_info.st_atim.tv_sec;
    382   const int64 last_accessed_nsec = file_info.st_atim.tv_nsec;
    383   const time_t creation_time_sec = file_info.st_ctim.tv_sec;
    384   const int64 creation_time_nsec = file_info.st_ctim.tv_nsec;
    385 #elif defined(OS_ANDROID)
    386   const time_t last_modified_sec = file_info.st_mtime;
    387   const int64 last_modified_nsec = file_info.st_mtime_nsec;
    388   const time_t last_accessed_sec = file_info.st_atime;
    389   const int64 last_accessed_nsec = file_info.st_atime_nsec;
    390   const time_t creation_time_sec = file_info.st_ctime;
    391   const int64 creation_time_nsec = file_info.st_ctime_nsec;
    392 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
    393   const time_t last_modified_sec = file_info.st_mtimespec.tv_sec;
    394   const int64 last_modified_nsec = file_info.st_mtimespec.tv_nsec;
    395   const time_t last_accessed_sec = file_info.st_atimespec.tv_sec;
    396   const int64 last_accessed_nsec = file_info.st_atimespec.tv_nsec;
    397   const time_t creation_time_sec = file_info.st_ctimespec.tv_sec;
    398   const int64 creation_time_nsec = file_info.st_ctimespec.tv_nsec;
    399 #else
    400   // TODO(gavinp): Investigate a good high resolution option for OS_NACL.
    401   const time_t last_modified_sec = file_info.st_mtime;
    402   const int64 last_modified_nsec = 0;
    403   const time_t last_accessed_sec = file_info.st_atime;
    404   const int64 last_accessed_nsec = 0;
    405   const time_t creation_time_sec = file_info.st_ctime;
    406   const int64 creation_time_nsec = 0;
    407 #endif
    408 
    409   info->last_modified =
    410       base::Time::FromTimeT(last_modified_sec) +
    411       base::TimeDelta::FromMicroseconds(last_modified_nsec /
    412                                         base::Time::kNanosecondsPerMicrosecond);
    413   info->last_accessed =
    414       base::Time::FromTimeT(last_accessed_sec) +
    415       base::TimeDelta::FromMicroseconds(last_accessed_nsec /
    416                                         base::Time::kNanosecondsPerMicrosecond);
    417   info->creation_time =
    418       base::Time::FromTimeT(creation_time_sec) +
    419       base::TimeDelta::FromMicroseconds(creation_time_nsec /
    420                                         base::Time::kNanosecondsPerMicrosecond);
    421   return true;
    422 }
    423 
    424 File::Error File::Lock() {
    425   return CallFctnlFlock(file_, true);
    426 }
    427 
    428 File::Error File::Unlock() {
    429   return CallFctnlFlock(file_, false);
    430 }
    431 
    432 // Static.
    433 File::Error File::OSErrorToFileError(int saved_errno) {
    434   switch (saved_errno) {
    435     case EACCES:
    436     case EISDIR:
    437     case EROFS:
    438     case EPERM:
    439       return FILE_ERROR_ACCESS_DENIED;
    440 #if !defined(OS_NACL)  // ETXTBSY not defined by NaCl.
    441     case ETXTBSY:
    442       return FILE_ERROR_IN_USE;
    443 #endif
    444     case EEXIST:
    445       return FILE_ERROR_EXISTS;
    446     case ENOENT:
    447       return FILE_ERROR_NOT_FOUND;
    448     case EMFILE:
    449       return FILE_ERROR_TOO_MANY_OPENED;
    450     case ENOMEM:
    451       return FILE_ERROR_NO_MEMORY;
    452     case ENOSPC:
    453       return FILE_ERROR_NO_SPACE;
    454     case ENOTDIR:
    455       return FILE_ERROR_NOT_A_DIRECTORY;
    456     default:
    457 #if !defined(OS_NACL)  // NaCl build has no metrics code.
    458       UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
    459                                   saved_errno);
    460 #endif
    461       return FILE_ERROR_FAILED;
    462   }
    463 }
    464 
    465 void File::SetPlatformFile(PlatformFile file) {
    466   DCHECK_EQ(file_, kInvalidPlatformFileValue);
    467   file_ = file;
    468 }
    469 
    470 }  // namespace base
    471