| 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/obfuscated_file_system_file_util.h" | |
| 6 | |
| 7 #include <queue> | |
| 8 #include <string> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/file_util.h" | |
| 12 #include "base/format_macros.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/message_loop.h" | |
| 15 #include "base/stl_util.h" | |
| 16 #include "base/string_number_conversions.h" | |
| 17 #include "base/stringprintf.h" | |
| 18 #include "base/sys_string_conversions.h" | |
| 19 #include "googleurl/src/gurl.h" | |
| 20 #include "webkit/fileapi/file_system_context.h" | |
| 21 #include "webkit/fileapi/file_system_operation_context.h" | |
| 22 #include "webkit/fileapi/file_system_path_manager.h" | |
| 23 #include "webkit/fileapi/file_system_quota_util.h" | |
| 24 #include "webkit/fileapi/file_system_util.h" | |
| 25 #include "webkit/fileapi/sandbox_mount_point_provider.h" | |
| 26 #include "webkit/quota/quota_manager.h" | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 const int64 kFlushDelaySeconds = 10 * 60; // 10 minutes | |
| 31 | |
| 32 const char kOriginDatabaseName[] = "Origins"; | |
| 33 const char kDirectoryDatabaseName[] = "Paths"; | |
| 34 | |
| 35 void InitFileInfo( | |
| 36 fileapi::FileSystemDirectoryDatabase::FileInfo* file_info, | |
| 37 fileapi::FileSystemDirectoryDatabase::FileId parent_id, | |
| 38 const FilePath::StringType& file_name) { | |
| 39 DCHECK(file_info); | |
| 40 file_info->parent_id = parent_id; | |
| 41 file_info->name = file_name; | |
| 42 } | |
| 43 | |
| 44 bool IsRootDirectory(const FilePath& virtual_path) { | |
| 45 return (virtual_path.empty() || | |
| 46 virtual_path.value() == FILE_PATH_LITERAL("/")); | |
| 47 } | |
| 48 | |
| 49 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of | |
| 50 // path storage under Linux. It's not clear if that will differ on Windows, on | |
| 51 // which FilePath uses wide chars [since they're converted to UTF-8 for storage | |
| 52 // anyway], but as long as the cost is high enough that one can't cheat on quota | |
| 53 // by storing data in paths, it doesn't need to be all that accurate. | |
| 54 const int64 kPathCreationQuotaCost = 146; // Bytes per inode, basically. | |
| 55 const int64 kPathByteQuotaCost = 2; // Bytes per byte of path length in UTF-8. | |
| 56 | |
| 57 int64 GetPathQuotaUsage( | |
| 58 int growth_in_number_of_paths, | |
| 59 int64 growth_in_bytes_of_path_length) { | |
| 60 return growth_in_number_of_paths * kPathCreationQuotaCost + | |
| 61 growth_in_bytes_of_path_length * kPathByteQuotaCost; | |
| 62 } | |
| 63 | |
| 64 bool AllocateQuotaForPath( | |
| 65 fileapi::FileSystemOperationContext* context, | |
| 66 int growth_in_number_of_paths, | |
| 67 int64 growth_in_bytes_of_path_length) { | |
| 68 int64 growth = GetPathQuotaUsage(growth_in_number_of_paths, | |
| 69 growth_in_bytes_of_path_length); | |
| 70 int64 new_quota = context->allowed_bytes_growth() - growth; | |
| 71 | |
| 72 if (growth <= 0 || new_quota >= 0) { | |
| 73 context->set_allowed_bytes_growth(new_quota); | |
| 74 return true; | |
| 75 } | |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 void UpdatePathQuotaUsage( | |
| 80 fileapi::FileSystemOperationContext* context, | |
| 81 const GURL& origin_url, | |
| 82 fileapi::FileSystemType type, | |
| 83 int growth_in_number_of_paths, // -1, 0, or 1 | |
| 84 int64 growth_in_bytes_of_path_length) { | |
| 85 int64 growth = GetPathQuotaUsage(growth_in_number_of_paths, | |
| 86 growth_in_bytes_of_path_length); | |
| 87 fileapi::FileSystemQuotaUtil* quota_util = | |
| 88 context->file_system_context()->GetQuotaUtil(type); | |
| 89 quota::QuotaManagerProxy* quota_manager_proxy = | |
| 90 context->file_system_context()->quota_manager_proxy(); | |
| 91 quota_util->UpdateOriginUsageOnFileThread(quota_manager_proxy, origin_url, | |
| 92 type, growth); | |
| 93 } | |
| 94 | |
| 95 const FilePath::CharType kLegacyDataDirectory[] = FILE_PATH_LITERAL("Legacy"); | |
| 96 | |
| 97 const FilePath::CharType kTemporaryDirectoryName[] = FILE_PATH_LITERAL("t"); | |
| 98 const FilePath::CharType kPersistentDirectoryName[] = FILE_PATH_LITERAL("p"); | |
| 99 | |
| 100 } // namespace | |
| 101 | |
| 102 namespace fileapi { | |
| 103 | |
| 104 using base::PlatformFile; | |
| 105 using base::PlatformFileError; | |
| 106 | |
| 107 ObfuscatedFileSystemFileUtil::ObfuscatedFileSystemFileUtil( | |
| 108 const FilePath& file_system_directory, | |
| 109 FileSystemFileUtil* underlying_file_util) | |
| 110 : file_system_directory_(file_system_directory), | |
| 111 underlying_file_util_(underlying_file_util) { | |
| 112 } | |
| 113 | |
| 114 ObfuscatedFileSystemFileUtil::~ObfuscatedFileSystemFileUtil() { | |
| 115 DropDatabases(); | |
| 116 } | |
| 117 | |
| 118 PlatformFileError ObfuscatedFileSystemFileUtil::CreateOrOpen( | |
| 119 FileSystemOperationContext* context, | |
| 120 const FilePath& virtual_path, int file_flags, | |
| 121 PlatformFile* file_handle, bool* created) { | |
| 122 DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE | | |
| 123 base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ | | |
| 124 base::PLATFORM_FILE_EXCLUSIVE_WRITE))); | |
| 125 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 126 context->src_origin_url(), context->src_type(), true); | |
| 127 if (!db) | |
| 128 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 129 FileId file_id; | |
| 130 if (!db->GetFileWithPath(virtual_path, &file_id)) { | |
| 131 // The file doesn't exist. | |
| 132 if (!(file_flags & (base::PLATFORM_FILE_CREATE | | |
| 133 base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_ALWAYS))) | |
| 134 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 135 FileId parent_id; | |
| 136 if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id)) | |
| 137 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 138 FileInfo file_info; | |
| 139 InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value()); | |
| 140 if (!AllocateQuotaForPath(context, 1, file_info.name.size())) | |
| 141 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 142 PlatformFileError error = CreateFile( | |
| 143 context, context->src_origin_url(), context->src_type(), FilePath(), | |
| 144 &file_info, file_flags, file_handle); | |
| 145 if (created && base::PLATFORM_FILE_OK == error) | |
| 146 *created = true; | |
| 147 return error; | |
| 148 } | |
| 149 if (file_flags & base::PLATFORM_FILE_CREATE) | |
| 150 return base::PLATFORM_FILE_ERROR_EXISTS; | |
| 151 | |
| 152 FileInfo file_info; | |
| 153 if (!db->GetFileInfo(file_id, &file_info)) { | |
| 154 NOTREACHED(); | |
| 155 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 156 } | |
| 157 if (file_info.is_directory()) | |
| 158 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; | |
| 159 FilePath local_path = DataPathToLocalPath(context->src_origin_url(), | |
| 160 context->src_type(), file_info.data_path); | |
| 161 base::PlatformFileError error = underlying_file_util_->CreateOrOpen( | |
| 162 context, local_path, file_flags, file_handle, created); | |
| 163 if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) { | |
| 164 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker. | |
| 165 // TODO(tzik): Delete database entry after ensuring the file lost. | |
| 166 context->file_system_context()->GetQuotaUtil(context->src_type())-> | |
| 167 InvalidateUsageCache(context->src_origin_url(), | |
| 168 context->src_type()); | |
| 169 LOG(WARNING) << "Lost a backing file."; | |
| 170 error = base::PLATFORM_FILE_ERROR_FAILED; | |
| 171 } | |
| 172 return error; | |
| 173 } | |
| 174 | |
| 175 PlatformFileError ObfuscatedFileSystemFileUtil::EnsureFileExists( | |
| 176 FileSystemOperationContext* context, | |
| 177 const FilePath& virtual_path, | |
| 178 bool* created) { | |
| 179 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 180 context->src_origin_url(), context->src_type(), true); | |
| 181 if (!db) | |
| 182 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 183 FileId file_id; | |
| 184 if (db->GetFileWithPath(virtual_path, &file_id)) { | |
| 185 FileInfo file_info; | |
| 186 if (!db->GetFileInfo(file_id, &file_info)) { | |
| 187 NOTREACHED(); | |
| 188 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 189 } | |
| 190 if (file_info.is_directory()) | |
| 191 return base::PLATFORM_FILE_ERROR_NOT_A_FILE; | |
| 192 if (created) | |
| 193 *created = false; | |
| 194 return base::PLATFORM_FILE_OK; | |
| 195 } | |
| 196 FileId parent_id; | |
| 197 if (!db->GetFileWithPath(virtual_path.DirName(), &parent_id)) | |
| 198 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 199 | |
| 200 FileInfo file_info; | |
| 201 InitFileInfo(&file_info, parent_id, virtual_path.BaseName().value()); | |
| 202 if (!AllocateQuotaForPath(context, 1, file_info.name.size())) | |
| 203 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 204 PlatformFileError error = CreateFile(context, context->src_origin_url(), | |
| 205 context->src_type(), FilePath(), &file_info, 0, NULL); | |
| 206 if (created && base::PLATFORM_FILE_OK == error) | |
| 207 *created = true; | |
| 208 return error; | |
| 209 } | |
| 210 | |
| 211 PlatformFileError ObfuscatedFileSystemFileUtil::GetLocalFilePath( | |
| 212 FileSystemOperationContext* context, | |
| 213 const FilePath& virtual_path, | |
| 214 FilePath* local_path) { | |
| 215 FilePath path = | |
| 216 GetLocalPath(context->src_origin_url(), context->src_type(), | |
| 217 virtual_path); | |
| 218 if (path.empty()) | |
| 219 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 220 | |
| 221 *local_path = path; | |
| 222 return base::PLATFORM_FILE_OK; | |
| 223 } | |
| 224 | |
| 225 PlatformFileError ObfuscatedFileSystemFileUtil::GetFileInfo( | |
| 226 FileSystemOperationContext* context, | |
| 227 const FilePath& virtual_path, | |
| 228 base::PlatformFileInfo* file_info, | |
| 229 FilePath* platform_file_path) { | |
| 230 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 231 context->src_origin_url(), context->src_type(), false); | |
| 232 if (!db) | |
| 233 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 234 FileId file_id; | |
| 235 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 236 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 237 FileInfo local_info; | |
| 238 return GetFileInfoInternal(db, context, file_id, | |
| 239 &local_info, file_info, platform_file_path); | |
| 240 } | |
| 241 | |
| 242 PlatformFileError ObfuscatedFileSystemFileUtil::ReadDirectory( | |
| 243 FileSystemOperationContext* context, | |
| 244 const FilePath& virtual_path, | |
| 245 std::vector<base::FileUtilProxy::Entry>* entries) { | |
| 246 // TODO(kkanetkar): Implement directory read in multiple chunks. | |
| 247 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 248 context->src_origin_url(), context->src_type(), false); | |
| 249 if (!db) { | |
| 250 if (IsRootDirectory(virtual_path)) { | |
| 251 // It's the root directory and the database hasn't been initialized yet. | |
| 252 entries->clear(); | |
| 253 return base::PLATFORM_FILE_OK; | |
| 254 } | |
| 255 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 256 } | |
| 257 FileId file_id; | |
| 258 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 259 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 260 FileInfo file_info; | |
| 261 if (!db->GetFileInfo(file_id, &file_info)) { | |
| 262 DCHECK(!file_id); | |
| 263 // It's the root directory and the database hasn't been initialized yet. | |
| 264 entries->clear(); | |
| 265 return base::PLATFORM_FILE_OK; | |
| 266 } | |
| 267 if (!file_info.is_directory()) | |
| 268 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 269 std::vector<FileId> children; | |
| 270 if (!db->ListChildren(file_id, &children)) { | |
| 271 NOTREACHED(); | |
| 272 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 273 } | |
| 274 std::vector<FileId>::iterator iter; | |
| 275 for (iter = children.begin(); iter != children.end(); ++iter) { | |
| 276 base::PlatformFileInfo platform_file_info; | |
| 277 FilePath file_path; | |
| 278 if (GetFileInfoInternal(db, context, *iter, | |
| 279 &file_info, &platform_file_info, &file_path) != | |
| 280 base::PLATFORM_FILE_OK) { | |
| 281 LOG(WARNING) << "Lost a backing file."; | |
| 282 // TODO(tzik): We found a file entry in directory database without | |
| 283 // backing file here. Track the inconsistency and remove the database | |
| 284 // entry if we can't recover it. | |
| 285 continue; | |
| 286 } | |
| 287 | |
| 288 base::FileUtilProxy::Entry entry; | |
| 289 entry.name = file_info.name; | |
| 290 entry.is_directory = file_info.is_directory(); | |
| 291 entry.size = entry.is_directory ? 0 : platform_file_info.size; | |
| 292 entry.last_modified_time = platform_file_info.last_modified; | |
| 293 entries->push_back(entry); | |
| 294 } | |
| 295 return base::PLATFORM_FILE_OK; | |
| 296 } | |
| 297 | |
| 298 PlatformFileError ObfuscatedFileSystemFileUtil::CreateDirectory( | |
| 299 FileSystemOperationContext* context, | |
| 300 const FilePath& virtual_path, | |
| 301 bool exclusive, | |
| 302 bool recursive) { | |
| 303 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 304 context->src_origin_url(), context->src_type(), true); | |
| 305 if (!db) | |
| 306 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 307 FileId file_id; | |
| 308 if (db->GetFileWithPath(virtual_path, &file_id)) { | |
| 309 FileInfo file_info; | |
| 310 if (exclusive) | |
| 311 return base::PLATFORM_FILE_ERROR_EXISTS; | |
| 312 if (!db->GetFileInfo(file_id, &file_info)) { | |
| 313 NOTREACHED(); | |
| 314 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 315 } | |
| 316 if (!file_info.is_directory()) | |
| 317 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; | |
| 318 return base::PLATFORM_FILE_OK; | |
| 319 } | |
| 320 | |
| 321 std::vector<FilePath::StringType> components; | |
| 322 virtual_path.GetComponents(&components); | |
| 323 FileId parent_id = 0; | |
| 324 size_t index; | |
| 325 for (index = 0; index < components.size(); ++index) { | |
| 326 FilePath::StringType name = components[index]; | |
| 327 if (name == FILE_PATH_LITERAL("/")) | |
| 328 continue; | |
| 329 if (!db->GetChildWithName(parent_id, name, &parent_id)) | |
| 330 break; | |
| 331 } | |
| 332 if (!recursive && components.size() - index > 1) | |
| 333 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 334 for (; index < components.size(); ++index) { | |
| 335 FileInfo file_info; | |
| 336 file_info.name = components[index]; | |
| 337 if (file_info.name == FILE_PATH_LITERAL("/")) | |
| 338 continue; | |
| 339 file_info.modification_time = base::Time::Now(); | |
| 340 file_info.parent_id = parent_id; | |
| 341 if (!AllocateQuotaForPath(context, 1, file_info.name.size())) | |
| 342 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 343 if (!db->AddFileInfo(file_info, &parent_id)) { | |
| 344 NOTREACHED(); | |
| 345 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 346 } | |
| 347 UpdatePathQuotaUsage(context, context->src_origin_url(), | |
| 348 context->src_type(), 1, file_info.name.size()); | |
| 349 } | |
| 350 return base::PLATFORM_FILE_OK; | |
| 351 } | |
| 352 | |
| 353 PlatformFileError ObfuscatedFileSystemFileUtil::CopyOrMoveFile( | |
| 354 FileSystemOperationContext* context, | |
| 355 const FilePath& src_file_path, | |
| 356 const FilePath& dest_file_path, | |
| 357 bool copy) { | |
| 358 // Cross-filesystem copies and moves should be handled via CopyInForeignFile. | |
| 359 DCHECK(context->src_origin_url() == context->dest_origin_url()); | |
| 360 DCHECK(context->src_type() == context->dest_type()); | |
| 361 | |
| 362 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 363 context->src_origin_url(), context->src_type(), true); | |
| 364 if (!db) | |
| 365 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 366 FileId src_file_id; | |
| 367 if (!db->GetFileWithPath(src_file_path, &src_file_id)) | |
| 368 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 369 FileId dest_file_id; | |
| 370 bool overwrite = db->GetFileWithPath(dest_file_path, &dest_file_id); | |
| 371 FileInfo src_file_info; | |
| 372 FileInfo dest_file_info; | |
| 373 if (!db->GetFileInfo(src_file_id, &src_file_info) || | |
| 374 src_file_info.is_directory()) { | |
| 375 NOTREACHED(); | |
| 376 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 377 } | |
| 378 if (overwrite) { | |
| 379 if (!db->GetFileInfo(dest_file_id, &dest_file_info) || | |
| 380 dest_file_info.is_directory()) { | |
| 381 NOTREACHED(); | |
| 382 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 383 } | |
| 384 } | |
| 385 /* | |
| 386 * Copy-with-overwrite | |
| 387 * Just overwrite data file | |
| 388 * Copy-without-overwrite | |
| 389 * Copy backing file | |
| 390 * Create new metadata pointing to new backing file. | |
| 391 * Move-with-overwrite | |
| 392 * transaction: | |
| 393 * Remove source entry. | |
| 394 * Point target entry to source entry's backing file. | |
| 395 * Delete target entry's old backing file | |
| 396 * Move-without-overwrite | |
| 397 * Just update metadata | |
| 398 */ | |
| 399 if (copy) { | |
| 400 FilePath src_data_path = DataPathToLocalPath(context->src_origin_url(), | |
| 401 context->src_type(), src_file_info.data_path); | |
| 402 if (!underlying_file_util_->PathExists(context, src_data_path)) { | |
| 403 // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker. | |
| 404 context->file_system_context()->GetQuotaUtil(context->src_type())-> | |
| 405 InvalidateUsageCache(context->src_origin_url(), | |
| 406 context->src_type()); | |
| 407 LOG(WARNING) << "Lost a backing file."; | |
| 408 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 409 } | |
| 410 | |
| 411 if (overwrite) { | |
| 412 FilePath dest_data_path = DataPathToLocalPath(context->src_origin_url(), | |
| 413 context->src_type(), dest_file_info.data_path); | |
| 414 return underlying_file_util_->CopyOrMoveFile(context, | |
| 415 src_data_path, dest_data_path, copy); | |
| 416 } else { | |
| 417 FileId dest_parent_id; | |
| 418 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { | |
| 419 NOTREACHED(); // We shouldn't be called in this case. | |
| 420 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 421 } | |
| 422 InitFileInfo(&dest_file_info, dest_parent_id, | |
| 423 dest_file_path.BaseName().value()); | |
| 424 if (!AllocateQuotaForPath(context, 1, dest_file_info.name.size())) | |
| 425 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 426 return CreateFile(context, context->dest_origin_url(), | |
| 427 context->dest_type(), src_data_path, &dest_file_info, 0, | |
| 428 NULL); | |
| 429 } | |
| 430 } else { // It's a move. | |
| 431 if (overwrite) { | |
| 432 AllocateQuotaForPath(context, -1, | |
| 433 -static_cast<int64>(src_file_info.name.size())); | |
| 434 if (!db->OverwritingMoveFile(src_file_id, dest_file_id)) | |
| 435 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 436 FilePath dest_data_path = DataPathToLocalPath(context->src_origin_url(), | |
| 437 context->src_type(), dest_file_info.data_path); | |
| 438 if (base::PLATFORM_FILE_OK != | |
| 439 underlying_file_util_->DeleteFile(context, dest_data_path)) | |
| 440 LOG(WARNING) << "Leaked a backing file."; | |
| 441 UpdatePathQuotaUsage(context, context->src_origin_url(), | |
| 442 context->src_type(), -1, | |
| 443 -static_cast<int64>(src_file_info.name.size())); | |
| 444 return base::PLATFORM_FILE_OK; | |
| 445 } else { | |
| 446 FileId dest_parent_id; | |
| 447 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { | |
| 448 NOTREACHED(); | |
| 449 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 450 } | |
| 451 if (!AllocateQuotaForPath( | |
| 452 context, 0, | |
| 453 static_cast<int64>(dest_file_path.BaseName().value().size()) | |
| 454 - static_cast<int64>(src_file_info.name.size()))) | |
| 455 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 456 src_file_info.parent_id = dest_parent_id; | |
| 457 src_file_info.name = dest_file_path.BaseName().value(); | |
| 458 if (!db->UpdateFileInfo(src_file_id, src_file_info)) | |
| 459 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 460 UpdatePathQuotaUsage( | |
| 461 context, context->src_origin_url(), context->src_type(), 0, | |
| 462 static_cast<int64>(dest_file_path.BaseName().value().size()) - | |
| 463 static_cast<int64>(src_file_path.BaseName().value().size())); | |
| 464 return base::PLATFORM_FILE_OK; | |
| 465 } | |
| 466 } | |
| 467 NOTREACHED(); | |
| 468 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 469 } | |
| 470 | |
| 471 PlatformFileError ObfuscatedFileSystemFileUtil::CopyInForeignFile( | |
| 472 FileSystemOperationContext* context, | |
| 473 const FilePath& src_file_path, | |
| 474 const FilePath& dest_file_path) { | |
| 475 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 476 context->dest_origin_url(), context->dest_type(), true); | |
| 477 if (!db) | |
| 478 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 479 FileId dest_file_id; | |
| 480 bool overwrite = db->GetFileWithPath(dest_file_path, &dest_file_id); | |
| 481 FileInfo dest_file_info; | |
| 482 if (overwrite) { | |
| 483 if (!db->GetFileInfo(dest_file_id, &dest_file_info) || | |
| 484 dest_file_info.is_directory()) { | |
| 485 NOTREACHED(); | |
| 486 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 487 } | |
| 488 FilePath dest_data_path = DataPathToLocalPath(context->dest_origin_url(), | |
| 489 context->dest_type(), dest_file_info.data_path); | |
| 490 return underlying_file_util_->CopyOrMoveFile(context, | |
| 491 src_file_path, dest_data_path, true /* copy */); | |
| 492 } else { | |
| 493 FileId dest_parent_id; | |
| 494 if (!db->GetFileWithPath(dest_file_path.DirName(), &dest_parent_id)) { | |
| 495 NOTREACHED(); // We shouldn't be called in this case. | |
| 496 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 497 } | |
| 498 InitFileInfo(&dest_file_info, dest_parent_id, | |
| 499 dest_file_path.BaseName().value()); | |
| 500 if (!AllocateQuotaForPath(context, 1, dest_file_info.name.size())) | |
| 501 return base::PLATFORM_FILE_ERROR_NO_SPACE; | |
| 502 return CreateFile(context, context->dest_origin_url(), | |
| 503 context->dest_type(), src_file_path, &dest_file_info, 0, NULL); | |
| 504 } | |
| 505 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 506 } | |
| 507 | |
| 508 PlatformFileError ObfuscatedFileSystemFileUtil::DeleteFile( | |
| 509 FileSystemOperationContext* context, | |
| 510 const FilePath& virtual_path) { | |
| 511 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 512 context->src_origin_url(), context->src_type(), true); | |
| 513 if (!db) | |
| 514 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 515 FileId file_id; | |
| 516 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 517 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 518 FileInfo file_info; | |
| 519 if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) { | |
| 520 NOTREACHED(); | |
| 521 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 522 } | |
| 523 if (!db->RemoveFileInfo(file_id)) { | |
| 524 NOTREACHED(); | |
| 525 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 526 } | |
| 527 AllocateQuotaForPath(context, -1, -static_cast<int64>(file_info.name.size())); | |
| 528 UpdatePathQuotaUsage(context, context->src_origin_url(), context->src_type(), | |
| 529 -1, -static_cast<int64>(file_info.name.size())); | |
| 530 FilePath data_path = DataPathToLocalPath(context->src_origin_url(), | |
| 531 context->src_type(), file_info.data_path); | |
| 532 if (base::PLATFORM_FILE_OK != | |
| 533 underlying_file_util_->DeleteFile(context, data_path)) | |
| 534 LOG(WARNING) << "Leaked a backing file."; | |
| 535 return base::PLATFORM_FILE_OK; | |
| 536 } | |
| 537 | |
| 538 PlatformFileError ObfuscatedFileSystemFileUtil::DeleteSingleDirectory( | |
| 539 FileSystemOperationContext* context, | |
| 540 const FilePath& virtual_path) { | |
| 541 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 542 context->src_origin_url(), context->src_type(), true); | |
| 543 if (!db) | |
| 544 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 545 FileId file_id; | |
| 546 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 547 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 548 FileInfo file_info; | |
| 549 if (!db->GetFileInfo(file_id, &file_info) || !file_info.is_directory()) { | |
| 550 NOTREACHED(); | |
| 551 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 552 } | |
| 553 if (!db->RemoveFileInfo(file_id)) | |
| 554 return base::PLATFORM_FILE_ERROR_NOT_EMPTY; | |
| 555 AllocateQuotaForPath(context, -1, -static_cast<int64>(file_info.name.size())); | |
| 556 UpdatePathQuotaUsage(context, context->src_origin_url(), context->src_type(), | |
| 557 -1, -static_cast<int64>(file_info.name.size())); | |
| 558 return base::PLATFORM_FILE_OK; | |
| 559 } | |
| 560 | |
| 561 PlatformFileError ObfuscatedFileSystemFileUtil::Touch( | |
| 562 FileSystemOperationContext* context, | |
| 563 const FilePath& virtual_path, | |
| 564 const base::Time& last_access_time, | |
| 565 const base::Time& last_modified_time) { | |
| 566 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 567 context->src_origin_url(), context->src_type(), false); | |
| 568 if (!db) | |
| 569 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 570 FileId file_id; | |
| 571 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 572 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 573 | |
| 574 FileInfo file_info; | |
| 575 if (!db->GetFileInfo(file_id, &file_info)) { | |
| 576 NOTREACHED(); | |
| 577 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 578 } | |
| 579 if (file_info.is_directory()) { | |
| 580 file_info.modification_time = last_modified_time; | |
| 581 if (!db->UpdateFileInfo(file_id, file_info)) | |
| 582 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 583 return base::PLATFORM_FILE_OK; | |
| 584 } | |
| 585 FilePath data_path = DataPathToLocalPath(context->src_origin_url(), | |
| 586 context->src_type(), file_info.data_path); | |
| 587 return underlying_file_util_->Touch( | |
| 588 context, data_path, last_access_time, last_modified_time); | |
| 589 } | |
| 590 | |
| 591 PlatformFileError ObfuscatedFileSystemFileUtil::Truncate( | |
| 592 FileSystemOperationContext* context, | |
| 593 const FilePath& virtual_path, | |
| 594 int64 length) { | |
| 595 FilePath local_path = | |
| 596 GetLocalPath(context->src_origin_url(), context->src_type(), | |
| 597 virtual_path); | |
| 598 if (local_path.empty()) | |
| 599 return base::PLATFORM_FILE_ERROR_NOT_FOUND; | |
| 600 return underlying_file_util_->Truncate( | |
| 601 context, local_path, length); | |
| 602 } | |
| 603 | |
| 604 bool ObfuscatedFileSystemFileUtil::PathExists( | |
| 605 FileSystemOperationContext* context, | |
| 606 const FilePath& virtual_path) { | |
| 607 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 608 context->src_origin_url(), context->src_type(), false); | |
| 609 if (!db) | |
| 610 return false; | |
| 611 FileId file_id; | |
| 612 return db->GetFileWithPath(virtual_path, &file_id); | |
| 613 } | |
| 614 | |
| 615 bool ObfuscatedFileSystemFileUtil::DirectoryExists( | |
| 616 FileSystemOperationContext* context, | |
| 617 const FilePath& virtual_path) { | |
| 618 if (IsRootDirectory(virtual_path)) { | |
| 619 // It's questionable whether we should return true or false for the | |
| 620 // root directory of nonexistent origin, but here we return true | |
| 621 // as the current implementation of ReadDirectory always returns an empty | |
| 622 // array (rather than erroring out with NOT_FOUND_ERR even) for | |
| 623 // nonexistent origins. | |
| 624 // Note: if you're going to change this behavior please also consider | |
| 625 // changiing the ReadDirectory's behavior! | |
| 626 return true; | |
| 627 } | |
| 628 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 629 context->src_origin_url(), context->src_type(), false); | |
| 630 if (!db) | |
| 631 return false; | |
| 632 FileId file_id; | |
| 633 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 634 return false; | |
| 635 FileInfo file_info; | |
| 636 if (!db->GetFileInfo(file_id, &file_info)) { | |
| 637 NOTREACHED(); | |
| 638 return false; | |
| 639 } | |
| 640 return file_info.is_directory(); | |
| 641 } | |
| 642 | |
| 643 bool ObfuscatedFileSystemFileUtil::IsDirectoryEmpty( | |
| 644 FileSystemOperationContext* context, | |
| 645 const FilePath& virtual_path) { | |
| 646 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 647 context->src_origin_url(), context->src_type(), false); | |
| 648 if (!db) | |
| 649 return true; // Not a great answer, but it's what others do. | |
| 650 FileId file_id; | |
| 651 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 652 return true; // Ditto. | |
| 653 FileInfo file_info; | |
| 654 if (!db->GetFileInfo(file_id, &file_info)) { | |
| 655 DCHECK(!file_id); | |
| 656 // It's the root directory and the database hasn't been initialized yet. | |
| 657 return true; | |
| 658 } | |
| 659 if (!file_info.is_directory()) | |
| 660 return true; | |
| 661 std::vector<FileId> children; | |
| 662 // TODO(ericu): This could easily be made faster with help from the database. | |
| 663 if (!db->ListChildren(file_id, &children)) | |
| 664 return true; | |
| 665 return children.empty(); | |
| 666 } | |
| 667 | |
| 668 class ObfuscatedFileSystemFileEnumerator | |
| 669 : public FileSystemFileUtil::AbstractFileEnumerator { | |
| 670 public: | |
| 671 ObfuscatedFileSystemFileEnumerator( | |
| 672 const FilePath& base_path, | |
| 673 FileSystemDirectoryDatabase* db, | |
| 674 FileSystemOperationContext* context, | |
| 675 FileSystemFileUtil* underlying_file_util, | |
| 676 const FilePath& virtual_root_path) | |
| 677 : base_path_(base_path), | |
| 678 db_(db), | |
| 679 context_(context), | |
| 680 underlying_file_util_(underlying_file_util) { | |
| 681 DCHECK(db_); | |
| 682 DCHECK(context_); | |
| 683 DCHECK(underlying_file_util_); | |
| 684 | |
| 685 FileId file_id; | |
| 686 FileInfo file_info; | |
| 687 if (!db_->GetFileWithPath(virtual_root_path, &file_id)) | |
| 688 return; | |
| 689 if (!db_->GetFileInfo(file_id, &file_info)) | |
| 690 return; | |
| 691 if (!file_info.is_directory()) | |
| 692 return; | |
| 693 FileRecord record = { file_id, file_info, virtual_root_path }; | |
| 694 display_queue_.push(record); | |
| 695 Next(); // Enumerators don't include the directory itself. | |
| 696 } | |
| 697 | |
| 698 ~ObfuscatedFileSystemFileEnumerator() {} | |
| 699 | |
| 700 virtual FilePath Next() OVERRIDE { | |
| 701 ProcessRecurseQueue(); | |
| 702 if (display_queue_.empty()) | |
| 703 return FilePath(); | |
| 704 current_ = display_queue_.front(); | |
| 705 display_queue_.pop(); | |
| 706 if (current_.file_info.is_directory()) | |
| 707 recurse_queue_.push(current_); | |
| 708 return current_.file_path; | |
| 709 } | |
| 710 | |
| 711 virtual int64 Size() OVERRIDE { | |
| 712 if (IsDirectory()) | |
| 713 return 0; | |
| 714 | |
| 715 base::PlatformFileInfo file_info; | |
| 716 FilePath platform_file_path; | |
| 717 | |
| 718 FilePath local_path = base_path_.Append(current_.file_info.data_path); | |
| 719 base::PlatformFileError error = underlying_file_util_->GetFileInfo( | |
| 720 context_, local_path, &file_info, &platform_file_path); | |
| 721 if (error != base::PLATFORM_FILE_OK) { | |
| 722 LOG(WARNING) << "Lost a backing file."; | |
| 723 return 0; | |
| 724 } | |
| 725 return file_info.size; | |
| 726 } | |
| 727 | |
| 728 virtual bool IsDirectory() OVERRIDE { | |
| 729 return current_.file_info.is_directory(); | |
| 730 } | |
| 731 | |
| 732 private: | |
| 733 typedef FileSystemDirectoryDatabase::FileId FileId; | |
| 734 typedef FileSystemDirectoryDatabase::FileInfo FileInfo; | |
| 735 | |
| 736 struct FileRecord { | |
| 737 FileId file_id; | |
| 738 FileInfo file_info; | |
| 739 FilePath file_path; | |
| 740 }; | |
| 741 | |
| 742 void ProcessRecurseQueue() { | |
| 743 while (display_queue_.empty() && !recurse_queue_.empty()) { | |
| 744 FileRecord directory = recurse_queue_.front(); | |
| 745 std::vector<FileId> children; | |
| 746 recurse_queue_.pop(); | |
| 747 if (!db_->ListChildren(directory.file_id, &children)) | |
| 748 return; | |
| 749 std::vector<FileId>::iterator iter; | |
| 750 for (iter = children.begin(); iter != children.end(); ++iter) { | |
| 751 FileRecord child; | |
| 752 child.file_id = *iter; | |
| 753 if (!db_->GetFileInfo(child.file_id, &child.file_info)) | |
| 754 return; | |
| 755 child.file_path = directory.file_path.Append(child.file_info.name); | |
| 756 display_queue_.push(child); | |
| 757 } | |
| 758 } | |
| 759 } | |
| 760 | |
| 761 std::queue<FileRecord> display_queue_; | |
| 762 std::queue<FileRecord> recurse_queue_; | |
| 763 FileRecord current_; | |
| 764 FilePath base_path_; | |
| 765 FileSystemDirectoryDatabase* db_; | |
| 766 FileSystemOperationContext* context_; | |
| 767 FileSystemFileUtil* underlying_file_util_; | |
| 768 }; | |
| 769 | |
| 770 class ObfuscatedFileSystemOriginEnumerator | |
| 771 : public ObfuscatedFileSystemFileUtil::AbstractOriginEnumerator { | |
| 772 public: | |
| 773 typedef FileSystemOriginDatabase::OriginRecord OriginRecord; | |
| 774 ObfuscatedFileSystemOriginEnumerator( | |
| 775 FileSystemOriginDatabase* origin_database, | |
| 776 const FilePath& base_path) | |
| 777 : base_path_(base_path) { | |
| 778 if (origin_database) | |
| 779 origin_database->ListAllOrigins(&origins_); | |
| 780 } | |
| 781 | |
| 782 ~ObfuscatedFileSystemOriginEnumerator() {} | |
| 783 | |
| 784 // Returns the next origin. Returns empty if there are no more origins. | |
| 785 virtual GURL Next() OVERRIDE { | |
| 786 OriginRecord record; | |
| 787 if (!origins_.empty()) { | |
| 788 record = origins_.back(); | |
| 789 origins_.pop_back(); | |
| 790 } | |
| 791 current_ = record; | |
| 792 return GetOriginURLFromIdentifier(record.origin); | |
| 793 } | |
| 794 | |
| 795 // Returns the current origin's information. | |
| 796 virtual bool HasFileSystemType(FileSystemType type) const OVERRIDE { | |
| 797 if (current_.path.empty()) | |
| 798 return false; | |
| 799 FilePath::StringType type_string = | |
| 800 ObfuscatedFileSystemFileUtil::GetDirectoryNameForType(type); | |
| 801 if (type_string.empty()) { | |
| 802 NOTREACHED(); | |
| 803 return false; | |
| 804 } | |
| 805 FilePath path = base_path_.Append(current_.path).Append(type_string); | |
| 806 return file_util::DirectoryExists(path); | |
| 807 } | |
| 808 | |
| 809 private: | |
| 810 std::vector<OriginRecord> origins_; | |
| 811 OriginRecord current_; | |
| 812 FilePath base_path_; | |
| 813 }; | |
| 814 | |
| 815 ObfuscatedFileSystemFileUtil::AbstractOriginEnumerator* | |
| 816 ObfuscatedFileSystemFileUtil::CreateOriginEnumerator() { | |
| 817 std::vector<FileSystemOriginDatabase::OriginRecord> origins; | |
| 818 | |
| 819 InitOriginDatabase(false); | |
| 820 return new ObfuscatedFileSystemOriginEnumerator( | |
| 821 origin_database_.get(), file_system_directory_); | |
| 822 } | |
| 823 | |
| 824 FileSystemFileUtil::AbstractFileEnumerator* | |
| 825 ObfuscatedFileSystemFileUtil::CreateFileEnumerator( | |
| 826 FileSystemOperationContext* context, | |
| 827 const FilePath& root_path) { | |
| 828 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 829 context->src_origin_url(), context->src_type(), false); | |
| 830 if (!db) | |
| 831 return new FileSystemFileUtil::EmptyFileEnumerator(); | |
| 832 return new ObfuscatedFileSystemFileEnumerator( | |
| 833 GetDirectoryForOriginAndType(context->src_origin_url(), | |
| 834 context->src_type(), false), | |
| 835 db, | |
| 836 context, | |
| 837 underlying_file_util_.get(), | |
| 838 root_path); | |
| 839 } | |
| 840 | |
| 841 PlatformFileError ObfuscatedFileSystemFileUtil::GetFileInfoInternal( | |
| 842 FileSystemDirectoryDatabase* db, | |
| 843 FileSystemOperationContext* context, | |
| 844 FileId file_id, | |
| 845 FileInfo* local_info, | |
| 846 base::PlatformFileInfo* file_info, | |
| 847 FilePath* platform_file_path) { | |
| 848 DCHECK(db); | |
| 849 DCHECK(context); | |
| 850 DCHECK(file_info); | |
| 851 DCHECK(platform_file_path); | |
| 852 | |
| 853 if (!db->GetFileInfo(file_id, local_info)) { | |
| 854 NOTREACHED(); | |
| 855 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 856 } | |
| 857 | |
| 858 if (local_info->is_directory()) { | |
| 859 file_info->is_directory = true; | |
| 860 file_info->is_symbolic_link = false; | |
| 861 file_info->last_modified = local_info->modification_time; | |
| 862 *platform_file_path = FilePath(); | |
| 863 // We don't fill in ctime or atime. | |
| 864 return base::PLATFORM_FILE_OK; | |
| 865 } | |
| 866 if (local_info->data_path.empty()) | |
| 867 return base::PLATFORM_FILE_ERROR_INVALID_OPERATION; | |
| 868 FilePath data_path = DataPathToLocalPath(context->src_origin_url(), | |
| 869 context->src_type(), local_info->data_path); | |
| 870 return underlying_file_util_->GetFileInfo( | |
| 871 context, data_path, file_info, platform_file_path); | |
| 872 } | |
| 873 | |
| 874 PlatformFileError ObfuscatedFileSystemFileUtil::CreateFile( | |
| 875 FileSystemOperationContext* context, | |
| 876 const GURL& origin_url, FileSystemType type, const FilePath& source_path, | |
| 877 FileInfo* file_info, int file_flags, PlatformFile* handle) { | |
| 878 if (handle) | |
| 879 *handle = base::kInvalidPlatformFileValue; | |
| 880 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 881 origin_url, type, true); | |
| 882 int64 number; | |
| 883 if (!db || !db->GetNextInteger(&number)) | |
| 884 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 885 // We use the third- and fourth-to-last digits as the directory. | |
| 886 int64 directory_number = number % 10000 / 100; | |
| 887 // TODO(ericu): local_path is an OS path; underlying_file_util_ isn't | |
| 888 // guaranteed to understand OS paths. | |
| 889 FilePath local_path = | |
| 890 GetDirectoryForOriginAndType(origin_url, type, false); | |
| 891 if (local_path.empty()) | |
| 892 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 893 | |
| 894 local_path = local_path.AppendASCII(StringPrintf("%02" PRIu64, | |
| 895 directory_number)); | |
| 896 PlatformFileError error; | |
| 897 error = underlying_file_util_->CreateDirectory( | |
| 898 context, local_path, false /* exclusive */, false /* recursive */); | |
| 899 if (base::PLATFORM_FILE_OK != error) | |
| 900 return error; | |
| 901 local_path = local_path.AppendASCII(StringPrintf("%08" PRIu64, number)); | |
| 902 FilePath data_path = LocalPathToDataPath(origin_url, type, local_path); | |
| 903 if (data_path.empty()) | |
| 904 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 905 bool created = false; | |
| 906 if (!source_path.empty()) { | |
| 907 DCHECK(!file_flags); | |
| 908 DCHECK(!handle); | |
| 909 error = underlying_file_util_->CopyOrMoveFile( | |
| 910 context, source_path, local_path, true /* copy */); | |
| 911 created = true; | |
| 912 } else { | |
| 913 FilePath path; | |
| 914 underlying_file_util_->GetLocalFilePath(context, local_path, &path); | |
| 915 if (file_util::PathExists(path)) { | |
| 916 if (!file_util::Delete(path, true)) { | |
| 917 NOTREACHED(); | |
| 918 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 919 } | |
| 920 LOG(WARNING) << "A stray file detected"; | |
| 921 context->file_system_context()->GetQuotaUtil(context->src_type())-> | |
| 922 InvalidateUsageCache(context->src_origin_url(), context->src_type()); | |
| 923 } | |
| 924 | |
| 925 if (handle) { | |
| 926 error = underlying_file_util_->CreateOrOpen( | |
| 927 context, local_path, file_flags, handle, &created); | |
| 928 // If this succeeds, we must close handle on any subsequent error. | |
| 929 } else { | |
| 930 DCHECK(!file_flags); // file_flags is only used by CreateOrOpen. | |
| 931 error = underlying_file_util_->EnsureFileExists( | |
| 932 context, local_path, &created); | |
| 933 } | |
| 934 } | |
| 935 if (error != base::PLATFORM_FILE_OK) | |
| 936 return error; | |
| 937 | |
| 938 if (!created) { | |
| 939 NOTREACHED(); | |
| 940 if (handle) { | |
| 941 DCHECK_NE(base::kInvalidPlatformFileValue, *handle); | |
| 942 base::ClosePlatformFile(*handle); | |
| 943 underlying_file_util_->DeleteFile(context, local_path); | |
| 944 } | |
| 945 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 946 } | |
| 947 file_info->data_path = data_path; | |
| 948 FileId file_id; | |
| 949 if (!db->AddFileInfo(*file_info, &file_id)) { | |
| 950 if (handle) { | |
| 951 DCHECK_NE(base::kInvalidPlatformFileValue, *handle); | |
| 952 base::ClosePlatformFile(*handle); | |
| 953 } | |
| 954 underlying_file_util_->DeleteFile(context, local_path); | |
| 955 return base::PLATFORM_FILE_ERROR_FAILED; | |
| 956 } | |
| 957 UpdatePathQuotaUsage(context, origin_url, type, 1, file_info->name.size()); | |
| 958 | |
| 959 return base::PLATFORM_FILE_OK; | |
| 960 } | |
| 961 | |
| 962 FilePath ObfuscatedFileSystemFileUtil::GetLocalPath( | |
| 963 const GURL& origin_url, | |
| 964 FileSystemType type, | |
| 965 const FilePath& virtual_path) { | |
| 966 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 967 origin_url, type, false); | |
| 968 if (!db) | |
| 969 return FilePath(); | |
| 970 FileId file_id; | |
| 971 if (!db->GetFileWithPath(virtual_path, &file_id)) | |
| 972 return FilePath(); | |
| 973 FileInfo file_info; | |
| 974 if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) { | |
| 975 NOTREACHED(); | |
| 976 return FilePath(); // Directories have no local path. | |
| 977 } | |
| 978 return DataPathToLocalPath(origin_url, type, file_info.data_path); | |
| 979 } | |
| 980 | |
| 981 FilePath ObfuscatedFileSystemFileUtil::GetDirectoryForOriginAndType( | |
| 982 const GURL& origin, FileSystemType type, bool create) { | |
| 983 FilePath origin_dir = GetDirectoryForOrigin(origin, create); | |
| 984 if (origin_dir.empty()) | |
| 985 return FilePath(); | |
| 986 FilePath::StringType type_string = GetDirectoryNameForType(type); | |
| 987 if (type_string.empty()) { | |
| 988 LOG(WARNING) << "Unknown filesystem type requested:" << type; | |
| 989 return FilePath(); | |
| 990 } | |
| 991 FilePath path = origin_dir.Append(type_string); | |
| 992 if (!file_util::DirectoryExists(path) && | |
| 993 (!create || !file_util::CreateDirectory(path))) | |
| 994 return FilePath(); | |
| 995 return path; | |
| 996 } | |
| 997 | |
| 998 bool ObfuscatedFileSystemFileUtil::DeleteDirectoryForOriginAndType( | |
| 999 const GURL& origin, FileSystemType type) { | |
| 1000 FilePath origin_type_path = GetDirectoryForOriginAndType(origin, type, false); | |
| 1001 if (!file_util::PathExists(origin_type_path)) | |
| 1002 return true; | |
| 1003 | |
| 1004 // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase. | |
| 1005 // We ignore its error now since 1) it doesn't matter the final result, and | |
| 1006 // 2) it always returns false in Windows because of LevelDB's implementation. | |
| 1007 // Information about failure would be useful for debugging. | |
| 1008 DestroyDirectoryDatabase(origin, type); | |
| 1009 if (!file_util::Delete(origin_type_path, true /* recursive */)) | |
| 1010 return false; | |
| 1011 | |
| 1012 FilePath origin_path = origin_type_path.DirName(); | |
| 1013 DCHECK_EQ(origin_path.value(), GetDirectoryForOrigin(origin, false).value()); | |
| 1014 | |
| 1015 // Delete the origin directory if the deleted one was the last remaining | |
| 1016 // type for the origin. | |
| 1017 if (file_util::Delete(origin_path, false /* recursive */)) { | |
| 1018 InitOriginDatabase(false); | |
| 1019 if (origin_database_.get()) | |
| 1020 origin_database_->RemovePathForOrigin(GetOriginIdentifierFromURL(origin)); | |
| 1021 } | |
| 1022 | |
| 1023 // At this point we are sure we had successfully deleted the origin/type | |
| 1024 // directory, so just returning true here. | |
| 1025 return true; | |
| 1026 } | |
| 1027 | |
| 1028 bool ObfuscatedFileSystemFileUtil::MigrateFromOldSandbox( | |
| 1029 const GURL& origin_url, FileSystemType type, const FilePath& src_root) { | |
| 1030 if (!DestroyDirectoryDatabase(origin_url, type)) | |
| 1031 return false; | |
| 1032 FilePath dest_root = GetDirectoryForOriginAndType(origin_url, type, true); | |
| 1033 if (dest_root.empty()) | |
| 1034 return false; | |
| 1035 FileSystemDirectoryDatabase* db = GetDirectoryDatabase( | |
| 1036 origin_url, type, true); | |
| 1037 if (!db) | |
| 1038 return false; | |
| 1039 | |
| 1040 file_util::FileEnumerator file_enum(src_root, true, | |
| 1041 static_cast<file_util::FileEnumerator::FileType>( | |
| 1042 file_util::FileEnumerator::FILES | | |
| 1043 file_util::FileEnumerator::DIRECTORIES)); | |
| 1044 FilePath src_full_path; | |
| 1045 size_t root_path_length = src_root.value().length() + 1; // +1 for the slash | |
| 1046 while (!(src_full_path = file_enum.Next()).empty()) { | |
| 1047 file_util::FileEnumerator::FindInfo info; | |
| 1048 file_enum.GetFindInfo(&info); | |
| 1049 FilePath relative_virtual_path = | |
| 1050 FilePath(src_full_path.value().substr(root_path_length)); | |
| 1051 if (relative_virtual_path.empty()) { | |
| 1052 LOG(WARNING) << "Failed to convert path to relative: " << | |
| 1053 src_full_path.value(); | |
| 1054 return false; | |
| 1055 } | |
| 1056 FileId file_id; | |
| 1057 if (db->GetFileWithPath(relative_virtual_path, &file_id)) { | |
| 1058 NOTREACHED(); // File already exists. | |
| 1059 return false; | |
| 1060 } | |
| 1061 if (!db->GetFileWithPath(relative_virtual_path.DirName(), &file_id)) { | |
| 1062 NOTREACHED(); // Parent doesn't exist. | |
| 1063 return false; | |
| 1064 } | |
| 1065 | |
| 1066 FileInfo file_info; | |
| 1067 file_info.name = src_full_path.BaseName().value(); | |
| 1068 if (file_util::FileEnumerator::IsDirectory(info)) { | |
| 1069 #if defined(OS_WIN) | |
| 1070 file_info.modification_time = | |
| 1071 base::Time::FromFileTime(info.ftLastWriteTime); | |
| 1072 #elif defined(OS_POSIX) | |
| 1073 file_info.modification_time = base::Time::FromTimeT(info.stat.st_mtime); | |
| 1074 #endif | |
| 1075 } else { | |
| 1076 file_info.data_path = | |
| 1077 FilePath(kLegacyDataDirectory).Append(relative_virtual_path); | |
| 1078 } | |
| 1079 file_info.parent_id = file_id; | |
| 1080 if (!db->AddFileInfo(file_info, &file_id)) { | |
| 1081 NOTREACHED(); | |
| 1082 return false; | |
| 1083 } | |
| 1084 } | |
| 1085 // TODO(ericu): Should we adjust the mtime of the root directory to match as | |
| 1086 // well? | |
| 1087 FilePath legacy_dest_dir = dest_root.Append(kLegacyDataDirectory); | |
| 1088 | |
| 1089 if (!file_util::Move(src_root, legacy_dest_dir)) { | |
| 1090 LOG(WARNING) << | |
| 1091 "The final step of a migration failed; I'll try to clean up."; | |
| 1092 db = NULL; | |
| 1093 DestroyDirectoryDatabase(origin_url, type); | |
| 1094 return false; | |
| 1095 } | |
| 1096 return true; | |
| 1097 } | |
| 1098 | |
| 1099 // static | |
| 1100 FilePath::StringType ObfuscatedFileSystemFileUtil::GetDirectoryNameForType( | |
| 1101 FileSystemType type) { | |
| 1102 switch (type) { | |
| 1103 case kFileSystemTypeTemporary: | |
| 1104 return kTemporaryDirectoryName; | |
| 1105 case kFileSystemTypePersistent: | |
| 1106 return kPersistentDirectoryName; | |
| 1107 case kFileSystemTypeUnknown: | |
| 1108 default: | |
| 1109 return FilePath::StringType(); | |
| 1110 } | |
| 1111 } | |
| 1112 | |
| 1113 FilePath ObfuscatedFileSystemFileUtil::DataPathToLocalPath( | |
| 1114 const GURL& origin, FileSystemType type, const FilePath& data_path) { | |
| 1115 FilePath root = GetDirectoryForOriginAndType(origin, type, false); | |
| 1116 if (root.empty()) | |
| 1117 return root; | |
| 1118 return root.Append(data_path); | |
| 1119 } | |
| 1120 | |
| 1121 FilePath ObfuscatedFileSystemFileUtil::LocalPathToDataPath( | |
| 1122 const GURL& origin, FileSystemType type, const FilePath& local_path) { | |
| 1123 FilePath root = GetDirectoryForOriginAndType(origin, type, false); | |
| 1124 if (root.empty()) | |
| 1125 return root; | |
| 1126 // This removes the root, including the trailing slash, leaving a relative | |
| 1127 // path. | |
| 1128 return FilePath(local_path.value().substr(root.value().length() + 1)); | |
| 1129 } | |
| 1130 | |
| 1131 // TODO: How to do the whole validation-without-creation thing? We may not have | |
| 1132 // quota even to create the database. Ah, in that case don't even get here? | |
| 1133 // Still doesn't answer the quota issue, though. | |
| 1134 FileSystemDirectoryDatabase* ObfuscatedFileSystemFileUtil::GetDirectoryDatabase( | |
| 1135 const GURL& origin, FileSystemType type, bool create) { | |
| 1136 std::string type_string = | |
| 1137 FileSystemPathManager::GetFileSystemTypeString(type); | |
| 1138 if (type_string.empty()) { | |
| 1139 LOG(WARNING) << "Unknown filesystem type requested:" << type; | |
| 1140 return NULL; | |
| 1141 } | |
| 1142 std::string key = GetOriginIdentifierFromURL(origin) + type_string; | |
| 1143 DirectoryMap::iterator iter = directories_.find(key); | |
| 1144 if (iter != directories_.end()) { | |
| 1145 MarkUsed(); | |
| 1146 return iter->second; | |
| 1147 } | |
| 1148 | |
| 1149 FilePath path = GetDirectoryForOriginAndType(origin, type, create); | |
| 1150 if (path.empty()) | |
| 1151 return NULL; | |
| 1152 if (!file_util::DirectoryExists(path)) { | |
| 1153 if (!file_util::CreateDirectory(path)) { | |
| 1154 LOG(WARNING) << "Failed to origin+type directory: " << path.value(); | |
| 1155 return NULL; | |
| 1156 } | |
| 1157 } | |
| 1158 MarkUsed(); | |
| 1159 path = path.AppendASCII(kDirectoryDatabaseName); | |
| 1160 FileSystemDirectoryDatabase* database = new FileSystemDirectoryDatabase(path); | |
| 1161 directories_[key] = database; | |
| 1162 return database; | |
| 1163 } | |
| 1164 | |
| 1165 FilePath ObfuscatedFileSystemFileUtil::GetDirectoryForOrigin( | |
| 1166 const GURL& origin, bool create) { | |
| 1167 if (!InitOriginDatabase(create)) | |
| 1168 return FilePath(); | |
| 1169 FilePath directory_name; | |
| 1170 std::string id = GetOriginIdentifierFromURL(origin); | |
| 1171 | |
| 1172 bool exists_in_db = origin_database_->HasOriginPath(id); | |
| 1173 if (!exists_in_db && !create) | |
| 1174 return FilePath(); | |
| 1175 if (!origin_database_->GetPathForOrigin(id, &directory_name)) | |
| 1176 return FilePath(); | |
| 1177 | |
| 1178 FilePath path = file_system_directory_.Append(directory_name); | |
| 1179 bool exists_in_fs = file_util::DirectoryExists(path); | |
| 1180 if (!exists_in_db && exists_in_fs) { | |
| 1181 if (!file_util::Delete(path, true)) | |
| 1182 return FilePath(); | |
| 1183 exists_in_fs = false; | |
| 1184 } | |
| 1185 | |
| 1186 if (!exists_in_fs) { | |
| 1187 if (!create || !file_util::CreateDirectory(path)) | |
| 1188 return FilePath(); | |
| 1189 } | |
| 1190 | |
| 1191 return path; | |
| 1192 } | |
| 1193 | |
| 1194 void ObfuscatedFileSystemFileUtil::MarkUsed() { | |
| 1195 if (timer_.IsRunning()) | |
| 1196 timer_.Reset(); | |
| 1197 else | |
| 1198 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kFlushDelaySeconds), | |
| 1199 this, &ObfuscatedFileSystemFileUtil::DropDatabases); | |
| 1200 } | |
| 1201 | |
| 1202 void ObfuscatedFileSystemFileUtil::DropDatabases() { | |
| 1203 origin_database_.reset(); | |
| 1204 STLDeleteContainerPairSecondPointers( | |
| 1205 directories_.begin(), directories_.end()); | |
| 1206 directories_.clear(); | |
| 1207 } | |
| 1208 | |
| 1209 // static | |
| 1210 int64 ObfuscatedFileSystemFileUtil::ComputeFilePathCost(const FilePath& path) { | |
| 1211 return GetPathQuotaUsage(1, path.BaseName().value().size()); | |
| 1212 } | |
| 1213 | |
| 1214 bool ObfuscatedFileSystemFileUtil::DestroyDirectoryDatabase( | |
| 1215 const GURL& origin, FileSystemType type) { | |
| 1216 std::string type_string = | |
| 1217 FileSystemPathManager::GetFileSystemTypeString(type); | |
| 1218 if (type_string.empty()) { | |
| 1219 LOG(WARNING) << "Unknown filesystem type requested:" << type; | |
| 1220 return true; | |
| 1221 } | |
| 1222 std::string key = GetOriginIdentifierFromURL(origin) + type_string; | |
| 1223 DirectoryMap::iterator iter = directories_.find(key); | |
| 1224 if (iter != directories_.end()) { | |
| 1225 FileSystemDirectoryDatabase* database = iter->second; | |
| 1226 directories_.erase(iter); | |
| 1227 delete database; | |
| 1228 } | |
| 1229 | |
| 1230 FilePath path = GetDirectoryForOriginAndType(origin, type, false); | |
| 1231 if (path.empty()) | |
| 1232 return true; | |
| 1233 if (!file_util::DirectoryExists(path)) | |
| 1234 return true; | |
| 1235 path = path.AppendASCII(kDirectoryDatabaseName); | |
| 1236 return FileSystemDirectoryDatabase::DestroyDatabase(path); | |
| 1237 } | |
| 1238 | |
| 1239 bool ObfuscatedFileSystemFileUtil::InitOriginDatabase(bool create) { | |
| 1240 if (!origin_database_.get()) { | |
| 1241 if (!create && !file_util::DirectoryExists(file_system_directory_)) | |
| 1242 return false; | |
| 1243 if (!file_util::CreateDirectory(file_system_directory_)) { | |
| 1244 LOG(WARNING) << "Failed to create FileSystem directory: " << | |
| 1245 file_system_directory_.value(); | |
| 1246 return false; | |
| 1247 } | |
| 1248 origin_database_.reset( | |
| 1249 new FileSystemOriginDatabase( | |
| 1250 file_system_directory_.AppendASCII(kOriginDatabaseName))); | |
| 1251 } | |
| 1252 return true; | |
| 1253 } | |
| 1254 | |
| 1255 } // namespace fileapi | |
| OLD | NEW |