From 0672fb41213ef7678ee87d90166a722d52dc20f8 Mon Sep 17 00:00:00 2001 From: Patrick Donnelly Date: Tue, 14 May 2024 22:39:05 -0400 Subject: [PATCH] qa: add test to verify recovery of alternate_name from journal Test without the fix: 2024-05-16T22:34:21.781 DEBUG:teuthology.orchestra.run.smithi044:> sudo adjust-ulimits ceph-coverage /home/ubuntu/cephtest/archive/coverage timeout 300 ceph --cluster ceph --admin-daemon /var/run/ceph/ceph-mds.a.asok --format=json dump tree /dir 0 ... [ { "accounted_rstat": { "rbytes": 4096, "rctime": "2024-05-16T22:34:15.251864+0000", "rfiles": 1, "rsnaps": 0, "rsubdirs": 1, "version": 0 }, "atime": "2024-05-16T22:34:14.498880+0000", "auth_pins": 0, "auth_state": { "replicas": {} }, "authlock": {}, "backtrace_version": 14, "btime": "2024-05-16T22:34:14.498880+0000", "change_attr": 3, "client_caps": [ { "client_id": 5184, "issued": "pAsLsXsFsx", "last_sent": 4, "pending": "pAsLsXsFsx", "wanted": "pAsLsXsFsxcral" } ], "client_ranges": [], "ctime": "2024-05-16T22:34:15.249864+0000", "damage_flags": 0, "dir_layout": { "dir_hash": 2, "unused1": 0, "unused2": 0, "unused3": 0 }, "dirfrags": [ { "auth_pins": 0, "auth_state": { "replicas": {} }, "committed_version": "0", "committing_version": "0", "dentries": [ { "alternate_name": "bUHUwH9E8uiVaf8xZ+zONcB1CToj53x5aUUnKdnNj5U37zbh28l1AaWwHhbOT3HyzqKjmSKKW1o4odQJc7nF9xrKIB8D3b4qqb2Cs6s7t2106hHhQk5/YV7DtpeNPZnorcTqxPM/hExtWHSS4P+S+Dpwj62hMyh/77sGhiW1Filvv1gQjV+sN/GozPNwHgfleadkUs1OkRkYtgWrCjbKP0MayRtiOLrVTRuYyOp/Qt3+XCIyiS87B9bUcOFjWratF+yR0kpJ0RYriix7NKVkBJ0kGWYSCY+PYjiLeMYJBMQcCxW/nwfVku+m6fgFJvb6pjEFxIk9zT5cunSImsjr", "auth_pins": 0, "auth_state": { "replicas": {} }, "inode": 1099511628283, "is_auth": true, "is_freezing": false, "is_frozen": false, "is_new": false, "is_null": false, "is_primary": true, "is_remote": false, "lock": {}, "nref": 2, "path": "dir/bUHUwH9E8uiVaf8xZ+zONcB1CToj53x5aUUnKdnNj5U37zbh28l1AaWwHhbOT3HyzqKjmSKKW1o4odQJc7nF9xrKIB8D3b4qqb2Cs6s7t2106hHhQk5,YV7DtpeNPZnorcTqxPM,hExtWHSS4P+S+Dpwj62hMyh,77sGhiW1Filvv1gQjV+sN,GozPNwHgfleadkUuZ+PMLCaKQXhuid9WvmHanxJnaabYDLj4VEz+EX2WsG", ... # fail + journal recovery 2024-05-16T22:35:31.077 DEBUG:teuthology.orchestra.run.smithi044:> sudo adjust-ulimits ceph-coverage /home/ubuntu/cephtest/archive/coverage timeout 300 ceph --cluster ceph --admin-daemon /var/run/ceph/ceph-mds.a.asok --format=json dump tree /dir 0 ... [ { "accounted_rstat": { "rbytes": 4096, "rctime": "2024-05-16T22:34:15.251864+0000", "rfiles": 1, "rsnaps": 0, "rsubdirs": 1, "version": 0 }, "atime": "2024-05-16T22:34:14.498880+0000", "auth_pins": 0, "auth_state": { "replicas": {} }, "authlock": {}, "backtrace_version": 14, "btime": "2024-05-16T22:34:14.498880+0000", "change_attr": 3, "client_caps": [], "client_ranges": [], "ctime": "2024-05-16T22:34:15.249864+0000", "damage_flags": 0, "dir_layout": { "dir_hash": 2, "unused1": 0, "unused2": 0, "unused3": 0 }, "dirfrags": [ { "auth_pins": 0, "auth_state": { "replicas": {} }, "committed_version": "5", "committing_version": "5", "dentries": [ { "alternate_name": "", "auth_pins": 0, "auth_state": { "replicas": {} }, "inode": 1099511628283, "is_auth": true, "is_freezing": false, "is_frozen": false, "is_new": false, "is_null": false, "is_primary": true, "is_remote": false, "lock": {}, "nref": 2, "path": "dir/bUHUwH9E8uiVaf8xZ+zONcB1CToj53x5aUUnKdnNj5U37zbh28l1AaWwHhbOT3HyzqKjmSKKW1o4odQJc7nF9xrKIB8D3b4qqb2Cs6s7t2106hHhQk5,YV7DtpeNPZnorcTqxPM,hExtWHSS4P+S+Dpwj62hMyh,77sGhiW1Filvv1gQjV+sN,GozPNwHgfleadkUuZ+PMLCaKQXhuid9WvmHanxJnaabYDLj4VEz+EX2WsG", Signed-off-by: Patrick Donnelly (cherry picked from commit ac092f63bf38eb81ee13de7dfd518ebadfc783cc) --- .../fscrypt/tasks/1-tests/fscrypt-common.yaml | 7 ++ qa/tasks/cephfs/test_fscrypt.py | 88 ++++++++++++++++++- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/qa/suites/fs/fscrypt/tasks/1-tests/fscrypt-common.yaml b/qa/suites/fs/fscrypt/tasks/1-tests/fscrypt-common.yaml index 5cb34d9818e..959adad0021 100644 --- a/qa/suites/fs/fscrypt/tasks/1-tests/fscrypt-common.yaml +++ b/qa/suites/fs/fscrypt/tasks/1-tests/fscrypt-common.yaml @@ -1,3 +1,10 @@ +overrides: + install: + extra_system_packages: + rpm: + - fscrypt + deb: + - fscrypt tasks: - cephfs_test_runner: fail_on_skip: false diff --git a/qa/tasks/cephfs/test_fscrypt.py b/qa/tasks/cephfs/test_fscrypt.py index a1836717f2a..d327c43c1fc 100644 --- a/qa/tasks/cephfs/test_fscrypt.py +++ b/qa/tasks/cephfs/test_fscrypt.py @@ -1,13 +1,95 @@ +from io import StringIO +from os.path import basename +import random +import string + from logging import getLogger -from io import StringIO +from tasks.cephfs.cephfs_test_case import CephFSTestCase from tasks.cephfs.xfstests_dev import XFSTestsDev - log = getLogger(__name__) +class FSCryptTestCase(CephFSTestCase): + CLIENTS_REQUIRED = 1 + + def setUp(self): + super().setUp() + + self.protector = ''.join(random.choice(string.ascii_letters) for _ in range(8)) + self.key_file = "/tmp/key" + self.path = "dir/" + + self.mount_a.run_shell_payload("sudo fscrypt --help") + self.mount_a.run_shell_payload("sudo fscrypt setup --help") + self.mount_a.run_shell_payload("sudo fscrypt setup --force --quiet") + self.mount_a.run_shell_payload("sudo fscrypt status") + self.mount_a.run_shell_payload(f"sudo fscrypt setup --quiet {self.mount_a.hostfs_mntpt}") + self.mount_a.run_shell_payload("sudo fscrypt status") + self.mount_a.run_shell_payload(f"sudo dd if=/dev/urandom of={self.key_file} bs=32 count=1") + self.mount_a.run_shell_payload(f"mkdir -p {self.path}") + self.mount_a.run_shell_payload(f"sudo fscrypt encrypt --quiet --source=raw_key --name={self.protector} --no-recovery --skip-unlock --key={self.key_file} {self.path}") + self.mount_a.run_shell_payload(f"sudo fscrypt unlock --quiet --key=/tmp/key {self.path}") + + def tearDown(self): + self.mount_a.run_shell_payload(f"sudo fscrypt purge --force --quiet {self.mount_a.hostfs_mntpt}") + + super().tearDown() + +class TestFSCrypt(FSCryptTestCase): + + def test_fscrypt_basic_mount(self): + """ + That fscrypt can be setup and ingest files. + """ + + self.mount_a.run_shell_payload(f"cp -av /usr/include {self.path}/") + +class TestFSCryptRecovery(FSCryptTestCase): + + def test_fscrypt_journal_recovery(self): + """ + That alternate_name can be recovered from the journal. + """ + + file = ''.join(random.choice(string.ascii_letters) for _ in range(255)) + + self.mount_a.run_shell_payload(f"cd {self.path} && dd if=/dev/urandom of={file} bs=512 count=1 oflag=sync && sync . && stat {file}") + + def verify_alternate_name(): + J = self.fs.read_cache("/dir", depth=0) + self.assertEqual(len(J), 1) + inode = J[0] + dirfrags = inode['dirfrags'] + self.assertEqual(len(dirfrags), 1) + dirfrag = dirfrags[0] + dentries = dirfrag['dentries'] + self.assertEqual(len(dentries), 1) + # we don't know it's encrypted name, so we cannot verify that it's {file} + dentry = dentries[0] + name = basename(dentry['path']) + # https://github.com/ceph/ceph-client/blob/fec50db7033ea478773b159e0e2efb135270e3b7/fs/ceph/crypto.h#L65-L90 + self.assertEqual(len(name), 240) + alternate_name = dentry['alternate_name'] + self.assertGreater(len(alternate_name), 240) + + verify_alternate_name() + + self.fs.fail() + + self.fs.journal_tool(['event', 'recover_dentries', 'list'], 0) + self.fs.journal_tool(['journal', 'reset', '--yes-i-really-really-mean-it'], 0) + + self.fs.set_joinable() + self.fs.wait_for_daemons() + + verify_alternate_name() + + self.mount_a.run_shell_payload(f"cd {self.path} && find") + self.mount_a.run_shell_payload(f"cd {self.path} && stat {file}") + -class TestFscrypt(XFSTestsDev): +class TestFSCryptXFS(XFSTestsDev): def setup_xfsprogs_devs(self): self.install_xfsprogs = True -- 2.39.5