| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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/quota_file_util.h" | 5 #include "webkit/fileapi/quota_file_util.h" |
| 6 | 6 |
| 7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "webkit/fileapi/file_system_context.h" | 9 #include "webkit/fileapi/file_system_context.h" |
| 10 #include "webkit/fileapi/file_system_operation_context.h" | 10 #include "webkit/fileapi/file_system_operation_context.h" |
| 11 #include "webkit/fileapi/file_system_path_manager.h" | 11 #include "webkit/fileapi/file_system_path_manager.h" |
| 12 #include "webkit/fileapi/file_system_quota_util.h" | 12 #include "webkit/fileapi/file_system_quota_util.h" |
| 13 #include "webkit/quota/quota_manager.h" | 13 #include "webkit/quota/quota_manager.h" |
| 14 | 14 |
| 15 using quota::QuotaManagerProxy; | 15 using quota::QuotaManagerProxy; |
| 16 | 16 |
| 17 namespace fileapi { | 17 namespace fileapi { |
| 18 | 18 |
| 19 const int64 QuotaFileUtil::kNoLimit = kint64max; | 19 const int64 QuotaFileUtil::kNoLimit = kint64max; |
| 20 | 20 |
| 21 // See the comment in the header file as for these constants. | 21 namespace { |
| 22 const int64 QuotaFileUtil::kFilePathCostPerChar = 2; | |
| 23 const int64 QuotaFileUtil::kFilePathCostPerFile = 146; | |
| 24 | 22 |
| 25 namespace { | 23 // Checks if copying in the same filesystem can be performed. |
| 24 // This method is not called for moving within a single filesystem. |
| 25 bool CanCopy( |
| 26 const FilePath& src_file_path, |
| 27 const FilePath& dest_file_path, |
| 28 int64 allowed_bytes_growth, |
| 29 int64* growth) { |
| 30 base::PlatformFileInfo src_file_info; |
| 31 if (!file_util::GetFileInfo(src_file_path, &src_file_info)) { |
| 32 // Falling through to the actual copy/move operation. |
| 33 return true; |
| 34 } |
| 35 base::PlatformFileInfo dest_file_info; |
| 36 if (!file_util::GetFileInfo(dest_file_path, &dest_file_info)) |
| 37 dest_file_info.size = 0; |
| 38 if (allowed_bytes_growth != QuotaFileUtil::kNoLimit && |
| 39 src_file_info.size - dest_file_info.size > allowed_bytes_growth) |
| 40 return false; |
| 41 if (growth != NULL) |
| 42 *growth = src_file_info.size - dest_file_info.size; |
| 43 |
| 44 return true; |
| 45 } |
| 26 | 46 |
| 27 // A helper class to hook quota_util() methods before and after modifications. | 47 // A helper class to hook quota_util() methods before and after modifications. |
| 28 class ScopedOriginUpdateHelper { | 48 class ScopedOriginUpdateHelper { |
| 29 public: | 49 public: |
| 30 explicit ScopedOriginUpdateHelper( | 50 explicit ScopedOriginUpdateHelper( |
| 31 FileSystemOperationContext* operation_context, | 51 FileSystemOperationContext* operation_context, |
| 32 const GURL& origin_url, | 52 const GURL& origin_url, |
| 33 FileSystemType type) | 53 FileSystemType type) |
| 34 : operation_context_(operation_context), | 54 : operation_context_(operation_context), |
| 35 origin_url_(origin_url), | 55 origin_url_(origin_url), |
| 36 type_(type) { | 56 type_(type) { |
| 37 DCHECK(operation_context_); | 57 DCHECK(operation_context_); |
| 38 DCHECK(operation_context_->file_system_context()); | 58 DCHECK(operation_context_->file_system_context()); |
| 39 DCHECK(type != kFileSystemTypeUnknown); | 59 DCHECK(type != kFileSystemTypeUnknown); |
| 40 quota_util_ = | 60 quota_util_ = |
| 41 operation_context_->file_system_context()->GetQuotaUtil(type_); | 61 operation_context_->file_system_context()->GetQuotaUtil(type_); |
| 42 quota_manager_proxy_ = | 62 quota_manager_proxy_ = |
| 43 operation_context_->file_system_context()->quota_manager_proxy(); | 63 operation_context_->file_system_context()->quota_manager_proxy(); |
| 44 if (quota_util_) | 64 if (quota_util_) |
| 45 quota_util_->StartUpdateOriginOnFileThread(origin_url_, type_); | 65 quota_util_->StartUpdateOriginOnFileThread(origin_url_, type_); |
| 46 } | 66 } |
| 47 | 67 |
| 48 ~ScopedOriginUpdateHelper() { | 68 ~ScopedOriginUpdateHelper() { |
| 49 if (quota_util_) | 69 if (quota_util_) |
| 50 quota_util_->EndUpdateOriginOnFileThread(origin_url_, type_); | 70 quota_util_->EndUpdateOriginOnFileThread(origin_url_, type_); |
| 51 } | 71 } |
| 52 | 72 |
| 53 void NotifyUpdate(int64 growth) { | 73 void NotifyUpdate(int64 growth) { |
| 54 if (operation_context_->allowed_bytes_growth() != QuotaFileUtil::kNoLimit) | 74 operation_context_->set_allowed_bytes_growth( |
| 55 operation_context_->set_allowed_bytes_growth( | 75 operation_context_->allowed_bytes_growth() - growth); |
| 56 operation_context_->allowed_bytes_growth() - growth); | |
| 57 if (quota_util_) | 76 if (quota_util_) |
| 58 quota_util_->UpdateOriginUsageOnFileThread( | 77 quota_util_->UpdateOriginUsageOnFileThread( |
| 59 quota_manager_proxy_, origin_url_, type_, growth); | 78 quota_manager_proxy_, origin_url_, type_, growth); |
| 60 } | 79 } |
| 61 | 80 |
| 62 private: | 81 private: |
| 63 FileSystemOperationContext* operation_context_; | 82 FileSystemOperationContext* operation_context_; |
| 64 FileSystemQuotaUtil* quota_util_; | 83 FileSystemQuotaUtil* quota_util_; |
| 65 QuotaManagerProxy* quota_manager_proxy_; | 84 QuotaManagerProxy* quota_manager_proxy_; |
| 66 const GURL& origin_url_; | 85 const GURL& origin_url_; |
| 67 FileSystemType type_; | 86 FileSystemType type_; |
| 68 DISALLOW_COPY_AND_ASSIGN(ScopedOriginUpdateHelper); | 87 DISALLOW_COPY_AND_ASSIGN(ScopedOriginUpdateHelper); |
| 69 }; | 88 }; |
| 70 | 89 |
| 71 } // namespace (anonymous) | 90 } // namespace (anonymous) |
| 72 | 91 |
| 73 QuotaFileUtil::QuotaFileUtil(FileSystemFileUtil* underlying_file_util) | 92 QuotaFileUtil::QuotaFileUtil(FileSystemFileUtil* underlying_file_util) |
| 74 : underlying_file_util_(underlying_file_util) { | 93 : underlying_file_util_(underlying_file_util) { |
| 75 } | 94 } |
| 76 | 95 |
| 77 QuotaFileUtil::~QuotaFileUtil() { | 96 QuotaFileUtil::~QuotaFileUtil() { |
| 78 } | 97 } |
| 79 | 98 |
| 80 // static | 99 // static |
| 81 QuotaFileUtil* QuotaFileUtil::CreateDefault() { | 100 QuotaFileUtil* QuotaFileUtil::CreateDefault() { |
| 82 return new QuotaFileUtil(new FileSystemFileUtil()); | 101 return new QuotaFileUtil(new FileSystemFileUtil()); |
| 83 } | 102 } |
| 84 | 103 |
| 85 int64 QuotaFileUtil::ComputeFilePathCost(const FilePath& file_path) const { | |
| 86 return kFilePathCostPerFile + | |
| 87 file_path.BaseName().value().length() * kFilePathCostPerChar; | |
| 88 } | |
| 89 | |
| 90 PlatformFileError QuotaFileUtil::CreateOrOpen( | |
| 91 FileSystemOperationContext* fs_context, | |
| 92 const FilePath& file_path, int file_flags, | |
| 93 PlatformFile* file_handle, bool* created) { | |
| 94 DCHECK(fs_context); | |
| 95 scoped_ptr<ScopedOriginUpdateHelper> helper; | |
| 96 | |
| 97 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); | |
| 98 int64 growth = 0; | |
| 99 | |
| 100 if (!file_util::PathExists(file_path)) | |
| 101 growth = ComputeFilePathCost(fs_context->src_virtual_path()); | |
| 102 | |
| 103 if (growth > 0) { | |
| 104 helper.reset(new ScopedOriginUpdateHelper( | |
| 105 fs_context, | |
| 106 fs_context->src_origin_url(), | |
| 107 fs_context->src_type())); | |
| 108 | |
| 109 if (allowed_bytes_growth != kNoLimit && growth > allowed_bytes_growth) | |
| 110 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 111 } | |
| 112 | |
| 113 base::PlatformFileError error = underlying_file_util_->CreateOrOpen( | |
| 114 fs_context, file_path, file_flags, file_handle, created); | |
| 115 | |
| 116 if (growth > 0) { | |
| 117 if (error == base::PLATFORM_FILE_OK) | |
| 118 helper->NotifyUpdate(growth); | |
| 119 } | |
| 120 | |
| 121 return error; | |
| 122 } | |
| 123 | |
| 124 PlatformFileError QuotaFileUtil::EnsureFileExists( | |
| 125 FileSystemOperationContext* fs_context, | |
| 126 const FilePath& file_path, | |
| 127 bool* created) { | |
| 128 DCHECK(fs_context); | |
| 129 scoped_ptr<ScopedOriginUpdateHelper> helper; | |
| 130 | |
| 131 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); | |
| 132 int64 growth = 0; | |
| 133 | |
| 134 if (!file_util::PathExists(file_path)) | |
| 135 growth = ComputeFilePathCost(fs_context->src_virtual_path()); | |
| 136 | |
| 137 if (growth > 0) { | |
| 138 helper.reset(new ScopedOriginUpdateHelper( | |
| 139 fs_context, | |
| 140 fs_context->src_origin_url(), | |
| 141 fs_context->src_type())); | |
| 142 | |
| 143 if (allowed_bytes_growth != kNoLimit && growth > allowed_bytes_growth) | |
| 144 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 145 } | |
| 146 | |
| 147 base::PlatformFileError error = underlying_file_util_->EnsureFileExists( | |
| 148 fs_context, file_path, created); | |
| 149 | |
| 150 if (growth > 0 &&error == base::PLATFORM_FILE_OK) | |
| 151 helper->NotifyUpdate(growth); | |
| 152 | |
| 153 return error; | |
| 154 } | |
| 155 | |
| 156 PlatformFileError QuotaFileUtil::CreateDirectory( | |
| 157 FileSystemOperationContext* fs_context, | |
| 158 const FilePath& file_path, | |
| 159 bool exclusive, | |
| 160 bool recursive) { | |
| 161 DCHECK(fs_context); | |
| 162 scoped_ptr<ScopedOriginUpdateHelper> helper; | |
| 163 | |
| 164 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); | |
| 165 int64 growth = 0; | |
| 166 | |
| 167 if (!exclusive || !file_util::PathExists(file_path)) { | |
| 168 if (recursive) { | |
| 169 FilePath last_path; | |
| 170 for (FilePath path = fs_context->src_virtual_path(); | |
| 171 path.value() != last_path.value() && | |
| 172 !fs_context->src_file_system_file_util()->PathExists( | |
| 173 fs_context, fs_context->src_virtual_path()); | |
| 174 path = path.DirName()) { | |
| 175 growth += ComputeFilePathCost(fs_context->src_virtual_path()); | |
| 176 last_path = path; | |
| 177 } | |
| 178 } else { | |
| 179 growth += ComputeFilePathCost(fs_context->src_virtual_path()); | |
| 180 } | |
| 181 } | |
| 182 | |
| 183 if (growth > 0) { | |
| 184 helper.reset(new ScopedOriginUpdateHelper( | |
| 185 fs_context, | |
| 186 fs_context->src_origin_url(), | |
| 187 fs_context->src_type())); | |
| 188 | |
| 189 if (allowed_bytes_growth != kNoLimit && growth > allowed_bytes_growth) | |
| 190 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 191 } | |
| 192 | |
| 193 base::PlatformFileError error = base::PLATFORM_FILE_OK; | |
| 194 error = underlying_file_util_->CreateDirectory( | |
| 195 fs_context, file_path, exclusive, recursive); | |
| 196 | |
| 197 if (growth > 0 && error == base::PLATFORM_FILE_OK) | |
| 198 helper->NotifyUpdate(growth); | |
| 199 | |
| 200 return error; | |
| 201 } | |
| 202 | |
| 203 base::PlatformFileError QuotaFileUtil::CopyOrMoveFile( | 104 base::PlatformFileError QuotaFileUtil::CopyOrMoveFile( |
| 204 FileSystemOperationContext* fs_context, | 105 FileSystemOperationContext* fs_context, |
| 205 const FilePath& src_file_path, | 106 const FilePath& src_file_path, |
| 206 const FilePath& dest_file_path, | 107 const FilePath& dest_file_path, |
| 207 bool copy) { | 108 bool copy) { |
| 208 DCHECK(fs_context); | 109 DCHECK(fs_context); |
| 209 | 110 |
| 210 // TODO(kinuko): For cross-filesystem move case we need 2 helpers, one for | 111 // TODO(kinuko): For cross-filesystem move case we need 2 helpers, one for |
| 211 // src and one for dest. | 112 // src and one for dest. |
| 212 ScopedOriginUpdateHelper helper( | 113 ScopedOriginUpdateHelper helper( |
| 213 fs_context, | 114 fs_context, |
| 214 fs_context->dest_origin_url(), | 115 fs_context->dest_origin_url(), |
| 215 fs_context->dest_type()); | 116 fs_context->dest_type()); |
| 216 | 117 |
| 217 int64 growth = 0; | 118 int64 growth = 0; |
| 218 | 119 |
| 219 // It assumes copy/move operations are always in the same fs currently. | 120 // It assumes copy/move operations are always in the same fs currently. |
| 220 // TODO(dmikurube): Do quota check if moving between different fs. | 121 // TODO(dmikurube): Do quota check if moving between different fs. |
| 221 if (copy) { | 122 if (copy) { |
| 222 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); | 123 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); |
| 223 // The third argument (growth) is not used for now. | 124 // The third argument (growth) is not used for now. |
| 224 if (!CanCopyFile(fs_context, src_file_path, dest_file_path, | 125 if (!CanCopy(src_file_path, dest_file_path, allowed_bytes_growth, &growth)) |
| 225 allowed_bytes_growth, &growth)) | |
| 226 return base::PLATFORM_FILE_ERROR_NO_SPACE; | 126 return base::PLATFORM_FILE_ERROR_NO_SPACE; |
| 227 } else { // move | 127 } else { |
| 228 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); | |
| 229 base::PlatformFileInfo dest_file_info; | 128 base::PlatformFileInfo dest_file_info; |
| 230 int64 src_file_path_cost = | |
| 231 ComputeFilePathCost(fs_context->src_virtual_path()); | |
| 232 int64 dest_file_path_cost = | |
| 233 ComputeFilePathCost(fs_context->dest_virtual_path()); | |
| 234 if (!file_util::GetFileInfo(dest_file_path, &dest_file_info)) | 129 if (!file_util::GetFileInfo(dest_file_path, &dest_file_info)) |
| 235 dest_file_info.size = 0; | 130 dest_file_info.size = 0; |
| 236 growth = -dest_file_info.size - src_file_path_cost + dest_file_path_cost; | 131 growth = -dest_file_info.size; |
| 237 if (allowed_bytes_growth != kNoLimit && growth > allowed_bytes_growth) | |
| 238 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 239 } | 132 } |
| 240 | 133 |
| 241 base::PlatformFileError error = underlying_file_util_->CopyOrMoveFile( | 134 base::PlatformFileError error = underlying_file_util_->CopyOrMoveFile( |
| 242 fs_context, src_file_path, dest_file_path, copy); | 135 fs_context, src_file_path, dest_file_path, copy); |
| 243 | 136 |
| 244 if (error == base::PLATFORM_FILE_OK) { | 137 if (error == base::PLATFORM_FILE_OK) { |
| 245 // TODO(kinuko): For cross-filesystem move case, call this with -growth | 138 // TODO(kinuko): For cross-filesystem move case, call this with -growth |
| 246 // for source and growth for dest. | 139 // for source and growth for dest. |
| 247 helper.NotifyUpdate(growth); | 140 helper.NotifyUpdate(growth); |
| 248 } | 141 } |
| 249 | 142 |
| 250 return error; | 143 return error; |
| 251 } | 144 } |
| 252 | 145 |
| 253 base::PlatformFileError QuotaFileUtil::DeleteFile( | 146 base::PlatformFileError QuotaFileUtil::DeleteFile( |
| 254 FileSystemOperationContext* fs_context, | 147 FileSystemOperationContext* fs_context, |
| 255 const FilePath& file_path) { | 148 const FilePath& file_path) { |
| 256 DCHECK(fs_context); | 149 DCHECK(fs_context); |
| 257 ScopedOriginUpdateHelper helper( | 150 ScopedOriginUpdateHelper helper( |
| 258 fs_context, | 151 fs_context, |
| 259 fs_context->src_origin_url(), | 152 fs_context->src_origin_url(), |
| 260 fs_context->src_type()); | 153 fs_context->src_type()); |
| 261 | 154 |
| 262 int64 growth = 0; | 155 int64 growth = 0; |
| 263 base::PlatformFileInfo file_info; | 156 base::PlatformFileInfo file_info; |
| 264 if (file_util::GetFileInfo(file_path, &file_info)) { | 157 if (!file_util::GetFileInfo(file_path, &file_info)) |
| 265 growth -= file_info.size + | 158 file_info.size = 0; |
| 266 ComputeFilePathCost(fs_context->src_virtual_path()); | 159 growth = -file_info.size; |
| 267 } | |
| 268 | 160 |
| 269 base::PlatformFileError error = underlying_file_util_->DeleteFile( | 161 base::PlatformFileError error = underlying_file_util_->DeleteFile( |
| 270 fs_context, file_path); | 162 fs_context, file_path); |
| 271 | 163 |
| 272 if (error == base::PLATFORM_FILE_OK) | 164 if (error == base::PLATFORM_FILE_OK) |
| 273 helper.NotifyUpdate(growth); | 165 helper.NotifyUpdate(growth); |
| 274 | 166 |
| 275 return error; | 167 return error; |
| 276 } | 168 } |
| 277 | 169 |
| 278 base::PlatformFileError QuotaFileUtil::DeleteSingleDirectory( | |
| 279 FileSystemOperationContext* fs_context, | |
| 280 const FilePath& file_path) { | |
| 281 DCHECK(fs_context); | |
| 282 ScopedOriginUpdateHelper helper( | |
| 283 fs_context, | |
| 284 fs_context->src_origin_url(), | |
| 285 fs_context->src_type()); | |
| 286 | |
| 287 int64 growth = 0; | |
| 288 if (file_util::DirectoryExists(file_path)) | |
| 289 growth -= ComputeFilePathCost(fs_context->src_virtual_path()); | |
| 290 | |
| 291 base::PlatformFileError error = underlying_file_util_->DeleteSingleDirectory( | |
| 292 fs_context, file_path); | |
| 293 | |
| 294 if (error == base::PLATFORM_FILE_OK) | |
| 295 helper.NotifyUpdate(growth); | |
| 296 | |
| 297 return error; | |
| 298 } | |
| 299 | |
| 300 base::PlatformFileError QuotaFileUtil::Truncate( | 170 base::PlatformFileError QuotaFileUtil::Truncate( |
| 301 FileSystemOperationContext* fs_context, | 171 FileSystemOperationContext* fs_context, |
| 302 const FilePath& path, | 172 const FilePath& path, |
| 303 int64 length) { | 173 int64 length) { |
| 304 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); | 174 int64 allowed_bytes_growth = fs_context->allowed_bytes_growth(); |
| 305 ScopedOriginUpdateHelper helper( | 175 ScopedOriginUpdateHelper helper( |
| 306 fs_context, | 176 fs_context, |
| 307 fs_context->src_origin_url(), | 177 fs_context->src_origin_url(), |
| 308 fs_context->src_type()); | 178 fs_context->src_type()); |
| 309 | 179 |
| 310 int64 growth = 0; | 180 int64 growth = 0; |
| 311 base::PlatformFileInfo file_info; | 181 base::PlatformFileInfo file_info; |
| 312 if (!file_util::GetFileInfo(path, &file_info)) | 182 if (!file_util::GetFileInfo(path, &file_info)) |
| 313 return base::PLATFORM_FILE_ERROR_FAILED; | 183 return base::PLATFORM_FILE_ERROR_FAILED; |
| 314 | 184 |
| 315 growth = length - file_info.size; | 185 growth = length - file_info.size; |
| 316 if (allowed_bytes_growth != kNoLimit && growth > allowed_bytes_growth) | 186 if (allowed_bytes_growth != kNoLimit && growth > allowed_bytes_growth) |
| 317 return base::PLATFORM_FILE_ERROR_NO_SPACE; | 187 return base::PLATFORM_FILE_ERROR_NO_SPACE; |
| 318 | 188 |
| 319 base::PlatformFileError error = underlying_file_util_->Truncate( | 189 base::PlatformFileError error = underlying_file_util_->Truncate( |
| 320 fs_context, path, length); | 190 fs_context, path, length); |
| 321 | 191 |
| 322 if (error == base::PLATFORM_FILE_OK) | 192 if (error == base::PLATFORM_FILE_OK) |
| 323 helper.NotifyUpdate(growth); | 193 helper.NotifyUpdate(growth); |
| 324 | 194 |
| 325 return error; | 195 return error; |
| 326 } | 196 } |
| 327 | 197 |
| 328 // Checks if copying in the same filesystem can be performed. | |
| 329 // This method is not called for moving within a single filesystem. | |
| 330 bool QuotaFileUtil::CanCopyFile( | |
| 331 FileSystemOperationContext* fs_context, | |
| 332 const FilePath& src_file_path, | |
| 333 const FilePath& dest_file_path, | |
| 334 int64 allowed_bytes_growth, | |
| 335 int64* growth) const { | |
| 336 DCHECK(growth); | |
| 337 base::PlatformFileInfo src_file_info; | |
| 338 if (!file_util::GetFileInfo(src_file_path, &src_file_info)) { | |
| 339 // Falling through to the actual copy/move operation. | |
| 340 return true; | |
| 341 } | |
| 342 base::PlatformFileInfo dest_file_info; | |
| 343 int dest_file_path_cost = 0; | |
| 344 if (!file_util::GetFileInfo(dest_file_path, &dest_file_info)) { | |
| 345 dest_file_info.size = 0; | |
| 346 dest_file_path_cost = ComputeFilePathCost(fs_context->dest_virtual_path()); | |
| 347 } | |
| 348 *growth = src_file_info.size - dest_file_info.size + dest_file_path_cost; | |
| 349 if (allowed_bytes_growth != QuotaFileUtil::kNoLimit && | |
| 350 *growth > allowed_bytes_growth) | |
| 351 return false; | |
| 352 | |
| 353 return true; | |
| 354 } | |
| 355 | |
| 356 } // namespace fileapi | 198 } // namespace fileapi |
| OLD | NEW |