Commit e5a99eb6 authored by Sergei Datsenko's avatar Sergei Datsenko Committed by Commit Bot

Mark removable gcache files with file attributes

ext4 encryption on ChromeOS doesn't allow access to inode attributes for files
in encrypted dir, so we need to indicate files that are ok to remove on low disk
space with extended attributes too.

BUG=chromium:879003

Change-Id: Ibf917ac255c93ea4e971b2a622b51e452257a4c7
Reviewed-on: https://chromium-review.googlesource.com/1196288Reviewed-by: default avatarStuart Langley <slangley@chromium.org>
Reviewed-by: default avatarSam McNally <sammc@chromium.org>
Commit-Queue: Sergei Datsenko <dats@chromium.org>
Cr-Commit-Position: refs/heads/master@{#587958}
parent 402a6278
......@@ -66,8 +66,22 @@ bool IsFileAttributesSupported(const base::FilePath& path) {
// Sets extended file attribute as |name| |value| pair.
bool SetExtendedFileAttributes(const base::FilePath& path,
const std::string& name, const std::string& value) {
return setxattr(path.value().c_str(), name.c_str(), value.c_str(),
value.size() + 1, 0) == 0;
if (setxattr(path.value().c_str(), name.c_str(), value.c_str(),
value.size() + 1, 0) != 0) {
PLOG(ERROR) << "setxattr: " << path.value();
return false;
}
return true;
}
// Remove extended file attribute with |name|.
bool UnsetExtendedFileAttributes(const base::FilePath& path,
const std::string& name) {
if (removexattr(path.value().c_str(), name.c_str()) != 0) {
PLOG(ERROR) << "removexattr: " << path.value();
return false;
}
return true;
}
// Changes attributes of the file with |flags|, e.g. FS_NODUMP_FL (cryptohome
......@@ -112,10 +126,10 @@ bool SetRemovable(const base::FilePath& path) {
return true;
}
FileAttributes flags = GetFileAttributes(path);
if (flags < 0) return false;
if ((flags & FS_NODUMP_FL) == FS_NODUMP_FL) return true;
return SetFileAttributes(path, flags | FS_NODUMP_FL);
bool xattr = flags >= 0 && SetFileAttributes(path, flags | FS_NODUMP_FL);
bool fattr = SetExtendedFileAttributes(
path, FileCache::kGCacheRemovableAttribute, "1");
return xattr || fattr;
}
// Marks the cache file to be unremovable by cryptohome, or do nothing if
......@@ -127,10 +141,10 @@ bool UnsetRemovable(const base::FilePath& path) {
return true;
}
FileAttributes flags = GetFileAttributes(path);
if (flags < 0) return false;
if ((flags & FS_NODUMP_FL) == 0) return true;
return SetFileAttributes(path, flags & ~FS_NODUMP_FL);
bool xattr = flags >= 0 && SetFileAttributes(path, flags & ~FS_NODUMP_FL);
bool fattr =
UnsetExtendedFileAttributes(path, FileCache::kGCacheRemovableAttribute);
return xattr || fattr;
}
// Marks |path| as drive cache dir, or do nothing if underlying filesystem
......@@ -165,6 +179,7 @@ const size_t kMaxNumOfEvictedCacheFiles = 30000;
// static
const char FileCache::kGCacheFilesAttribute[] = "user.GCacheFiles";
const char FileCache::kGCacheRemovableAttribute[] = "user.GCacheRemovable";
FileCache::FileCache(ResourceMetadataStorage* storage,
const base::FilePath& cache_file_directory,
......
......@@ -55,6 +55,8 @@ class FileCache {
public:
// The file extended attribute assigned to Drive cache directory.
static const char kGCacheFilesAttribute[];
// The file extended attribute assigned to files that can be removed.
static const char kGCacheRemovableAttribute[];
// Enum defining type of file operation e.g. copy or move, etc.
enum FileOperationType {
......
......@@ -65,6 +65,15 @@ bool HasRemovableFlag(const base::FilePath& file_path) {
return (GetFileAttributes(file_path) & FS_NODUMP_FL) == FS_NODUMP_FL;
}
bool HasRemovableAttribute(const base::FilePath& file_path) {
return getxattr(file_path.value().c_str(),
FileCache::kGCacheRemovableAttribute, nullptr, 0) >= 0;
}
bool IsMarkedAsRemovable(const base::FilePath& path) {
return HasRemovableFlag(path) && HasRemovableAttribute(path);
}
} // namespace
// Tests FileCache methods working with the blocking task runner.
......@@ -440,7 +449,7 @@ TEST_F(FileCacheTest, Store) {
EXPECT_TRUE(base::ContentsEqual(src_file_path, cache_file_path));
base::FilePath dest_file_path = GetCacheFilePath(id);
EXPECT_TRUE(HasRemovableFlag((dest_file_path)));
EXPECT_TRUE(IsMarkedAsRemovable(dest_file_path));
// Store a non-existent file.
EXPECT_EQ(FILE_ERROR_FAILED, cache_->Store(
......@@ -455,7 +464,7 @@ TEST_F(FileCacheTest, Store) {
EXPECT_TRUE(entry.file_specific_info().cache_state().is_present());
EXPECT_TRUE(entry.file_specific_info().cache_state().md5().empty());
EXPECT_TRUE(entry.file_specific_info().cache_state().is_dirty());
EXPECT_FALSE(HasRemovableFlag((dest_file_path)));
EXPECT_FALSE(IsMarkedAsRemovable(dest_file_path));
// No free space available.
fake_free_disk_space_getter_->set_default_value(0);
......@@ -482,21 +491,21 @@ TEST_F(FileCacheTest, PinAndUnpin) {
const base::FilePath dest_file_path = GetCacheFilePath(id);
EXPECT_EQ(FILE_ERROR_OK, metadata_storage_->GetEntry(id, &entry));
EXPECT_FALSE(entry.file_specific_info().cache_state().is_pinned());
EXPECT_TRUE(HasRemovableFlag((dest_file_path)));
EXPECT_TRUE(IsMarkedAsRemovable(dest_file_path));
// Pin the existing file.
EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(id));
EXPECT_EQ(FILE_ERROR_OK, metadata_storage_->GetEntry(id, &entry));
EXPECT_TRUE(entry.file_specific_info().cache_state().is_pinned());
EXPECT_FALSE(HasRemovableFlag((dest_file_path)));
EXPECT_FALSE(IsMarkedAsRemovable(dest_file_path));
// Unpin the file.
EXPECT_EQ(FILE_ERROR_OK, cache_->Unpin(id));
EXPECT_EQ(FILE_ERROR_OK, metadata_storage_->GetEntry(id, &entry));
EXPECT_FALSE(entry.file_specific_info().cache_state().is_pinned());
EXPECT_TRUE(HasRemovableFlag((dest_file_path)));
EXPECT_TRUE(IsMarkedAsRemovable(dest_file_path));
// Pin a non-present file.
std::string id_non_present = "id_non_present";
......@@ -571,7 +580,7 @@ TEST_F(FileCacheTest, OpenForWrite) {
EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
const base::FilePath dest_file = GetCacheFilePath(id);
EXPECT_TRUE(HasRemovableFlag((dest_file)));
EXPECT_TRUE(IsMarkedAsRemovable(dest_file));
// Open (1).
std::unique_ptr<base::ScopedClosureRunner> file_closer1;
......@@ -581,7 +590,7 @@ TEST_F(FileCacheTest, OpenForWrite) {
// Entry is dirty.
EXPECT_EQ(FILE_ERROR_OK, metadata_storage_->GetEntry(id, &entry));
EXPECT_TRUE(entry.file_specific_info().cache_state().is_dirty());
EXPECT_FALSE(HasRemovableFlag((dest_file)));
EXPECT_FALSE(IsMarkedAsRemovable((dest_file)));
// Open (2).
std::unique_ptr<base::ScopedClosureRunner> file_closer2;
......@@ -663,7 +672,7 @@ TEST_F(FileCacheTest, ClearDirty) {
FileCache::FILE_OPERATION_COPY));
const base::FilePath dest_file = GetCacheFilePath(id);
EXPECT_TRUE(HasRemovableFlag((dest_file)));
EXPECT_TRUE(IsMarkedAsRemovable(dest_file));
// Open the file.
std::unique_ptr<base::ScopedClosureRunner> file_closer;
......@@ -672,11 +681,11 @@ TEST_F(FileCacheTest, ClearDirty) {
// Entry is dirty.
EXPECT_EQ(FILE_ERROR_OK, metadata_storage_->GetEntry(id, &entry));
EXPECT_TRUE(entry.file_specific_info().cache_state().is_dirty());
EXPECT_FALSE(HasRemovableFlag((dest_file)));
EXPECT_FALSE(IsMarkedAsRemovable(dest_file));
// Cannot clear the dirty bit of an opened entry.
EXPECT_EQ(FILE_ERROR_IN_USE, cache_->ClearDirty(id));
EXPECT_FALSE(HasRemovableFlag((dest_file)));
EXPECT_FALSE(IsMarkedAsRemovable(dest_file));
// Close the file and clear the dirty bit.
file_closer.reset();
......@@ -686,7 +695,7 @@ TEST_F(FileCacheTest, ClearDirty) {
// Entry is not dirty.
EXPECT_EQ(FILE_ERROR_OK, metadata_storage_->GetEntry(id, &entry));
EXPECT_FALSE(entry.file_specific_info().cache_state().is_dirty());
EXPECT_TRUE(HasRemovableFlag((dest_file)));
EXPECT_TRUE(IsMarkedAsRemovable(dest_file));
}
TEST_F(FileCacheTest, Remove) {
......@@ -852,10 +861,10 @@ TEST_F(FileCacheTest, FixMetadataAndFileAttributes) {
ASSERT_TRUE(cache_->Initialize());
// Check result.
EXPECT_FALSE(HasRemovableFlag(file_path_a));
EXPECT_FALSE(HasRemovableFlag((file_path_b)));
EXPECT_TRUE(HasRemovableFlag((file_path_c)));
EXPECT_FALSE(HasRemovableFlag((file_path_d)));
EXPECT_FALSE(IsMarkedAsRemovable(file_path_a));
EXPECT_FALSE(IsMarkedAsRemovable(file_path_b));
EXPECT_TRUE(IsMarkedAsRemovable(file_path_c));
EXPECT_FALSE(IsMarkedAsRemovable(file_path_d));
EXPECT_FALSE(base::PathExists(file_path_f));
EXPECT_EQ(FILE_ERROR_OK, metadata_storage_->GetEntry(id_e, &entry_e));
......@@ -864,7 +873,7 @@ TEST_F(FileCacheTest, FixMetadataAndFileAttributes) {
EXPECT_FALSE(entry_f.file_specific_info().cache_state().is_present());
// Check the cache dir has appropriate attributes.
EXPECT_TRUE(HasRemovableFlag((cache_files_dir_)));
EXPECT_TRUE(HasRemovableFlag(cache_files_dir_));
EXPECT_GE(getxattr(cache_files_dir_.value().c_str(),
FileCache::kGCacheFilesAttribute, nullptr, 0), 0);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment