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
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 @@
 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 @@
   }
 }
 
-// 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 @@
   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 @@
   // 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 @@
       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 @@
     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 @@
                  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 @@
     // 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 @@
 
   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 @@
 
   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 @@
   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 @@
   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 @@
                             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 @@
   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 @@
   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 @@
   // 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 @@
   }
 
   // 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..20328552 100644
--- a/chrome/browser/chromeos/gdata/gdata_cache.h
+++ b/chrome/browser/chromeos/gdata/gdata_cache.h
@@ -20,6 +20,8 @@
 
 namespace gdata {
 
+class GDataCacheMetadata;
+
 // Callback for SetMountedStateOnUIThread.
 typedef base::Callback<void(base::PlatformFileError error,
                             const FilePath& file_path)> SetMountedStateCallback;
@@ -168,9 +170,6 @@
     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 @@
   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 @@
   // 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 @@
   // 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 @@
   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 @@
         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 @@
     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 @@
 
 }  // 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 @@
   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();