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..9162534a6fb3a188a028aa8433fc2ef99826ee46 |
--- /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, |
+ FileUtil* 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 FileUtil::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 FileUtil::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 FileUtil::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 |
+FileUtil::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()); |
+ FileUtil* 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 FileUtil::CopyOrMoveDirectory( |
+ FileSystemOperationContext* context, |
+ const FilePath& src_file_path, |
+ const FilePath& dest_file_path, |
+ bool copy) { |
+ FileUtil* 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 FileUtil::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 FileUtil::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 |