blob: 52f2c70f8010eaa802e3eb02db7d246cbf7b4ce4 [file] [log] [blame]
// Copyright (c) 2011 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 "webkit/fileapi/file_system_file_util.h"
#include "base/file_util_proxy.h"
// This also removes the destination directory if it's non-empty and all other
// checks are passed (so that the copy/move correctly overwrites the
// destination).
// TODO(ericu, dmikurube): This method won't work with obfuscation and quota
// since all (file_util::) operations should consider obfuscation and quota.
// We will need to virtualize all these calls. We should do that by making this
// method a non-virtual member of FileSystemFileUtil, but changing all of its
// file_util calls to be FileSystemFileUtil calls. That way when we override
// them for obfuscation or quota, it'll just work.
static base::PlatformFileError PerformCommonCheckAndPreparationForMoveAndCopy(
const FilePath& src_file_path,
const FilePath& dest_file_path) {
// Exits earlier if the source path does not exist.
if (!file_util::PathExists(src_file_path))
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
// The parent of the |dest_file_path| does not exist.
if (!file_util::DirectoryExists(dest_file_path.DirName()))
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
// It is an error to try to copy/move an entry into its child.
if (src_file_path.IsParent(dest_file_path))
return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
// Now it is ok to return if the |dest_file_path| does not exist.
if (!file_util::PathExists(dest_file_path))
return base::PLATFORM_FILE_OK;
// |src_file_path| exists and is a directory.
// |dest_file_path| exists and is a file.
bool src_is_directory = file_util::DirectoryExists(src_file_path);
bool dest_is_directory = file_util::DirectoryExists(dest_file_path);
if (src_is_directory && !dest_is_directory)
return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
// |src_file_path| exists and is a file.
// |dest_file_path| exists and is a directory.
if (!src_is_directory && dest_is_directory)
return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
// It is an error to copy/move an entry into the same path.
if (src_file_path.value() == dest_file_path.value())
return base::PLATFORM_FILE_ERROR_EXISTS;
if (dest_is_directory) {
// It is an error to copy/move an entry to a non-empty directory.
// Otherwise the copy/move attempt must overwrite the destination, but
// the file_util's Copy or Move method doesn't perform overwrite
// on all platforms, so we delete the destination directory here.
// TODO(kinuko): may be better to change the file_util::{Copy,Move}.
if (!file_util::Delete(dest_file_path, false /* recursive */)) {
if (!file_util::IsDirectoryEmpty(dest_file_path))
return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
return base::PLATFORM_FILE_ERROR_FAILED;
}
}
return base::PLATFORM_FILE_OK;
}
namespace fileapi {
FileSystemFileUtil* FileSystemFileUtil::GetInstance() {
return Singleton<FileSystemFileUtil>::get();
}
PlatformFileError FileSystemFileUtil::CreateOrOpen(
FileSystemOperationContext* unused,
const FilePath& file_path, int file_flags,
PlatformFile* file_handle, bool* created) {
if (!file_util::DirectoryExists(file_path.DirName())) {
// If its parent does not exist, should return NOT_FOUND error.
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
}
PlatformFileError error_code = base::PLATFORM_FILE_OK;
*file_handle = base::CreatePlatformFile(file_path, file_flags,
created, &error_code);
return error_code;
}
PlatformFileError FileSystemFileUtil::Close(
FileSystemOperationContext* unused,
PlatformFile file_handle) {
if (!base::ClosePlatformFile(file_handle))
return base::PLATFORM_FILE_ERROR_FAILED;
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::EnsureFileExists(
FileSystemOperationContext* unused,
const FilePath& file_path,
bool* created) {
if (!file_util::DirectoryExists(file_path.DirName()))
// If its parent does not exist, should return NOT_FOUND error.
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
PlatformFileError error_code = base::PLATFORM_FILE_OK;
// Tries to create the |file_path| exclusively. This should fail
// with base::PLATFORM_FILE_ERROR_EXISTS if the path already exists.
PlatformFile handle = base::CreatePlatformFile(
file_path,
base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
created, &error_code);
if (error_code == base::PLATFORM_FILE_ERROR_EXISTS) {
// Make sure created_ is false.
*created = false;
error_code = base::PLATFORM_FILE_OK;
}
if (handle != base::kInvalidPlatformFileValue)
base::ClosePlatformFile(handle);
return error_code;
}
PlatformFileError FileSystemFileUtil::GetFileInfo(
FileSystemOperationContext* unused,
const FilePath& file_path,
base::PlatformFileInfo* file_info) {
if (!file_util::PathExists(file_path))
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
if (!file_util::GetFileInfo(file_path, file_info))
return base::PLATFORM_FILE_ERROR_FAILED;
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::ReadDirectory(
FileSystemOperationContext* unused,
const FilePath& file_path,
std::vector<base::FileUtilProxy::Entry>* entries) {
// TODO(kkanetkar): Implement directory read in multiple chunks.
if (!file_util::DirectoryExists(file_path))
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
file_util::FileEnumerator file_enum(
file_path, false, static_cast<file_util::FileEnumerator::FILE_TYPE>(
file_util::FileEnumerator::FILES |
file_util::FileEnumerator::DIRECTORIES));
FilePath current;
while (!(current = file_enum.Next()).empty()) {
base::FileUtilProxy::Entry entry;
file_util::FileEnumerator::FindInfo info;
file_enum.GetFindInfo(&info);
entry.is_directory = file_enum.IsDirectory(info);
// This will just give the entry's name instead of entire path
// if we use current.value().
entry.name = file_util::FileEnumerator::GetFilename(info).value();
entries->push_back(entry);
}
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::CreateDirectory(
FileSystemOperationContext* unused,
const FilePath& file_path,
bool exclusive) {
bool path_exists = file_util::PathExists(file_path);
if (!file_util::PathExists(file_path.DirName()))
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
// If parent dir of file doesn't exist.
if (exclusive && path_exists)
return base::PLATFORM_FILE_ERROR_EXISTS;
// If file exists at the path.
if (path_exists && !file_util::DirectoryExists(file_path))
return base::PLATFORM_FILE_ERROR_EXISTS;
if (!file_util::CreateDirectory(file_path))
return base::PLATFORM_FILE_ERROR_FAILED;
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::Copy(
FileSystemOperationContext* unused,
const FilePath& src_file_path,
const FilePath& dest_file_path) {
PlatformFileError error_code;
error_code =
PerformCommonCheckAndPreparationForMoveAndCopy(
src_file_path, dest_file_path);
if (error_code != base::PLATFORM_FILE_OK)
return error_code;
if (!file_util::CopyDirectory(src_file_path, dest_file_path,
true /* recursive */))
return base::PLATFORM_FILE_ERROR_FAILED;
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::Move(
FileSystemOperationContext* unused,
const FilePath& src_file_path,
const FilePath& dest_file_path) {
PlatformFileError error_code;
error_code =
PerformCommonCheckAndPreparationForMoveAndCopy(
src_file_path, dest_file_path);
if (error_code != base::PLATFORM_FILE_OK)
return error_code;
if (!file_util::Move(src_file_path, dest_file_path))
return base::PLATFORM_FILE_ERROR_FAILED;
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::Delete(
FileSystemOperationContext* unused,
const FilePath& file_path,
bool recursive) {
if (!file_util::PathExists(file_path)) {
return base::PLATFORM_FILE_ERROR_NOT_FOUND;
}
if (!file_util::Delete(file_path, recursive)) {
if (!recursive && !file_util::IsDirectoryEmpty(file_path)) {
return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
}
return base::PLATFORM_FILE_ERROR_FAILED;
}
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::Touch(
FileSystemOperationContext* unused,
const FilePath& file_path,
const base::Time& last_access_time,
const base::Time& last_modified_time) {
if (!file_util::TouchFile(
file_path, last_access_time, last_modified_time))
return base::PLATFORM_FILE_ERROR_FAILED;
return base::PLATFORM_FILE_OK;
}
PlatformFileError FileSystemFileUtil::Truncate(
FileSystemOperationContext* unused,
const FilePath& file_path,
int64 length) {
PlatformFileError error_code(base::PLATFORM_FILE_ERROR_FAILED);
PlatformFile file =
base::CreatePlatformFile(
file_path,
base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
NULL,
&error_code);
if (error_code != base::PLATFORM_FILE_OK) {
return error_code;
}
if (!base::TruncatePlatformFile(file, length))
error_code = base::PLATFORM_FILE_ERROR_FAILED;
base::ClosePlatformFile(file);
return error_code;
}
} // namespace fileapi