Home | History | Annotate | Download | only in fileapi
      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 "storage/browser/fileapi/native_file_util.h"
      6 
      7 #include "base/files/file.h"
      8 #include "base/files/file_enumerator.h"
      9 #include "base/files/file_util.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "storage/browser/fileapi/file_system_operation_context.h"
     12 #include "storage/browser/fileapi/file_system_url.h"
     13 
     14 namespace storage {
     15 
     16 namespace {
     17 
     18 // Sets permissions on directory at |dir_path| based on the target platform.
     19 // Returns true on success, or false otherwise.
     20 //
     21 // TODO(benchan): Find a better place outside webkit to host this function.
     22 bool SetPlatformSpecificDirectoryPermissions(const base::FilePath& dir_path) {
     23 #if defined(OS_CHROMEOS)
     24     // System daemons on Chrome OS may run as a user different than the Chrome
     25     // process but need to access files under the directories created here.
     26     // Because of that, grant the execute permission on the created directory
     27     // to group and other users.
     28     if (HANDLE_EINTR(chmod(dir_path.value().c_str(),
     29                            S_IRWXU | S_IXGRP | S_IXOTH)) != 0) {
     30       return false;
     31     }
     32 #endif
     33     // Keep the directory permissions unchanged on non-Chrome OS platforms.
     34     return true;
     35 }
     36 
     37 // Copies a file |from| to |to|, and ensure the written content is synced to
     38 // the disk. This is essentially base::CopyFile followed by fsync().
     39 bool CopyFileAndSync(const base::FilePath& from, const base::FilePath& to) {
     40   base::File infile(from, base::File::FLAG_OPEN | base::File::FLAG_READ);
     41   if (!infile.IsValid()) {
     42     return false;
     43   }
     44 
     45   base::File outfile(to,
     46                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
     47   if (!outfile.IsValid()) {
     48     return false;
     49   }
     50 
     51   const int kBufferSize = 32768;
     52   std::vector<char> buffer(kBufferSize);
     53 
     54   for (;;) {
     55     int bytes_read = infile.ReadAtCurrentPos(&buffer[0], kBufferSize);
     56     if (bytes_read < 0)
     57       return false;
     58     if (bytes_read == 0)
     59       break;
     60     for (int bytes_written = 0; bytes_written < bytes_read; ) {
     61       int bytes_written_partial = outfile.WriteAtCurrentPos(
     62           &buffer[bytes_written], bytes_read - bytes_written);
     63       if (bytes_written_partial < 0)
     64         return false;
     65       bytes_written += bytes_written_partial;
     66     }
     67   }
     68 
     69   return outfile.Flush();
     70 }
     71 
     72 }  // namespace
     73 
     74 using base::PlatformFile;
     75 
     76 class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
     77  public:
     78   NativeFileEnumerator(const base::FilePath& root_path,
     79                        bool recursive,
     80                        int file_type)
     81     : file_enum_(root_path, recursive, file_type) {
     82   }
     83 
     84   virtual ~NativeFileEnumerator() {}
     85 
     86   virtual base::FilePath Next() OVERRIDE;
     87   virtual int64 Size() OVERRIDE;
     88   virtual base::Time LastModifiedTime() OVERRIDE;
     89   virtual bool IsDirectory() OVERRIDE;
     90 
     91  private:
     92   base::FileEnumerator file_enum_;
     93   base::FileEnumerator::FileInfo file_util_info_;
     94 };
     95 
     96 base::FilePath NativeFileEnumerator::Next() {
     97   base::FilePath rv = file_enum_.Next();
     98   if (!rv.empty())
     99     file_util_info_ = file_enum_.GetInfo();
    100   return rv;
    101 }
    102 
    103 int64 NativeFileEnumerator::Size() {
    104   return file_util_info_.GetSize();
    105 }
    106 
    107 base::Time NativeFileEnumerator::LastModifiedTime() {
    108   return file_util_info_.GetLastModifiedTime();
    109 }
    110 
    111 bool NativeFileEnumerator::IsDirectory() {
    112   return file_util_info_.IsDirectory();
    113 }
    114 
    115 NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination(
    116     const FileSystemURL& dest_url, bool copy) {
    117   if (copy) {
    118     return dest_url.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC ?
    119         COPY_SYNC : COPY_NOSYNC;
    120   }
    121   return MOVE;
    122 }
    123 
    124 base::File NativeFileUtil::CreateOrOpen(const base::FilePath& path,
    125                                         int file_flags) {
    126   if (!base::DirectoryExists(path.DirName())) {
    127     // If its parent does not exist, should return NOT_FOUND error.
    128     return base::File(base::File::FILE_ERROR_NOT_FOUND);
    129   }
    130 
    131   // TODO(rvargas): Check |file_flags| instead. See bug 356358.
    132   if (base::DirectoryExists(path))
    133     return base::File(base::File::FILE_ERROR_NOT_A_FILE);
    134 
    135   return base::File(path, file_flags);
    136 }
    137 
    138 base::File::Error NativeFileUtil::EnsureFileExists(
    139     const base::FilePath& path,
    140     bool* created) {
    141   if (!base::DirectoryExists(path.DirName()))
    142     // If its parent does not exist, should return NOT_FOUND error.
    143     return base::File::FILE_ERROR_NOT_FOUND;
    144 
    145   // Tries to create the |path| exclusively.  This should fail
    146   // with base::File::FILE_ERROR_EXISTS if the path already exists.
    147   base::File file(path, base::File::FLAG_CREATE | base::File::FLAG_READ);
    148 
    149   if (file.IsValid()) {
    150     if (created)
    151       *created = file.created();
    152     return base::File::FILE_OK;
    153   }
    154 
    155   base::File::Error error_code = file.error_details();
    156   if (error_code == base::File::FILE_ERROR_EXISTS) {
    157     // Make sure created_ is false.
    158     if (created)
    159       *created = false;
    160     error_code = base::File::FILE_OK;
    161   }
    162   return error_code;
    163 }
    164 
    165 base::File::Error NativeFileUtil::CreateDirectory(
    166     const base::FilePath& path,
    167     bool exclusive,
    168     bool recursive) {
    169   // If parent dir of file doesn't exist.
    170   if (!recursive && !base::PathExists(path.DirName()))
    171     return base::File::FILE_ERROR_NOT_FOUND;
    172 
    173   bool path_exists = base::PathExists(path);
    174   if (exclusive && path_exists)
    175     return base::File::FILE_ERROR_EXISTS;
    176 
    177   // If file exists at the path.
    178   if (path_exists && !base::DirectoryExists(path))
    179     return base::File::FILE_ERROR_EXISTS;
    180 
    181   if (!base::CreateDirectory(path))
    182     return base::File::FILE_ERROR_FAILED;
    183 
    184   if (!SetPlatformSpecificDirectoryPermissions(path)) {
    185     // Since some file systems don't support permission setting, we do not treat
    186     // an error from the function as the failure of copying. Just log it.
    187     LOG(WARNING) << "Setting directory permission failed: "
    188         << path.AsUTF8Unsafe();
    189   }
    190 
    191   return base::File::FILE_OK;
    192 }
    193 
    194 base::File::Error NativeFileUtil::GetFileInfo(
    195     const base::FilePath& path,
    196     base::File::Info* file_info) {
    197   if (!base::PathExists(path))
    198     return base::File::FILE_ERROR_NOT_FOUND;
    199 
    200   if (!base::GetFileInfo(path, file_info))
    201     return base::File::FILE_ERROR_FAILED;
    202   return base::File::FILE_OK;
    203 }
    204 
    205 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
    206     NativeFileUtil::CreateFileEnumerator(const base::FilePath& root_path,
    207                                          bool recursive) {
    208   return make_scoped_ptr(new NativeFileEnumerator(
    209       root_path, recursive,
    210       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES))
    211       .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
    212 }
    213 
    214 base::File::Error NativeFileUtil::Touch(
    215     const base::FilePath& path,
    216     const base::Time& last_access_time,
    217     const base::Time& last_modified_time) {
    218   if (!base::TouchFile(path, last_access_time, last_modified_time))
    219     return base::File::FILE_ERROR_FAILED;
    220   return base::File::FILE_OK;
    221 }
    222 
    223 base::File::Error NativeFileUtil::Truncate(const base::FilePath& path,
    224                                            int64 length) {
    225   base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
    226   if (!file.IsValid())
    227     return file.error_details();
    228 
    229   if (!file.SetLength(length))
    230     return base::File::FILE_ERROR_FAILED;
    231 
    232   return base::File::FILE_OK;
    233 }
    234 
    235 bool NativeFileUtil::PathExists(const base::FilePath& path) {
    236   return base::PathExists(path);
    237 }
    238 
    239 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) {
    240   return base::DirectoryExists(path);
    241 }
    242 
    243 base::File::Error NativeFileUtil::CopyOrMoveFile(
    244     const base::FilePath& src_path,
    245     const base::FilePath& dest_path,
    246     FileSystemOperation::CopyOrMoveOption option,
    247     CopyOrMoveMode mode) {
    248   base::File::Info info;
    249   base::File::Error error = NativeFileUtil::GetFileInfo(src_path, &info);
    250   if (error != base::File::FILE_OK)
    251     return error;
    252   if (info.is_directory)
    253     return base::File::FILE_ERROR_NOT_A_FILE;
    254   base::Time last_modified = info.last_modified;
    255 
    256   error = NativeFileUtil::GetFileInfo(dest_path, &info);
    257   if (error != base::File::FILE_OK &&
    258       error != base::File::FILE_ERROR_NOT_FOUND)
    259     return error;
    260   if (info.is_directory)
    261     return base::File::FILE_ERROR_INVALID_OPERATION;
    262   if (error == base::File::FILE_ERROR_NOT_FOUND) {
    263     error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info);
    264     if (error != base::File::FILE_OK)
    265       return error;
    266     if (!info.is_directory)
    267       return base::File::FILE_ERROR_NOT_FOUND;
    268   }
    269 
    270   switch (mode) {
    271     case COPY_NOSYNC:
    272       if (!base::CopyFile(src_path, dest_path))
    273         return base::File::FILE_ERROR_FAILED;
    274       break;
    275     case COPY_SYNC:
    276       if (!CopyFileAndSync(src_path, dest_path))
    277         return base::File::FILE_ERROR_FAILED;
    278       break;
    279     case MOVE:
    280       if (!base::Move(src_path, dest_path))
    281         return base::File::FILE_ERROR_FAILED;
    282       break;
    283   }
    284 
    285   // Preserve the last modified time. Do not return error here even if
    286   // the setting is failed, because the copy itself is successfully done.
    287   if (option == FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED)
    288     base::TouchFile(dest_path, last_modified, last_modified);
    289 
    290   return base::File::FILE_OK;
    291 }
    292 
    293 base::File::Error NativeFileUtil::DeleteFile(const base::FilePath& path) {
    294   if (!base::PathExists(path))
    295     return base::File::FILE_ERROR_NOT_FOUND;
    296   if (base::DirectoryExists(path))
    297     return base::File::FILE_ERROR_NOT_A_FILE;
    298   if (!base::DeleteFile(path, false))
    299     return base::File::FILE_ERROR_FAILED;
    300   return base::File::FILE_OK;
    301 }
    302 
    303 base::File::Error NativeFileUtil::DeleteDirectory(const base::FilePath& path) {
    304   if (!base::PathExists(path))
    305     return base::File::FILE_ERROR_NOT_FOUND;
    306   if (!base::DirectoryExists(path))
    307     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
    308   if (!base::IsDirectoryEmpty(path))
    309     return base::File::FILE_ERROR_NOT_EMPTY;
    310   if (!base::DeleteFile(path, false))
    311     return base::File::FILE_ERROR_FAILED;
    312   return base::File::FILE_OK;
    313 }
    314 
    315 }  // namespace storage
    316