Chromium Code Reviews| 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 } | |
|
kinuko
2012/03/06 23:46:36
nit: looks like we can/should remove { } for singl
tzik
2012/03/07 00:27:13
Done.
| |
| 97 return CopyOrMoveFile(src_root_path_, dest_root_path_); | |
| 98 } | |
| 99 | |
| 100 PlatformFileError CrossFileUtilHelper::PerformErrorCheckAndPreparation() { | |
| 101 // Exits earlier if the source path does not exist. | |
| 102 if (!src_util_->PathExists(context_, src_root_path_)) | |
| 103 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 104 | |
| 105 // The parent of the |dest_root_path_| does not exist. | |
| 106 if (!ParentExists(dest_root_path_, dest_util_)) | |
| 107 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 108 | |
| 109 // It is an error to try to copy/move an entry into its child. | |
| 110 if (same_file_system_ && src_root_path_.IsParent(dest_root_path_)) | |
| 111 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; | |
| 112 | |
| 113 // Now it is ok to return if the |dest_root_path_| does not exist. | |
| 114 if (!dest_util_->PathExists(context_, dest_root_path_)) | |
| 115 return base::PLATFORM_FILE_OK; | |
| 116 | |
| 117 // |src_root_path_| exists and is a directory. | |
| 118 // |dest_root_path_| exists and is a file. | |
| 119 bool src_is_directory = src_util_->DirectoryExists(context_, src_root_path_); | |
| 120 bool dest_is_directory = | |
| 121 dest_util_->DirectoryExists(context_, dest_root_path_); | |
| 122 | |
| 123 // Either one of |src_root_path_| or |dest_root_path_| is directory, | |
| 124 // while the other is not. | |
| 125 if (src_is_directory != dest_is_directory) | |
| 126 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; | |
| 127 | |
| 128 // It is an error to copy/move an entry into the same path. | |
| 129 if (same_file_system_ && (src_root_path_.internal_path() == | |
| 130 dest_root_path_.internal_path())) | |
|
kinuko
2012/03/06 23:46:36
indent looks slightly off?
tzik
2012/03/07 00:27:13
Done.
| |
| 131 return base::PLATFORM_FILE_ERROR_EXISTS; | |
| 132 | |
| 133 if (dest_is_directory) { | |
| 134 // It is an error to copy/move an entry to a non-empty directory. | |
| 135 // Otherwise the copy/move attempt must overwrite the destination, but | |
| 136 // the file_util's Copy or Move method doesn't perform overwrite | |
| 137 // on all platforms, so we delete the destination directory here. | |
| 138 if (base::PLATFORM_FILE_OK != | |
| 139 dest_util_->DeleteSingleDirectory(context_, dest_root_path_)) { | |
| 140 if (!dest_util_->IsDirectoryEmpty(context_, dest_root_path_)) | |
| 141 return base::PLATFORM_FILE_ERROR_NOT_EMPTY; | |
| 142 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 143 } | |
| 144 } | |
| 145 return base::PLATFORM_FILE_OK; | |
| 146 } | |
| 147 | |
| 148 bool CrossFileUtilHelper::ParentExists( | |
| 149 const FileSystemPath& path, FileSystemFileUtil* file_util) { | |
| 150 // If path is in the root, path.DirName() will be ".", | |
| 151 // since we use paths with no leading '/'. | |
| 152 FilePath parent = path.internal_path().DirName(); | |
| 153 if (parent == FilePath(FILE_PATH_LITERAL("."))) | |
| 154 return true; | |
| 155 return file_util->DirectoryExists( | |
| 156 context_, path.WithInternalPath(parent)); | |
| 157 } | |
| 158 | |
| 159 PlatformFileError CrossFileUtilHelper::CopyOrMoveDirectory( | |
| 160 const FileSystemPath& src_path, | |
| 161 const FileSystemPath& dest_path) { | |
| 162 // At this point we must have gone through | |
| 163 // PerformErrorCheckAndPreparationForMoveAndCopy so this must be true. | |
| 164 DCHECK(!same_file_system_ || | |
| 165 !src_path.IsParent(dest_path)); | |
| 166 | |
| 167 PlatformFileError error = dest_util_->CreateDirectory( | |
| 168 context_, dest_path, false, false); | |
| 169 if (error != base::PLATFORM_FILE_OK) | |
| 170 return error; | |
| 171 | |
| 172 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( | |
| 173 src_util_->CreateFileEnumerator(context_, src_path)); | |
| 174 FilePath src_file_path_each; | |
| 175 while (!(src_file_path_each = file_enum->Next()).empty()) { | |
| 176 FilePath dest_file_path_each(dest_path.internal_path()); | |
| 177 src_path.internal_path().AppendRelativePath( | |
| 178 src_file_path_each, &dest_file_path_each); | |
| 179 | |
| 180 if (file_enum->IsDirectory()) { | |
| 181 PlatformFileError error = dest_util_->CreateDirectory( | |
| 182 context_, | |
| 183 dest_path.WithInternalPath(dest_file_path_each), | |
| 184 true /* exclusive */, false /* recursive */); | |
| 185 if (error != base::PLATFORM_FILE_OK) | |
| 186 return error; | |
| 187 } else { | |
| 188 PlatformFileError error = CopyOrMoveFile( | |
| 189 src_path.WithInternalPath(src_file_path_each), | |
| 190 dest_path.WithInternalPath(dest_file_path_each)); | |
| 191 if (error != base::PLATFORM_FILE_OK) | |
| 192 return error; | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 if (operation_ == OPERATION_MOVE) { | |
| 197 PlatformFileError error = | |
| 198 FileUtilHelper::Delete(context_, src_util_, | |
| 199 src_path, true /* recursive */); | |
| 200 if (error != base::PLATFORM_FILE_OK) | |
| 201 return error; | |
| 202 } | |
| 203 | |
| 204 return base::PLATFORM_FILE_OK; | |
| 205 } | |
| 206 | |
| 207 PlatformFileError CrossFileUtilHelper::CopyOrMoveFile( | |
| 208 const FileSystemPath& src_path, | |
| 209 const FileSystemPath& dest_path) { | |
| 210 if (same_file_system_) { | |
| 211 DCHECK(src_util_ == dest_util_); | |
| 212 // Source and destination are in the same FileSystemFileUtil; now we can | |
| 213 // safely call FileSystemFileUtil method on src_util_ (== dest_util_). | |
| 214 return src_util_->CopyOrMoveFile(context_, src_path, dest_path, | |
| 215 operation_ == OPERATION_COPY); | |
| 216 } | |
| 217 | |
| 218 // Resolve the src_path's underlying file path. | |
| 219 base::PlatformFileInfo file_info; | |
| 220 FilePath platform_file_path; | |
| 221 PlatformFileError error = src_util_->GetFileInfo( | |
| 222 context_, src_path, &file_info, &platform_file_path); | |
| 223 if (error != base::PLATFORM_FILE_OK) | |
| 224 return error; | |
| 225 | |
| 226 // Call CopyInForeignFile() on the dest_util_ with the resolved source path | |
| 227 // to perform limited cross-FileSystemFileUtil copy/move. | |
| 228 error = dest_util_->CopyInForeignFile( | |
| 229 context_, src_path.WithInternalPath(platform_file_path), dest_path); | |
| 230 | |
| 231 if (operation_ == OPERATION_COPY || error != base::PLATFORM_FILE_OK) | |
| 232 return error; | |
| 233 return src_util_->DeleteFile(context_, src_path); | |
| 234 } | |
| 235 | |
| 236 } // anonymous namespace | |
| 237 | |
| 238 // static | |
| 239 base::PlatformFileError FileUtilHelper::Copy( | |
| 240 FileSystemOperationContext* context, | |
| 241 FileSystemFileUtil* src_file_util, | |
| 242 FileSystemFileUtil* dest_file_util, | |
| 243 const FileSystemPath& src_root_path, | |
| 244 const FileSystemPath& dest_root_path) { | |
| 245 return CrossFileUtilHelper(context, src_file_util, dest_file_util, | |
| 246 src_root_path, dest_root_path, | |
| 247 CrossFileUtilHelper::OPERATION_COPY).DoWork(); | |
| 248 } | |
| 249 | |
| 250 // static | |
| 251 base::PlatformFileError FileUtilHelper::Move( | |
| 252 FileSystemOperationContext* context, | |
| 253 FileSystemFileUtil* src_file_util, | |
| 254 FileSystemFileUtil* dest_file_util, | |
| 255 const FileSystemPath& src_root_path, | |
| 256 const FileSystemPath& dest_root_path) { | |
| 257 return CrossFileUtilHelper(context, src_file_util, dest_file_util, | |
| 258 src_root_path, dest_root_path, | |
| 259 CrossFileUtilHelper::OPERATION_MOVE).DoWork(); | |
| 260 } | |
| 261 | |
| 262 // static | |
| 15 base::PlatformFileError FileUtilHelper::Delete( | 263 base::PlatformFileError FileUtilHelper::Delete( |
| 16 FileSystemOperationContext* context, | 264 FileSystemOperationContext* context, |
| 17 FileSystemFileUtil* file_util, | 265 FileSystemFileUtil* file_util, |
| 18 const FileSystemPath& path, | 266 const FileSystemPath& path, |
| 19 bool recursive) { | 267 bool recursive) { |
| 20 if (file_util->DirectoryExists(context, path)) { | 268 if (file_util->DirectoryExists(context, path)) { |
| 21 if (!recursive) | 269 if (!recursive) |
| 22 return file_util->DeleteSingleDirectory(context, path); | 270 return file_util->DeleteSingleDirectory(context, path); |
| 23 else | 271 else |
| 24 return DeleteDirectoryRecursive(context, file_util, path); | 272 return DeleteDirectoryRecursive(context, file_util, path); |
| 25 } else { | 273 } else { |
| 26 return file_util->DeleteFile(context, path); | 274 return file_util->DeleteFile(context, path); |
| 27 } | 275 } |
| 28 } | 276 } |
| 29 | 277 |
| 278 // static | |
| 30 base::PlatformFileError FileUtilHelper::DeleteDirectoryRecursive( | 279 base::PlatformFileError FileUtilHelper::DeleteDirectoryRecursive( |
| 31 FileSystemOperationContext* context, | 280 FileSystemOperationContext* context, |
| 32 FileSystemFileUtil* file_util, | 281 FileSystemFileUtil* file_util, |
| 33 const FileSystemPath& path) { | 282 const FileSystemPath& path) { |
| 34 | 283 |
| 35 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( | 284 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> file_enum( |
| 36 file_util->CreateFileEnumerator(context, path)); | 285 file_util->CreateFileEnumerator(context, path)); |
| 37 FilePath file_path_each; | 286 FilePath file_path_each; |
| 38 std::stack<FilePath> directories; | 287 std::stack<FilePath> directories; |
| 39 while (!(file_path_each = file_enum->Next()).empty()) { | 288 while (!(file_path_each = file_enum->Next()).empty()) { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 53 context, path.WithInternalPath(directories.top())); | 302 context, path.WithInternalPath(directories.top())); |
| 54 if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND && | 303 if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND && |
| 55 error != base::PLATFORM_FILE_OK) | 304 error != base::PLATFORM_FILE_OK) |
| 56 return error; | 305 return error; |
| 57 directories.pop(); | 306 directories.pop(); |
| 58 } | 307 } |
| 59 return file_util->DeleteSingleDirectory(context, path); | 308 return file_util->DeleteSingleDirectory(context, path); |
| 60 } | 309 } |
| 61 | 310 |
| 62 } // namespace fileapi | 311 } // namespace fileapi |
| OLD | NEW |