| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/chromeos/drive/file_cache.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/bind_helpers.h" | |
| 11 #include "base/callback_helpers.h" | |
| 12 #include "base/files/file_enumerator.h" | |
| 13 #include "base/files/file_util.h" | |
| 14 #include "base/location.h" | |
| 15 #include "base/logging.h" | |
| 16 #include "base/metrics/histogram.h" | |
| 17 #include "base/strings/string_util.h" | |
| 18 #include "base/strings/stringprintf.h" | |
| 19 #include "base/sys_info.h" | |
| 20 #include "chrome/browser/chromeos/drive/file_system_core_util.h" | |
| 21 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h" | |
| 22 #include "chromeos/chromeos_constants.h" | |
| 23 #include "components/drive/drive.pb.h" | |
| 24 #include "components/drive/drive_api_util.h" | |
| 25 #include "google_apis/drive/task_util.h" | |
| 26 #include "net/base/filename_util.h" | |
| 27 #include "net/base/mime_sniffer.h" | |
| 28 #include "net/base/mime_util.h" | |
| 29 #include "third_party/cros_system_api/constants/cryptohome.h" | |
| 30 | |
| 31 namespace drive { | |
| 32 namespace internal { | |
| 33 namespace { | |
| 34 | |
| 35 // Returns ID extracted from the path. | |
| 36 std::string GetIdFromPath(const base::FilePath& path) { | |
| 37 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe()); | |
| 38 } | |
| 39 | |
| 40 } // namespace | |
| 41 | |
| 42 FileCache::FileCache(ResourceMetadataStorage* storage, | |
| 43 const base::FilePath& cache_file_directory, | |
| 44 base::SequencedTaskRunner* blocking_task_runner, | |
| 45 FreeDiskSpaceGetterInterface* free_disk_space_getter) | |
| 46 : cache_file_directory_(cache_file_directory), | |
| 47 blocking_task_runner_(blocking_task_runner), | |
| 48 storage_(storage), | |
| 49 free_disk_space_getter_(free_disk_space_getter), | |
| 50 weak_ptr_factory_(this) { | |
| 51 DCHECK(blocking_task_runner_.get()); | |
| 52 } | |
| 53 | |
| 54 FileCache::~FileCache() { | |
| 55 // Must be on the sequenced worker pool, as |metadata_| must be deleted on | |
| 56 // the sequenced worker pool. | |
| 57 AssertOnSequencedWorkerPool(); | |
| 58 } | |
| 59 | |
| 60 base::FilePath FileCache::GetCacheFilePath(const std::string& id) const { | |
| 61 return cache_file_directory_.Append( | |
| 62 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id))); | |
| 63 } | |
| 64 | |
| 65 void FileCache::AssertOnSequencedWorkerPool() { | |
| 66 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 67 } | |
| 68 | |
| 69 bool FileCache::IsUnderFileCacheDirectory(const base::FilePath& path) const { | |
| 70 return cache_file_directory_.IsParent(path); | |
| 71 } | |
| 72 | |
| 73 bool FileCache::FreeDiskSpaceIfNeededFor(int64 num_bytes) { | |
| 74 AssertOnSequencedWorkerPool(); | |
| 75 | |
| 76 // Do nothing and return if we have enough space. | |
| 77 if (HasEnoughSpaceFor(num_bytes, cache_file_directory_)) | |
| 78 return true; | |
| 79 | |
| 80 // Otherwise, try to free up the disk space. | |
| 81 DVLOG(1) << "Freeing up disk space for " << num_bytes; | |
| 82 | |
| 83 // Remove all entries unless specially marked. | |
| 84 scoped_ptr<ResourceMetadataStorage::Iterator> it = storage_->GetIterator(); | |
| 85 for (; !it->IsAtEnd(); it->Advance()) { | |
| 86 if (it->GetValue().file_specific_info().has_cache_state() && | |
| 87 !it->GetValue().file_specific_info().cache_state().is_pinned() && | |
| 88 !it->GetValue().file_specific_info().cache_state().is_dirty() && | |
| 89 !mounted_files_.count(it->GetID())) { | |
| 90 ResourceEntry entry(it->GetValue()); | |
| 91 entry.mutable_file_specific_info()->clear_cache_state(); | |
| 92 storage_->PutEntry(entry); | |
| 93 } | |
| 94 } | |
| 95 if (it->HasError()) | |
| 96 return false; | |
| 97 | |
| 98 // Remove all files which have no corresponding cache entries. | |
| 99 base::FileEnumerator enumerator(cache_file_directory_, | |
| 100 false, // not recursive | |
| 101 base::FileEnumerator::FILES); | |
| 102 ResourceEntry entry; | |
| 103 for (base::FilePath current = enumerator.Next(); !current.empty(); | |
| 104 current = enumerator.Next()) { | |
| 105 std::string id = GetIdFromPath(current); | |
| 106 FileError error = storage_->GetEntry(id, &entry); | |
| 107 if (error == FILE_ERROR_NOT_FOUND || | |
| 108 (error == FILE_ERROR_OK && | |
| 109 !entry.file_specific_info().cache_state().is_present())) | |
| 110 base::DeleteFile(current, false /* recursive */); | |
| 111 else if (error != FILE_ERROR_OK) | |
| 112 return false; | |
| 113 } | |
| 114 | |
| 115 // Check the disk space again. | |
| 116 return HasEnoughSpaceFor(num_bytes, cache_file_directory_); | |
| 117 } | |
| 118 | |
| 119 FileError FileCache::GetFile(const std::string& id, | |
| 120 base::FilePath* cache_file_path) { | |
| 121 AssertOnSequencedWorkerPool(); | |
| 122 DCHECK(cache_file_path); | |
| 123 | |
| 124 ResourceEntry entry; | |
| 125 FileError error = storage_->GetEntry(id, &entry); | |
| 126 if (error != FILE_ERROR_OK) | |
| 127 return error; | |
| 128 if (!entry.file_specific_info().cache_state().is_present()) | |
| 129 return FILE_ERROR_NOT_FOUND; | |
| 130 | |
| 131 *cache_file_path = GetCacheFilePath(id); | |
| 132 return FILE_ERROR_OK; | |
| 133 } | |
| 134 | |
| 135 FileError FileCache::Store(const std::string& id, | |
| 136 const std::string& md5, | |
| 137 const base::FilePath& source_path, | |
| 138 FileOperationType file_operation_type) { | |
| 139 AssertOnSequencedWorkerPool(); | |
| 140 | |
| 141 ResourceEntry entry; | |
| 142 FileError error = storage_->GetEntry(id, &entry); | |
| 143 if (error != FILE_ERROR_OK) | |
| 144 return error; | |
| 145 | |
| 146 int64 file_size = 0; | |
| 147 if (file_operation_type == FILE_OPERATION_COPY) { | |
| 148 if (!base::GetFileSize(source_path, &file_size)) { | |
| 149 LOG(WARNING) << "Couldn't get file size for: " << source_path.value(); | |
| 150 return FILE_ERROR_FAILED; | |
| 151 } | |
| 152 } | |
| 153 if (!FreeDiskSpaceIfNeededFor(file_size)) | |
| 154 return FILE_ERROR_NO_LOCAL_SPACE; | |
| 155 | |
| 156 // If file is mounted, return error. | |
| 157 if (mounted_files_.count(id)) | |
| 158 return FILE_ERROR_IN_USE; | |
| 159 | |
| 160 base::FilePath dest_path = GetCacheFilePath(id); | |
| 161 bool success = false; | |
| 162 switch (file_operation_type) { | |
| 163 case FILE_OPERATION_MOVE: | |
| 164 success = base::Move(source_path, dest_path); | |
| 165 break; | |
| 166 case FILE_OPERATION_COPY: | |
| 167 success = base::CopyFile(source_path, dest_path); | |
| 168 break; | |
| 169 default: | |
| 170 NOTREACHED(); | |
| 171 } | |
| 172 | |
| 173 if (!success) { | |
| 174 LOG(ERROR) << "Failed to store: " | |
| 175 << "source_path = " << source_path.value() << ", " | |
| 176 << "dest_path = " << dest_path.value() << ", " | |
| 177 << "file_operation_type = " << file_operation_type; | |
| 178 return FILE_ERROR_FAILED; | |
| 179 } | |
| 180 | |
| 181 // Now that file operations have completed, update metadata. | |
| 182 FileCacheEntry* cache_state = | |
| 183 entry.mutable_file_specific_info()->mutable_cache_state(); | |
| 184 cache_state->set_md5(md5); | |
| 185 cache_state->set_is_present(true); | |
| 186 if (md5.empty()) | |
| 187 cache_state->set_is_dirty(true); | |
| 188 return storage_->PutEntry(entry); | |
| 189 } | |
| 190 | |
| 191 FileError FileCache::Pin(const std::string& id) { | |
| 192 AssertOnSequencedWorkerPool(); | |
| 193 | |
| 194 ResourceEntry entry; | |
| 195 FileError error = storage_->GetEntry(id, &entry); | |
| 196 if (error != FILE_ERROR_OK) | |
| 197 return error; | |
| 198 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( | |
| 199 true); | |
| 200 return storage_->PutEntry(entry); | |
| 201 } | |
| 202 | |
| 203 FileError FileCache::Unpin(const std::string& id) { | |
| 204 AssertOnSequencedWorkerPool(); | |
| 205 | |
| 206 // Unpinning a file means its entry must exist in cache. | |
| 207 ResourceEntry entry; | |
| 208 FileError error = storage_->GetEntry(id, &entry); | |
| 209 if (error != FILE_ERROR_OK) | |
| 210 return error; | |
| 211 | |
| 212 // Now that file operations have completed, update metadata. | |
| 213 if (entry.file_specific_info().cache_state().is_present()) { | |
| 214 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( | |
| 215 false); | |
| 216 } else { | |
| 217 // Remove the existing entry if we are unpinning a non-present file. | |
| 218 entry.mutable_file_specific_info()->clear_cache_state(); | |
| 219 } | |
| 220 error = storage_->PutEntry(entry); | |
| 221 if (error != FILE_ERROR_OK) | |
| 222 return error; | |
| 223 | |
| 224 // Now it's a chance to free up space if needed. | |
| 225 FreeDiskSpaceIfNeededFor(0); | |
| 226 | |
| 227 return FILE_ERROR_OK; | |
| 228 } | |
| 229 | |
| 230 FileError FileCache::MarkAsMounted(const std::string& id, | |
| 231 base::FilePath* cache_file_path) { | |
| 232 AssertOnSequencedWorkerPool(); | |
| 233 DCHECK(cache_file_path); | |
| 234 | |
| 235 // Get cache entry associated with the id and md5 | |
| 236 ResourceEntry entry; | |
| 237 FileError error = storage_->GetEntry(id, &entry); | |
| 238 if (error != FILE_ERROR_OK) | |
| 239 return error; | |
| 240 if (!entry.file_specific_info().cache_state().is_present()) | |
| 241 return FILE_ERROR_NOT_FOUND; | |
| 242 | |
| 243 if (mounted_files_.count(id)) | |
| 244 return FILE_ERROR_INVALID_OPERATION; | |
| 245 | |
| 246 // Ensure the file is readable to cros_disks. See crbug.com/236994. | |
| 247 base::FilePath path = GetCacheFilePath(id); | |
| 248 if (!base::SetPosixFilePermissions( | |
| 249 path, | |
| 250 base::FILE_PERMISSION_READ_BY_USER | | |
| 251 base::FILE_PERMISSION_WRITE_BY_USER | | |
| 252 base::FILE_PERMISSION_READ_BY_GROUP | | |
| 253 base::FILE_PERMISSION_READ_BY_OTHERS)) | |
| 254 return FILE_ERROR_FAILED; | |
| 255 | |
| 256 mounted_files_.insert(id); | |
| 257 | |
| 258 *cache_file_path = path; | |
| 259 return FILE_ERROR_OK; | |
| 260 } | |
| 261 | |
| 262 FileError FileCache::OpenForWrite( | |
| 263 const std::string& id, | |
| 264 scoped_ptr<base::ScopedClosureRunner>* file_closer) { | |
| 265 AssertOnSequencedWorkerPool(); | |
| 266 | |
| 267 // Marking a file dirty means its entry and actual file blob must exist in | |
| 268 // cache. | |
| 269 ResourceEntry entry; | |
| 270 FileError error = storage_->GetEntry(id, &entry); | |
| 271 if (error != FILE_ERROR_OK) | |
| 272 return error; | |
| 273 if (!entry.file_specific_info().cache_state().is_present()) { | |
| 274 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id; | |
| 275 return FILE_ERROR_NOT_FOUND; | |
| 276 } | |
| 277 | |
| 278 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true); | |
| 279 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5(); | |
| 280 error = storage_->PutEntry(entry); | |
| 281 if (error != FILE_ERROR_OK) | |
| 282 return error; | |
| 283 | |
| 284 write_opened_files_[id]++; | |
| 285 file_closer->reset(new base::ScopedClosureRunner( | |
| 286 base::Bind(&google_apis::RunTaskWithTaskRunner, | |
| 287 blocking_task_runner_, | |
| 288 base::Bind(&FileCache::CloseForWrite, | |
| 289 weak_ptr_factory_.GetWeakPtr(), | |
| 290 id)))); | |
| 291 return FILE_ERROR_OK; | |
| 292 } | |
| 293 | |
| 294 bool FileCache::IsOpenedForWrite(const std::string& id) { | |
| 295 AssertOnSequencedWorkerPool(); | |
| 296 return write_opened_files_.count(id) != 0; | |
| 297 } | |
| 298 | |
| 299 FileError FileCache::UpdateMd5(const std::string& id) { | |
| 300 AssertOnSequencedWorkerPool(); | |
| 301 | |
| 302 if (IsOpenedForWrite(id)) | |
| 303 return FILE_ERROR_IN_USE; | |
| 304 | |
| 305 ResourceEntry entry; | |
| 306 FileError error = storage_->GetEntry(id, &entry); | |
| 307 if (error != FILE_ERROR_OK) | |
| 308 return error; | |
| 309 if (!entry.file_specific_info().cache_state().is_present()) | |
| 310 return FILE_ERROR_NOT_FOUND; | |
| 311 | |
| 312 const std::string& md5 = | |
| 313 util::GetMd5Digest(GetCacheFilePath(id), &in_shutdown_); | |
| 314 if (in_shutdown_.IsSet()) | |
| 315 return FILE_ERROR_ABORT; | |
| 316 if (md5.empty()) | |
| 317 return FILE_ERROR_NOT_FOUND; | |
| 318 | |
| 319 entry.mutable_file_specific_info()->mutable_cache_state()->set_md5(md5); | |
| 320 return storage_->PutEntry(entry); | |
| 321 } | |
| 322 | |
| 323 FileError FileCache::ClearDirty(const std::string& id) { | |
| 324 AssertOnSequencedWorkerPool(); | |
| 325 | |
| 326 if (IsOpenedForWrite(id)) | |
| 327 return FILE_ERROR_IN_USE; | |
| 328 | |
| 329 // Clearing a dirty file means its entry and actual file blob must exist in | |
| 330 // cache. | |
| 331 ResourceEntry entry; | |
| 332 FileError error = storage_->GetEntry(id, &entry); | |
| 333 if (error != FILE_ERROR_OK) | |
| 334 return error; | |
| 335 if (!entry.file_specific_info().cache_state().is_present()) { | |
| 336 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: " | |
| 337 << id; | |
| 338 return FILE_ERROR_NOT_FOUND; | |
| 339 } | |
| 340 | |
| 341 // If a file is not dirty (it should have been marked dirty via OpenForWrite), | |
| 342 // clearing its dirty state is an invalid operation. | |
| 343 if (!entry.file_specific_info().cache_state().is_dirty()) { | |
| 344 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id; | |
| 345 return FILE_ERROR_INVALID_OPERATION; | |
| 346 } | |
| 347 | |
| 348 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty( | |
| 349 false); | |
| 350 return storage_->PutEntry(entry); | |
| 351 } | |
| 352 | |
| 353 FileError FileCache::Remove(const std::string& id) { | |
| 354 AssertOnSequencedWorkerPool(); | |
| 355 | |
| 356 ResourceEntry entry; | |
| 357 | |
| 358 // If entry doesn't exist, nothing to do. | |
| 359 FileError error = storage_->GetEntry(id, &entry); | |
| 360 if (error == FILE_ERROR_NOT_FOUND) | |
| 361 return FILE_ERROR_OK; | |
| 362 if (error != FILE_ERROR_OK) | |
| 363 return error; | |
| 364 if (!entry.file_specific_info().has_cache_state()) | |
| 365 return FILE_ERROR_OK; | |
| 366 | |
| 367 // Cannot delete a mounted file. | |
| 368 if (mounted_files_.count(id)) | |
| 369 return FILE_ERROR_IN_USE; | |
| 370 | |
| 371 // Delete the file. | |
| 372 base::FilePath path = GetCacheFilePath(id); | |
| 373 if (!base::DeleteFile(path, false /* recursive */)) | |
| 374 return FILE_ERROR_FAILED; | |
| 375 | |
| 376 // Now that all file operations have completed, remove from metadata. | |
| 377 entry.mutable_file_specific_info()->clear_cache_state(); | |
| 378 return storage_->PutEntry(entry); | |
| 379 } | |
| 380 | |
| 381 bool FileCache::ClearAll() { | |
| 382 AssertOnSequencedWorkerPool(); | |
| 383 | |
| 384 // Remove files. | |
| 385 base::FileEnumerator enumerator(cache_file_directory_, | |
| 386 false, // not recursive | |
| 387 base::FileEnumerator::FILES); | |
| 388 for (base::FilePath file = enumerator.Next(); !file.empty(); | |
| 389 file = enumerator.Next()) | |
| 390 base::DeleteFile(file, false /* recursive */); | |
| 391 | |
| 392 return true; | |
| 393 } | |
| 394 | |
| 395 bool FileCache::Initialize() { | |
| 396 AssertOnSequencedWorkerPool(); | |
| 397 | |
| 398 // Older versions do not clear MD5 when marking entries dirty. | |
| 399 // Clear MD5 of all dirty entries to deal with old data. | |
| 400 scoped_ptr<ResourceMetadataStorage::Iterator> it = storage_->GetIterator(); | |
| 401 for (; !it->IsAtEnd(); it->Advance()) { | |
| 402 if (it->GetValue().file_specific_info().cache_state().is_dirty()) { | |
| 403 ResourceEntry new_entry(it->GetValue()); | |
| 404 new_entry.mutable_file_specific_info()->mutable_cache_state()-> | |
| 405 clear_md5(); | |
| 406 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK) | |
| 407 return false; | |
| 408 } | |
| 409 } | |
| 410 if (it->HasError()) | |
| 411 return false; | |
| 412 | |
| 413 if (!RenameCacheFilesToNewFormat()) | |
| 414 return false; | |
| 415 return true; | |
| 416 } | |
| 417 | |
| 418 void FileCache::Destroy() { | |
| 419 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 420 | |
| 421 in_shutdown_.Set(); | |
| 422 | |
| 423 // Destroy myself on the blocking pool. | |
| 424 // Note that base::DeletePointer<> cannot be used as the destructor of this | |
| 425 // class is private. | |
| 426 blocking_task_runner_->PostTask( | |
| 427 FROM_HERE, | |
| 428 base::Bind(&FileCache::DestroyOnBlockingPool, base::Unretained(this))); | |
| 429 } | |
| 430 | |
| 431 void FileCache::DestroyOnBlockingPool() { | |
| 432 AssertOnSequencedWorkerPool(); | |
| 433 delete this; | |
| 434 } | |
| 435 | |
| 436 bool FileCache::RecoverFilesFromCacheDirectory( | |
| 437 const base::FilePath& dest_directory, | |
| 438 const ResourceMetadataStorage::RecoveredCacheInfoMap& | |
| 439 recovered_cache_info) { | |
| 440 int file_number = 1; | |
| 441 | |
| 442 base::FileEnumerator enumerator(cache_file_directory_, | |
| 443 false, // not recursive | |
| 444 base::FileEnumerator::FILES); | |
| 445 for (base::FilePath current = enumerator.Next(); !current.empty(); | |
| 446 current = enumerator.Next()) { | |
| 447 const std::string& id = GetIdFromPath(current); | |
| 448 ResourceEntry entry; | |
| 449 FileError error = storage_->GetEntry(id, &entry); | |
| 450 if (error != FILE_ERROR_OK && error != FILE_ERROR_NOT_FOUND) | |
| 451 return false; | |
| 452 if (error == FILE_ERROR_OK && | |
| 453 entry.file_specific_info().cache_state().is_present()) { | |
| 454 // This file is managed by FileCache, no need to recover it. | |
| 455 continue; | |
| 456 } | |
| 457 | |
| 458 // If a cache entry which is non-dirty and has matching MD5 is found in | |
| 459 // |recovered_cache_entries|, it means the current file is already uploaded | |
| 460 // to the server. Just delete it instead of recovering it. | |
| 461 ResourceMetadataStorage::RecoveredCacheInfoMap::const_iterator it = | |
| 462 recovered_cache_info.find(id); | |
| 463 if (it != recovered_cache_info.end()) { | |
| 464 // Due to the DB corruption, cache info might be recovered from old | |
| 465 // revision. Perform MD5 check even when is_dirty is false just in case. | |
| 466 if (!it->second.is_dirty && | |
| 467 it->second.md5 == util::GetMd5Digest(current, &in_shutdown_)) { | |
| 468 base::DeleteFile(current, false /* recursive */); | |
| 469 continue; | |
| 470 } | |
| 471 } | |
| 472 | |
| 473 // Read file contents to sniff mime type. | |
| 474 std::vector<char> content(net::kMaxBytesToSniff); | |
| 475 const int read_result = | |
| 476 base::ReadFile(current, &content[0], content.size()); | |
| 477 if (read_result < 0) { | |
| 478 LOG(WARNING) << "Cannot read: " << current.value(); | |
| 479 return false; | |
| 480 } | |
| 481 if (read_result == 0) // Skip empty files. | |
| 482 continue; | |
| 483 | |
| 484 // Use recovered file name if available, otherwise decide file name with | |
| 485 // sniffed mime type. | |
| 486 base::FilePath dest_base_name(FILE_PATH_LITERAL("file")); | |
| 487 std::string mime_type; | |
| 488 if (it != recovered_cache_info.end() && !it->second.title.empty()) { | |
| 489 // We can use a file name recovered from the trashed DB. | |
| 490 dest_base_name = base::FilePath::FromUTF8Unsafe(it->second.title); | |
| 491 } else if (net::SniffMimeType(&content[0], read_result, | |
| 492 net::FilePathToFileURL(current), | |
| 493 std::string(), &mime_type) || | |
| 494 net::SniffMimeTypeFromLocalData(&content[0], read_result, | |
| 495 &mime_type)) { | |
| 496 // Change base name for common mime types. | |
| 497 if (net::MatchesMimeType("image/*", mime_type)) { | |
| 498 dest_base_name = base::FilePath(FILE_PATH_LITERAL("image")); | |
| 499 } else if (net::MatchesMimeType("video/*", mime_type)) { | |
| 500 dest_base_name = base::FilePath(FILE_PATH_LITERAL("video")); | |
| 501 } else if (net::MatchesMimeType("audio/*", mime_type)) { | |
| 502 dest_base_name = base::FilePath(FILE_PATH_LITERAL("audio")); | |
| 503 } | |
| 504 | |
| 505 // Estimate extension from mime type. | |
| 506 std::vector<base::FilePath::StringType> extensions; | |
| 507 base::FilePath::StringType extension; | |
| 508 if (net::GetPreferredExtensionForMimeType(mime_type, &extension)) | |
| 509 extensions.push_back(extension); | |
| 510 else | |
| 511 net::GetExtensionsForMimeType(mime_type, &extensions); | |
| 512 | |
| 513 // Add extension if possible. | |
| 514 if (!extensions.empty()) | |
| 515 dest_base_name = dest_base_name.AddExtension(extensions[0]); | |
| 516 } | |
| 517 | |
| 518 // Add file number to the file name and move. | |
| 519 const base::FilePath& dest_path = dest_directory.Append(dest_base_name) | |
| 520 .InsertBeforeExtensionASCII(base::StringPrintf("%08d", file_number++)); | |
| 521 if (!base::CreateDirectory(dest_directory) || | |
| 522 !base::Move(current, dest_path)) { | |
| 523 LOG(WARNING) << "Failed to move: " << current.value() | |
| 524 << " to " << dest_path.value(); | |
| 525 return false; | |
| 526 } | |
| 527 } | |
| 528 UMA_HISTOGRAM_COUNTS("Drive.NumberOfCacheFilesRecoveredAfterDBCorruption", | |
| 529 file_number - 1); | |
| 530 return true; | |
| 531 } | |
| 532 | |
| 533 FileError FileCache::MarkAsUnmounted(const base::FilePath& file_path) { | |
| 534 AssertOnSequencedWorkerPool(); | |
| 535 DCHECK(IsUnderFileCacheDirectory(file_path)); | |
| 536 | |
| 537 std::string id = GetIdFromPath(file_path); | |
| 538 | |
| 539 // Get the entry associated with the id. | |
| 540 ResourceEntry entry; | |
| 541 FileError error = storage_->GetEntry(id, &entry); | |
| 542 if (error != FILE_ERROR_OK) | |
| 543 return error; | |
| 544 | |
| 545 std::set<std::string>::iterator it = mounted_files_.find(id); | |
| 546 if (it == mounted_files_.end()) | |
| 547 return FILE_ERROR_INVALID_OPERATION; | |
| 548 | |
| 549 mounted_files_.erase(it); | |
| 550 return FILE_ERROR_OK; | |
| 551 } | |
| 552 | |
| 553 bool FileCache::HasEnoughSpaceFor(int64 num_bytes, | |
| 554 const base::FilePath& path) { | |
| 555 int64 free_space = 0; | |
| 556 if (free_disk_space_getter_) | |
| 557 free_space = free_disk_space_getter_->AmountOfFreeDiskSpace(); | |
| 558 else | |
| 559 free_space = base::SysInfo::AmountOfFreeDiskSpace(path); | |
| 560 | |
| 561 // Subtract this as if this portion does not exist. | |
| 562 free_space -= cryptohome::kMinFreeSpaceInBytes; | |
| 563 return (free_space >= num_bytes); | |
| 564 } | |
| 565 | |
| 566 bool FileCache::RenameCacheFilesToNewFormat() { | |
| 567 base::FileEnumerator enumerator(cache_file_directory_, | |
| 568 false, // not recursive | |
| 569 base::FileEnumerator::FILES); | |
| 570 for (base::FilePath current = enumerator.Next(); !current.empty(); | |
| 571 current = enumerator.Next()) { | |
| 572 base::FilePath new_path = current.RemoveExtension(); | |
| 573 if (!new_path.Extension().empty()) { | |
| 574 // Delete files with multiple extensions. | |
| 575 if (!base::DeleteFile(current, false /* recursive */)) | |
| 576 return false; | |
| 577 continue; | |
| 578 } | |
| 579 const std::string& id = GetIdFromPath(new_path); | |
| 580 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id)); | |
| 581 if (new_path != current && !base::Move(current, new_path)) | |
| 582 return false; | |
| 583 } | |
| 584 return true; | |
| 585 } | |
| 586 | |
| 587 void FileCache::CloseForWrite(const std::string& id) { | |
| 588 AssertOnSequencedWorkerPool(); | |
| 589 | |
| 590 std::map<std::string, int>::iterator it = write_opened_files_.find(id); | |
| 591 if (it == write_opened_files_.end()) | |
| 592 return; | |
| 593 | |
| 594 DCHECK_LT(0, it->second); | |
| 595 --it->second; | |
| 596 if (it->second == 0) | |
| 597 write_opened_files_.erase(it); | |
| 598 | |
| 599 // Update last modified date. | |
| 600 ResourceEntry entry; | |
| 601 FileError error = storage_->GetEntry(id, &entry); | |
| 602 if (error != FILE_ERROR_OK) { | |
| 603 LOG(ERROR) << "Failed to get entry: " << id << ", " | |
| 604 << FileErrorToString(error); | |
| 605 return; | |
| 606 } | |
| 607 entry.mutable_file_info()->set_last_modified( | |
| 608 base::Time::Now().ToInternalValue()); | |
| 609 error = storage_->PutEntry(entry); | |
| 610 if (error != FILE_ERROR_OK) { | |
| 611 LOG(ERROR) << "Failed to put entry: " << id << ", " | |
| 612 << FileErrorToString(error); | |
| 613 } | |
| 614 } | |
| 615 | |
| 616 } // namespace internal | |
| 617 } // namespace drive | |
| OLD | NEW |