OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "webkit/fileapi/fileapi_file_util.h" |
| 6 |
| 7 #include <stack> |
| 8 |
| 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "webkit/fileapi/file_system_operation_context.h" |
| 11 |
| 12 namespace fileapi { |
| 13 |
| 14 namespace { |
| 15 |
| 16 // This assumes that the root exists. |
| 17 bool ParentExists(FileSystemOperationContext* context, |
| 18 FileUtil* file_util, const FilePath& file_path) { |
| 19 // If file_path is in the root, file_path.DirName() will be ".", |
| 20 // since we use paths with no leading '/'. |
| 21 FilePath parent = file_path.DirName(); |
| 22 if (parent == FilePath(FILE_PATH_LITERAL("."))) |
| 23 return true; |
| 24 return file_util->DirectoryExists(context, parent); |
| 25 } |
| 26 |
| 27 } // namespace |
| 28 |
| 29 PlatformFileError FileUtil::Copy( |
| 30 FileSystemOperationContext* context, |
| 31 const FilePath& src_file_path, |
| 32 const FilePath& dest_file_path) { |
| 33 PlatformFileError error_code; |
| 34 error_code = |
| 35 PerformCommonCheckAndPreparationForMoveAndCopy( |
| 36 context, src_file_path, dest_file_path); |
| 37 if (error_code != base::PLATFORM_FILE_OK) |
| 38 return error_code; |
| 39 |
| 40 if (DirectoryExists(context, src_file_path)) |
| 41 return CopyOrMoveDirectory(context, src_file_path, dest_file_path, |
| 42 true /* copy */); |
| 43 return CopyOrMoveFileHelper(context, src_file_path, dest_file_path, |
| 44 true /* copy */); |
| 45 } |
| 46 |
| 47 |
| 48 PlatformFileError FileUtil::Move( |
| 49 FileSystemOperationContext* context, |
| 50 const FilePath& src_file_path, |
| 51 const FilePath& dest_file_path) { |
| 52 PlatformFileError error_code; |
| 53 error_code = |
| 54 PerformCommonCheckAndPreparationForMoveAndCopy( |
| 55 context, src_file_path, dest_file_path); |
| 56 if (error_code != base::PLATFORM_FILE_OK) |
| 57 return error_code; |
| 58 |
| 59 // TODO(dmikurube): ReplaceFile if in the same domain and filesystem type. |
| 60 if (DirectoryExists(context, src_file_path)) |
| 61 return CopyOrMoveDirectory(context, src_file_path, dest_file_path, |
| 62 false /* copy */); |
| 63 return CopyOrMoveFileHelper(context, src_file_path, dest_file_path, |
| 64 false /* copy */); |
| 65 } |
| 66 |
| 67 PlatformFileError FileUtil::Delete( |
| 68 FileSystemOperationContext* context, |
| 69 const FilePath& file_path, |
| 70 bool recursive) { |
| 71 if (DirectoryExists(context, file_path)) { |
| 72 if (!recursive) |
| 73 return DeleteSingleDirectory(context, file_path); |
| 74 else |
| 75 return DeleteDirectoryRecursive(context, file_path); |
| 76 } else { |
| 77 return DeleteFile(context, file_path); |
| 78 } |
| 79 } |
| 80 |
| 81 PlatformFileError |
| 82 FileUtil::PerformCommonCheckAndPreparationForMoveAndCopy( |
| 83 FileSystemOperationContext* context, |
| 84 const FilePath& src_file_path, |
| 85 const FilePath& dest_file_path) { |
| 86 bool same_file_system = |
| 87 (context->src_origin_url() == context->dest_origin_url()) && |
| 88 (context->src_type() == context->dest_type()); |
| 89 FileUtil* dest_util = context->dest_file_util(); |
| 90 DCHECK(dest_util); |
| 91 if (same_file_system) |
| 92 DCHECK(context->src_file_util() == context->dest_file_util()); |
| 93 // All the single-path virtual FSFU methods expect the context information |
| 94 // to be in the src_* variables, not the dest_* variables, so we have to |
| 95 // make a new context if we want to call them on the dest_file_path. |
| 96 scoped_ptr<FileSystemOperationContext> dest_context( |
| 97 context->CreateInheritedContextForDest()); |
| 98 |
| 99 // Exits earlier if the source path does not exist. |
| 100 if (!PathExists(context, src_file_path)) |
| 101 return base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| 102 |
| 103 // The parent of the |dest_file_path| does not exist. |
| 104 if (!ParentExists(dest_context.get(), dest_util, dest_file_path)) |
| 105 return base::PLATFORM_FILE_ERROR_NOT_FOUND; |
| 106 |
| 107 // It is an error to try to copy/move an entry into its child. |
| 108 if (same_file_system && src_file_path.IsParent(dest_file_path)) |
| 109 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| 110 |
| 111 // Now it is ok to return if the |dest_file_path| does not exist. |
| 112 if (!dest_util->PathExists(dest_context.get(), dest_file_path)) |
| 113 return base::PLATFORM_FILE_OK; |
| 114 |
| 115 // |src_file_path| exists and is a directory. |
| 116 // |dest_file_path| exists and is a file. |
| 117 bool src_is_directory = DirectoryExists(context, src_file_path); |
| 118 bool dest_is_directory = |
| 119 dest_util->DirectoryExists(dest_context.get(), dest_file_path); |
| 120 if (src_is_directory && !dest_is_directory) |
| 121 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| 122 |
| 123 // |src_file_path| exists and is a file. |
| 124 // |dest_file_path| exists and is a directory. |
| 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_file_path.value() == dest_file_path.value())) |
| 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 // TODO(kinuko): may be better to change the file_util::{Copy,Move}. |
| 138 PlatformFileError error = dest_util->Delete( |
| 139 dest_context.get(), dest_file_path, false /* recursive */); |
| 140 context->ImportAllowedBytesGrowth(*dest_context); |
| 141 if (base::PLATFORM_FILE_OK != error) { |
| 142 if (!dest_util->IsDirectoryEmpty(dest_context.get(), dest_file_path)) |
| 143 return base::PLATFORM_FILE_ERROR_NOT_EMPTY; |
| 144 return base::PLATFORM_FILE_ERROR_FAILED; |
| 145 } |
| 146 } |
| 147 return base::PLATFORM_FILE_OK; |
| 148 } |
| 149 |
| 150 PlatformFileError FileUtil::CopyOrMoveDirectory( |
| 151 FileSystemOperationContext* context, |
| 152 const FilePath& src_file_path, |
| 153 const FilePath& dest_file_path, |
| 154 bool copy) { |
| 155 FileUtil* dest_util = context->dest_file_util(); |
| 156 scoped_ptr<FileSystemOperationContext> dest_context( |
| 157 context->CreateInheritedContextForDest()); |
| 158 |
| 159 // Re-check PerformCommonCheckAndPreparationForMoveAndCopy() by DCHECK. |
| 160 DCHECK(DirectoryExists(context, src_file_path)); |
| 161 DCHECK(ParentExists(dest_context.get(), dest_util, dest_file_path)); |
| 162 DCHECK(!dest_util->PathExists(dest_context.get(), dest_file_path)); |
| 163 if ((context->src_origin_url() == context->dest_origin_url()) && |
| 164 (context->src_type() == context->dest_type())) |
| 165 DCHECK(!src_file_path.IsParent(dest_file_path)); |
| 166 |
| 167 if (!dest_util->DirectoryExists(dest_context.get(), dest_file_path)) { |
| 168 PlatformFileError error = dest_util->CreateDirectory(dest_context.get(), |
| 169 dest_file_path, false, false); |
| 170 context->ImportAllowedBytesGrowth(*dest_context); |
| 171 if (error != base::PLATFORM_FILE_OK) |
| 172 return error; |
| 173 } |
| 174 |
| 175 scoped_ptr<AbstractFileEnumerator> file_enum( |
| 176 CreateFileEnumerator(context, src_file_path)); |
| 177 FilePath src_file_path_each; |
| 178 while (!(src_file_path_each = file_enum->Next()).empty()) { |
| 179 FilePath dest_file_path_each(dest_file_path); |
| 180 src_file_path.AppendRelativePath(src_file_path_each, &dest_file_path_each); |
| 181 |
| 182 if (file_enum->IsDirectory()) { |
| 183 scoped_ptr<FileSystemOperationContext> new_directory_context( |
| 184 dest_context->CreateInheritedContextWithNewVirtualPaths( |
| 185 dest_file_path_each, FilePath())); |
| 186 PlatformFileError error = dest_util->CreateDirectory( |
| 187 new_directory_context.get(), dest_file_path_each, false, false); |
| 188 context->ImportAllowedBytesGrowth(*new_directory_context); |
| 189 if (error != base::PLATFORM_FILE_OK) |
| 190 return error; |
| 191 } else { |
| 192 scoped_ptr<FileSystemOperationContext> copy_context( |
| 193 context->CreateInheritedContextWithNewVirtualPaths( |
| 194 src_file_path_each, dest_file_path_each)); |
| 195 PlatformFileError error = CopyOrMoveFileHelper( |
| 196 copy_context.get(), src_file_path_each, dest_file_path_each, copy); |
| 197 context->ImportAllowedBytesGrowth(*copy_context); |
| 198 if (error != base::PLATFORM_FILE_OK) |
| 199 return error; |
| 200 } |
| 201 } |
| 202 |
| 203 if (!copy) { |
| 204 PlatformFileError error = Delete(context, src_file_path, true); |
| 205 if (error != base::PLATFORM_FILE_OK) |
| 206 return error; |
| 207 } |
| 208 return base::PLATFORM_FILE_OK; |
| 209 } |
| 210 |
| 211 PlatformFileError FileUtil::CopyOrMoveFileHelper( |
| 212 FileSystemOperationContext* context, |
| 213 const FilePath& src_file_path, |
| 214 const FilePath& dest_file_path, |
| 215 bool copy) { |
| 216 // CopyOrMoveFile here is the virtual overridden member function. |
| 217 if ((context->src_origin_url() == context->dest_origin_url()) && |
| 218 (context->src_type() == context->dest_type())) { |
| 219 DCHECK(context->src_file_util() == context->dest_file_util()); |
| 220 return CopyOrMoveFile(context, src_file_path, dest_file_path, copy); |
| 221 } |
| 222 base::PlatformFileInfo file_info; |
| 223 FilePath platform_file_path; |
| 224 PlatformFileError error_code; |
| 225 error_code = |
| 226 GetFileInfo(context, src_file_path, &file_info, &platform_file_path); |
| 227 if (error_code != base::PLATFORM_FILE_OK) |
| 228 return error_code; |
| 229 |
| 230 DCHECK(context->dest_file_util()); |
| 231 error_code = context->dest_file_util()->CopyInForeignFile( |
| 232 context, platform_file_path, dest_file_path); |
| 233 if (copy || error_code != base::PLATFORM_FILE_OK) |
| 234 return error_code; |
| 235 return DeleteFile(context, src_file_path); |
| 236 } |
| 237 |
| 238 PlatformFileError FileUtil::DeleteDirectoryRecursive( |
| 239 FileSystemOperationContext* context, |
| 240 const FilePath& file_path) { |
| 241 scoped_ptr<AbstractFileEnumerator> file_enum( |
| 242 CreateFileEnumerator(context, file_path)); |
| 243 FilePath file_path_each; |
| 244 |
| 245 std::stack<FilePath> directories; |
| 246 while (!(file_path_each = file_enum->Next()).empty()) { |
| 247 if (file_enum->IsDirectory()) { |
| 248 directories.push(file_path_each); |
| 249 } else { |
| 250 // DeleteFile here is the virtual overridden member function. |
| 251 scoped_ptr<FileSystemOperationContext> inherited_context( |
| 252 context->CreateInheritedContextWithNewVirtualPaths( |
| 253 file_path_each, FilePath())); |
| 254 PlatformFileError error = |
| 255 DeleteFile(inherited_context.get(), file_path_each); |
| 256 context->ImportAllowedBytesGrowth(*inherited_context); |
| 257 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) |
| 258 return base::PLATFORM_FILE_ERROR_FAILED; |
| 259 else if (error != base::PLATFORM_FILE_OK) |
| 260 return error; |
| 261 } |
| 262 } |
| 263 |
| 264 while (!directories.empty()) { |
| 265 scoped_ptr<FileSystemOperationContext> inherited_context( |
| 266 context->CreateInheritedContextWithNewVirtualPaths( |
| 267 directories.top(), FilePath())); |
| 268 PlatformFileError error = |
| 269 DeleteSingleDirectory(inherited_context.get(), directories.top()); |
| 270 context->ImportAllowedBytesGrowth(*inherited_context); |
| 271 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) |
| 272 return base::PLATFORM_FILE_ERROR_FAILED; |
| 273 else if (error != base::PLATFORM_FILE_OK) |
| 274 return error; |
| 275 directories.pop(); |
| 276 } |
| 277 return DeleteSingleDirectory(context, file_path); |
| 278 } |
| 279 |
| 280 } // namespace fileapi |
OLD | NEW |