summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/drive/file_cache.cc25
-rw-r--r--components/drive/file_cache.h8
-rw-r--r--components/drive/file_cache_unittest.cc107
3 files changed, 132 insertions, 8 deletions
diff --git a/components/drive/file_cache.cc b/components/drive/file_cache.cc
index 73d28d4..c9ceb10 100644
--- a/components/drive/file_cache.cc
+++ b/components/drive/file_cache.cc
@@ -4,6 +4,8 @@
#include "components/drive/file_cache.h"
+#include <unistd.h>
+
#include <queue>
#include <vector>
@@ -681,7 +683,8 @@ bool FileCache::RenameCacheFilesToNewFormat() {
// static
bool FileCache::MigrateCacheFiles(const base::FilePath& from,
- const base::FilePath& to,
+ const base::FilePath& to_files,
+ const base::FilePath& to_links,
ResourceMetadataStorage* metadata_storage) {
scoped_ptr<ResourceMetadataStorage::Iterator> it =
metadata_storage->GetIterator();
@@ -691,17 +694,31 @@ bool FileCache::MigrateCacheFiles(const base::FilePath& from,
continue;
}
+ // Ignore missing cache file case since it never succeeds.
+ // TODO(yawano): handle this case at metadata validation in FileCache.
const base::FilePath move_from = GetPathForId(from, entry.local_id());
if (!base::PathExists(move_from)) {
continue;
}
- const base::FilePath move_to = GetPathForId(to, entry.local_id());
+ // Create hard link to cache file if it's pinned or dirty. cryptohome will
+ // not delete a cache file if there is a hard link to it.
+ const FileCacheEntry& file_cache_entry =
+ entry.file_specific_info().cache_state();
+ if (file_cache_entry.is_pinned() || file_cache_entry.is_dirty()) {
+ const base::FilePath link_path = GetPathForId(to_links, entry.local_id());
+ int link_result = link(move_from.AsUTF8Unsafe().c_str(),
+ link_path.AsUTF8Unsafe().c_str());
+ if (link_result != 0 && errno != EEXIST) {
+ return false;
+ }
+ }
+
+ // Move cache file.
+ const base::FilePath move_to = GetPathForId(to_files, entry.local_id());
if (!base::Move(move_from, move_to)) {
return false;
}
-
- // TODO(yawano): create hard link if entry is marked as pinned or dirty.
}
return true;
diff --git a/components/drive/file_cache.h b/components/drive/file_cache.h
index c637566..2dad5da 100644
--- a/components/drive/file_cache.h
+++ b/components/drive/file_cache.h
@@ -157,10 +157,12 @@ class FileCache {
const ResourceMetadataStorage::RecoveredCacheInfoMap&
recovered_cache_info);
- // Migrates cache files from |from| directory to |to| directory. Returns true
- // for success.
+ // Migrates cache files from |from| directory to |to_files| directory with
+ // creating links in |to_links| directory if necessary. Returns true for
+ // success.
static bool MigrateCacheFiles(const base::FilePath& from,
- const base::FilePath& to,
+ const base::FilePath& to_files,
+ const base::FilePath& to_links,
ResourceMetadataStorage* metadata_storage);
private:
diff --git a/components/drive/file_cache_unittest.cc b/components/drive/file_cache_unittest.cc
index 93e7824..fd273c7 100644
--- a/components/drive/file_cache_unittest.cc
+++ b/components/drive/file_cache_unittest.cc
@@ -6,6 +6,9 @@
#include <stddef.h>
#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <string>
#include <vector>
@@ -37,9 +40,18 @@ const base::FilePath::CharType kCacheFileDirectory[] =
FILE_PATH_LITERAL("files");
const base::FilePath::CharType kNewCacheFileDirectory[] =
FILE_PATH_LITERAL("blobs");
+const base::FilePath::CharType kLinkDirectory[] = FILE_PATH_LITERAL("links");
const int kTemporaryFileSizeInBytes = 10;
+int GetNumberOfLinks(const base::FilePath& file_path) {
+ struct stat result;
+ if (stat(file_path.AsUTF8Unsafe().c_str(), &result) != 0) {
+ return -1;
+ }
+ return result.st_nlink;
+}
+
} // namespace
// Tests FileCache methods working with the blocking task runner.
@@ -709,8 +721,10 @@ TEST_F(FileCacheTest, MigrateCacheFiles) {
temp_dir_.path().Append(kCacheFileDirectory);
const base::FilePath new_cache_dir =
temp_dir_.path().Append(kNewCacheFileDirectory);
+ const base::FilePath link_dir = temp_dir_.path().Append(kLinkDirectory);
ASSERT_TRUE(base::CreateDirectory(old_cache_dir));
ASSERT_TRUE(base::CreateDirectory(new_cache_dir));
+ ASSERT_TRUE(base::CreateDirectory(link_dir));
// Entry A: cache file in old cache directory with metadata.
const std::string id_a = "id_a";
@@ -746,16 +760,107 @@ TEST_F(FileCacheTest, MigrateCacheFiles) {
true);
ASSERT_EQ(FILE_ERROR_OK, metadata_storage_->PutEntry(entry_d));
+ // Entry E: pinned cache file.
+ const std::string id_e = "id_e";
+ ResourceEntry entry_e;
+ entry_e.set_local_id(id_e);
+ FileCacheEntry* file_cache_entry_e =
+ entry_e.mutable_file_specific_info()->mutable_cache_state();
+ file_cache_entry_e->set_is_present(true);
+ file_cache_entry_e->set_is_pinned(true);
+ file_cache_entry_e->set_is_dirty(false);
+ ASSERT_EQ(FILE_ERROR_OK, metadata_storage_->PutEntry(entry_e));
+ const base::FilePath old_file_path_e = old_cache_dir.AppendASCII(id_e);
+ const base::FilePath new_file_path_e = new_cache_dir.AppendASCII(id_e);
+ const base::FilePath link_path_e = link_dir.AppendASCII(id_e);
+ ASSERT_TRUE(base::CopyFile(temp_file, old_file_path_e));
+
+ // Entry F: dirty cache file.
+ const std::string id_f = "id_f";
+ ResourceEntry entry_f;
+ entry_f.set_local_id(id_f);
+ FileCacheEntry* file_cache_entry_f =
+ entry_f.mutable_file_specific_info()->mutable_cache_state();
+ file_cache_entry_f->set_is_present(true);
+ file_cache_entry_f->set_is_pinned(false);
+ file_cache_entry_f->set_is_dirty(true);
+ ASSERT_EQ(FILE_ERROR_OK, metadata_storage_->PutEntry(entry_f));
+ const base::FilePath old_file_path_f = old_cache_dir.AppendASCII(id_f);
+ const base::FilePath new_file_path_f = new_cache_dir.AppendASCII(id_f);
+ const base::FilePath link_path_f = link_dir.AppendASCII(id_f);
+ ASSERT_TRUE(base::CopyFile(temp_file, old_file_path_f));
+
+ // Entry G: partially migrated pinned cache file.
+ const std::string id_g = "id_g";
+ ResourceEntry entry_g;
+ entry_g.set_local_id(id_g);
+ FileCacheEntry* file_cache_entry_g =
+ entry_g.mutable_file_specific_info()->mutable_cache_state();
+ file_cache_entry_g->set_is_present(true);
+ file_cache_entry_g->set_is_pinned(true);
+ file_cache_entry_g->set_is_dirty(false);
+ ASSERT_EQ(FILE_ERROR_OK, metadata_storage_->PutEntry(entry_g));
+ const base::FilePath old_file_path_g = old_cache_dir.AppendASCII(id_g);
+ const base::FilePath new_file_path_g = new_cache_dir.AppendASCII(id_g);
+ const base::FilePath link_path_g = link_dir.AppendASCII(id_g);
+ ASSERT_TRUE(base::CopyFile(temp_file, old_file_path_g));
+ ASSERT_EQ(0, link(old_file_path_g.AsUTF8Unsafe().c_str(),
+ link_path_g.AsUTF8Unsafe().c_str()));
+
+ // Entry H: pinned entry without cache file.
+ const std::string id_h = "id_h";
+ ResourceEntry entry_h;
+ entry_h.set_local_id(id_h);
+ FileCacheEntry* file_cache_entry_h =
+ entry_h.mutable_file_specific_info()->mutable_cache_state();
+ file_cache_entry_h->set_is_present(true);
+ file_cache_entry_h->set_is_pinned(true);
+ file_cache_entry_h->set_is_dirty(false);
+ ASSERT_EQ(FILE_ERROR_OK, metadata_storage_->PutEntry(entry_h));
+
+ // Entry I: already migrated pinned cache file.
+ const std::string id_i = "id_i";
+ ResourceEntry entry_i;
+ entry_i.set_local_id(id_i);
+ FileCacheEntry* file_cache_entry_i =
+ entry_i.mutable_file_specific_info()->mutable_cache_state();
+ file_cache_entry_i->set_is_present(true);
+ file_cache_entry_i->set_is_pinned(true);
+ file_cache_entry_i->set_is_dirty(false);
+ ASSERT_EQ(FILE_ERROR_OK, metadata_storage_->PutEntry(entry_i));
+ const base::FilePath new_file_path_i = new_cache_dir.AppendASCII(id_i);
+ const base::FilePath link_path_i = link_dir.AppendASCII(id_i);
+ ASSERT_TRUE(base::CopyFile(temp_file, new_file_path_i));
+ ASSERT_EQ(0, link(new_file_path_i.AsUTF8Unsafe().c_str(),
+ link_path_i.AsUTF8Unsafe().c_str()));
+
// Run migration.
ASSERT_TRUE(FileCache::MigrateCacheFiles(old_cache_dir, new_cache_dir,
- metadata_storage_.get()));
+ link_dir, metadata_storage_.get()));
// Check result.
EXPECT_FALSE(base::PathExists(old_file_path_a));
EXPECT_TRUE(base::PathExists(new_file_path_a));
+ EXPECT_EQ(1, GetNumberOfLinks(new_file_path_a));
// MigrateCacheFiles doesn't delete invalid cache file.
EXPECT_TRUE(base::PathExists(old_file_path_b));
EXPECT_TRUE(base::PathExists(new_file_path_c));
+ EXPECT_EQ(1, GetNumberOfLinks(new_file_path_c));
+ EXPECT_FALSE(base::PathExists(old_file_path_e));
+ EXPECT_TRUE(base::PathExists(new_file_path_e));
+ EXPECT_TRUE(base::PathExists(link_path_e));
+ EXPECT_EQ(2, GetNumberOfLinks(new_file_path_e));
+ EXPECT_FALSE(base::PathExists(old_file_path_f));
+ EXPECT_TRUE(base::PathExists(new_file_path_f));
+ EXPECT_TRUE(base::PathExists(link_path_f));
+ EXPECT_EQ(2, GetNumberOfLinks(new_file_path_f));
+ EXPECT_FALSE(base::PathExists(old_file_path_g));
+ EXPECT_TRUE(base::PathExists(new_file_path_g));
+ EXPECT_TRUE(base::PathExists(link_path_g));
+ EXPECT_EQ(2, GetNumberOfLinks(new_file_path_g));
+ EXPECT_TRUE(base::PathExists(new_file_path_i));
+ EXPECT_TRUE(base::PathExists(link_path_i));
+ EXPECT_EQ(2, GetNumberOfLinks(new_file_path_i));
}
TEST_F(FileCacheTest, ClearAll) {