| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "storage/browser/fileapi/native_file_util.h" | 
 |  | 
 | #include <stdint.h> | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/files/file_enumerator.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "storage/browser/fileapi/file_system_operation_context.h" | 
 | #include "storage/browser/fileapi/file_system_url.h" | 
 | #include "storage/common/fileapi/file_system_mount_option.h" | 
 |  | 
 | namespace storage { | 
 |  | 
 | namespace { | 
 |  | 
 | // Sets permissions on directory at |dir_path| based on the target platform. | 
 | // Returns true on success, or false otherwise. | 
 | // | 
 | // TODO(benchan): Find a better place outside webkit to host this function. | 
 | bool SetPlatformSpecificDirectoryPermissions(const base::FilePath& dir_path) { | 
 | #if defined(OS_CHROMEOS) | 
 |     // System daemons on Chrome OS may run as a user different than the Chrome | 
 |     // process but need to access files under the directories created here. | 
 |     // Because of that, grant the execute permission on the created directory | 
 |     // to group and other users. | 
 |     if (HANDLE_EINTR(chmod(dir_path.value().c_str(), | 
 |                            S_IRWXU | S_IXGRP | S_IXOTH)) != 0) { | 
 |       return false; | 
 |     } | 
 | #endif | 
 |     // Keep the directory permissions unchanged on non-Chrome OS platforms. | 
 |     return true; | 
 | } | 
 |  | 
 | // Copies a file |from| to |to|, and ensure the written content is synced to | 
 | // the disk. This is essentially base::CopyFile followed by fsync(). | 
 | bool CopyFileAndSync(const base::FilePath& from, const base::FilePath& to) { | 
 |   base::File infile(from, base::File::FLAG_OPEN | base::File::FLAG_READ); | 
 |   if (!infile.IsValid()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   base::File outfile(to, | 
 |                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | 
 |   if (!outfile.IsValid()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   const int kBufferSize = 32768; | 
 |   std::vector<char> buffer(kBufferSize); | 
 |  | 
 |   for (;;) { | 
 |     int bytes_read = infile.ReadAtCurrentPos(&buffer[0], kBufferSize); | 
 |     if (bytes_read < 0) | 
 |       return false; | 
 |     if (bytes_read == 0) | 
 |       break; | 
 |     for (int bytes_written = 0; bytes_written < bytes_read; ) { | 
 |       int bytes_written_partial = outfile.WriteAtCurrentPos( | 
 |           &buffer[bytes_written], bytes_read - bytes_written); | 
 |       if (bytes_written_partial < 0) | 
 |         return false; | 
 |       bytes_written += bytes_written_partial; | 
 |     } | 
 |   } | 
 |  | 
 |   return outfile.Flush(); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | using base::PlatformFile; | 
 |  | 
 | class NativeFileEnumerator : public FileSystemFileUtil::AbstractFileEnumerator { | 
 |  public: | 
 |   NativeFileEnumerator(const base::FilePath& root_path, | 
 |                        bool recursive, | 
 |                        int file_type) | 
 |     : file_enum_(root_path, recursive, file_type) { | 
 |   } | 
 |  | 
 |   ~NativeFileEnumerator() override {} | 
 |  | 
 |   base::FilePath Next() override; | 
 |   int64_t Size() override; | 
 |   base::Time LastModifiedTime() override; | 
 |   bool IsDirectory() override; | 
 |  | 
 |  private: | 
 |   base::FileEnumerator file_enum_; | 
 |   base::FileEnumerator::FileInfo file_util_info_; | 
 | }; | 
 |  | 
 | base::FilePath NativeFileEnumerator::Next() { | 
 |   base::FilePath rv = file_enum_.Next(); | 
 |   if (!rv.empty()) | 
 |     file_util_info_ = file_enum_.GetInfo(); | 
 |   return rv; | 
 | } | 
 |  | 
 | int64_t NativeFileEnumerator::Size() { | 
 |   return file_util_info_.GetSize(); | 
 | } | 
 |  | 
 | base::Time NativeFileEnumerator::LastModifiedTime() { | 
 |   return file_util_info_.GetLastModifiedTime(); | 
 | } | 
 |  | 
 | bool NativeFileEnumerator::IsDirectory() { | 
 |   return file_util_info_.IsDirectory(); | 
 | } | 
 |  | 
 | NativeFileUtil::CopyOrMoveMode NativeFileUtil::CopyOrMoveModeForDestination( | 
 |     const FileSystemURL& dest_url, bool copy) { | 
 |   if (copy) { | 
 |     return dest_url.mount_option().flush_policy() == | 
 |                    FlushPolicy::FLUSH_ON_COMPLETION | 
 |                ? COPY_SYNC | 
 |                : COPY_NOSYNC; | 
 |   } | 
 |   return MOVE; | 
 | } | 
 |  | 
 | base::File NativeFileUtil::CreateOrOpen(const base::FilePath& path, | 
 |                                         int file_flags) { | 
 |   if (!base::DirectoryExists(path.DirName())) { | 
 |     // If its parent does not exist, should return NOT_FOUND error. | 
 |     return base::File(base::File::FILE_ERROR_NOT_FOUND); | 
 |   } | 
 |  | 
 |   // TODO(rvargas): Check |file_flags| instead. See bug 356358. | 
 |   if (base::DirectoryExists(path)) | 
 |     return base::File(base::File::FILE_ERROR_NOT_A_FILE); | 
 |  | 
 |   return base::File(path, file_flags); | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::EnsureFileExists( | 
 |     const base::FilePath& path, | 
 |     bool* created) { | 
 |   if (!base::DirectoryExists(path.DirName())) | 
 |     // If its parent does not exist, should return NOT_FOUND error. | 
 |     return base::File::FILE_ERROR_NOT_FOUND; | 
 |  | 
 |   // Tries to create the |path| exclusively.  This should fail | 
 |   // with base::File::FILE_ERROR_EXISTS if the path already exists. | 
 |   base::File file(path, base::File::FLAG_CREATE | base::File::FLAG_READ); | 
 |  | 
 |   if (file.IsValid()) { | 
 |     if (created) | 
 |       *created = file.created(); | 
 |     return base::File::FILE_OK; | 
 |   } | 
 |  | 
 |   base::File::Error error_code = file.error_details(); | 
 |   if (error_code == base::File::FILE_ERROR_EXISTS) { | 
 |     // Make sure created_ is false. | 
 |     if (created) | 
 |       *created = false; | 
 |     error_code = base::File::FILE_OK; | 
 |   } | 
 |   return error_code; | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::CreateDirectory( | 
 |     const base::FilePath& path, | 
 |     bool exclusive, | 
 |     bool recursive) { | 
 |   // If parent dir of file doesn't exist. | 
 |   if (!recursive && !base::PathExists(path.DirName())) | 
 |     return base::File::FILE_ERROR_NOT_FOUND; | 
 |  | 
 |   bool path_exists = base::PathExists(path); | 
 |   if (exclusive && path_exists) | 
 |     return base::File::FILE_ERROR_EXISTS; | 
 |  | 
 |   // If file exists at the path. | 
 |   if (path_exists && !base::DirectoryExists(path)) | 
 |     return base::File::FILE_ERROR_EXISTS; | 
 |  | 
 |   if (!base::CreateDirectory(path)) | 
 |     return base::File::FILE_ERROR_FAILED; | 
 |  | 
 |   if (!SetPlatformSpecificDirectoryPermissions(path)) { | 
 |     // Since some file systems don't support permission setting, we do not treat | 
 |     // an error from the function as the failure of copying. Just log it. | 
 |     LOG(WARNING) << "Setting directory permission failed: " | 
 |         << path.AsUTF8Unsafe(); | 
 |   } | 
 |  | 
 |   return base::File::FILE_OK; | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::GetFileInfo( | 
 |     const base::FilePath& path, | 
 |     base::File::Info* file_info) { | 
 |   if (!base::PathExists(path)) | 
 |     return base::File::FILE_ERROR_NOT_FOUND; | 
 |  | 
 |   if (!base::GetFileInfo(path, file_info)) | 
 |     return base::File::FILE_ERROR_FAILED; | 
 |   return base::File::FILE_OK; | 
 | } | 
 |  | 
 | std::unique_ptr<FileSystemFileUtil::AbstractFileEnumerator> | 
 | NativeFileUtil::CreateFileEnumerator(const base::FilePath& root_path, | 
 |                                      bool recursive) { | 
 |   return base::MakeUnique<NativeFileEnumerator>( | 
 |       root_path, recursive, | 
 |       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::Touch( | 
 |     const base::FilePath& path, | 
 |     const base::Time& last_access_time, | 
 |     const base::Time& last_modified_time) { | 
 |   if (!base::TouchFile(path, last_access_time, last_modified_time)) | 
 |     return base::File::FILE_ERROR_FAILED; | 
 |   return base::File::FILE_OK; | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::Truncate(const base::FilePath& path, | 
 |                                            int64_t length) { | 
 |   base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE); | 
 |   if (!file.IsValid()) | 
 |     return file.error_details(); | 
 |  | 
 |   if (!file.SetLength(length)) | 
 |     return base::File::FILE_ERROR_FAILED; | 
 |  | 
 |   return base::File::FILE_OK; | 
 | } | 
 |  | 
 | bool NativeFileUtil::PathExists(const base::FilePath& path) { | 
 |   return base::PathExists(path); | 
 | } | 
 |  | 
 | bool NativeFileUtil::DirectoryExists(const base::FilePath& path) { | 
 |   return base::DirectoryExists(path); | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::CopyOrMoveFile( | 
 |     const base::FilePath& src_path, | 
 |     const base::FilePath& dest_path, | 
 |     FileSystemOperation::CopyOrMoveOption option, | 
 |     CopyOrMoveMode mode) { | 
 |   base::File::Info info; | 
 |   base::File::Error error = NativeFileUtil::GetFileInfo(src_path, &info); | 
 |   if (error != base::File::FILE_OK) | 
 |     return error; | 
 |   if (info.is_directory) | 
 |     return base::File::FILE_ERROR_NOT_A_FILE; | 
 |   base::Time last_modified = info.last_modified; | 
 |  | 
 |   error = NativeFileUtil::GetFileInfo(dest_path, &info); | 
 |   if (error != base::File::FILE_OK && | 
 |       error != base::File::FILE_ERROR_NOT_FOUND) | 
 |     return error; | 
 |   if (info.is_directory) | 
 |     return base::File::FILE_ERROR_INVALID_OPERATION; | 
 |   if (error == base::File::FILE_ERROR_NOT_FOUND) { | 
 |     error = NativeFileUtil::GetFileInfo(dest_path.DirName(), &info); | 
 |     if (error != base::File::FILE_OK) | 
 |       return error; | 
 |     if (!info.is_directory) | 
 |       return base::File::FILE_ERROR_NOT_FOUND; | 
 |   } | 
 |  | 
 |   switch (mode) { | 
 |     case COPY_NOSYNC: | 
 |       if (!base::CopyFile(src_path, dest_path)) | 
 |         return base::File::FILE_ERROR_FAILED; | 
 |       break; | 
 |     case COPY_SYNC: | 
 |       if (!CopyFileAndSync(src_path, dest_path)) | 
 |         return base::File::FILE_ERROR_FAILED; | 
 |       break; | 
 |     case MOVE: | 
 |       if (!base::Move(src_path, dest_path)) | 
 |         return base::File::FILE_ERROR_FAILED; | 
 |       break; | 
 |   } | 
 |  | 
 |   // Preserve the last modified time. Do not return error here even if | 
 |   // the setting is failed, because the copy itself is successfully done. | 
 |   if (option == FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED) | 
 |     base::TouchFile(dest_path, last_modified, last_modified); | 
 |  | 
 |   return base::File::FILE_OK; | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::DeleteFile(const base::FilePath& path) { | 
 |   if (!base::PathExists(path)) | 
 |     return base::File::FILE_ERROR_NOT_FOUND; | 
 |   if (base::DirectoryExists(path)) | 
 |     return base::File::FILE_ERROR_NOT_A_FILE; | 
 |   if (!base::DeleteFile(path, false)) | 
 |     return base::File::FILE_ERROR_FAILED; | 
 |   return base::File::FILE_OK; | 
 | } | 
 |  | 
 | base::File::Error NativeFileUtil::DeleteDirectory(const base::FilePath& path) { | 
 |   if (!base::PathExists(path)) | 
 |     return base::File::FILE_ERROR_NOT_FOUND; | 
 |   if (!base::DirectoryExists(path)) | 
 |     return base::File::FILE_ERROR_NOT_A_DIRECTORY; | 
 |   if (!base::IsDirectoryEmpty(path)) | 
 |     return base::File::FILE_ERROR_NOT_EMPTY; | 
 |   if (!base::DeleteFile(path, false)) | 
 |     return base::File::FILE_ERROR_FAILED; | 
 |   return base::File::FILE_OK; | 
 | } | 
 |  | 
 | }  // namespace storage |