diff options
author | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-18 12:54:59 +0000 |
---|---|---|
committer | hashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-18 12:54:59 +0000 |
commit | ca5f6da423ead7dd1b799ad04a5d5faa39d5076a (patch) | |
tree | 4e23dfccc1457d0e2438a8a9589a4a5972f1898c | |
parent | e9f4d35f34e3d53a1faf7a115393c10719e59967 (diff) | |
download | chromium_src-ca5f6da423ead7dd1b799ad04a5d5faa39d5076a.zip chromium_src-ca5f6da423ead7dd1b799ad04a5d5faa39d5076a.tar.gz chromium_src-ca5f6da423ead7dd1b799ad04a5d5faa39d5076a.tar.bz2 |
chromeos: Separate purely data operating part of GDataCache as GDataCacheMetadata
Spliting data operating part makes it easy to ensure that the data is accessed only on the blocking pool.
BUG=132926
TEST=unit_tests --gtest_filter="GData*"
Review URL: https://chromiumcodereview.appspot.com/10556037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@142698 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_cache.cc | 312 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_cache.h | 40 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_cache_metadata.cc | 266 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_cache_metadata.h | 107 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_cache_metadata_unittest.cc | 193 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_cache_unittest.cc | 197 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc | 15 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_util.cc | 3 | ||||
-rw-r--r-- | chrome/browser/chromeos/gdata/gdata_util.h | 4 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 2 |
11 files changed, 622 insertions, 519 deletions
diff --git a/chrome/browser/chromeos/gdata/gdata_cache.cc b/chrome/browser/chromeos/gdata/gdata_cache.cc index 6c13a93..32539f3 100644 --- a/chrome/browser/chromeos/gdata/gdata_cache.cc +++ b/chrome/browser/chromeos/gdata/gdata_cache.cc @@ -12,6 +12,7 @@ #include "base/stringprintf.h" #include "base/string_util.h" #include "base/sys_info.h" +#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h" #include "chrome/browser/chromeos/gdata/gdata_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_constants.h" @@ -23,12 +24,9 @@ using content::BrowserThread; namespace gdata { namespace { -const char kWildCard[] = "*"; - const FilePath::CharType kSymLinkToDevNull[] = FILE_PATH_LITERAL("/dev/null"); const char kLocallyModifiedFileExtension[] = "local"; -const char kMountedArchiveFileExtension[] = "mounted"; const FilePath::CharType kGDataCacheVersionDir[] = FILE_PATH_LITERAL("v1"); const FilePath::CharType kGDataCacheMetaDir[] = FILE_PATH_LITERAL("meta"); @@ -167,46 +165,6 @@ base::PlatformFileError SystemToPlatformError(int error) { } } -// Creates cache directory and its sub-directories if they don't exist. -// TODO(glotov): take care of this when the setup and cleanup part is landed, -// noting that these directories need to be created for development in linux box -// and unittest. (http://crosbug.com/27577) -base::PlatformFileError CreateCacheDirectories( - const std::vector<FilePath>& paths_to_create) { - base::PlatformFileError error = base::PLATFORM_FILE_OK; - - for (size_t i = 0; i < paths_to_create.size(); ++i) { - if (file_util::DirectoryExists(paths_to_create[i])) - continue; - - if (!file_util::CreateDirectory(paths_to_create[i])) { - // Error creating this directory, record error and proceed with next one. - error = SystemToPlatformError(errno); - PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value(); - } else { - DVLOG(1) << "Created directory " << paths_to_create[i].value(); - } - } - - return error; -} - -// Changes the permissions of |file_path| to |permissions|. -// Returns the platform error code of the operation. -base::PlatformFileError ChangeFilePermissions(const FilePath& file_path, - mode_t permissions) { - base::PlatformFileError error = base::PLATFORM_FILE_OK; - - if (HANDLE_EINTR(chmod(file_path.value().c_str(), permissions)) != 0) { - error = SystemToPlatformError(errno); - PLOG(ERROR) << "Error changing permissions of " << file_path.value(); - } else { - DVLOG(1) << "Changed permissions of " << file_path.value(); - } - - return error; -} - // Modifies cache state of file on blocking pool, which involves: // - moving or copying file (per |file_operation_type|) from |source_path| to // |dest_path| if they're different @@ -435,7 +393,7 @@ FilePath GDataCache::GetCacheFilePath(const std::string& resource_id, if (file_origin == CACHED_FILE_MOUNTED) { DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT); base_name += FilePath::kExtensionSeparator; - base_name += kMountedArchiveFileExtension; + base_name += util::kMountedArchiveFileExtension; } return GetCacheDirectoryPath(sub_dir_type).Append(base_name); } @@ -470,7 +428,7 @@ void GDataCache::FreeDiskSpaceIfNeededFor(int64 num_bytes, // Otherwise, try to free up the disk space. DVLOG(1) << "Freeing up disk space for " << num_bytes; // First remove temporary files from the cache map. - RemoveTemporaryFiles(); + metadata_->RemoveTemporaryFiles(); // Then remove all files under "tmp" directory. RemoveAllFiles(GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP)); @@ -693,236 +651,11 @@ void GDataCache::RequestInitializeOnUIThread() { base::Bind(&GDataCache::OnInitialized, ui_weak_ptr_)); } -class GDataCacheMap : public GDataCache { - public: - GDataCacheMap( - const FilePath& cache_root_path, - base::SequencedWorkerPool* pool, - const base::SequencedWorkerPool::SequenceToken& sequence_token); - - protected: - virtual ~GDataCacheMap(); - - private: - // GDataCache implementation. - virtual void Initialize() OVERRIDE; - virtual void SetCacheMap(const CacheMap& new_cache_map) OVERRIDE; - virtual void UpdateCache(const std::string& resource_id, - const std::string& md5, - CacheSubDirectoryType subdir, - int cache_state) OVERRIDE; - virtual void RemoveFromCache(const std::string& resource_id) OVERRIDE; - virtual scoped_ptr<CacheEntry> GetCacheEntry(const std::string& resource_id, - const std::string& md5) OVERRIDE; - virtual void RemoveTemporaryFiles() OVERRIDE; - - // Scans cache subdirectory |sub_dir_type| and build or update |cache_map| - // with found file blobs or symlinks. - void ScanCacheDirectory(CacheSubDirectoryType sub_dir_type, - CacheMap* cache_map); - - CacheMap cache_map_; -}; - -GDataCacheMap::GDataCacheMap( - const FilePath& cache_root_path, - base::SequencedWorkerPool* pool, - const base::SequencedWorkerPool::SequenceToken& sequence_token) - : GDataCache(cache_root_path, pool, sequence_token) { -} - -GDataCacheMap::~GDataCacheMap() { - AssertOnSequencedWorkerPool(); - cache_map_.clear(); -} - -void GDataCacheMap::Initialize() { - AssertOnSequencedWorkerPool(); - - base::PlatformFileError error = CreateCacheDirectories(cache_paths()); - if (error != base::PLATFORM_FILE_OK) - return; - - // Change permissions of cache persistent directory to u+rwx,og+x in order to - // allow archive files in that directory to be mounted by cros-disks. - error = ChangeFilePermissions( - GetCacheDirectoryPath(CACHE_TYPE_PERSISTENT), - S_IRWXU | S_IXGRP | S_IXOTH); - if (error != base::PLATFORM_FILE_OK) - return; - - DVLOG(1) << "Scanning directories"; - - // Scan cache persistent and tmp directories to enumerate all files and create - // corresponding entries for cache map. - CacheMap cache_map; - ScanCacheDirectory(CACHE_TYPE_PERSISTENT, &cache_map); - ScanCacheDirectory(CACHE_TYPE_TMP, &cache_map); - - // Then scan pinned and outgoing directories to update existing entries in - // cache map, or create new ones for pinned symlinks to /dev/null which target - // nothing. - // Pinned and outgoing directories should be scanned after the persistent - // directory as we'll add PINNED and DIRTY states respectively to the existing - // files in the persistent directory per the contents of the pinned and - // outgoing directories. - ScanCacheDirectory(CACHE_TYPE_PINNED, &cache_map); - ScanCacheDirectory(CACHE_TYPE_OUTGOING, &cache_map); - - SetCacheMap(cache_map); - - DVLOG(1) << "Directory scan finished"; -} - -void GDataCacheMap::SetCacheMap(const CacheMap& new_cache_map) { - AssertOnSequencedWorkerPool(); - cache_map_ = new_cache_map; -} - -void GDataCacheMap::UpdateCache(const std::string& resource_id, - const std::string& md5, - CacheSubDirectoryType subdir, - int cache_state) { - AssertOnSequencedWorkerPool(); - - CacheMap::iterator iter = cache_map_.find(resource_id); - if (iter == cache_map_.end()) { // New resource, create new entry. - // Makes no sense to create new entry if cache state is NONE. - DCHECK(cache_state != CACHE_STATE_NONE); - if (cache_state != CACHE_STATE_NONE) { - CacheEntry cache_entry(md5, subdir, cache_state); - cache_map_.insert(std::make_pair(resource_id, cache_entry)); - DVLOG(1) << "Added res_id=" << resource_id - << ", " << cache_entry.ToString(); - } - } else { // Resource exists. - // If cache state is NONE, delete entry from cache map. - if (cache_state == CACHE_STATE_NONE) { - DVLOG(1) << "Deleting res_id=" << resource_id - << ", " << iter->second.ToString(); - cache_map_.erase(iter); - } else { // Otherwise, update entry in cache map. - iter->second.md5 = md5; - iter->second.sub_dir_type = subdir; - iter->second.cache_state = cache_state; - DVLOG(1) << "Updated res_id=" << resource_id - << ", " << iter->second.ToString(); - } - } -} - -void GDataCacheMap::RemoveFromCache(const std::string& resource_id) { - AssertOnSequencedWorkerPool(); - - CacheMap::iterator iter = cache_map_.find(resource_id); - if (iter != cache_map_.end()) { - // Delete the CacheEntry and remove it from the map. - cache_map_.erase(iter); - } -} - -scoped_ptr<GDataCache::CacheEntry> GDataCacheMap::GetCacheEntry( +scoped_ptr<GDataCache::CacheEntry> GDataCache::GetCacheEntry( const std::string& resource_id, const std::string& md5) { AssertOnSequencedWorkerPool(); - - CacheMap::iterator iter = cache_map_.find(resource_id); - if (iter == cache_map_.end()) { - DVLOG(1) << "Can't find " << resource_id << " in cache map"; - return scoped_ptr<CacheEntry>(); - } - - scoped_ptr<CacheEntry> cache_entry(new CacheEntry(iter->second)); - - // If entry is not dirty, it's only valid if matches with non-empty |md5|. - // If entry is dirty, its md5 may have been replaced by "local" during cache - // initialization, so we don't compare md5. - if (!cache_entry->IsDirty() && !md5.empty() && cache_entry->md5 != md5) { - DVLOG(1) << "Non-matching md5: want=" << md5 - << ", found=[res_id=" << resource_id - << ", " << cache_entry->ToString() - << "]"; - return scoped_ptr<CacheEntry>(); - } - - DVLOG(1) << "Found entry for res_id=" << resource_id - << ", " << cache_entry->ToString(); - - return cache_entry.Pass(); -} - -void GDataCacheMap::RemoveTemporaryFiles() { - AssertOnSequencedWorkerPool(); - - CacheMap::iterator iter = cache_map_.begin(); - while (iter != cache_map_.end()) { - if (iter->second.sub_dir_type == CACHE_TYPE_TMP) { - // Post-increment the iterator to avoid iterator invalidation. - cache_map_.erase(iter++); - } else { - ++iter; - } - } -} - -void GDataCacheMap::ScanCacheDirectory(CacheSubDirectoryType sub_dir_type, - CacheMap* cache_map) { - file_util::FileEnumerator enumerator( - GetCacheDirectoryPath(sub_dir_type), - false, // not recursive - static_cast<file_util::FileEnumerator::FileType>( - file_util::FileEnumerator::FILES | - file_util::FileEnumerator::SHOW_SYM_LINKS), - kWildCard); - for (FilePath current = enumerator.Next(); !current.empty(); - current = enumerator.Next()) { - // Extract resource_id and md5 from filename. - std::string resource_id; - std::string md5; - std::string extra_extension; - util::ParseCacheFilePath(current, &resource_id, &md5, &extra_extension); - - // Determine cache state. - int cache_state = CACHE_STATE_NONE; - // If we're scanning pinned directory and if entry already exists, just - // update its pinned state. - if (sub_dir_type == CACHE_TYPE_PINNED) { - CacheMap::iterator iter = cache_map->find(resource_id); - if (iter != cache_map->end()) { // Entry exists, update pinned state. - iter->second.cache_state = SetCachePinned(iter->second.cache_state); - continue; - } - // Entry doesn't exist, this is a special symlink that refers to - // /dev/null; follow through to create an entry with the PINNED but not - // PRESENT state. - cache_state = SetCachePinned(cache_state); - } else if (sub_dir_type == CACHE_TYPE_OUTGOING) { - // If we're scanning outgoing directory, entry must exist, update its - // dirty state. - // If entry doesn't exist, it's a logic error from previous execution, - // ignore this outgoing symlink and move on. - CacheMap::iterator iter = cache_map->find(resource_id); - if (iter != cache_map->end()) { // Entry exists, update dirty state. - iter->second.cache_state = SetCacheDirty(iter->second.cache_state); - } else { - NOTREACHED() << "Dirty cache file MUST have actual file blob"; - } - continue; - } else if (extra_extension == kMountedArchiveFileExtension) { - // Mounted archives in cache should be unmounted upon logout/shutdown. - // But if we encounter a mounted file at start, delete it and create an - // entry with not PRESENT state. - DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT); - file_util::Delete(current, false); - } else { - // Scanning other directories means that cache file is actually present. - cache_state = SetCachePresent(cache_state); - } - - // Create and insert new entry into cache map. - cache_map->insert(std::make_pair( - resource_id, CacheEntry(md5, sub_dir_type, cache_state))); - } + return metadata_->GetCacheEntry(resource_id, md5); } // static @@ -931,7 +664,7 @@ GDataCache* GDataCache::CreateGDataCacheOnUIThread( base::SequencedWorkerPool* pool, const base::SequencedWorkerPool::SequenceToken& sequence_token) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - return new GDataCacheMap(cache_root_path, pool, sequence_token); + return new GDataCache(cache_root_path, pool, sequence_token); } void GDataCache::DestroyOnUIThread() { @@ -947,6 +680,15 @@ void GDataCache::DestroyOnUIThread() { base::Unretained(this))); } +void GDataCache::Initialize() { + AssertOnSequencedWorkerPool(); + + GDataCacheMetadataMap* cache_data = + new GDataCacheMetadataMap(pool_, sequence_token_); + cache_data->Initialize(cache_paths_); + metadata_.reset(cache_data); +} + void GDataCache::Destroy() { AssertOnSequencedWorkerPool(); delete this; @@ -1047,12 +789,12 @@ void GDataCache::Store(const std::string& resource_id, // if base name of |dest_path| (i.e. escaped resource_id) contains the // extension separator '.', ReplaceExtension will remove it and everything // after it. The result will be nothing like the escaped resource_id. - stale_filenames_pattern = FilePath(dest_path.value() + kWildCard); + stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard); } else { // Replace md5 extension with '*' i.e. "<resource_id>.*". // Note that ReplaceExtension automatically prefixes the extension with the // extension separator '.'. - stale_filenames_pattern = dest_path.ReplaceExtension(kWildCard); + stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard); } // Delete files that match |stale_filenames_pattern| except for |dest_path|. @@ -1060,7 +802,7 @@ void GDataCache::Store(const std::string& resource_id, if (*error == base::PLATFORM_FILE_OK) { // Now that file operations have completed, update cache map. - UpdateCache(resource_id, md5, sub_dir_type, cache_state); + metadata_->UpdateCache(resource_id, md5, sub_dir_type, cache_state); } } @@ -1148,7 +890,7 @@ void GDataCache::Pin(const std::string& resource_id, if (*error == base::PLATFORM_FILE_OK) { // Now that file operations have completed, update cache map. - UpdateCache(resource_id, md5, sub_dir_type, cache_state); + metadata_->UpdateCache(resource_id, md5, sub_dir_type, cache_state); } } @@ -1227,7 +969,7 @@ void GDataCache::Unpin(const std::string& resource_id, if (*error == base::PLATFORM_FILE_OK) { // Now that file operations have completed, update cache map. int cache_state = ClearCachePinned(cache_entry->cache_state); - UpdateCache(resource_id, md5, sub_dir_type, cache_state); + metadata_->UpdateCache(resource_id, md5, sub_dir_type, cache_state); } } @@ -1245,7 +987,7 @@ void GDataCache::SetMountedState(const FilePath& file_path, std::string extra_extension; util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension); // The extra_extension shall be ".mounted" iff we're unmounting. - DCHECK(!to_mount == (extra_extension == kMountedArchiveFileExtension)); + DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension)); // Get cache entry associated with the resource_id and md5 scoped_ptr<CacheEntry> cache_entry = GetCacheEntry( @@ -1291,7 +1033,7 @@ void GDataCache::SetMountedState(const FilePath& file_path, FILE_OPERATION_MOVE, FilePath(), false); if (*error == base::PLATFORM_FILE_OK) { // Now that cache operation is complete, update cache map - UpdateCache(resource_id, md5, dest_subdir, cache_state); + metadata_->UpdateCache(resource_id, md5, dest_subdir, cache_state); } } @@ -1395,7 +1137,7 @@ void GDataCache::MarkDirty(const std::string& resource_id, if (*error == base::PLATFORM_FILE_OK) { // Now that file operations have completed, update cache map. int cache_state = SetCacheDirty(cache_entry->cache_state); - UpdateCache(resource_id, md5, sub_dir_type, cache_state); + metadata_->UpdateCache(resource_id, md5, sub_dir_type, cache_state); } } @@ -1543,7 +1285,7 @@ void GDataCache::ClearDirty(const std::string& resource_id, if (*error == base::PLATFORM_FILE_OK) { // Now that file operations have completed, update cache map. int cache_state = ClearCacheDirty(cache_entry->cache_state); - UpdateCache(resource_id, md5, sub_dir_type, cache_state); + metadata_->UpdateCache(resource_id, md5, sub_dir_type, cache_state); } } @@ -1579,11 +1321,11 @@ void GDataCache::Remove(const std::string& resource_id, // For files in persistent and tmp dirs, delete files that match // "<resource_id>.*". paths_to_delete.push_back(GetCacheFilePath(resource_id, - kWildCard, + util::kWildCard, CACHE_TYPE_PERSISTENT, CACHED_FILE_FROM_SERVER)); paths_to_delete.push_back(GetCacheFilePath(resource_id, - kWildCard, + util::kWildCard, CACHE_TYPE_TMP, CACHED_FILE_FROM_SERVER)); @@ -1607,7 +1349,7 @@ void GDataCache::Remove(const std::string& resource_id, } // Now that all file operations have completed, remove from cache map. - RemoveFromCache(resource_id); + metadata_->RemoveFromCache(resource_id); *error = base::PLATFORM_FILE_OK; } diff --git a/chrome/browser/chromeos/gdata/gdata_cache.h b/chrome/browser/chromeos/gdata/gdata_cache.h index fb92c6e..2032855 100644 --- a/chrome/browser/chromeos/gdata/gdata_cache.h +++ b/chrome/browser/chromeos/gdata/gdata_cache.h @@ -20,6 +20,8 @@ class Profile; namespace gdata { +class GDataCacheMetadata; + // Callback for SetMountedStateOnUIThread. typedef base::Callback<void(base::PlatformFileError error, const FilePath& file_path)> SetMountedStateCallback; @@ -168,9 +170,6 @@ class GDataCache { return cache_state &= ~CACHE_STATE_MOUNTED; } - // A map table of cache file's resource id to its CacheEntry* entry. - typedef std::map<std::string, CacheEntry> CacheMap; - // Returns the sub-directory under gdata cache directory for the given sub // directory type. Example: <user_profile_dir>/GCache/v1/tmp // @@ -281,38 +280,16 @@ class GDataCache { void RemoveOnUIThread(const std::string& resource_id, const CacheOperationCallback& callback); - // TODO(hashimoto): Remove this method when crbug.com/131756 is fixed. - const std::vector<FilePath>& cache_paths() const { return cache_paths_; } - // Utility method to call Initialize on UI thread. void RequestInitializeOnUIThread(); - // Initializes cache. - virtual void Initialize() = 0; - - // Sets |cache_map_| data member to formal parameter |new_cache_map|. - virtual void SetCacheMap(const CacheMap& new_cache_map) = 0; - - // Updates cache map with entry corresponding to |resource_id|. - // Creates new entry if it doesn't exist, otherwise update the entry. - virtual void UpdateCache(const std::string& resource_id, - const std::string& md5, - CacheSubDirectoryType subdir, - int cache_state) = 0; - - // Removes entry corresponding to |resource_id| from cache map. - virtual void RemoveFromCache(const std::string& resource_id) = 0; - // Returns the cache entry for file corresponding to |resource_id| and |md5| // if entry exists in cache map. Otherwise, returns NULL. // |md5| can be empty if only matching |resource_id| is desired, which may // happen when looking for pinned entries where symlinks' filenames have no // extension and hence no md5. - virtual scoped_ptr<CacheEntry> GetCacheEntry(const std::string& resource_id, - const std::string& md5) = 0; - - // Removes temporary files (files in CACHE_TYPE_TMP) from the cache map. - virtual void RemoveTemporaryFiles() = 0; + scoped_ptr<CacheEntry> GetCacheEntry(const std::string& resource_id, + const std::string& md5); // Factory methods for GDataCache. // |pool| and |sequence_token| are used to assert that the functions are @@ -333,7 +310,7 @@ class GDataCache { // TODO(satorux): Write a unit test for this. static FilePath GetCacheRootPath(Profile* profile); - protected: + private: GDataCache( const FilePath& cache_root_path, base::SequencedWorkerPool* pool_, @@ -344,8 +321,8 @@ class GDataCache { // with the right sequence ID. If not, DCHECK will fail. void AssertOnSequencedWorkerPool(); - private: - friend class GDataCacheTest; + // Initializes the cache. + void Initialize(); // Deletes the cache. void Destroy(); @@ -427,6 +404,9 @@ class GDataCache { base::SequencedWorkerPool* pool_; const base::SequencedWorkerPool::SequenceToken sequence_token_; + // The cache state data. This member must be access only on the blocking pool. + scoped_ptr<GDataCacheMetadata> metadata_; + // WeakPtrFactory and WeakPtr bound to the UI thread. base::WeakPtrFactory<GDataCache> ui_weak_ptr_factory_; base::WeakPtr<GDataCache> ui_weak_ptr_; diff --git a/chrome/browser/chromeos/gdata/gdata_cache_metadata.cc b/chrome/browser/chromeos/gdata/gdata_cache_metadata.cc new file mode 100644 index 0000000..dc91d48 --- /dev/null +++ b/chrome/browser/chromeos/gdata/gdata_cache_metadata.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h" + +#include "base/file_util.h" +#include "chrome/browser/chromeos/gdata/gdata_util.h" + +namespace gdata { + +namespace { + +// Creates cache directory and its sub-directories if they don't exist. +// TODO(glotov): take care of this when the setup and cleanup part is landed, +// noting that these directories need to be created for development in linux box +// and unittest. (http://crosbug.com/27577) +bool CreateCacheDirectories(const std::vector<FilePath>& paths_to_create) { + bool success = true; + + for (size_t i = 0; i < paths_to_create.size(); ++i) { + if (file_util::DirectoryExists(paths_to_create[i])) + continue; + + if (!file_util::CreateDirectory(paths_to_create[i])) { + // Error creating this directory, record error and proceed with next one. + success = false; + PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value(); + } else { + DVLOG(1) << "Created directory " << paths_to_create[i].value(); + } + } + return success; +} + +// Changes the permissions of |file_path| to |permissions|. +bool ChangeFilePermissions(const FilePath& file_path, mode_t permissions) { + if (HANDLE_EINTR(chmod(file_path.value().c_str(), permissions)) != 0) { + PLOG(ERROR) << "Error changing permissions of " << file_path.value(); + return false; + } + DVLOG(1) << "Changed permissions of " << file_path.value(); + return true; +} + +} // namespace + +GDataCacheMetadata::GDataCacheMetadata( + base::SequencedWorkerPool* pool, + const base::SequencedWorkerPool::SequenceToken& sequence_token) + : pool_(pool), + sequence_token_(sequence_token) { + AssertOnSequencedWorkerPool(); +} + +GDataCacheMetadata::~GDataCacheMetadata() { + AssertOnSequencedWorkerPool(); +} + +void GDataCacheMetadata::AssertOnSequencedWorkerPool() { + DCHECK(!pool_ || pool_->IsRunningSequenceOnCurrentThread(sequence_token_)); +} + +GDataCacheMetadataMap::GDataCacheMetadataMap( + base::SequencedWorkerPool* pool, + const base::SequencedWorkerPool::SequenceToken& sequence_token) + : GDataCacheMetadata(pool, sequence_token) { + AssertOnSequencedWorkerPool(); +} + +GDataCacheMetadataMap::~GDataCacheMetadataMap() { + AssertOnSequencedWorkerPool(); +} + + +void GDataCacheMetadataMap::Initialize( + const std::vector<FilePath>& cache_paths) { + AssertOnSequencedWorkerPool(); + + if (cache_paths.size() < GDataCache::NUM_CACHE_TYPES) { + DLOG(ERROR) << "Size of cache_paths is invalid."; + return; + } + + if (!CreateCacheDirectories(cache_paths)) + return; + + // Change permissions of cache persistent directory to u+rwx,og+x in order to + // allow archive files in that directory to be mounted by cros-disks. + if (!ChangeFilePermissions(cache_paths[GDataCache::CACHE_TYPE_PERSISTENT], + S_IRWXU | S_IXGRP | S_IXOTH)) + return; + + DVLOG(1) << "Scanning directories"; + + // Scan cache persistent and tmp directories to enumerate all files and create + // corresponding entries for cache map. + ScanCacheDirectory(cache_paths, GDataCache::CACHE_TYPE_PERSISTENT, + &cache_map_); + ScanCacheDirectory(cache_paths, GDataCache::CACHE_TYPE_TMP, &cache_map_); + + // Then scan pinned and outgoing directories to update existing entries in + // cache map, or create new ones for pinned symlinks to /dev/null which target + // nothing. + // Pinned and outgoing directories should be scanned after the persistent + // directory as we'll add PINNED and DIRTY states respectively to the existing + // files in the persistent directory per the contents of the pinned and + // outgoing directories. + ScanCacheDirectory(cache_paths, GDataCache::CACHE_TYPE_PINNED, &cache_map_); + ScanCacheDirectory(cache_paths, GDataCache::CACHE_TYPE_OUTGOING, &cache_map_); + + DVLOG(1) << "Directory scan finished"; +} + +void GDataCacheMetadataMap::UpdateCache(const std::string& resource_id, + const std::string& md5, + GDataCache::CacheSubDirectoryType subdir, + int cache_state) { + AssertOnSequencedWorkerPool(); + + CacheMap::iterator iter = cache_map_.find(resource_id); + if (iter == cache_map_.end()) { // New resource, create new entry. + // Makes no sense to create new entry if cache state is NONE. + DCHECK(cache_state != GDataCache::CACHE_STATE_NONE); + if (cache_state != GDataCache::CACHE_STATE_NONE) { + GDataCache::CacheEntry cache_entry(md5, subdir, cache_state); + cache_map_.insert(std::make_pair(resource_id, cache_entry)); + DVLOG(1) << "Added res_id=" << resource_id + << ", " << cache_entry.ToString(); + } + } else { // Resource exists. + // If cache state is NONE, delete entry from cache map. + if (cache_state == GDataCache::CACHE_STATE_NONE) { + DVLOG(1) << "Deleting res_id=" << resource_id + << ", " << iter->second.ToString(); + cache_map_.erase(iter); + } else { // Otherwise, update entry in cache map. + iter->second.md5 = md5; + iter->second.sub_dir_type = subdir; + iter->second.cache_state = cache_state; + DVLOG(1) << "Updated res_id=" << resource_id + << ", " << iter->second.ToString(); + } + } +} + +void GDataCacheMetadataMap::RemoveFromCache(const std::string& resource_id) { + AssertOnSequencedWorkerPool(); + + CacheMap::iterator iter = cache_map_.find(resource_id); + if (iter != cache_map_.end()) { + // Delete the CacheEntry and remove it from the map. + cache_map_.erase(iter); + } +} + +scoped_ptr<GDataCache::CacheEntry> GDataCacheMetadataMap::GetCacheEntry( + const std::string& resource_id, + const std::string& md5) { + AssertOnSequencedWorkerPool(); + + CacheMap::iterator iter = cache_map_.find(resource_id); + if (iter == cache_map_.end()) { + DVLOG(1) << "Can't find " << resource_id << " in cache map"; + return scoped_ptr<GDataCache::CacheEntry>(); + } + + scoped_ptr<GDataCache::CacheEntry> cache_entry( + new GDataCache::CacheEntry(iter->second)); + + // If entry is not dirty, it's only valid if matches with non-empty |md5|. + // If entry is dirty, its md5 may have been replaced by "local" during cache + // initialization, so we don't compare md5. + if (!cache_entry->IsDirty() && !md5.empty() && cache_entry->md5 != md5) { + DVLOG(1) << "Non-matching md5: want=" << md5 + << ", found=[res_id=" << resource_id + << ", " << cache_entry->ToString() + << "]"; + return scoped_ptr<GDataCache::CacheEntry>(); + } + + DVLOG(1) << "Found entry for res_id=" << resource_id + << ", " << cache_entry->ToString(); + + return cache_entry.Pass(); +} + +void GDataCacheMetadataMap::RemoveTemporaryFiles() { + AssertOnSequencedWorkerPool(); + + CacheMap::iterator iter = cache_map_.begin(); + while (iter != cache_map_.end()) { + if (iter->second.sub_dir_type == GDataCache::CACHE_TYPE_TMP) { + // Post-increment the iterator to avoid iterator invalidation. + cache_map_.erase(iter++); + } else { + ++iter; + } + } +} + +void GDataCacheMetadataMap::ScanCacheDirectory( + const std::vector<FilePath>& cache_paths, + GDataCache::CacheSubDirectoryType sub_dir_type, + CacheMap* cache_map) { + file_util::FileEnumerator enumerator( + cache_paths[sub_dir_type], + false, // not recursive + static_cast<file_util::FileEnumerator::FileType>( + file_util::FileEnumerator::FILES | + file_util::FileEnumerator::SHOW_SYM_LINKS), + util::kWildCard); + for (FilePath current = enumerator.Next(); !current.empty(); + current = enumerator.Next()) { + // Extract resource_id and md5 from filename. + std::string resource_id; + std::string md5; + std::string extra_extension; + util::ParseCacheFilePath(current, &resource_id, &md5, &extra_extension); + + // Determine cache state. + int cache_state = GDataCache::CACHE_STATE_NONE; + // If we're scanning pinned directory and if entry already exists, just + // update its pinned state. + if (sub_dir_type == GDataCache::CACHE_TYPE_PINNED) { + CacheMap::iterator iter = cache_map->find(resource_id); + if (iter != cache_map->end()) { // Entry exists, update pinned state. + iter->second.cache_state = + GDataCache::SetCachePinned(iter->second.cache_state); + continue; + } + // Entry doesn't exist, this is a special symlink that refers to + // /dev/null; follow through to create an entry with the PINNED but not + // PRESENT state. + cache_state = GDataCache::SetCachePinned(cache_state); + } else if (sub_dir_type == GDataCache::CACHE_TYPE_OUTGOING) { + // If we're scanning outgoing directory, entry must exist, update its + // dirty state. + // If entry doesn't exist, it's a logic error from previous execution, + // ignore this outgoing symlink and move on. + CacheMap::iterator iter = cache_map->find(resource_id); + if (iter != cache_map->end()) { // Entry exists, update dirty state. + iter->second.cache_state = + GDataCache::SetCacheDirty(iter->second.cache_state); + } else { + NOTREACHED() << "Dirty cache file MUST have actual file blob"; + } + continue; + } else if (extra_extension == util::kMountedArchiveFileExtension) { + // Mounted archives in cache should be unmounted upon logout/shutdown. + // But if we encounter a mounted file at start, delete it and create an + // entry with not PRESENT state. + DCHECK(sub_dir_type == GDataCache::CACHE_TYPE_PERSISTENT); + file_util::Delete(current, false); + } else { + // Scanning other directories means that cache file is actually present. + cache_state = GDataCache::SetCachePresent(cache_state); + } + + // Create and insert new entry into cache map. + cache_map->insert(std::make_pair( + resource_id, GDataCache::CacheEntry(md5, sub_dir_type, cache_state))); + } +} + +} // namespace gdata diff --git a/chrome/browser/chromeos/gdata/gdata_cache_metadata.h b/chrome/browser/chromeos/gdata/gdata_cache_metadata.h new file mode 100644 index 0000000..0f5dade --- /dev/null +++ b/chrome/browser/chromeos/gdata/gdata_cache_metadata.h @@ -0,0 +1,107 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_GDATA_GDATA_CACHE_METADATA_H_ +#define CHROME_BROWSER_CHROMEOS_GDATA_GDATA_CACHE_METADATA_H_ +#pragma once + +#include "base/file_path.h" +#include "base/gtest_prod_util.h" +#include "chrome/browser/chromeos/gdata/gdata_cache.h" + +namespace gdata { + +// GDataCacheMetadata is interface to maintain metadata of GDataCache's cached +// files. This class only manages metadata. File operations are done by +// GDataCache. +// All member access including ctor and dtor must be made on the blocking pool. +class GDataCacheMetadata { + public: + // |pool| and |sequence_token| are used to assert that the functions are + // called on the right sequenced worker pool with the right sequence token. + // + // For testing, the thread assertion can be disabled by passing NULL and + // the default value of SequenceToken. + GDataCacheMetadata( + base::SequencedWorkerPool* pool, + const base::SequencedWorkerPool::SequenceToken& sequence_token); + virtual ~GDataCacheMetadata(); + + // Updates cache map with entry corresponding to |resource_id|. + // Creates new entry if it doesn't exist, otherwise update the entry. + virtual void UpdateCache(const std::string& resource_id, + const std::string& md5, + GDataCache::CacheSubDirectoryType subdir, + int cache_state) = 0; + + // Removes entry corresponding to |resource_id| from cache map. + virtual void RemoveFromCache(const std::string& resource_id) = 0; + + // Returns the cache entry for file corresponding to |resource_id| and |md5| + // if entry exists in cache map. Otherwise, returns NULL. + // |md5| can be empty if only matching |resource_id| is desired, which may + // happen when looking for pinned entries where symlinks' filenames have no + // extension and hence no md5. + virtual scoped_ptr<GDataCache::CacheEntry> GetCacheEntry( + const std::string& resource_id, + const std::string& md5) = 0; + + // Removes temporary files (files in CACHE_TYPE_TMP) from the cache map. + virtual void RemoveTemporaryFiles() = 0; + + protected: + // Checks whether the current thread is on the right sequenced worker pool + // with the right sequence ID. If not, DCHECK will fail. + void AssertOnSequencedWorkerPool(); + + private: + base::SequencedWorkerPool* pool_; + const base::SequencedWorkerPool::SequenceToken& sequence_token_; + + DISALLOW_COPY_AND_ASSIGN(GDataCacheMetadata); +}; + +// GDataCacheMetadata implementation with std::map; +class GDataCacheMetadataMap : public GDataCacheMetadata { + public: + GDataCacheMetadataMap( + base::SequencedWorkerPool* pool, + const base::SequencedWorkerPool::SequenceToken& sequence_token); + virtual ~GDataCacheMetadataMap(); + + // Initializes the data. + void Initialize(const std::vector<FilePath>& cache_paths); + + // GDataCacheMetadata overrides: + virtual void UpdateCache(const std::string& resource_id, + const std::string& md5, + GDataCache::CacheSubDirectoryType subdir, + int cache_state) OVERRIDE; + virtual void RemoveFromCache(const std::string& resource_id) OVERRIDE; + virtual scoped_ptr<GDataCache::CacheEntry> GetCacheEntry( + const std::string& resource_id, + const std::string& md5) OVERRIDE; + virtual void RemoveTemporaryFiles() OVERRIDE; + + private: + friend class GDataCacheMetadataMapTest; + FRIEND_TEST_ALL_PREFIXES(GDataCacheMetadataMapTest, RemoveTemporaryFilesTest); + + // A map table of cache file's resource id to its CacheEntry* entry. + typedef std::map<std::string, GDataCache::CacheEntry> CacheMap; + + // Scans cache subdirectory and build or update |cache_map| + // with found file blobs or symlinks. + void ScanCacheDirectory(const std::vector<FilePath>& cache_paths, + GDataCache::CacheSubDirectoryType sub_dir_type, + CacheMap* cache_map); + + CacheMap cache_map_; + + DISALLOW_COPY_AND_ASSIGN(GDataCacheMetadataMap); +}; + +} // namespace gdata + +#endif // CHROME_BROWSER_CHROMEOS_GDATA_GDATA_CACHE_METADATA_H_ diff --git a/chrome/browser/chromeos/gdata/gdata_cache_metadata_unittest.cc b/chrome/browser/chromeos/gdata/gdata_cache_metadata_unittest.cc new file mode 100644 index 0000000..b4d112b --- /dev/null +++ b/chrome/browser/chromeos/gdata/gdata_cache_metadata_unittest.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace gdata { + +class GDataCacheMetadataMapTest : public testing::Test { + public: + GDataCacheMetadataMapTest() {} + + virtual void SetUp() OVERRIDE { + metadata_.reset(new GDataCacheMetadataMap( + NULL, base::SequencedWorkerPool::SequenceToken())); + std::vector<FilePath> empty_cache_paths; + metadata_->Initialize(empty_cache_paths); + } + + virtual void TearDown() OVERRIDE { + metadata_.reset(); + } + + protected: + // Helper function to insert an item with key |resource_id| into |cache_map|. + // |md5|, |sub_dir_type|, |cache_state| are used to create the value + // CacheEntry. + void InsertIntoMap(GDataCacheMetadataMap::CacheMap* cache_map, + const std::string& resource_id, + const std::string& md5, + GDataCache::CacheSubDirectoryType sub_dir_type, + int cache_state) { + cache_map->insert(std::make_pair( + resource_id, GDataCache::CacheEntry(md5, sub_dir_type, cache_state))); + } + + scoped_ptr<GDataCacheMetadataMap> metadata_; +}; + +// Test all the methods of GDataCacheMetadataMap except for +// RemoveTemporaryFiles. +TEST_F(GDataCacheMetadataMapTest, CacheTest) { + // Save an initial entry. + std::string test_resource_id("test_resource_id"); + std::string test_file_md5("test_file_md5"); + GDataCache::CacheSubDirectoryType test_sub_dir_type( + GDataCache::CACHE_TYPE_PERSISTENT); + int test_cache_state(GDataCache::CACHE_STATE_PRESENT); + metadata_->UpdateCache(test_resource_id, test_file_md5, + test_sub_dir_type, test_cache_state); + + // Test that the entry can be retrieved. + scoped_ptr<GDataCache::CacheEntry> cache_entry = + metadata_->GetCacheEntry(test_resource_id, test_file_md5); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); + EXPECT_EQ(test_cache_state, cache_entry->cache_state); + + // Empty md5 should also work. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, std::string()).Pass(); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + + // resource_id doesn't exist. + cache_entry = metadata_->GetCacheEntry("not_found_resource_id", + std::string()).Pass(); + EXPECT_FALSE(cache_entry.get()); + + // md5 doesn't match. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, "mismatch_md5").Pass(); + EXPECT_FALSE(cache_entry.get()); + + // Update all attributes. + test_file_md5 = "test_file_md5_2"; + test_sub_dir_type = GDataCache::CACHE_TYPE_PINNED; + test_cache_state = GDataCache::CACHE_STATE_PINNED; + metadata_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, + test_cache_state); + + // Make sure the values took. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, test_file_md5).Pass(); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); + EXPECT_EQ(test_cache_state, cache_entry->cache_state); + + // Empty m5 should work. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, std::string()).Pass(); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + + // Test dirty cache. + test_file_md5 = "test_file_md5_3"; + test_sub_dir_type = GDataCache::CACHE_TYPE_TMP; + test_cache_state = GDataCache::CACHE_STATE_DIRTY; + metadata_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, + test_cache_state); + + // Make sure the values took. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, test_file_md5).Pass(); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); + EXPECT_EQ(test_cache_state, cache_entry->cache_state); + + // Empty md5 should work. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, std::string()).Pass(); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + + // Mismatched md5 should also work for dirty entries. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, "mismatch_md5").Pass(); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + + // Remove the entry. + metadata_->RemoveFromCache(test_resource_id); + cache_entry = + metadata_->GetCacheEntry(test_resource_id, std::string()).Pass(); + EXPECT_FALSE(cache_entry.get()); + + // Add another one. + test_resource_id = "test_resource_id_2"; + test_file_md5 = "test_file_md5_4"; + test_sub_dir_type = GDataCache::CACHE_TYPE_TMP_DOWNLOADS; + test_cache_state = GDataCache::CACHE_STATE_PRESENT; + metadata_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, + test_cache_state); + + // Make sure the values took. + cache_entry = + metadata_->GetCacheEntry(test_resource_id, test_file_md5).Pass(); + ASSERT_TRUE(cache_entry.get()); + EXPECT_EQ(test_file_md5, cache_entry->md5); + EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); + EXPECT_EQ(test_cache_state, cache_entry->cache_state); + + // Update with CACHE_STATE_NONE should evict the entry. + test_file_md5 = "test_file_md5_5"; + test_sub_dir_type = GDataCache::CACHE_TYPE_TMP_DOCUMENTS; + test_cache_state = GDataCache::CACHE_STATE_NONE; + metadata_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, + test_cache_state); + + cache_entry = + metadata_->GetCacheEntry(test_resource_id, std::string()).Pass(); + EXPECT_FALSE(cache_entry.get()); +} + +// Test GDataCacheMetadataMap::RemoveTemporaryFiles. +TEST_F(GDataCacheMetadataMapTest, RemoveTemporaryFilesTest) { + GDataCacheMetadataMap::CacheMap cache_map; + InsertIntoMap(&cache_map, + "<resource_id_1>", + "<md5>", + GDataCache::CACHE_TYPE_TMP, + GDataCache::CACHE_STATE_PRESENT); + InsertIntoMap(&cache_map, + "<resource_id_2>", + "<md5>", + GDataCache::CACHE_TYPE_PINNED, + GDataCache::CACHE_STATE_PRESENT); + InsertIntoMap(&cache_map, + "<resource_id_3>", + "<md5>", + GDataCache::CACHE_TYPE_OUTGOING, + GDataCache::CACHE_STATE_PRESENT); + InsertIntoMap(&cache_map, + "<resource_id_4>", + "<md5>", + GDataCache::CACHE_TYPE_TMP, + GDataCache::CACHE_STATE_PRESENT); + + metadata_->cache_map_ = cache_map; + metadata_->RemoveTemporaryFiles(); + // resource 1 and 4 should be gone, as these are CACHE_TYPE_TMP. + EXPECT_FALSE(metadata_->GetCacheEntry("<resource_id_1>", "").get()); + EXPECT_TRUE(metadata_->GetCacheEntry("<resource_id_2>", "").get()); + EXPECT_TRUE(metadata_->GetCacheEntry("<resource_id_3>", "").get()); + EXPECT_FALSE(metadata_->GetCacheEntry("<resource_id_4>", "").get()); +} + +} // namespace gdata diff --git a/chrome/browser/chromeos/gdata/gdata_cache_unittest.cc b/chrome/browser/chromeos/gdata/gdata_cache_unittest.cc deleted file mode 100644 index 554e658..0000000 --- a/chrome/browser/chromeos/gdata/gdata_cache_unittest.cc +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/chromeos/gdata/gdata_cache.h" - -#include <string> -#include <utility> -#include <vector> - -#include "base/message_loop.h" -#include "base/memory/scoped_ptr.h" -#include "base/scoped_temp_dir.h" -#include "content/public/test/test_browser_thread.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace gdata { -namespace { - -const char kTestCacheRootPath[] = "/"; - -// Helper function to insert an item with key |resource_id| into |cache_map|. -// |md5|, |sub_dir_type|, |cache_state| are used to create the value CacheEntry. -void InsertIntoMap(GDataCache::CacheMap* cache_map, - const std::string& resource_id, const std::string& md5, - GDataCache::CacheSubDirectoryType sub_dir_type, int cache_state) { - cache_map->insert(std::make_pair(resource_id, - GDataCache::CacheEntry(md5, sub_dir_type, cache_state))); -} - -} // namespace - -class GDataCacheTest : public testing::Test { - public: - GDataCacheTest() - : ui_thread_(content::BrowserThread::UI, &message_loop_), - cache_(NULL) {} - - virtual void SetUp() OVERRIDE { - cache_ = GDataCache::CreateGDataCacheOnUIThread( - FilePath(kTestCacheRootPath), - NULL, - base::SequencedWorkerPool::SequenceToken()); - } - - virtual void TearDown() OVERRIDE { - delete cache_; - cache_ = NULL; - } - - protected: - MessageLoopForUI message_loop_; - content::TestBrowserThread ui_thread_; - GDataCache* cache_; -}; - -// Test all the api methods of GDataCache except for RemoveTemporaryFiles. -TEST_F(GDataCacheTest, CacheTest) { - // Save an initial entry. - std::string test_resource_id("test_resource_id"); - std::string test_file_md5("test_file_md5"); - GDataCache::CacheSubDirectoryType test_sub_dir_type( - GDataCache::CACHE_TYPE_PERSISTENT); - int test_cache_state(GDataCache::CACHE_STATE_PRESENT); - cache_->UpdateCache(test_resource_id, test_file_md5, - test_sub_dir_type, test_cache_state); - - // Test that the entry can be retrieved. - scoped_ptr<GDataCache::CacheEntry> cache_entry = - cache_->GetCacheEntry(test_resource_id, test_file_md5); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); - EXPECT_EQ(test_cache_state, cache_entry->cache_state); - - // Empty md5 should also work. - cache_entry = cache_->GetCacheEntry(test_resource_id, std::string()).Pass(); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - - // resource_id doesn't exist. - cache_entry = cache_->GetCacheEntry("not_found_resource_id", - std::string()).Pass(); - EXPECT_FALSE(cache_entry.get()); - - // md5 doesn't match. - cache_entry = cache_->GetCacheEntry(test_resource_id, "mismatch_md5").Pass(); - EXPECT_FALSE(cache_entry.get()); - - // Update all attributes. - test_file_md5 = "test_file_md5_2"; - test_sub_dir_type = GDataCache::CACHE_TYPE_PINNED; - test_cache_state = GDataCache::CACHE_STATE_PINNED; - cache_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, - test_cache_state); - - // Make sure the values took. - cache_entry = cache_->GetCacheEntry(test_resource_id, test_file_md5).Pass(); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); - EXPECT_EQ(test_cache_state, cache_entry->cache_state); - - // Empty m5 should work. - cache_entry = cache_->GetCacheEntry(test_resource_id, std::string()).Pass(); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - - // Test dirty cache. - test_file_md5 = "test_file_md5_3"; - test_sub_dir_type = GDataCache::CACHE_TYPE_TMP; - test_cache_state = GDataCache::CACHE_STATE_DIRTY; - cache_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, - test_cache_state); - - // Make sure the values took. - cache_entry = cache_->GetCacheEntry(test_resource_id, test_file_md5).Pass(); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); - EXPECT_EQ(test_cache_state, cache_entry->cache_state); - - // Empty md5 should work. - cache_entry = cache_->GetCacheEntry(test_resource_id, std::string()).Pass(); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - - // Mismatched md5 should also work for dirty entries. - cache_entry = cache_->GetCacheEntry(test_resource_id, "mismatch_md5").Pass(); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - - // Remove the entry. - cache_->RemoveFromCache(test_resource_id); - cache_entry = cache_->GetCacheEntry(test_resource_id, std::string()).Pass(); - EXPECT_FALSE(cache_entry.get()); - - // Add another one. - test_resource_id = "test_resource_id_2"; - test_file_md5 = "test_file_md5_4"; - test_sub_dir_type = GDataCache::CACHE_TYPE_TMP_DOWNLOADS; - test_cache_state = GDataCache::CACHE_STATE_PRESENT; - cache_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, - test_cache_state); - - // Make sure the values took. - cache_entry = cache_->GetCacheEntry(test_resource_id, test_file_md5).Pass(); - ASSERT_TRUE(cache_entry.get()); - EXPECT_EQ(test_file_md5, cache_entry->md5); - EXPECT_EQ(test_sub_dir_type, cache_entry->sub_dir_type); - EXPECT_EQ(test_cache_state, cache_entry->cache_state); - - // Update with CACHE_STATE_NONE should evict the entry. - test_file_md5 = "test_file_md5_5"; - test_sub_dir_type = GDataCache::CACHE_TYPE_TMP_DOCUMENTS; - test_cache_state = GDataCache::CACHE_STATE_NONE; - cache_->UpdateCache(test_resource_id, test_file_md5, test_sub_dir_type, - test_cache_state); - - cache_entry = cache_->GetCacheEntry(test_resource_id, std::string()).Pass(); - EXPECT_FALSE(cache_entry.get()); -} - -// Test GDataCache::RemoveTemporaryFiles. -TEST_F(GDataCacheTest, RemoveTemporaryFilesTest) { - GDataCache::CacheMap cache_map; - InsertIntoMap(&cache_map, - "<resource_id_1>", - "<md5>", - GDataCache::CACHE_TYPE_TMP, - GDataCache::CACHE_STATE_PRESENT); - InsertIntoMap(&cache_map, - "<resource_id_2>", - "<md5>", - GDataCache::CACHE_TYPE_PINNED, - GDataCache::CACHE_STATE_PRESENT); - InsertIntoMap(&cache_map, - "<resource_id_3>", - "<md5>", - GDataCache::CACHE_TYPE_OUTGOING, - GDataCache::CACHE_STATE_PRESENT); - InsertIntoMap(&cache_map, - "<resource_id_4>", - "<md5>", - GDataCache::CACHE_TYPE_TMP, - GDataCache::CACHE_STATE_PRESENT); - - cache_->SetCacheMap(cache_map); - cache_->RemoveTemporaryFiles(); - // resource 1 and 4 should be gone, as these are CACHE_TYPE_TMP. - EXPECT_FALSE(cache_->GetCacheEntry("<resource_id_1>", "").get()); - EXPECT_TRUE(cache_->GetCacheEntry("<resource_id_2>", "").get()); - EXPECT_TRUE(cache_->GetCacheEntry("<resource_id_3>", "").get()); - EXPECT_FALSE(cache_->GetCacheEntry("<resource_id_4>", "").get()); -} - -} // namespace gdata diff --git a/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc b/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc index a7085e2..40d0632 100644 --- a/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc +++ b/chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc @@ -513,7 +513,7 @@ class GDataFileSystemTest : public testing::Test { GDataCache::CACHE_TYPE_TMP, GDataCache::CACHED_FILE_FROM_SERVER); FilePath expected_path = - file_system_->cache_->cache_paths()[GDataCache::CACHE_TYPE_TMP]; + file_system_->cache_->GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP); expected_path = expected_path.Append(expected_filename); EXPECT_EQ(expected_path, actual_path); @@ -897,14 +897,17 @@ class GDataFileSystemTest : public testing::Test { DVLOG(1) << "PrepareForInitCacheTest start"; // Create gdata cache sub directories. ASSERT_TRUE(file_util::CreateDirectory( - file_system_->cache_->cache_paths()[ - GDataCache::CACHE_TYPE_PERSISTENT])); + file_system_->cache_->GetCacheDirectoryPath( + GDataCache::CACHE_TYPE_PERSISTENT))); ASSERT_TRUE(file_util::CreateDirectory( - file_system_->cache_->cache_paths()[GDataCache::CACHE_TYPE_TMP])); + file_system_->cache_->GetCacheDirectoryPath( + GDataCache::CACHE_TYPE_TMP))); ASSERT_TRUE(file_util::CreateDirectory( - file_system_->cache_->cache_paths()[GDataCache::CACHE_TYPE_PINNED])); + file_system_->cache_->GetCacheDirectoryPath( + GDataCache::CACHE_TYPE_PINNED))); ASSERT_TRUE(file_util::CreateDirectory( - file_system_->cache_->cache_paths()[GDataCache::CACHE_TYPE_OUTGOING])); + file_system_->cache_->GetCacheDirectoryPath( + GDataCache::CACHE_TYPE_OUTGOING))); // Dump some files into cache dirs so that // GDataFileSystem::InitializeCacheOnBlockingPool would scan through them diff --git a/chrome/browser/chromeos/gdata/gdata_util.cc b/chrome/browser/chromeos/gdata/gdata_util.cc index 14be552..fe24e5d 100644 --- a/chrome/browser/chromeos/gdata/gdata_util.cc +++ b/chrome/browser/chromeos/gdata/gdata_util.cc @@ -173,6 +173,9 @@ void OnGetFileInfoForInsertGDataCachePathsPermissions( } // namespace +const char kMountedArchiveFileExtension[] = "mounted"; +const char kWildCard[] = "*"; + const FilePath& GetGDataMountPointPath() { CR_DEFINE_STATIC_LOCAL(FilePath, gdata_mount_path, (FilePath::FromUTF8Unsafe(kGDataMountPointPath))); diff --git a/chrome/browser/chromeos/gdata/gdata_util.h b/chrome/browser/chromeos/gdata/gdata_util.h index 7da949e..7ce9393 100644 --- a/chrome/browser/chromeos/gdata/gdata_util.h +++ b/chrome/browser/chromeos/gdata/gdata_util.h @@ -36,6 +36,10 @@ enum GDataSearchPathType { GDATA_SEARCH_PATH_RESULT_CHILD }; +// Path constants +extern const char kMountedArchiveFileExtension[]; +extern const char kWildCard[]; + // Returns the GData mount point path, which looks like "/special/gdata". const FilePath& GetGDataMountPointPath(); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 0d7a22e..4a5d7fe 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -524,6 +524,8 @@ 'browser/chromeos/gdata/gdata_auth_service.h', 'browser/chromeos/gdata/gdata_cache.cc', 'browser/chromeos/gdata/gdata_cache.h', + 'browser/chromeos/gdata/gdata_cache_metadata.cc', + 'browser/chromeos/gdata/gdata_cache_metadata.h', 'browser/chromeos/gdata/gdata_db.h', 'browser/chromeos/gdata/gdata_db_factory.cc', 'browser/chromeos/gdata/gdata_db_factory.h', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index bb12bf5..1034949 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1079,7 +1079,7 @@ 'browser/chromeos/extensions/file_browser_notifications_unittest.cc', 'browser/chromeos/external_metrics_unittest.cc', 'browser/chromeos/gdata/drive_webapps_registry_unittest.cc', - 'browser/chromeos/gdata/gdata_cache_unittest.cc', + 'browser/chromeos/gdata/gdata_cache_metadata_unittest.cc', 'browser/chromeos/gdata/gdata_db_unittest.cc', 'browser/chromeos/gdata/gdata_file_system_unittest.cc', 'browser/chromeos/gdata/gdata_files_unittest.cc', |