test: add a testcase for copy_tree() and fs-verity
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <linux/fsverity.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -591,4 +593,120 @@ TEST(copy_verify_linked) {
|
||||
assert_se(faccessat(tfd, "to_2", F_OK, AT_SYMLINK_NOFOLLOW) < 0 && errno == ENOENT);
|
||||
}
|
||||
|
||||
static bool enable_fsverity(int dirfd, const char *name, int algo, const char *salt, size_t salt_size) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
ASSERT_OK_ERRNO(fd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_NOCTTY));
|
||||
|
||||
struct fsverity_enable_arg enable_arg = {
|
||||
.version = 1,
|
||||
.hash_algorithm = algo,
|
||||
.block_size = 4096,
|
||||
.salt_size = salt_size,
|
||||
.salt_ptr = (uintptr_t) salt
|
||||
};
|
||||
|
||||
if (ioctl(fd, FS_IOC_ENABLE_VERITY, &enable_arg) == 0)
|
||||
return true;
|
||||
|
||||
/* We might get this if we're on a filesystem without support. */
|
||||
ASSERT_TRUE(IN_SET(errno, EOPNOTSUPP, ENOTTY));
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct fsverity_digest* alloc_fsverity_digest(size_t digest_size) {
|
||||
struct fsverity_digest *digest = malloc(sizeof (struct fsverity_digest) + digest_size);
|
||||
ASSERT_NOT_NULL(digest);
|
||||
digest->digest_size = digest_size;
|
||||
return digest;
|
||||
}
|
||||
|
||||
static bool measure_fsverity(int dirfd, const char *name, struct fsverity_digest *digest) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
ASSERT_OK_ERRNO(fd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_NOCTTY));
|
||||
if (ioctl(fd, FS_IOC_MEASURE_VERITY, digest) == 0)
|
||||
return true;
|
||||
|
||||
/* Make sure that the error is caused by the file simply not having fs-verity. */
|
||||
ASSERT_TRUE(errno == ENODATA);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void assert_no_fsverity(int dirfd, const char *name) {
|
||||
_cleanup_free_ struct fsverity_digest *digest = alloc_fsverity_digest(0);
|
||||
ASSERT_FALSE(measure_fsverity(dirfd, name, digest));
|
||||
}
|
||||
|
||||
static void assert_fsverity_eq(int dirfd_a, int dirfd_b, const char *name) {
|
||||
_cleanup_free_ struct fsverity_digest *digest_a = alloc_fsverity_digest(64);
|
||||
_cleanup_free_ struct fsverity_digest *digest_b = alloc_fsverity_digest(64);
|
||||
|
||||
bool enabled_a = measure_fsverity(dirfd_a, name, digest_a);
|
||||
bool enabled_b = measure_fsverity(dirfd_b, name, digest_b);
|
||||
ASSERT_EQ(enabled_a, enabled_b);
|
||||
if (enabled_a) {
|
||||
ASSERT_EQ(digest_a->digest_algorithm, digest_b->digest_algorithm);
|
||||
ASSERT_EQ(digest_a->digest_size, digest_b->digest_size);
|
||||
ASSERT_EQ(memcmp(digest_a->digest, digest_b->digest, digest_a->digest_size), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_RET(copy_with_verity) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *srcp = NULL, *dstp = NULL, *badsrcp = NULL, *baddstp = NULL;
|
||||
const char *files[] = { "disabled", "simple", "bigsha", "salty" };
|
||||
_cleanup_close_ int src = -EBADF, dst = -EBADF, badsrc = -EBADF, baddst = -EBADF;
|
||||
|
||||
/* We're more likely to hit a filesystem with fs-verity enabled on /var/tmp than on /tmp (tmpfs) */
|
||||
ASSERT_OK(src = mkdtemp_open("/var/tmp/test-copy_file-src.XXXXXX", 0, &srcp));
|
||||
ASSERT_OK(dst = mkdtemp_open("/var/tmp/test-copy_file-dst.XXXXXX", 0, &dstp));
|
||||
|
||||
/* Populate some data to differentiate the files. */
|
||||
FOREACH_ELEMENT(file, files)
|
||||
ASSERT_OK(write_string_file_at(src, *file, "src file", WRITE_STRING_FILE_CREATE));
|
||||
|
||||
/* Enable on some file using a range of options */
|
||||
if (!enable_fsverity(src, "simple", FS_VERITY_HASH_ALG_SHA256, NULL, 0))
|
||||
return log_tests_skipped_errno(errno, "/var/tmp: fs-verity is not supported here");
|
||||
ASSERT_TRUE(enable_fsverity(src, "bigsha", FS_VERITY_HASH_ALG_SHA512, NULL, 0));
|
||||
ASSERT_TRUE(enable_fsverity(src, "salty", FS_VERITY_HASH_ALG_SHA512, "edamame", 8));
|
||||
|
||||
/* Copy without fs-verity enabled and make sure nothing is set on the destination */
|
||||
ASSERT_OK(copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REPLACE|COPY_MERGE, NULL, NULL));
|
||||
FOREACH_ELEMENT(file, files)
|
||||
assert_no_fsverity(dst, *file);
|
||||
|
||||
/* Copy *with* fs-verity enabled and make sure it works properly */
|
||||
int r = copy_tree_at(src, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL);
|
||||
if (r == -ENOTTY)
|
||||
/* This can happen on some versions of btrfs, for example */
|
||||
return log_tests_skipped_errno(errno, "/var/tmp: fs-verity supported, but not reading metadata");
|
||||
ASSERT_OK(r);
|
||||
FOREACH_ELEMENT(file, files)
|
||||
assert_fsverity_eq(src, dst, *file);
|
||||
|
||||
/* Now try to create files where we know fs-verity doesn't work: tmpfs */
|
||||
ASSERT_OK(badsrc = mkdtemp_open("/tmp/test-copy_file-src.XXXXXX", 0, &badsrcp));
|
||||
ASSERT_OK(baddst = mkdtemp_open("/tmp/test-copy_file-src.XXXXXX", 0, &baddstp));
|
||||
|
||||
/* Populate the source, same as before */
|
||||
FOREACH_ELEMENT(file, files)
|
||||
ASSERT_OK(write_string_file_at(badsrc, *file, "src file", WRITE_STRING_FILE_CREATE));
|
||||
|
||||
/* Ensure the attempting to enable fs-verity here will fail */
|
||||
if (enable_fsverity(badsrc, "simple", FS_VERITY_HASH_ALG_SHA256, NULL, 0))
|
||||
return log_tests_skipped_errno(errno, "/tmp: fs-verity *is* unexpectedly supported here");
|
||||
|
||||
/* Copy from our non-verity filesystem into dst, requesting verity and making sure we notice that
|
||||
* we failed to read verity from the source. */
|
||||
ASSERT_ERROR(copy_tree_at(badsrc, ".", dst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ENOTTY);
|
||||
|
||||
/* Copy from our verity filesystem into our baddst, requesting verity and making sure we notice that
|
||||
* we failed to set verity on the destination. */
|
||||
ASSERT_ERROR(copy_tree_at(src, ".", baddst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ENOTTY);
|
||||
|
||||
/* Of course this should fail too... */
|
||||
ASSERT_ERROR(copy_tree_at(badsrc, ".", baddst, ".", UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_REPLACE|COPY_MERGE|COPY_PRESERVE_FS_VERITY, NULL, NULL), ENOTTY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
Reference in New Issue
Block a user