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