| 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/gdata/gdata_cache_metadata.h" | |
| 6 | |
| 7 #include <leveldb/db.h> | |
| 8 | |
| 9 #include "base/file_util.h" | |
| 10 #include "base/sequenced_task_runner.h" | |
| 11 #include "chrome/browser/chromeos/gdata/drive.pb.h" | |
| 12 #include "chrome/browser/chromeos/gdata/gdata_util.h" | |
| 13 | |
| 14 namespace gdata { | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 // A map table of resource ID to file path. | |
| 19 typedef std::map<std::string, FilePath> ResourceIdToFilePathMap; | |
| 20 | |
| 21 const FilePath::CharType kGDataCacheMetadataDBPath[] = | |
| 22 FILE_PATH_LITERAL("cache_metadata.db"); | |
| 23 | |
| 24 // Returns true if |file_path| is a valid symbolic link as |sub_dir_type|. | |
| 25 // Otherwise, returns false with the reason. | |
| 26 bool IsValidSymbolicLink(const FilePath& file_path, | |
| 27 GDataCache::CacheSubDirectoryType sub_dir_type, | |
| 28 const std::vector<FilePath>& cache_paths, | |
| 29 std::string* reason) { | |
| 30 DCHECK(sub_dir_type == GDataCache::CACHE_TYPE_PINNED || | |
| 31 sub_dir_type == GDataCache::CACHE_TYPE_OUTGOING); | |
| 32 | |
| 33 FilePath destination; | |
| 34 if (!file_util::ReadSymbolicLink(file_path, &destination)) { | |
| 35 *reason = "failed to read the symlink (maybe not a symlink)"; | |
| 36 return false; | |
| 37 } | |
| 38 | |
| 39 if (!file_util::PathExists(destination)) { | |
| 40 *reason = "pointing to a non-existent file"; | |
| 41 return false; | |
| 42 } | |
| 43 | |
| 44 // pinned-but-not-fetched files are symlinks to kSymLinkToDevNull. | |
| 45 if (sub_dir_type == GDataCache::CACHE_TYPE_PINNED && | |
| 46 destination == FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull)) { | |
| 47 return true; | |
| 48 } | |
| 49 | |
| 50 // The destination file should be in the persistent directory. | |
| 51 if (!cache_paths[GDataCache::CACHE_TYPE_PERSISTENT].IsParent(destination)) { | |
| 52 *reason = "pointing to a file outside of persistent directory"; | |
| 53 return false; | |
| 54 } | |
| 55 | |
| 56 return true; | |
| 57 } | |
| 58 | |
| 59 // Remove invalid files from persistent directory. | |
| 60 // | |
| 61 // 1) dirty-but-not-committed files. The dirty files should be committed | |
| 62 // (i.e. symlinks created in 'outgoing' directory) before shutdown, but the | |
| 63 // symlinks may not be created if the system shuts down unexpectedly. | |
| 64 // | |
| 65 // 2) neither dirty nor pinned. Files in the persistent directory should be | |
| 66 // in the either of the states. | |
| 67 void RemoveInvalidFilesFromPersistentDirectory( | |
| 68 const ResourceIdToFilePathMap& persistent_file_map, | |
| 69 const ResourceIdToFilePathMap& outgoing_file_map, | |
| 70 GDataCacheMetadata::CacheMap* cache_map) { | |
| 71 for (ResourceIdToFilePathMap::const_iterator iter = | |
| 72 persistent_file_map.begin(); | |
| 73 iter != persistent_file_map.end(); ++iter) { | |
| 74 const std::string& resource_id = iter->first; | |
| 75 const FilePath& file_path = iter->second; | |
| 76 | |
| 77 GDataCacheMetadata::CacheMap::iterator cache_map_iter = | |
| 78 cache_map->find(resource_id); | |
| 79 if (cache_map_iter != cache_map->end()) { | |
| 80 const DriveCacheEntry& cache_entry = cache_map_iter->second; | |
| 81 // If the file is dirty but not committed, remove it. | |
| 82 if (cache_entry.is_dirty() && | |
| 83 outgoing_file_map.count(resource_id) == 0) { | |
| 84 LOG(WARNING) << "Removing dirty-but-not-committed file: " | |
| 85 << file_path.value(); | |
| 86 file_util::Delete(file_path, false); | |
| 87 cache_map->erase(cache_map_iter); | |
| 88 } else if (!cache_entry.is_dirty() && | |
| 89 !cache_entry.is_pinned()) { | |
| 90 // If the file is neither dirty nor pinned, remove it. | |
| 91 LOG(WARNING) << "Removing persistent-but-dangling file: " | |
| 92 << file_path.value(); | |
| 93 file_util::Delete(file_path, false); | |
| 94 cache_map->erase(cache_map_iter); | |
| 95 } | |
| 96 } | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 // Scans cache subdirectory and build or update |cache_map| | |
| 101 // with found file blobs or symlinks. | |
| 102 // | |
| 103 // The resource IDs and file paths of discovered files are collected as a | |
| 104 // ResourceIdToFilePathMap, if these are processed properly. | |
| 105 void ScanCacheDirectory( | |
| 106 const std::vector<FilePath>& cache_paths, | |
| 107 GDataCache::CacheSubDirectoryType sub_dir_type, | |
| 108 GDataCacheMetadata::CacheMap* cache_map, | |
| 109 ResourceIdToFilePathMap* processed_file_map) { | |
| 110 DCHECK(cache_map); | |
| 111 DCHECK(processed_file_map); | |
| 112 | |
| 113 file_util::FileEnumerator enumerator(cache_paths[sub_dir_type], | |
| 114 false, // not recursive | |
| 115 file_util::FileEnumerator::FILES | | |
| 116 file_util::FileEnumerator::SHOW_SYM_LINKS, | |
| 117 util::kWildCard); | |
| 118 for (FilePath current = enumerator.Next(); !current.empty(); | |
| 119 current = enumerator.Next()) { | |
| 120 // Extract resource_id and md5 from filename. | |
| 121 std::string resource_id; | |
| 122 std::string md5; | |
| 123 std::string extra_extension; | |
| 124 util::ParseCacheFilePath(current, &resource_id, &md5, &extra_extension); | |
| 125 | |
| 126 // Determine cache state. | |
| 127 DriveCacheEntry cache_entry; | |
| 128 cache_entry.set_md5(md5); | |
| 129 // If we're scanning pinned directory and if entry already exists, just | |
| 130 // update its pinned state. | |
| 131 if (sub_dir_type == GDataCache::CACHE_TYPE_PINNED) { | |
| 132 std::string reason; | |
| 133 if (!IsValidSymbolicLink(current, sub_dir_type, cache_paths, &reason)) { | |
| 134 LOG(WARNING) << "Removing an invalid symlink: " << current.value() | |
| 135 << ": " << reason; | |
| 136 file_util::Delete(current, false); | |
| 137 continue; | |
| 138 } | |
| 139 | |
| 140 GDataCacheMetadata::CacheMap::iterator iter = | |
| 141 cache_map->find(resource_id); | |
| 142 if (iter != cache_map->end()) { // Entry exists, update pinned state. | |
| 143 iter->second.set_is_pinned(true); | |
| 144 | |
| 145 processed_file_map->insert(std::make_pair(resource_id, current)); | |
| 146 continue; | |
| 147 } | |
| 148 // Entry doesn't exist, this is a special symlink that refers to | |
| 149 // /dev/null; follow through to create an entry with the PINNED but not | |
| 150 // PRESENT state. | |
| 151 cache_entry.set_is_pinned(true); | |
| 152 } else if (sub_dir_type == GDataCache::CACHE_TYPE_OUTGOING) { | |
| 153 std::string reason; | |
| 154 if (!IsValidSymbolicLink(current, sub_dir_type, cache_paths, &reason)) { | |
| 155 LOG(WARNING) << "Removing an invalid symlink: " << current.value() | |
| 156 << ": " << reason; | |
| 157 file_util::Delete(current, false); | |
| 158 continue; | |
| 159 } | |
| 160 | |
| 161 // If we're scanning outgoing directory, entry must exist and be dirty. | |
| 162 // Otherwise, it's a logic error from previous execution, remove this | |
| 163 // outgoing symlink and move on. | |
| 164 GDataCacheMetadata::CacheMap::iterator iter = | |
| 165 cache_map->find(resource_id); | |
| 166 if (iter == cache_map->end() || !iter->second.is_dirty()) { | |
| 167 LOG(WARNING) << "Removing an symlink to a non-dirty file: " | |
| 168 << current.value(); | |
| 169 file_util::Delete(current, false); | |
| 170 continue; | |
| 171 } | |
| 172 | |
| 173 processed_file_map->insert(std::make_pair(resource_id, current)); | |
| 174 continue; | |
| 175 } else if (sub_dir_type == GDataCache::CACHE_TYPE_PERSISTENT || | |
| 176 sub_dir_type == GDataCache::CACHE_TYPE_TMP) { | |
| 177 if (sub_dir_type == GDataCache::CACHE_TYPE_PERSISTENT) | |
| 178 cache_entry.set_is_persistent(true); | |
| 179 | |
| 180 if (file_util::IsLink(current)) { | |
| 181 LOG(WARNING) << "Removing a symlink in persistent/tmp directory" | |
| 182 << current.value(); | |
| 183 file_util::Delete(current, false); | |
| 184 continue; | |
| 185 } | |
| 186 if (extra_extension == util::kMountedArchiveFileExtension) { | |
| 187 // Mounted archives in cache should be unmounted upon logout/shutdown. | |
| 188 // But if we encounter a mounted file at start, delete it and create an | |
| 189 // entry with not PRESENT state. | |
| 190 DCHECK(sub_dir_type == GDataCache::CACHE_TYPE_PERSISTENT); | |
| 191 file_util::Delete(current, false); | |
| 192 } else { | |
| 193 // The cache file is present. | |
| 194 cache_entry.set_is_present(true); | |
| 195 | |
| 196 // Adds the dirty bit if |md5| indicates that the file is dirty, and | |
| 197 // the file is in the persistent directory. | |
| 198 if (md5 == util::kLocallyModifiedFileExtension) { | |
| 199 if (sub_dir_type == GDataCache::CACHE_TYPE_PERSISTENT) { | |
| 200 cache_entry.set_is_dirty(true); | |
| 201 } else { | |
| 202 LOG(WARNING) << "Removing a dirty file in tmp directory: " | |
| 203 << current.value(); | |
| 204 file_util::Delete(current, false); | |
| 205 continue; | |
| 206 } | |
| 207 } | |
| 208 } | |
| 209 } else { | |
| 210 NOTREACHED() << "Unexpected sub directory type: " << sub_dir_type; | |
| 211 } | |
| 212 | |
| 213 // Create and insert new entry into cache map. | |
| 214 cache_map->insert(std::make_pair(resource_id, cache_entry)); | |
| 215 processed_file_map->insert(std::make_pair(resource_id, current)); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 void ScanCachePaths(const std::vector<FilePath>& cache_paths, | |
| 220 GDataCacheMetadata::CacheMap* cache_map) { | |
| 221 DVLOG(1) << "Scanning directories"; | |
| 222 | |
| 223 // Scan cache persistent and tmp directories to enumerate all files and create | |
| 224 // corresponding entries for cache map. | |
| 225 ResourceIdToFilePathMap persistent_file_map; | |
| 226 ScanCacheDirectory(cache_paths, | |
| 227 GDataCache::CACHE_TYPE_PERSISTENT, | |
| 228 cache_map, | |
| 229 &persistent_file_map); | |
| 230 ResourceIdToFilePathMap tmp_file_map; | |
| 231 ScanCacheDirectory(cache_paths, | |
| 232 GDataCache::CACHE_TYPE_TMP, | |
| 233 cache_map, | |
| 234 &tmp_file_map); | |
| 235 | |
| 236 // Then scan pinned directory to update existing entries in cache map, or | |
| 237 // create new ones for pinned symlinks to /dev/null which target nothing. | |
| 238 // | |
| 239 // Pinned directory should be scanned after the persistent directory as | |
| 240 // we'll add PINNED states to the existing files in the persistent | |
| 241 // directory per the contents of the pinned directory. | |
| 242 ResourceIdToFilePathMap pinned_file_map; | |
| 243 ScanCacheDirectory(cache_paths, | |
| 244 GDataCache::CACHE_TYPE_PINNED, | |
| 245 cache_map, | |
| 246 &pinned_file_map); | |
| 247 // Then scan outgoing directory to check if dirty-files are committed | |
| 248 // properly (i.e. symlinks created in outgoing directory). | |
| 249 ResourceIdToFilePathMap outgoing_file_map; | |
| 250 ScanCacheDirectory(cache_paths, | |
| 251 GDataCache::CACHE_TYPE_OUTGOING, | |
| 252 cache_map, | |
| 253 &outgoing_file_map); | |
| 254 | |
| 255 RemoveInvalidFilesFromPersistentDirectory(persistent_file_map, | |
| 256 outgoing_file_map, | |
| 257 cache_map); | |
| 258 DVLOG(1) << "Directory scan finished"; | |
| 259 } | |
| 260 | |
| 261 // Returns true if |md5| matches the one in |cache_entry| with some | |
| 262 // exceptions. See the function definition for details. | |
| 263 bool CheckIfMd5Matches( | |
| 264 const std::string& md5, | |
| 265 const DriveCacheEntry& cache_entry) { | |
| 266 if (cache_entry.is_dirty()) { | |
| 267 // If the entry is dirty, its MD5 may have been replaced by "local" | |
| 268 // during cache initialization, so we don't compare MD5. | |
| 269 return true; | |
| 270 } else if (cache_entry.is_pinned() && cache_entry.md5().empty()) { | |
| 271 // If the entry is pinned, it's ok for the entry to have an empty | |
| 272 // MD5. This can happen if the pinned file is not fetched. MD5 for pinned | |
| 273 // files are collected from files in "persistent" directory, but the | |
| 274 // persistent files do not exisit if these are not fetched yet. | |
| 275 return true; | |
| 276 } else if (md5.empty()) { | |
| 277 // If the MD5 matching is not requested, don't check MD5. | |
| 278 return true; | |
| 279 } else if (md5 == cache_entry.md5()) { | |
| 280 // Otherwise, compare the MD5. | |
| 281 return true; | |
| 282 } | |
| 283 return false; | |
| 284 } | |
| 285 | |
| 286 //////////////////////////////////////////////////////////////////////////////// | |
| 287 // GDataCacheMetadata implementation with std::map. | |
| 288 // Used for testing. | |
| 289 | |
| 290 class FakeGDataCacheMetadata : public GDataCacheMetadata { | |
| 291 public: | |
| 292 explicit FakeGDataCacheMetadata( | |
| 293 base::SequencedTaskRunner* blocking_task_runner); | |
| 294 | |
| 295 private: | |
| 296 virtual ~FakeGDataCacheMetadata(); | |
| 297 | |
| 298 // GDataCacheMetadata overrides: | |
| 299 virtual void Initialize(const std::vector<FilePath>& cache_paths) OVERRIDE; | |
| 300 virtual void AddOrUpdateCacheEntry( | |
| 301 const std::string& resource_id, | |
| 302 const DriveCacheEntry& cache_entry) OVERRIDE; | |
| 303 virtual void RemoveCacheEntry(const std::string& resource_id) OVERRIDE; | |
| 304 virtual bool GetCacheEntry(const std::string& resource_id, | |
| 305 const std::string& md5, | |
| 306 DriveCacheEntry* cache_entry) OVERRIDE; | |
| 307 virtual void RemoveTemporaryFiles() OVERRIDE; | |
| 308 virtual void Iterate(const IterateCallback& callback) OVERRIDE; | |
| 309 virtual void ForceRescanForTesting( | |
| 310 const std::vector<FilePath>& cache_paths) OVERRIDE; | |
| 311 | |
| 312 CacheMap cache_map_; | |
| 313 | |
| 314 DISALLOW_COPY_AND_ASSIGN(FakeGDataCacheMetadata); | |
| 315 }; | |
| 316 | |
| 317 FakeGDataCacheMetadata::FakeGDataCacheMetadata( | |
| 318 base::SequencedTaskRunner* blocking_task_runner) | |
| 319 : GDataCacheMetadata(blocking_task_runner) { | |
| 320 AssertOnSequencedWorkerPool(); | |
| 321 } | |
| 322 | |
| 323 FakeGDataCacheMetadata::~FakeGDataCacheMetadata() { | |
| 324 AssertOnSequencedWorkerPool(); | |
| 325 } | |
| 326 | |
| 327 void FakeGDataCacheMetadata::Initialize( | |
| 328 const std::vector<FilePath>& cache_paths) { | |
| 329 AssertOnSequencedWorkerPool(); | |
| 330 | |
| 331 ScanCachePaths(cache_paths, &cache_map_); | |
| 332 } | |
| 333 | |
| 334 void FakeGDataCacheMetadata::AddOrUpdateCacheEntry( | |
| 335 const std::string& resource_id, | |
| 336 const DriveCacheEntry& cache_entry) { | |
| 337 AssertOnSequencedWorkerPool(); | |
| 338 | |
| 339 CacheMap::iterator iter = cache_map_.find(resource_id); | |
| 340 if (iter == cache_map_.end()) { // New resource, create new entry. | |
| 341 cache_map_.insert(std::make_pair(resource_id, cache_entry)); | |
| 342 } else { // Resource exists. | |
| 343 cache_map_[resource_id] = cache_entry; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 void FakeGDataCacheMetadata::RemoveCacheEntry(const std::string& resource_id) { | |
| 348 AssertOnSequencedWorkerPool(); | |
| 349 | |
| 350 CacheMap::iterator iter = cache_map_.find(resource_id); | |
| 351 if (iter != cache_map_.end()) { | |
| 352 // Delete the CacheEntry and remove it from the map. | |
| 353 cache_map_.erase(iter); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 bool FakeGDataCacheMetadata::GetCacheEntry(const std::string& resource_id, | |
| 358 const std::string& md5, | |
| 359 DriveCacheEntry* entry) { | |
| 360 DCHECK(entry); | |
| 361 AssertOnSequencedWorkerPool(); | |
| 362 | |
| 363 CacheMap::iterator iter = cache_map_.find(resource_id); | |
| 364 if (iter == cache_map_.end()) { | |
| 365 DVLOG(1) << "Can't find " << resource_id << " in cache map"; | |
| 366 return false; | |
| 367 } | |
| 368 | |
| 369 const DriveCacheEntry& cache_entry = iter->second; | |
| 370 | |
| 371 if (!CheckIfMd5Matches(md5, cache_entry)) { | |
| 372 return false; | |
| 373 } | |
| 374 | |
| 375 *entry = cache_entry; | |
| 376 return true; | |
| 377 } | |
| 378 | |
| 379 void FakeGDataCacheMetadata::RemoveTemporaryFiles() { | |
| 380 AssertOnSequencedWorkerPool(); | |
| 381 | |
| 382 CacheMap::iterator iter = cache_map_.begin(); | |
| 383 while (iter != cache_map_.end()) { | |
| 384 if (!iter->second.is_persistent()) { | |
| 385 // Post-increment the iterator to avoid iterator invalidation. | |
| 386 cache_map_.erase(iter++); | |
| 387 } else { | |
| 388 ++iter; | |
| 389 } | |
| 390 } | |
| 391 } | |
| 392 | |
| 393 void FakeGDataCacheMetadata::Iterate(const IterateCallback& callback) { | |
| 394 AssertOnSequencedWorkerPool(); | |
| 395 | |
| 396 for (CacheMap::const_iterator iter = cache_map_.begin(); | |
| 397 iter != cache_map_.end(); ++iter) { | |
| 398 callback.Run(iter->first, iter->second); | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 void FakeGDataCacheMetadata::ForceRescanForTesting( | |
| 403 const std::vector<FilePath>& cache_paths) { | |
| 404 AssertOnSequencedWorkerPool(); | |
| 405 | |
| 406 ScanCachePaths(cache_paths, &cache_map_); | |
| 407 } | |
| 408 | |
| 409 //////////////////////////////////////////////////////////////////////////////// | |
| 410 // GDataCacheMetadata implementation with level::db. | |
| 411 | |
| 412 class GDataCacheMetadataDB : public GDataCacheMetadata { | |
| 413 public: | |
| 414 explicit GDataCacheMetadataDB( | |
| 415 base::SequencedTaskRunner* blocking_task_runner); | |
| 416 | |
| 417 private: | |
| 418 virtual ~GDataCacheMetadataDB(); | |
| 419 | |
| 420 // GDataCacheMetadata overrides: | |
| 421 virtual void Initialize(const std::vector<FilePath>& cache_paths) OVERRIDE; | |
| 422 virtual void AddOrUpdateCacheEntry( | |
| 423 const std::string& resource_id, | |
| 424 const DriveCacheEntry& cache_entry) OVERRIDE; | |
| 425 virtual void RemoveCacheEntry(const std::string& resource_id) OVERRIDE; | |
| 426 virtual bool GetCacheEntry(const std::string& resource_id, | |
| 427 const std::string& md5, | |
| 428 DriveCacheEntry* cache_entry) OVERRIDE; | |
| 429 virtual void RemoveTemporaryFiles() OVERRIDE; | |
| 430 virtual void Iterate(const IterateCallback& callback) OVERRIDE; | |
| 431 virtual void ForceRescanForTesting( | |
| 432 const std::vector<FilePath>& cache_paths) OVERRIDE; | |
| 433 | |
| 434 // Helper function to insert |cache_map| entries into the database. | |
| 435 void InsertMapIntoDB(const CacheMap& cache_map); | |
| 436 | |
| 437 scoped_ptr<leveldb::DB> level_db_; | |
| 438 | |
| 439 DISALLOW_COPY_AND_ASSIGN(GDataCacheMetadataDB); | |
| 440 }; | |
| 441 | |
| 442 GDataCacheMetadataDB::GDataCacheMetadataDB( | |
| 443 base::SequencedTaskRunner* blocking_task_runner) | |
| 444 : GDataCacheMetadata(blocking_task_runner) { | |
| 445 AssertOnSequencedWorkerPool(); | |
| 446 } | |
| 447 | |
| 448 GDataCacheMetadataDB::~GDataCacheMetadataDB() { | |
| 449 AssertOnSequencedWorkerPool(); | |
| 450 } | |
| 451 | |
| 452 void GDataCacheMetadataDB::Initialize( | |
| 453 const std::vector<FilePath>& cache_paths) { | |
| 454 AssertOnSequencedWorkerPool(); | |
| 455 | |
| 456 const FilePath db_path = | |
| 457 cache_paths[GDataCache::CACHE_TYPE_META].Append( | |
| 458 kGDataCacheMetadataDBPath); | |
| 459 DVLOG(1) << "db path=" << db_path.value(); | |
| 460 | |
| 461 const bool db_exists = file_util::PathExists(db_path); | |
| 462 | |
| 463 leveldb::DB* level_db = NULL; | |
| 464 leveldb::Options options; | |
| 465 options.create_if_missing = true; | |
| 466 leveldb::Status db_status = leveldb::DB::Open(options, db_path.value(), | |
| 467 &level_db); | |
| 468 DCHECK(level_db); | |
| 469 // TODO(achuith,hashimoto,satorux): If db cannot be opened, we should try to | |
| 470 // recover it. If that fails, we should just delete it and either rescan or | |
| 471 // refetch the feed. crbug.com/137545. | |
| 472 DCHECK(db_status.ok()); | |
| 473 level_db_.reset(level_db); | |
| 474 | |
| 475 // We scan the cache directories to initialize the cache database if we | |
| 476 // were previously using the cache map. | |
| 477 // TODO(achuith,hashimoto,satorux): Delete ScanCachePaths in M23. | |
| 478 // crbug.com/137542 | |
| 479 if (!db_exists) { | |
| 480 CacheMap cache_map; | |
| 481 ScanCachePaths(cache_paths, &cache_map); | |
| 482 InsertMapIntoDB(cache_map); | |
| 483 } | |
| 484 } | |
| 485 | |
| 486 void GDataCacheMetadataDB::InsertMapIntoDB(const CacheMap& cache_map) { | |
| 487 DVLOG(1) << "InsertMapIntoDB"; | |
| 488 for (CacheMap::const_iterator it = cache_map.begin(); | |
| 489 it != cache_map.end(); ++it) { | |
| 490 AddOrUpdateCacheEntry(it->first, it->second); | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 void GDataCacheMetadataDB::AddOrUpdateCacheEntry( | |
| 495 const std::string& resource_id, | |
| 496 const DriveCacheEntry& cache_entry) { | |
| 497 AssertOnSequencedWorkerPool(); | |
| 498 | |
| 499 DVLOG(1) << "AddOrUpdateCacheEntry, resource_id=" << resource_id; | |
| 500 std::string serialized; | |
| 501 const bool ok = cache_entry.SerializeToString(&serialized); | |
| 502 if (ok) | |
| 503 level_db_->Put(leveldb::WriteOptions(), | |
| 504 leveldb::Slice(resource_id), | |
| 505 leveldb::Slice(serialized)); | |
| 506 } | |
| 507 | |
| 508 void GDataCacheMetadataDB::RemoveCacheEntry(const std::string& resource_id) { | |
| 509 AssertOnSequencedWorkerPool(); | |
| 510 | |
| 511 DVLOG(1) << "RemoveCacheEntry, resource_id=" << resource_id; | |
| 512 level_db_->Delete(leveldb::WriteOptions(), leveldb::Slice(resource_id)); | |
| 513 } | |
| 514 | |
| 515 bool GDataCacheMetadataDB::GetCacheEntry(const std::string& resource_id, | |
| 516 const std::string& md5, | |
| 517 DriveCacheEntry* entry) { | |
| 518 DCHECK(entry); | |
| 519 AssertOnSequencedWorkerPool(); | |
| 520 | |
| 521 std::string serialized; | |
| 522 const leveldb::Status status = level_db_->Get(leveldb::ReadOptions(), | |
| 523 leveldb::Slice(resource_id), &serialized); | |
| 524 if (!status.ok()) { | |
| 525 DVLOG(1) << "Can't find " << resource_id << " in cache db"; | |
| 526 return false; | |
| 527 } | |
| 528 | |
| 529 DriveCacheEntry cache_entry; | |
| 530 const bool ok = cache_entry.ParseFromString(serialized); | |
| 531 if (!ok) { | |
| 532 LOG(ERROR) << "Failed to parse " << serialized; | |
| 533 return false; | |
| 534 } | |
| 535 | |
| 536 if (!CheckIfMd5Matches(md5, cache_entry)) { | |
| 537 return false; | |
| 538 } | |
| 539 | |
| 540 *entry = cache_entry; | |
| 541 return true; | |
| 542 } | |
| 543 | |
| 544 void GDataCacheMetadataDB::RemoveTemporaryFiles() { | |
| 545 AssertOnSequencedWorkerPool(); | |
| 546 | |
| 547 scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator( | |
| 548 leveldb::ReadOptions())); | |
| 549 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { | |
| 550 DriveCacheEntry cache_entry; | |
| 551 const bool ok = cache_entry.ParseFromString(iter->value().ToString()); | |
| 552 if (ok && !cache_entry.is_persistent()) | |
| 553 level_db_->Delete(leveldb::WriteOptions(), iter->key()); | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 void GDataCacheMetadataDB::Iterate(const IterateCallback& callback) { | |
| 558 AssertOnSequencedWorkerPool(); | |
| 559 | |
| 560 scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator( | |
| 561 leveldb::ReadOptions())); | |
| 562 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { | |
| 563 DriveCacheEntry cache_entry; | |
| 564 const bool ok = cache_entry.ParseFromString(iter->value().ToString()); | |
| 565 if (ok) | |
| 566 callback.Run(iter->key().ToString(), cache_entry); | |
| 567 } | |
| 568 } | |
| 569 | |
| 570 void GDataCacheMetadataDB::ForceRescanForTesting( | |
| 571 const std::vector<FilePath>& cache_paths) { | |
| 572 AssertOnSequencedWorkerPool(); | |
| 573 | |
| 574 CacheMap cache_map; | |
| 575 ScanCachePaths(cache_paths, &cache_map); | |
| 576 InsertMapIntoDB(cache_map); | |
| 577 } | |
| 578 | |
| 579 } // namespace | |
| 580 | |
| 581 GDataCacheMetadata::GDataCacheMetadata( | |
| 582 base::SequencedTaskRunner* blocking_task_runner) | |
| 583 : blocking_task_runner_(blocking_task_runner) { | |
| 584 AssertOnSequencedWorkerPool(); | |
| 585 } | |
| 586 | |
| 587 GDataCacheMetadata::~GDataCacheMetadata() { | |
| 588 AssertOnSequencedWorkerPool(); | |
| 589 } | |
| 590 | |
| 591 // static | |
| 592 scoped_ptr<GDataCacheMetadata> GDataCacheMetadata::CreateGDataCacheMetadata( | |
| 593 base::SequencedTaskRunner* blocking_task_runner) { | |
| 594 return scoped_ptr<GDataCacheMetadata>( | |
| 595 new GDataCacheMetadataDB(blocking_task_runner)); | |
| 596 } | |
| 597 | |
| 598 // static | |
| 599 scoped_ptr<GDataCacheMetadata> | |
| 600 GDataCacheMetadata::CreateGDataCacheMetadataForTesting( | |
| 601 base::SequencedTaskRunner* blocking_task_runner) { | |
| 602 return scoped_ptr<GDataCacheMetadata>( | |
| 603 new FakeGDataCacheMetadata(blocking_task_runner)); | |
| 604 } | |
| 605 | |
| 606 void GDataCacheMetadata::AssertOnSequencedWorkerPool() { | |
| 607 DCHECK(!blocking_task_runner_ || | |
| 608 blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 609 } | |
| 610 | |
| 611 } // namespace gdata | |
| OLD | NEW |