Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2009 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 
     11 #include "base/eintr_wrapper.h"
     12 #include "base/file_path.h"
     13 #include "base/logging.h"
     14 #include "base/utf_string_conversions.h"
     15 
     16 namespace base {
     17 
     18 #if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \
     19     (defined(OS_MACOSX) && \
     20      MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
     21 typedef struct stat stat_wrapper_t;
     22 static int CallFstat(int fd, stat_wrapper_t *sb) {
     23   return fstat(fd, sb);
     24 }
     25 #else
     26 typedef struct stat64 stat_wrapper_t;
     27 static int CallFstat(int fd, stat_wrapper_t *sb) {
     28   return fstat64(fd, sb);
     29 }
     30 #endif
     31 
     32 // TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
     33 PlatformFile CreatePlatformFile(const FilePath& name, int flags,
     34                                 bool* created, PlatformFileError* error_code) {
     35   int open_flags = 0;
     36   if (flags & PLATFORM_FILE_CREATE)
     37     open_flags = O_CREAT | O_EXCL;
     38 
     39   if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
     40     DCHECK(!open_flags);
     41     open_flags = O_CREAT | O_TRUNC;
     42   }
     43 
     44   if (!open_flags && !(flags & PLATFORM_FILE_OPEN) &&
     45       !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
     46     NOTREACHED();
     47     errno = EOPNOTSUPP;
     48     *error_code = error_code ? PLATFORM_FILE_ERROR_FAILED : PLATFORM_FILE_OK;
     49     return kInvalidPlatformFileValue;
     50   }
     51 
     52   if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) {
     53     open_flags |= O_RDWR;
     54   } else if (flags & PLATFORM_FILE_WRITE) {
     55     open_flags |= O_WRONLY;
     56   } else if (!(flags & PLATFORM_FILE_READ) &&
     57              !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) &&
     58              !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
     59     NOTREACHED();
     60   }
     61 
     62   if (flags & PLATFORM_FILE_TRUNCATE) {
     63     DCHECK(flags & PLATFORM_FILE_WRITE);
     64     open_flags |= O_TRUNC;
     65   }
     66 
     67   COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero);
     68 
     69   int descriptor =
     70       HANDLE_EINTR(open(name.value().c_str(), open_flags, S_IRUSR | S_IWUSR));
     71 
     72   if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
     73     if (descriptor > 0) {
     74       if (created)
     75         *created = false;
     76     } else {
     77       open_flags |= O_CREAT;
     78       if (flags & PLATFORM_FILE_EXCLUSIVE_READ ||
     79           flags & PLATFORM_FILE_EXCLUSIVE_WRITE) {
     80         open_flags |= O_EXCL;   // together with O_CREAT implies O_NOFOLLOW
     81       }
     82       descriptor = HANDLE_EINTR(
     83           open(name.value().c_str(), open_flags, S_IRUSR | S_IWUSR));
     84       if (created && descriptor > 0)
     85         *created = true;
     86     }
     87   }
     88 
     89   if (created && (descriptor > 0) &&
     90       (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE)))
     91     *created = true;
     92 
     93   if ((descriptor > 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) {
     94     unlink(name.value().c_str());
     95   }
     96 
     97   if (error_code) {
     98     if (descriptor >= 0)
     99       *error_code = PLATFORM_FILE_OK;
    100     else {
    101       switch (errno) {
    102         case EACCES:
    103         case EISDIR:
    104         case EROFS:
    105         case EPERM:
    106           *error_code = PLATFORM_FILE_ERROR_ACCESS_DENIED;
    107           break;
    108         case ETXTBSY:
    109           *error_code = PLATFORM_FILE_ERROR_IN_USE;
    110           break;
    111         case EEXIST:
    112           *error_code = PLATFORM_FILE_ERROR_EXISTS;
    113           break;
    114         case ENOENT:
    115           *error_code = PLATFORM_FILE_ERROR_NOT_FOUND;
    116           break;
    117         case EMFILE:
    118           *error_code = PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
    119           break;
    120         case ENOMEM:
    121           *error_code = PLATFORM_FILE_ERROR_NO_MEMORY;
    122           break;
    123         case ENOSPC:
    124           *error_code = PLATFORM_FILE_ERROR_NO_SPACE;
    125           break;
    126         case ENOTDIR:
    127           *error_code = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    128           break;
    129         default:
    130           *error_code = PLATFORM_FILE_ERROR_FAILED;
    131       }
    132     }
    133   }
    134 
    135   return descriptor;
    136 }
    137 
    138 bool ClosePlatformFile(PlatformFile file) {
    139   return !HANDLE_EINTR(close(file));
    140 }
    141 
    142 int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
    143   if (file < 0)
    144     return -1;
    145 
    146   return HANDLE_EINTR(pread(file, data, size, offset));
    147 }
    148 
    149 int WritePlatformFile(PlatformFile file, int64 offset,
    150                       const char* data, int size) {
    151   if (file < 0)
    152     return -1;
    153 
    154   return HANDLE_EINTR(pwrite(file, data, size, offset));
    155 }
    156 
    157 bool TruncatePlatformFile(PlatformFile file, int64 length) {
    158   return ((file >= 0) && !HANDLE_EINTR(ftruncate(file, length)));
    159 }
    160 
    161 bool FlushPlatformFile(PlatformFile file) {
    162   return !HANDLE_EINTR(fsync(file));
    163 }
    164 
    165 bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time,
    166                        const base::Time& last_modified_time) {
    167 #ifdef ANDROID
    168   // futimes() isn't actually POSIX.
    169   // TODO: Check all callers to see if this needs to work correctly on Android.
    170   return false;
    171 #else
    172   if (file < 0)
    173     return false;
    174 
    175   timeval times[2];
    176   times[0] = last_access_time.ToTimeVal();
    177   times[1] = last_modified_time.ToTimeVal();
    178   return !futimes(file, times);
    179 #endif
    180 }
    181 
    182 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
    183   if (!info)
    184     return false;
    185 
    186   stat_wrapper_t file_info;
    187   if (CallFstat(file, &file_info))
    188     return false;
    189 
    190   info->is_directory = S_ISDIR(file_info.st_mode);
    191   info->is_symbolic_link = S_ISLNK(file_info.st_mode);
    192   info->size = file_info.st_size;
    193   info->last_modified = base::Time::FromTimeT(file_info.st_mtime);
    194   info->last_accessed = base::Time::FromTimeT(file_info.st_atime);
    195   info->creation_time = base::Time::FromTimeT(file_info.st_ctime);
    196   return true;
    197 }
    198 
    199 }  // namespace base
    200