| Index: webkit/fileapi/fileapi_file_util.cc
|
| diff --git a/webkit/fileapi/fileapi_file_util.cc b/webkit/fileapi/fileapi_file_util.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9e548c5781ce87ab6cc9397ff2929cb734d57a83
|
| --- /dev/null
|
| +++ b/webkit/fileapi/fileapi_file_util.cc
|
| @@ -0,0 +1,280 @@
|
| +// 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/fileapi_file_util.h"
|
| +
|
| +#include <stack>
|
| +
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "webkit/fileapi/file_system_operation_context.h"
|
| +
|
| +namespace fileapi {
|
| +
|
| +namespace {
|
| +
|
| +// This assumes that the root exists.
|
| +bool ParentExists(FileSystemOperationContext* context,
|
| + FileApiFileUtil* file_util, const FilePath& file_path) {
|
| + // If file_path is in the root, file_path.DirName() will be ".",
|
| + // since we use paths with no leading '/'.
|
| + FilePath parent = file_path.DirName();
|
| + if (parent == FilePath(FILE_PATH_LITERAL(".")))
|
| + return true;
|
| + return file_util->DirectoryExists(context, parent);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +PlatformFileError FileApiFileUtil::Copy(
|
| + FileSystemOperationContext* context,
|
| + const FilePath& src_file_path,
|
| + const FilePath& dest_file_path) {
|
| + PlatformFileError error_code;
|
| + error_code =
|
| + PerformCommonCheckAndPreparationForMoveAndCopy(
|
| + context, src_file_path, dest_file_path);
|
| + if (error_code != base::PLATFORM_FILE_OK)
|
| + return error_code;
|
| +
|
| + if (DirectoryExists(context, src_file_path))
|
| + return CopyOrMoveDirectory(context, src_file_path, dest_file_path,
|
| + true /* copy */);
|
| + return CopyOrMoveFileHelper(context, src_file_path, dest_file_path,
|
| + true /* copy */);
|
| +}
|
| +
|
| +
|
| +PlatformFileError FileApiFileUtil::Move(
|
| + FileSystemOperationContext* context,
|
| + const FilePath& src_file_path,
|
| + const FilePath& dest_file_path) {
|
| + PlatformFileError error_code;
|
| + error_code =
|
| + PerformCommonCheckAndPreparationForMoveAndCopy(
|
| + context, src_file_path, dest_file_path);
|
| + if (error_code != base::PLATFORM_FILE_OK)
|
| + return error_code;
|
| +
|
| + // TODO(dmikurube): ReplaceFile if in the same domain and filesystem type.
|
| + if (DirectoryExists(context, src_file_path))
|
| + return CopyOrMoveDirectory(context, src_file_path, dest_file_path,
|
| + false /* copy */);
|
| + return CopyOrMoveFileHelper(context, src_file_path, dest_file_path,
|
| + false /* copy */);
|
| +}
|
| +
|
| +PlatformFileError FileApiFileUtil::Delete(
|
| + FileSystemOperationContext* context,
|
| + const FilePath& file_path,
|
| + bool recursive) {
|
| + if (DirectoryExists(context, file_path)) {
|
| + if (!recursive)
|
| + return DeleteSingleDirectory(context, file_path);
|
| + else
|
| + return DeleteDirectoryRecursive(context, file_path);
|
| + } else {
|
| + return DeleteFile(context, file_path);
|
| + }
|
| +}
|
| +
|
| +PlatformFileError
|
| +FileApiFileUtil::PerformCommonCheckAndPreparationForMoveAndCopy(
|
| + FileSystemOperationContext* context,
|
| + const FilePath& src_file_path,
|
| + const FilePath& dest_file_path) {
|
| + bool same_file_system =
|
| + (context->src_origin_url() == context->dest_origin_url()) &&
|
| + (context->src_type() == context->dest_type());
|
| + FileApiFileUtil* dest_util = context->dest_file_util();
|
| + DCHECK(dest_util);
|
| + if (same_file_system)
|
| + DCHECK(context->src_file_util() == context->dest_file_util());
|
| + // All the single-path virtual FSFU methods expect the context information
|
| + // to be in the src_* variables, not the dest_* variables, so we have to
|
| + // make a new context if we want to call them on the dest_file_path.
|
| + scoped_ptr<FileSystemOperationContext> dest_context(
|
| + context->CreateInheritedContextForDest());
|
| +
|
| + // Exits earlier if the source path does not exist.
|
| + if (!PathExists(context, src_file_path))
|
| + return base::PLATFORM_FILE_ERROR_NOT_FOUND;
|
| +
|
| + // The parent of the |dest_file_path| does not exist.
|
| + if (!ParentExists(dest_context.get(), dest_util, dest_file_path))
|
| + return base::PLATFORM_FILE_ERROR_NOT_FOUND;
|
| +
|
| + // It is an error to try to copy/move an entry into its child.
|
| + if (same_file_system && 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 (!dest_util->PathExists(dest_context.get(), 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 = DirectoryExists(context, src_file_path);
|
| + bool dest_is_directory =
|
| + dest_util->DirectoryExists(dest_context.get(), dest_file_path);
|
| + if (src_is_directory && !dest_is_directory)
|
| + return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
|
| +
|
| + // |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_INVALID_OPERATION;
|
| +
|
| + // It is an error to copy/move an entry into the same path.
|
| + if (same_file_system && (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}.
|
| + PlatformFileError error = dest_util->Delete(
|
| + dest_context.get(), dest_file_path, false /* recursive */);
|
| + context->ImportAllowedBytesGrowth(*dest_context);
|
| + if (base::PLATFORM_FILE_OK != error) {
|
| + if (!dest_util->IsDirectoryEmpty(dest_context.get(), dest_file_path))
|
| + return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
|
| + return base::PLATFORM_FILE_ERROR_FAILED;
|
| + }
|
| + }
|
| + return base::PLATFORM_FILE_OK;
|
| +}
|
| +
|
| +PlatformFileError FileApiFileUtil::CopyOrMoveDirectory(
|
| + FileSystemOperationContext* context,
|
| + const FilePath& src_file_path,
|
| + const FilePath& dest_file_path,
|
| + bool copy) {
|
| + FileApiFileUtil* dest_util = context->dest_file_util();
|
| + scoped_ptr<FileSystemOperationContext> dest_context(
|
| + context->CreateInheritedContextForDest());
|
| +
|
| + // Re-check PerformCommonCheckAndPreparationForMoveAndCopy() by DCHECK.
|
| + DCHECK(DirectoryExists(context, src_file_path));
|
| + DCHECK(ParentExists(dest_context.get(), dest_util, dest_file_path));
|
| + DCHECK(!dest_util->PathExists(dest_context.get(), dest_file_path));
|
| + if ((context->src_origin_url() == context->dest_origin_url()) &&
|
| + (context->src_type() == context->dest_type()))
|
| + DCHECK(!src_file_path.IsParent(dest_file_path));
|
| +
|
| + if (!dest_util->DirectoryExists(dest_context.get(), dest_file_path)) {
|
| + PlatformFileError error = dest_util->CreateDirectory(dest_context.get(),
|
| + dest_file_path, false, false);
|
| + context->ImportAllowedBytesGrowth(*dest_context);
|
| + if (error != base::PLATFORM_FILE_OK)
|
| + return error;
|
| + }
|
| +
|
| + scoped_ptr<AbstractFileEnumerator> file_enum(
|
| + CreateFileEnumerator(context, src_file_path));
|
| + FilePath src_file_path_each;
|
| + while (!(src_file_path_each = file_enum->Next()).empty()) {
|
| + FilePath dest_file_path_each(dest_file_path);
|
| + src_file_path.AppendRelativePath(src_file_path_each, &dest_file_path_each);
|
| +
|
| + if (file_enum->IsDirectory()) {
|
| + scoped_ptr<FileSystemOperationContext> new_directory_context(
|
| + dest_context->CreateInheritedContextWithNewVirtualPaths(
|
| + dest_file_path_each, FilePath()));
|
| + PlatformFileError error = dest_util->CreateDirectory(
|
| + new_directory_context.get(), dest_file_path_each, false, false);
|
| + context->ImportAllowedBytesGrowth(*new_directory_context);
|
| + if (error != base::PLATFORM_FILE_OK)
|
| + return error;
|
| + } else {
|
| + scoped_ptr<FileSystemOperationContext> copy_context(
|
| + context->CreateInheritedContextWithNewVirtualPaths(
|
| + src_file_path_each, dest_file_path_each));
|
| + PlatformFileError error = CopyOrMoveFileHelper(
|
| + copy_context.get(), src_file_path_each, dest_file_path_each, copy);
|
| + context->ImportAllowedBytesGrowth(*copy_context);
|
| + if (error != base::PLATFORM_FILE_OK)
|
| + return error;
|
| + }
|
| + }
|
| +
|
| + if (!copy) {
|
| + PlatformFileError error = Delete(context, src_file_path, true);
|
| + if (error != base::PLATFORM_FILE_OK)
|
| + return error;
|
| + }
|
| + return base::PLATFORM_FILE_OK;
|
| +}
|
| +
|
| +PlatformFileError FileApiFileUtil::CopyOrMoveFileHelper(
|
| + FileSystemOperationContext* context,
|
| + const FilePath& src_file_path,
|
| + const FilePath& dest_file_path,
|
| + bool copy) {
|
| + // CopyOrMoveFile here is the virtual overridden member function.
|
| + if ((context->src_origin_url() == context->dest_origin_url()) &&
|
| + (context->src_type() == context->dest_type())) {
|
| + DCHECK(context->src_file_util() == context->dest_file_util());
|
| + return CopyOrMoveFile(context, src_file_path, dest_file_path, copy);
|
| + }
|
| + base::PlatformFileInfo file_info;
|
| + FilePath platform_file_path;
|
| + PlatformFileError error_code;
|
| + error_code =
|
| + GetFileInfo(context, src_file_path, &file_info, &platform_file_path);
|
| + if (error_code != base::PLATFORM_FILE_OK)
|
| + return error_code;
|
| +
|
| + DCHECK(context->dest_file_util());
|
| + error_code = context->dest_file_util()->CopyInForeignFile(
|
| + context, platform_file_path, dest_file_path);
|
| + if (copy || error_code != base::PLATFORM_FILE_OK)
|
| + return error_code;
|
| + return DeleteFile(context, src_file_path);
|
| +}
|
| +
|
| +PlatformFileError FileApiFileUtil::DeleteDirectoryRecursive(
|
| + FileSystemOperationContext* context,
|
| + const FilePath& file_path) {
|
| + scoped_ptr<AbstractFileEnumerator> file_enum(
|
| + CreateFileEnumerator(context, file_path));
|
| + FilePath file_path_each;
|
| +
|
| + std::stack<FilePath> directories;
|
| + while (!(file_path_each = file_enum->Next()).empty()) {
|
| + if (file_enum->IsDirectory()) {
|
| + directories.push(file_path_each);
|
| + } else {
|
| + // DeleteFile here is the virtual overridden member function.
|
| + scoped_ptr<FileSystemOperationContext> inherited_context(
|
| + context->CreateInheritedContextWithNewVirtualPaths(
|
| + file_path_each, FilePath()));
|
| + PlatformFileError error =
|
| + DeleteFile(inherited_context.get(), file_path_each);
|
| + context->ImportAllowedBytesGrowth(*inherited_context);
|
| + if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
|
| + return base::PLATFORM_FILE_ERROR_FAILED;
|
| + else if (error != base::PLATFORM_FILE_OK)
|
| + return error;
|
| + }
|
| + }
|
| +
|
| + while (!directories.empty()) {
|
| + scoped_ptr<FileSystemOperationContext> inherited_context(
|
| + context->CreateInheritedContextWithNewVirtualPaths(
|
| + directories.top(), FilePath()));
|
| + PlatformFileError error =
|
| + DeleteSingleDirectory(inherited_context.get(), directories.top());
|
| + context->ImportAllowedBytesGrowth(*inherited_context);
|
| + if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
|
| + return base::PLATFORM_FILE_ERROR_FAILED;
|
| + else if (error != base::PLATFORM_FILE_OK)
|
| + return error;
|
| + directories.pop();
|
| + }
|
| + return DeleteSingleDirectory(context, file_path);
|
| +}
|
| +
|
| +} // namespace fileapi
|
|
|