summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-18 12:54:59 +0000
committerhashimoto@chromium.org <hashimoto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-18 12:54:59 +0000
commitca5f6da423ead7dd1b799ad04a5d5faa39d5076a (patch)
tree4e23dfccc1457d0e2438a8a9589a4a5972f1898c
parente9f4d35f34e3d53a1faf7a115393c10719e59967 (diff)
downloadchromium_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.cc312
-rw-r--r--chrome/browser/chromeos/gdata/gdata_cache.h40
-rw-r--r--chrome/browser/chromeos/gdata/gdata_cache_metadata.cc266
-rw-r--r--chrome/browser/chromeos/gdata/gdata_cache_metadata.h107
-rw-r--r--chrome/browser/chromeos/gdata/gdata_cache_metadata_unittest.cc193
-rw-r--r--chrome/browser/chromeos/gdata/gdata_cache_unittest.cc197
-rw-r--r--chrome/browser/chromeos/gdata/gdata_file_system_unittest.cc15
-rw-r--r--chrome/browser/chromeos/gdata/gdata_util.cc3
-rw-r--r--chrome/browser/chromeos/gdata/gdata_util.h4
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi2
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',