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 "webkit/browser/fileapi/native_file_util.h"
      6 
      7 #include "base/file_util.h"
      8 #include "base/files/file_enumerator.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "net/base/file_stream.h"
     11 #include "webkit/browser/fileapi/file_system_operation_context.h"
     12 #include "webkit/browser/fileapi/file_system_url.h"
     13 
     14 namespace fileapi {
     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   net::FileStream infile(NULL);
     41   if (infile.OpenSync(from,
     42           base::PLATFORM_FILE_OPEN | base:: PLATFORM_FILE_READ) < 0) {
     43     return false;
     44   }
     45 
     46   net::FileStream outfile(NULL);
     47   if (outfile.OpenSync(to,
     48           base::PLATFORM_FILE_CREATE_ALWAYS | base:: PLATFORM_FILE_WRITE) < 0) {
     49     return false;
     50   }
     51 
     52   const int kBufferSize = 32768;
     53   std::vector<char> buffer(kBufferSize);
     54 
     55   for (;;) {
     56     int bytes_read = infile.ReadSync(&buffer[0], kBufferSize);
     57     if (bytes_read < 0)
     58       return false;
     59     if (bytes_read == 0)
     60       break;
     61     for (int bytes_written = 0; bytes_written < bytes_read; ) {
     62       int bytes_written_partial = outfile.WriteSync(&buffer[bytes_written],
     63                                                     bytes_read - bytes_written);
     64       if (bytes_written_partial < 0)
     65         return false;
     66       bytes_written += bytes_written_partial;
     67     }
     68   }
     69 
     70   return outfile.FlushSync() >= 0;
     71 }
     72 
     73 }  // namespace
     74 
     75 using base::PlatformFile;
     76 using base::PlatformFileError;
     77 
     78 class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator {
     79  public:
     80   NativeFileEnumerator(const base::FilePath& root_path,
     81                        bool recursive,
     82                        int file_type)
     83     : file_enum_(root_path, recursive, file_type) {
     84   }
     85 
     86   virtual ~NativeFileEnumerator() {}
     87 
     88   virtual base::FilePath Next() OVERRIDE;
     89   virtual int64 Size() OVERRIDE;
     90   virtual base::Time LastModifiedTime() OVERRIDE;
     91   virtual bool IsDirectory() OVERRIDE;
     92 
     93  private:
     94   base::FileEnumerator file_enum_;
     95   base::FileEnumerator::FileInfo file_util_info_;
     96 };
     97 
     98 base::FilePath NativeFileEnumerator::Next() {
     99   base::FilePath rv = file_enum_.Next();
    100   if (!rv.empty())
    101     file_util_info_ = file_enum_.GetInfo();
    102   return rv;
    103 }
    104 
    105 int64 NativeFileEnumerator::Size() {
    106   return file_util_info_.GetSize();
    107 }
    108 
    109 base::Time NativeFileEnumerator::LastModifiedTime() {
    110   return file_util_info_.GetLastModifiedTime();
    111 }
    112 
    113 bool NativeFileEnumerator::IsDirectory() {
    114   return file_util_info_.IsDirectory();
    115 }
    116 
    117 NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination(
    118     const FileSystemURL& dest_url, bool copy) {
    119   if (copy) {
    120     return dest_url.mount_option().copy_sync_option() == COPY_SYNC_OPTION_SYNC ?
    121         COPY_SYNC : COPY_NOSYNC;
    122   }
    123   return MOVE;
    124 }
    125 
    126 PlatformFileError NativeFileUtil::CreateOrOpen(
    127     const base::FilePath& path, int file_flags,
    128     PlatformFile* file_handle, bool* created) {
    129   if (!base::DirectoryExists(path.DirName())) {
    130     // If its parent does not exist, should return NOT_FOUND error.
    131     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    132   }
    133   if (base::DirectoryExists(path))
    134     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    135   PlatformFileError error_code = base::PLATFORM_FILE_OK;
    136   *file_handle = base::CreatePlatformFile(path, file_flags,
    137                                           created, &error_code);
    138   return error_code;
    139 }
    140 
    141 PlatformFileError NativeFileUtil::Close(PlatformFile file_handle) {
    142   if (!base::ClosePlatformFile(file_handle))
    143     return base::PLATFORM_FILE_ERROR_FAILED;
    144   return base::PLATFORM_FILE_OK;
    145 }
    146 
    147 PlatformFileError NativeFileUtil::EnsureFileExists(
    148     const base::FilePath& path,
    149     bool* created) {
    150   if (!base::DirectoryExists(path.DirName()))
    151     // If its parent does not exist, should return NOT_FOUND error.
    152     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    153   PlatformFileError error_code = base::PLATFORM_FILE_OK;
    154   // Tries to create the |path| exclusively.  This should fail
    155   // with base::PLATFORM_FILE_ERROR_EXISTS if the path already exists.
    156   PlatformFile handle = base::CreatePlatformFile(
    157       path,
    158       base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
    159       created, &error_code);
    160   if (error_code == base::PLATFORM_FILE_ERROR_EXISTS) {
    161     // Make sure created_ is false.
    162     if (created)
    163       *created = false;
    164     error_code = base::PLATFORM_FILE_OK;
    165   }
    166   if (handle != base::kInvalidPlatformFileValue)
    167     base::ClosePlatformFile(handle);
    168   return error_code;
    169 }
    170 
    171 PlatformFileError NativeFileUtil::CreateDirectory(
    172     const base::FilePath& path,
    173     bool exclusive,
    174     bool recursive) {
    175   // If parent dir of file doesn't exist.
    176   if (!recursive && !base::PathExists(path.DirName()))
    177     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    178 
    179   bool path_exists = base::PathExists(path);
    180   if (exclusive && path_exists)
    181     return base::PLATFORM_FILE_ERROR_EXISTS;
    182 
    183   // If file exists at the path.
    184   if (path_exists && !base::DirectoryExists(path))
    185     return base::PLATFORM_FILE_ERROR_EXISTS;
    186 
    187   if (!base::CreateDirectory(path))
    188     return base::PLATFORM_FILE_ERROR_FAILED;
    189 
    190   if (!SetPlatformSpecificDirectoryPermissions(path)) {
    191     // Since some file systems don't support permission setting, we do not treat
    192     // an error from the function as the failure of copying. Just log it.
    193     LOG(WARNING) << "Setting directory permission failed: "
    194         << path.AsUTF8Unsafe();
    195   }
    196 
    197   return base::PLATFORM_FILE_OK;
    198 }
    199 
    200 PlatformFileError NativeFileUtil::GetFileInfo(
    201     const base::FilePath& path,
    202     base::PlatformFileInfo* file_info) {
    203   if (!base::PathExists(path))
    204     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    205   if (!base::GetFileInfo(path, file_info))
    206     return base::PLATFORM_FILE_ERROR_FAILED;
    207   return base::PLATFORM_FILE_OK;
    208 }
    209 
    210 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
    211     NativeFileUtil::CreateFileEnumerator(const base::FilePath& root_path,
    212                                          bool recursive) {
    213   return make_scoped_ptr(new NativeFileEnumerator(
    214       root_path, recursive,
    215       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES))
    216       .PassAs<FileSystemFileUtil::AbstractFileEnumerator>();
    217 }
    218 
    219 PlatformFileError NativeFileUtil::Touch(
    220     const base::FilePath& path,
    221     const base::Time& last_access_time,
    222     const base::Time& last_modified_time) {
    223   if (!base::TouchFile(path, last_access_time, last_modified_time))
    224     return base::PLATFORM_FILE_ERROR_FAILED;
    225   return base::PLATFORM_FILE_OK;
    226 }
    227 
    228 PlatformFileError NativeFileUtil::Truncate(
    229     const base::FilePath& path, int64 length) {
    230   PlatformFileError error_code(base::PLATFORM_FILE_ERROR_FAILED);
    231   PlatformFile file =
    232       base::CreatePlatformFile(
    233           path,
    234           base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
    235           NULL,
    236           &error_code);
    237   if (error_code != base::PLATFORM_FILE_OK) {
    238     return error_code;
    239   }
    240   DCHECK_NE(base::kInvalidPlatformFileValue, file);
    241   if (!base::TruncatePlatformFile(file, length))
    242     error_code = base::PLATFORM_FILE_ERROR_FAILED;
    243   base::ClosePlatformFile(file);
    244   return error_code;
    245 }
    246 
    247 bool NativeFileUtil::PathExists(const base::FilePath& path) {
    248   return base::PathExists(path);
    249 }
    250 
    251 bool NativeFileUtil::DirectoryExists(const base::FilePath& path) {
    252   return base::DirectoryExists(path);
    253 }
    254 
    255 PlatformFileError NativeFileUtil::CopyOrMoveFile(
    256     const base::FilePath& src_path,
    257     const base::FilePath& dest_path,
    258     FileSystemOperation::CopyOrMoveOption option,
    259     CopyOrMoveMode mode) {
    260   base::PlatformFileInfo info;
    261   base::PlatformFileError error = NativeFileUtil::GetFileInfo(src_path, &info);
    262   if (error != base::PLATFORM_FILE_OK)
    263     return error;
    264   if (info.is_directory)
    265     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    266   base::Time last_modified = info.last_modified;
    267 
    268   error = NativeFileUtil::GetFileInfo(dest_path, &info);
    269   if (error != base::PLATFORM_FILE_OK &&
    270       error != base::PLATFORM_FILE_ERROR_NOT_FOUND)
    271     return error;
    272   if (info.is_directory)
    273     return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
    274   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
    275     error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info);
    276     if (error != base::PLATFORM_FILE_OK)
    277       return error;
    278     if (!info.is_directory)
    279       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    280   }
    281 
    282   switch (mode) {
    283     case COPY_NOSYNC:
    284       if (!base::CopyFile(src_path, dest_path))
    285         return base::PLATFORM_FILE_ERROR_FAILED;
    286       break;
    287     case COPY_SYNC:
    288       if (!CopyFileAndSync(src_path, dest_path))
    289         return base::PLATFORM_FILE_ERROR_FAILED;
    290       break;
    291     case MOVE:
    292       if (!base::Move(src_path, dest_path))
    293         return base::PLATFORM_FILE_ERROR_FAILED;
    294       break;
    295   }
    296 
    297   // Preserve the last modified time. Do not return error here even if
    298   // the setting is failed, because the copy itself is successfully done.
    299   if (option == FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED)
    300     base::TouchFile(dest_path, last_modified, last_modified);
    301 
    302   return base::PLATFORM_FILE_OK;
    303 }
    304 
    305 PlatformFileError NativeFileUtil::DeleteFile(const base::FilePath& path) {
    306   if (!base::PathExists(path))
    307     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    308   if (base::DirectoryExists(path))
    309     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    310   if (!base::DeleteFile(path, false))
    311     return base::PLATFORM_FILE_ERROR_FAILED;
    312   return base::PLATFORM_FILE_OK;
    313 }
    314 
    315 PlatformFileError NativeFileUtil::DeleteDirectory(const base::FilePath& path) {
    316   if (!base::PathExists(path))
    317     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    318   if (!base::DirectoryExists(path))
    319     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    320   if (!base::IsDirectoryEmpty(path))
    321     return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
    322   if (!base::DeleteFile(path, false))
    323     return base::PLATFORM_FILE_ERROR_FAILED;
    324   return base::PLATFORM_FILE_OK;
    325 }
    326 
    327 }  // namespace fileapi
    328