Home | History | Annotate | Download | only in base
      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/platform_file.h"
      6 
      7 #include <fcntl.h>
      8 #include <errno.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 #include "base/posix/eintr_wrapper.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/threading/thread_restrictions.h"
     18 
     19 #if defined(OS_ANDROID)
     20 #include "base/os_compat_android.h"
     21 #endif
     22 
     23 namespace base {
     24 
     25 // Make sure our Whence mappings match the system headers.
     26 COMPILE_ASSERT(PLATFORM_FILE_FROM_BEGIN   == SEEK_SET &&
     27                PLATFORM_FILE_FROM_CURRENT == SEEK_CUR &&
     28                PLATFORM_FILE_FROM_END     == SEEK_END, whence_matches_system);
     29 
     30 namespace {
     31 
     32 #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
     33 typedef struct stat stat_wrapper_t;
     34 static int CallFstat(int fd, stat_wrapper_t *sb) {
     35   base::ThreadRestrictions::AssertIOAllowed();
     36   return fstat(fd, sb);
     37 }
     38 #else
     39 typedef struct stat64 stat_wrapper_t;
     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 PlatformFileError 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 ErrnoToPlatformFileError(errno);
     86   return PLATFORM_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 PlatformFileError CallFctnlFlock(PlatformFile file, bool do_lock) {
    113   NOTIMPLEMENTED();  // NaCl doesn't implement flock struct.
    114   return PLATFORM_FILE_ERROR_INVALID_OPERATION;
    115 }
    116 #endif  // defined(OS_NACL)
    117 
    118 }  // namespace
    119 
    120 // NaCl doesn't implement system calls to open files directly.
    121 #if !defined(OS_NACL)
    122 // TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
    123 PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
    124                                       int flags,
    125                                       bool* created,
    126                                       PlatformFileError* error) {
    127   base::ThreadRestrictions::AssertIOAllowed();
    128 
    129   int open_flags = 0;
    130   if (flags & PLATFORM_FILE_CREATE)
    131     open_flags = O_CREAT | O_EXCL;
    132 
    133   if (created)
    134     *created = false;
    135 
    136   if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
    137     DCHECK(!open_flags);
    138     open_flags = O_CREAT | O_TRUNC;
    139   }
    140 
    141   if (flags & PLATFORM_FILE_OPEN_TRUNCATED) {
    142     DCHECK(!open_flags);
    143     DCHECK(flags & PLATFORM_FILE_WRITE);
    144     open_flags = O_TRUNC;
    145   }
    146 
    147   if (!open_flags && !(flags & PLATFORM_FILE_OPEN) &&
    148       !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
    149     NOTREACHED();
    150     errno = EOPNOTSUPP;
    151     if (error)
    152       *error = PLATFORM_FILE_ERROR_FAILED;
    153     return kInvalidPlatformFileValue;
    154   }
    155 
    156   if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) {
    157     open_flags |= O_RDWR;
    158   } else if (flags & PLATFORM_FILE_WRITE) {
    159     open_flags |= O_WRONLY;
    160   } else if (!(flags & PLATFORM_FILE_READ) &&
    161              !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) &&
    162              !(flags & PLATFORM_FILE_APPEND) &&
    163              !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
    164     NOTREACHED();
    165   }
    166 
    167   if (flags & PLATFORM_FILE_TERMINAL_DEVICE)
    168     open_flags |= O_NOCTTY | O_NDELAY;
    169 
    170   if (flags & PLATFORM_FILE_APPEND && flags & PLATFORM_FILE_READ)
    171     open_flags |= O_APPEND | O_RDWR;
    172   else if (flags & PLATFORM_FILE_APPEND)
    173     open_flags |= O_APPEND | O_WRONLY;
    174 
    175   COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero);
    176 
    177   int mode = S_IRUSR | S_IWUSR;
    178 #if defined(OS_CHROMEOS)
    179   mode |= S_IRGRP | S_IROTH;
    180 #endif
    181 
    182   int descriptor =
    183       HANDLE_EINTR(open(name.value().c_str(), open_flags, mode));
    184 
    185   if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
    186     if (descriptor < 0) {
    187       open_flags |= O_CREAT;
    188       if (flags & PLATFORM_FILE_EXCLUSIVE_READ ||
    189           flags & PLATFORM_FILE_EXCLUSIVE_WRITE) {
    190         open_flags |= O_EXCL;   // together with O_CREAT implies O_NOFOLLOW
    191       }
    192       descriptor = HANDLE_EINTR(
    193           open(name.value().c_str(), open_flags, mode));
    194       if (created && descriptor >= 0)
    195         *created = true;
    196     }
    197   }
    198 
    199   if (created && (descriptor >= 0) &&
    200       (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE)))
    201     *created = true;
    202 
    203   if ((descriptor >= 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) {
    204     unlink(name.value().c_str());
    205   }
    206 
    207   if (error) {
    208     if (descriptor >= 0)
    209       *error = PLATFORM_FILE_OK;
    210     else
    211       *error = ErrnoToPlatformFileError(errno);
    212   }
    213 
    214   return descriptor;
    215 }
    216 
    217 FILE* FdopenPlatformFile(PlatformFile file, const char* mode) {
    218   return fdopen(file, mode);
    219 }
    220 #endif  // !defined(OS_NACL)
    221 
    222 bool ClosePlatformFile(PlatformFile file) {
    223   base::ThreadRestrictions::AssertIOAllowed();
    224   return !IGNORE_EINTR(close(file));
    225 }
    226 
    227 int64 SeekPlatformFile(PlatformFile file,
    228                        PlatformFileWhence whence,
    229                        int64 offset) {
    230   base::ThreadRestrictions::AssertIOAllowed();
    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 ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
    238   base::ThreadRestrictions::AssertIOAllowed();
    239   if (file < 0 || size < 0)
    240     return -1;
    241 
    242   int bytes_read = 0;
    243   int rv;
    244   do {
    245     rv = HANDLE_EINTR(pread(file, data + bytes_read,
    246                             size - bytes_read, offset + bytes_read));
    247     if (rv <= 0)
    248       break;
    249 
    250     bytes_read += rv;
    251   } while (bytes_read < size);
    252 
    253   return bytes_read ? bytes_read : rv;
    254 }
    255 
    256 int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) {
    257   base::ThreadRestrictions::AssertIOAllowed();
    258   if (file < 0 || size < 0)
    259     return -1;
    260 
    261   int bytes_read = 0;
    262   int rv;
    263   do {
    264     rv = HANDLE_EINTR(read(file, data, size));
    265     if (rv <= 0)
    266       break;
    267 
    268     bytes_read += rv;
    269   } while (bytes_read < size);
    270 
    271   return bytes_read ? bytes_read : rv;
    272 }
    273 
    274 int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset,
    275                                  char* data, int size) {
    276   base::ThreadRestrictions::AssertIOAllowed();
    277   if (file < 0)
    278     return -1;
    279 
    280   return HANDLE_EINTR(pread(file, data, size, offset));
    281 }
    282 
    283 int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
    284                                        char* data, int size) {
    285   base::ThreadRestrictions::AssertIOAllowed();
    286   if (file < 0 || size < 0)
    287     return -1;
    288 
    289   return HANDLE_EINTR(read(file, data, size));
    290 }
    291 
    292 int WritePlatformFile(PlatformFile file, int64 offset,
    293                       const char* data, int size) {
    294   base::ThreadRestrictions::AssertIOAllowed();
    295 
    296   if (IsOpenAppend(file))
    297     return WritePlatformFileAtCurrentPos(file, data, size);
    298 
    299   if (file < 0 || 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 WritePlatformFileAtCurrentPos(PlatformFile file,
    317                                   const char* data, int size) {
    318   base::ThreadRestrictions::AssertIOAllowed();
    319   if (file < 0 || 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 WritePlatformFileCurPosNoBestEffort(PlatformFile file,
    336                                         const char* data, int size) {
    337   base::ThreadRestrictions::AssertIOAllowed();
    338   if (file < 0 || size < 0)
    339     return -1;
    340 
    341   return HANDLE_EINTR(write(file, data, size));
    342 }
    343 
    344 bool TruncatePlatformFile(PlatformFile file, int64 length) {
    345   base::ThreadRestrictions::AssertIOAllowed();
    346   return ((file >= 0) && !CallFtruncate(file, length));
    347 }
    348 
    349 bool FlushPlatformFile(PlatformFile file) {
    350   base::ThreadRestrictions::AssertIOAllowed();
    351   return !CallFsync(file);
    352 }
    353 
    354 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time,
    355                        const base::Time& last_modified_time) {
    356   base::ThreadRestrictions::AssertIOAllowed();
    357   if (file < 0)
    358     return false;
    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 GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
    368   if (!info)
    369     return false;
    370 
    371   stat_wrapper_t file_info;
    372   if (CallFstat(file, &file_info))
    373     return false;
    374 
    375   info->is_directory = S_ISDIR(file_info.st_mode);
    376   info->is_symbolic_link = S_ISLNK(file_info.st_mode);
    377   info->size = file_info.st_size;
    378 
    379 #if defined(OS_LINUX)
    380   const time_t last_modified_sec = file_info.st_mtim.tv_sec;
    381   const int64 last_modified_nsec = file_info.st_mtim.tv_nsec;
    382   const time_t last_accessed_sec = file_info.st_atim.tv_sec;
    383   const int64 last_accessed_nsec = file_info.st_atim.tv_nsec;
    384   const time_t creation_time_sec = file_info.st_ctim.tv_sec;
    385   const int64 creation_time_nsec = file_info.st_ctim.tv_nsec;
    386 #elif defined(OS_ANDROID)
    387   const time_t last_modified_sec = file_info.st_mtime;
    388   const int64 last_modified_nsec = file_info.st_mtime_nsec;
    389   const time_t last_accessed_sec = file_info.st_atime;
    390   const int64 last_accessed_nsec = file_info.st_atime_nsec;
    391   const time_t creation_time_sec = file_info.st_ctime;
    392   const int64 creation_time_nsec = file_info.st_ctime_nsec;
    393 #elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
    394   const time_t last_modified_sec = file_info.st_mtimespec.tv_sec;
    395   const int64 last_modified_nsec = file_info.st_mtimespec.tv_nsec;
    396   const time_t last_accessed_sec = file_info.st_atimespec.tv_sec;
    397   const int64 last_accessed_nsec = file_info.st_atimespec.tv_nsec;
    398   const time_t creation_time_sec = file_info.st_ctimespec.tv_sec;
    399   const int64 creation_time_nsec = file_info.st_ctimespec.tv_nsec;
    400 #else
    401   // TODO(gavinp): Investigate a good high resolution option for OS_NACL.
    402   const time_t last_modified_sec = file_info.st_mtime;
    403   const int64 last_modified_nsec = 0;
    404   const time_t last_accessed_sec = file_info.st_atime;
    405   const int64 last_accessed_nsec = 0;
    406   const time_t creation_time_sec = file_info.st_ctime;
    407   const int64 creation_time_nsec = 0;
    408 #endif
    409 
    410   info->last_modified =
    411       base::Time::FromTimeT(last_modified_sec) +
    412       base::TimeDelta::FromMicroseconds(last_modified_nsec /
    413                                         base::Time::kNanosecondsPerMicrosecond);
    414   info->last_accessed =
    415       base::Time::FromTimeT(last_accessed_sec) +
    416       base::TimeDelta::FromMicroseconds(last_accessed_nsec /
    417                                         base::Time::kNanosecondsPerMicrosecond);
    418   info->creation_time =
    419       base::Time::FromTimeT(creation_time_sec) +
    420       base::TimeDelta::FromMicroseconds(creation_time_nsec /
    421                                         base::Time::kNanosecondsPerMicrosecond);
    422   return true;
    423 }
    424 
    425 PlatformFileError LockPlatformFile(PlatformFile file) {
    426   return CallFctnlFlock(file, true);
    427 }
    428 
    429 PlatformFileError UnlockPlatformFile(PlatformFile file) {
    430   return CallFctnlFlock(file, false);
    431 }
    432 
    433 PlatformFileError ErrnoToPlatformFileError(int saved_errno) {
    434   switch (saved_errno) {
    435     case EACCES:
    436     case EISDIR:
    437     case EROFS:
    438     case EPERM:
    439       return PLATFORM_FILE_ERROR_ACCESS_DENIED;
    440 #if !defined(OS_NACL)  // ETXTBSY not defined by NaCl.
    441     case ETXTBSY:
    442       return PLATFORM_FILE_ERROR_IN_USE;
    443 #endif
    444     case EEXIST:
    445       return PLATFORM_FILE_ERROR_EXISTS;
    446     case ENOENT:
    447       return PLATFORM_FILE_ERROR_NOT_FOUND;
    448     case EMFILE:
    449       return PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
    450     case ENOMEM:
    451       return PLATFORM_FILE_ERROR_NO_MEMORY;
    452     case ENOSPC:
    453       return PLATFORM_FILE_ERROR_NO_SPACE;
    454     case ENOTDIR:
    455       return PLATFORM_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 PLATFORM_FILE_ERROR_FAILED;
    462   }
    463 }
    464 
    465 }  // namespace base
    466