[FilesTrash] Set extended attribute for low-disk cleanup
In a low-disk space scenario the Trash subdirectories represent a unit
of deletion that should be prioritised over the remainder of the user's
profile. To enable this, set an xattr for the user.TrackedDirectoryName
of both "trash_files" and "trash_info" to represent these locations. The
subdirectories have been chosen as DriveFS exposes their trash
implementation via FUSE and explicitly disable the ability to delete the
trash folder, so target the subdirectories instead.
NOTE: In unit tests the temp directories are created on /tmp which is
backed by a tmpfs filesystem. Unfortunately (without some kernel flags
being flipped) xattr are not settable on this filesystem. So don't set
them for unittests, this functionality will be tested via tast instead
once chromium has rolled into chromeos.
Bug: b:243879434
Test: unit_tests --gtest_filter=TrashIOTaskTest*
Test: deploy chrome then getfattr -d -m ".*" ~/MyFiles/.Trash
Change-Id: Ie591dd073f4475e284b5b7b7fd17d37a77126faa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3881625
Reviewed-by: Luciano Pacheco <lucmult@chromium.org>
Commit-Queue: Ben Reich <benreich@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1044939}
diff --git a/chrome/browser/ash/file_manager/trash_common_util.cc b/chrome/browser/ash/file_manager/trash_common_util.cc
index 8d3d342..353713e0 100644
--- a/chrome/browser/ash/file_manager/trash_common_util.cc
+++ b/chrome/browser/ash/file_manager/trash_common_util.cc
@@ -15,6 +15,7 @@
constexpr char kInfoFolderName[] = "info";
constexpr char kFilesFolderName[] = "files";
constexpr char kTrashInfoExtension[] = ".trashinfo";
+constexpr char kTrackedDirectoryName[] = "user.TrackedDirectoryName";
TrashLocation::TrashLocation(const base::FilePath supplied_relative_folder_path,
const base::FilePath supplied_mount_point_path,
diff --git a/chrome/browser/ash/file_manager/trash_common_util.h b/chrome/browser/ash/file_manager/trash_common_util.h
index 31570c6..bc50ad8 100644
--- a/chrome/browser/ash/file_manager/trash_common_util.h
+++ b/chrome/browser/ash/file_manager/trash_common_util.h
@@ -26,6 +26,10 @@
// Constant representing the ".trashinfo" extension for metadata files.
extern const char kTrashInfoExtension[];
+// Constant representing the extended attribute "user.TrackedDirectoryName" used
+// to track the files and info directories for deletion by cryptohome.
+extern const char kTrackedDirectoryName[];
+
struct TrashLocation {
TrashLocation(const base::FilePath supplied_relative_folder_path,
const base::FilePath supplied_mount_point_path,
diff --git a/chrome/browser/ash/file_manager/trash_io_task.cc b/chrome/browser/ash/file_manager/trash_io_task.cc
index b838b7f..d2f342d 100644
--- a/chrome/browser/ash/file_manager/trash_io_task.cc
+++ b/chrome/browser/ash/file_manager/trash_io_task.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/ash/file_manager/trash_io_task.h"
+#include <sys/xattr.h>
+
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/strings/strcat.h"
@@ -81,6 +83,16 @@
base::FILE_PERMISSION_EXECUTE_BY_OTHERS);
}
+base::File::Error SetTrackedExtendedAttribute(const base::FilePath& path) {
+ auto tracked_name = base::StrCat({"trash_", path.BaseName().value()});
+ if (lsetxattr(path.value().c_str(), trash::kTrackedDirectoryName,
+ tracked_name.c_str(), tracked_name.size(), 0) < 0) {
+ PLOG(ERROR) << "Failed to set the xattr";
+ return base::File::FILE_ERROR_FAILED;
+ }
+ return base::File::FILE_OK;
+}
+
TrashEntry::TrashEntry() : deletion_time(base::Time::Now()) {}
TrashEntry::~TrashEntry() = default;
@@ -333,20 +345,42 @@
return;
}
+ auto on_setup_complete_callback = base::BindOnce(
+ &TrashIOTask::OnSetupSubDirectory, weak_ptr_factory_.GetWeakPtr(),
+ base::OwnedRef(it), trash_subdirectory);
+
content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&StartCreateDirectoryOnIOThread, file_system_context_,
trash_subdirectory,
base::BindPostTask(
base::SequencedTaskRunnerHandle::Get(),
- base::BindOnce(&TrashIOTask::OnSetupSubDirectory,
+ base::BindOnce(&TrashIOTask::SetDirectoryTracking,
weak_ptr_factory_.GetWeakPtr(),
- base::OwnedRef(it), trash_subdirectory),
+ std::move(on_setup_complete_callback),
+ MakeRelativePathAbsoluteFromBasePath(
+ trash_subdirectory.path())),
FROM_HERE)),
base::BindOnce(&TrashIOTask::SetCurrentOperationID,
weak_ptr_factory_.GetWeakPtr()));
}
+void TrashIOTask::SetDirectoryTracking(
+ base::OnceCallback<void(base::File::Error)> on_setup_complete_callback,
+ const base::FilePath& trash_subdirectory,
+ base::File::Error error) {
+ if (error != base::File::FILE_OK) {
+ std::move(on_setup_complete_callback).Run(error);
+ return;
+ }
+
+ base::ThreadPool::PostTaskAndReplyWithResult(
+ FROM_HERE, {base::MayBlock()},
+ base::BindOnce(&SetTrackedExtendedAttribute,
+ std::move(trash_subdirectory)),
+ std::move(on_setup_complete_callback));
+}
+
void TrashIOTask::OnSetupSubDirectory(
trash::TrashPathsMap::const_iterator& it,
const storage::FileSystemURL trash_subdirectory,
diff --git a/chrome/browser/ash/file_manager/trash_io_task.h b/chrome/browser/ash/file_manager/trash_io_task.h
index 37d24d0..30ac324 100644
--- a/chrome/browser/ash/file_manager/trash_io_task.h
+++ b/chrome/browser/ash/file_manager/trash_io_task.h
@@ -114,6 +114,14 @@
// in the instance .Trash folder does not exist.
void SetupSubDirectory(trash::TrashPathsMap::const_iterator& it,
const storage::FileSystemURL trash_subdirectory);
+
+ // During low-disk space situations, cryptohome needs a way to identify
+ // folders to purge. Trash should be considered prior to the rest of the
+ // users' profile.
+ void SetDirectoryTracking(
+ base::OnceCallback<void(base::File::Error)> on_setup_complete_callback,
+ const base::FilePath& trash_subdirectory,
+ base::File::Error error);
void OnSetupSubDirectory(trash::TrashPathsMap::const_iterator& it,
const storage::FileSystemURL trash_subdirectory,
base::File::Error error);
diff --git a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
index 4df9836..1f2e11f 100644
--- a/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
+++ b/chrome/browser/ash/file_manager/trash_io_task_unittest.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/ash/file_manager/trash_io_task.h"
+#include <sys/xattr.h>
+
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/rand_util.h"
@@ -59,6 +61,19 @@
return testing::ExplainMatchResult(matcher, errors, result_listener);
}
+std::string GetTrackedExtendedAttributeAsString(const base::FilePath& path) {
+ ssize_t output_size =
+ lgetxattr(path.value().c_str(), trash::kTrackedDirectoryName, nullptr, 0);
+ EXPECT_GT(output_size, 0);
+ std::vector<char> output_value(output_size);
+ EXPECT_GT(lgetxattr(path.value().c_str(), trash::kTrackedDirectoryName,
+ output_value.data(), output_size),
+ 0);
+ std::string xattr;
+ xattr.assign(output_value.data(), output_size);
+ return xattr;
+}
+
class TrashIOTaskTest : public TrashBaseTest {
public:
TrashIOTaskTest() = default;
@@ -70,13 +85,24 @@
void AssertTrashSetup(const base::FilePath& parent_path) {
base::FilePath trash_path = parent_path.Append(trash::kTrashFolderName);
ASSERT_TRUE(base::DirectoryExists(trash_path));
- ASSERT_TRUE(
- base::DirectoryExists(trash_path.Append(trash::kFilesFolderName)));
- ASSERT_TRUE(base::DirectoryExists(trash_path.Append(trash::kInfoFolderName)));
+
+ auto files_path = trash_path.Append(trash::kFilesFolderName);
+ ASSERT_TRUE(base::DirectoryExists(files_path));
+
+ auto info_path = trash_path.Append(trash::kInfoFolderName);
+ ASSERT_TRUE(base::DirectoryExists(info_path));
int mode = 0;
ASSERT_TRUE(base::GetPosixFilePermissions(trash_path, &mode));
EXPECT_EQ(mode, 0711);
+
+ constexpr char expected_files_xattr[] = "trash_files";
+ auto actual_files_xattr = GetTrackedExtendedAttributeAsString(files_path);
+ EXPECT_EQ(actual_files_xattr, expected_files_xattr);
+
+ constexpr char expected_info_xattr[] = "trash_info";
+ auto actual_info_xattr = GetTrackedExtendedAttributeAsString(info_path);
+ EXPECT_EQ(actual_info_xattr, expected_info_xattr);
}
void ExpectFileContents(const base::FilePath& path,