| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/fileapi/file_util_helper.h" | 5 #include "webkit/fileapi/file_util_helper.h" |
| 6 | 6 |
| 7 #include "webkit/fileapi/file_system_file_util.h" | 7 #include "webkit/fileapi/file_system_file_util.h" |
| 8 #include "webkit/fileapi/file_system_operation_context.h" | 8 #include "webkit/fileapi/file_system_operation_context.h" |
| 9 #include "webkit/fileapi/file_system_path.h" | 9 #include "webkit/fileapi/file_system_path.h" |
| 10 | 10 |
| 11 using base::PlatformFileError; | 11 using base::PlatformFileError; |
| 12 | 12 |
| 13 namespace fileapi { | 13 namespace fileapi { |
| 14 | 14 |
| 15 namespace { |
| 16 |
| 17 // A helper class for cross-FileUtil Copy/Move operations. |
| 18 class CrossFileUtilHelper { |
| 19 public: |
| 20 enum Operation { |
| 21 OPERATION_COPY, |
| 22 OPERATION_MOVE |
| 23 }; |
| 24 |
| 25 CrossFileUtilHelper(FileSystemOperationContext* context, |
| 26 FileSystemFileUtil* src_util, |
| 27 FileSystemFileUtil* dest_util, |
| 28 const FileSystemPath& src_path, |
| 29 const FileSystemPath& dest_path, |
| 30 Operation operation); |
| 31 ~CrossFileUtilHelper(); |
| 32 |
| 33 base::PlatformFileError DoWork(); |
| 34 |
| 35 private: |
| 36 // Performs common pre-operation check and preparation. |
| 37 // This may delete the destination directory if it's empty. |
| 38 base::PlatformFileError PerformErrorCheckAndPreparation(); |
| 39 |
| 40 // This assumes that the root exists. |
| 41 bool ParentExists(const FileSystemPath& path, FileSystemFileUtil* file_util); |
| 42 |
| 43 // Performs recursive copy or move by calling CopyOrMoveFile for individual |
| 44 // files. Operations for recursive traversal are encapsulated in this method. |
| 45 // It assumes src_path and dest_path have passed |
| 46 // PerformErrorCheckAndPreparationForMoveAndCopy(). |
| 47 base::PlatformFileError CopyOrMoveDirectory( |
| 48 const FileSystemPath& src_path, |
| 49 const FileSystemPath& dest_path); |
| 50 |
| 51 // Determines whether a simple same-filesystem move or copy can be done. If |
| 52 // so, it delegates to CopyOrMoveFile. Otherwise it looks up the true |
| 53 // platform path of the source file, delegates to CopyInForeignFile, and [for |
| 54 // move] calls DeleteFile on the source file. |
| 55 base::PlatformFileError CopyOrMoveFile( |
| 56 const FileSystemPath& src_path, |
| 57 const FileSystemPath& dest_path); |
| 58 |
| 59 FileSystemOperationContext* context_; |
| 60 FileSystemFileUtil* src_util_; // Not owned. |
| 61 FileSystemFileUtil* dest_util_; // Not owned. |
| 62 const FileSystemPath& src_root_path_; |
| 63 const FileSystemPath& dest_root_path_; |
| 64 Operation operation_; |
| 65 bool same_file_system_; |
| 66 |
| 67 DISALLOW_COPY_AND_ASSIGN(CrossFileUtilHelper); |
| 68 }; |
| 69 |
| 70 CrossFileUtilHelper::CrossFileUtilHelper( |
| 71 FileSystemOperationContext* context, |
| 72 FileSystemFileUtil* src_util, |
| 73 FileSystemFileUtil* dest_util, |
| 74 const FileSystemPath& src_path, |
| 75 const FileSystemPath& dest_path, |
| 76 Operation operation) |
| 77 : context_(context), |
| 78 src_util_(src_util), |
| 79 dest_util_(dest_util), |
| 80 src_root_path_(src_path), |
| 81 dest_root_path_(dest_path), |
| 82 operation_(operation) { |
| 83 same_file_system_ = |
| 84 src_root_path_.origin() == dest_root_path_.origin() && |
| 85 src_root_path_.type() == dest_root_path_.type(); |
| 86 } |
| 87 |
| 88 CrossFileUtilHelper::~CrossFileUtilHelper() {} |
| 89 |
| 90 base::PlatformFileError CrossFileUtilHelper::DoWork() { |
| 91 base::PlatformFileError error = PerformErrorCheckAndPreparation(); |
| 92 if (error != base::PLATFORM_FILE_OK) |
| 93 return error; |
| 94 if (src_util_->DirectoryExists(context_, src_root_path_)) |
| 95 return CopyOrMoveDirectory(src_root_path_, dest_root_path_); |
| 96 return CopyOrMoveFile(src_root_path_, dest_root_path_); |
| 97 } |
| 98 |
| 99 PlatformFileError CrossFileUtilHelper::PerformErrorCheckAndPreparation() { |
| 100 // Exits earlier if the source path does not exist. |
| 101 if (!src_util_->PathExists(context_, src_root_path_)) |
| 102 return base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| 103 |
| 104 // The parent of the |dest_root_path_| does not exist. |
| 105 if (!ParentExists(dest_root_path_, dest_util_)) |
| 106 return base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| 107 |
| 108 // It is an error to try to copy/move an entry into its child. |
| 109 if (same_file_system_ && src_root_path_.IsParent(dest_root_path_)) |
| 110 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| 111 |
| 112 // Now it is ok to return if the |dest_root_path_| does not exist. |
| 113 if (!dest_util_->PathExists(context_, dest_root_path_)) |
| 114 return base::PLATFORM_FILE_OK; |
| 115 |
| 116 // |src_root_path_| exists and is a directory. |
| 117 // |dest_root_path_| exists and is a file. |
| 118 bool src_is_directory = src_util_->DirectoryExists(context_, src_root_path_); |
| 119 bool dest_is_directory = |
| 120 dest_util_->DirectoryExists(context_, dest_root_path_); |
| 121 |
| 122 // Either one of |src_root_path_| or |dest_root_path_| is directory, |
| 123 // while the other is not. |
| 124 if (src_is_directory != dest_is_directory) |
| 125 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| 126 |
| 127 // It is an error to copy/move an entry into the same path. |
| 128 if (same_file_system_ && |
| 129 src_root_path_.internal_path() == dest_root_path_.internal_path()) |
| 130 return base::PLATFORM_FILE_ERROR_EXISTS; |
| 131 |
| 132 if (dest_is_directory) { |
| 133 // It is an error to copy/move an entry to a non-empty directory. |
| 134 // Otherwise the copy/move attempt must overwrite the destination, but |
| 135 // the file_util's Copy or Move method doesn't perform overwrite |
| 136 // on all platforms, so we delete the destination directory here. |
| 137 if (base::PLATFORM_FILE_OK != |
| 138 dest_util_->DeleteSingleDirectory(context_, dest_root_path_)) { |
| 139 if (!dest_util_->IsDirectoryEmpty(context_, dest_root_path_)) |
| 140 return base::PLATFORM_FILE_ERROR_NOT_EMPTY; |
| 141 return base::PLATFORM_FILE_ERROR_FAILED; |
| 142 } |
| 143 } |
| 144 return base::PLATFORM_FILE_OK; |
| 145 } |
| 146 |
| 147 bool CrossFileUtilHelper::ParentExists( |
| 148 const FileSystemPath& path, FileSystemFileUtil* file_util) { |
| 149 // If path is in the root, path.DirName() will be ".", |
| 150 // since we use paths with no leading '/'. |
| 151 FilePath parent = path.internal_path().DirName(); |
| 152 if (parent == FilePath(FILE_PATH_LITERAL("."))) |
| 153 return true; |
| 154 return file_util->DirectoryExists( |
| 155 context_, path.WithInternalPath(parent)); |
| 156 } |
| 157 |
| 158 PlatformFileError CrossFileUtilHelper::CopyOrMoveDirectory( |
| 159 const FileSystemPath& src_path, |
| 160 const FileSystemPath& dest_path) { |
| 161 // At this point we must have gone through |
| 162 // PerformErrorCheckAndPreparationForMoveAndCopy so this must be true. |
| 163 DCHECK(!same_file_system_ || |
| 164 !src_path.IsParent(dest_path)); |
| 165 |
| 166 PlatformFileError error = dest_util_->CreateDirectory( |
| 167 context_, dest_path, false, false); |
| 168 if (error != base::PLATFORM_FILE_OK) |
| 169 return error; |
| 170 |
| 171 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( |
| 172 src_util_->CreateFileEnumerator(context_, src_path, true)); |
| 173 FilePath src_file_path_each; |
| 174 while (!(src_file_path_each = file_enum->Next()).empty()) { |
| 175 FilePath dest_file_path_each(dest_path.internal_path()); |
| 176 src_path.internal_path().AppendRelativePath( |
| 177 src_file_path_each, &dest_file_path_each); |
| 178 |
| 179 if (file_enum->IsDirectory()) { |
| 180 PlatformFileError error = dest_util_->CreateDirectory( |
| 181 context_, |
| 182 dest_path.WithInternalPath(dest_file_path_each), |
| 183 true /* exclusive */, false /* recursive */); |
| 184 if (error != base::PLATFORM_FILE_OK) |
| 185 return error; |
| 186 } else { |
| 187 PlatformFileError error = CopyOrMoveFile( |
| 188 src_path.WithInternalPath(src_file_path_each), |
| 189 dest_path.WithInternalPath(dest_file_path_each)); |
| 190 if (error != base::PLATFORM_FILE_OK) |
| 191 return error; |
| 192 } |
| 193 } |
| 194 |
| 195 if (operation_ == OPERATION_MOVE) { |
| 196 PlatformFileError error = |
| 197 FileUtilHelper::Delete(context_, src_util_, |
| 198 src_path, true /* recursive */); |
| 199 if (error != base::PLATFORM_FILE_OK) |
| 200 return error; |
| 201 } |
| 202 |
| 203 return base::PLATFORM_FILE_OK; |
| 204 } |
| 205 |
| 206 PlatformFileError CrossFileUtilHelper::CopyOrMoveFile( |
| 207 const FileSystemPath& src_path, |
| 208 const FileSystemPath& dest_path) { |
| 209 if (same_file_system_) { |
| 210 DCHECK(src_util_ == dest_util_); |
| 211 // Source and destination are in the same FileSystemFileUtil; now we can |
| 212 // safely call FileSystemFileUtil method on src_util_ (== dest_util_). |
| 213 return src_util_->CopyOrMoveFile(context_, src_path, dest_path, |
| 214 operation_ == OPERATION_COPY); |
| 215 } |
| 216 |
| 217 // Resolve the src_path's underlying file path. |
| 218 base::PlatformFileInfo file_info; |
| 219 FilePath platform_file_path; |
| 220 PlatformFileError error = src_util_->GetFileInfo( |
| 221 context_, src_path, &file_info, &platform_file_path); |
| 222 if (error != base::PLATFORM_FILE_OK) |
| 223 return error; |
| 224 |
| 225 // Call CopyInForeignFile() on the dest_util_ with the resolved source path |
| 226 // to perform limited cross-FileSystemFileUtil copy/move. |
| 227 error = dest_util_->CopyInForeignFile( |
| 228 context_, src_path.WithInternalPath(platform_file_path), dest_path); |
| 229 |
| 230 if (operation_ == OPERATION_COPY || error != base::PLATFORM_FILE_OK) |
| 231 return error; |
| 232 return src_util_->DeleteFile(context_, src_path); |
| 233 } |
| 234 |
| 235 } // anonymous namespace |
| 236 |
| 237 // static |
| 238 base::PlatformFileError FileUtilHelper::Copy( |
| 239 FileSystemOperationContext* context, |
| 240 FileSystemFileUtil* src_file_util, |
| 241 FileSystemFileUtil* dest_file_util, |
| 242 const FileSystemPath& src_root_path, |
| 243 const FileSystemPath& dest_root_path) { |
| 244 return CrossFileUtilHelper(context, src_file_util, dest_file_util, |
| 245 src_root_path, dest_root_path, |
| 246 CrossFileUtilHelper::OPERATION_COPY).DoWork(); |
| 247 } |
| 248 |
| 249 // static |
| 250 base::PlatformFileError FileUtilHelper::Move( |
| 251 FileSystemOperationContext* context, |
| 252 FileSystemFileUtil* src_file_util, |
| 253 FileSystemFileUtil* dest_file_util, |
| 254 const FileSystemPath& src_root_path, |
| 255 const FileSystemPath& dest_root_path) { |
| 256 return CrossFileUtilHelper(context, src_file_util, dest_file_util, |
| 257 src_root_path, dest_root_path, |
| 258 CrossFileUtilHelper::OPERATION_MOVE).DoWork(); |
| 259 } |
| 260 |
| 261 // static |
| 15 base::PlatformFileError FileUtilHelper::Delete( | 262 base::PlatformFileError FileUtilHelper::Delete( |
| 16 FileSystemOperationContext* context, | 263 FileSystemOperationContext* context, |
| 17 FileSystemFileUtil* file_util, | 264 FileSystemFileUtil* file_util, |
| 18 const FileSystemPath& path, | 265 const FileSystemPath& path, |
| 19 bool recursive) { | 266 bool recursive) { |
| 20 if (file_util->DirectoryExists(context, path)) { | 267 if (file_util->DirectoryExists(context, path)) { |
| 21 if (!recursive) | 268 if (!recursive) |
| 22 return file_util->DeleteSingleDirectory(context, path); | 269 return file_util->DeleteSingleDirectory(context, path); |
| 23 else | 270 else |
| 24 return DeleteDirectoryRecursive(context, file_util, path); | 271 return DeleteDirectoryRecursive(context, file_util, path); |
| 25 } else { | 272 } else { |
| 26 return file_util->DeleteFile(context, path); | 273 return file_util->DeleteFile(context, path); |
| 27 } | 274 } |
| 28 } | 275 } |
| 29 | 276 |
| 277 // static |
| 30 base::PlatformFileError FileUtilHelper::DeleteDirectoryRecursive( | 278 base::PlatformFileError FileUtilHelper::DeleteDirectoryRecursive( |
| 31 FileSystemOperationContext* context, | 279 FileSystemOperationContext* context, |
| 32 FileSystemFileUtil* file_util, | 280 FileSystemFileUtil* file_util, |
| 33 const FileSystemPath& path) { | 281 const FileSystemPath& path) { |
| 34 | 282 |
| 35 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( | 283 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( |
| 36 file_util->CreateFileEnumerator(context, path, true /* recursive */)); | 284 file_util->CreateFileEnumerator(context, path, true /* recursive */)); |
| 37 FilePath file_path_each; | 285 FilePath file_path_each; |
| 38 std::stack<FilePath> directories; | 286 std::stack<FilePath> directories; |
| 39 while (!(file_path_each = file_enum->Next()).empty()) { | 287 while (!(file_path_each = file_enum->Next()).empty()) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 53 context, path.WithInternalPath(directories.top())); | 301 context, path.WithInternalPath(directories.top())); |
| 54 if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND && | 302 if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND && |
| 55 error != base::PLATFORM_FILE_OK) | 303 error != base::PLATFORM_FILE_OK) |
| 56 return error; | 304 return error; |
| 57 directories.pop(); | 305 directories.pop(); |
| 58 } | 306 } |
| 59 return file_util->DeleteSingleDirectory(context, path); | 307 return file_util->DeleteSingleDirectory(context, path); |
| 60 } | 308 } |
| 61 | 309 |
| 62 } // namespace fileapi | 310 } // namespace fileapi |
| OLD | NEW |