Skip to content

Commit 959f007

Browse files
committed
Merge PR ceph#55792 into main
* refs/pull/55792/head: tools/cephfs: recover alternate_name of dentries from journal qa: add test to verify recovery of alternate_name from journal tools/cephfs/JournalTool: add some more debugging tools/cephfs/JournalTool: remove extraneous 0x in debug output mds: dump alternate_name to formatter mds: add warning about encoding new fields Reviewed-by: Christopher Hoffman <[email protected]> Reviewed-by: Xiubo Li <[email protected]>
2 parents 49d666a + bebec1f commit 959f007

File tree

6 files changed

+128
-20
lines changed

6 files changed

+128
-20
lines changed

qa/suites/fs/fscrypt/tasks/1-tests/fscrypt-common.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
overrides:
2+
install:
3+
extra_system_packages:
4+
rpm:
5+
- fscrypt
6+
deb:
7+
- fscrypt
18
tasks:
29
- cephfs_test_runner:
310
fail_on_skip: false

qa/tasks/cephfs/test_fscrypt.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,95 @@
1+
from io import StringIO
2+
from os.path import basename
3+
import random
4+
import string
5+
16
from logging import getLogger
27

3-
from io import StringIO
8+
from tasks.cephfs.cephfs_test_case import CephFSTestCase
49
from tasks.cephfs.xfstests_dev import XFSTestsDev
510

6-
711
log = getLogger(__name__)
812

13+
class FSCryptTestCase(CephFSTestCase):
14+
CLIENTS_REQUIRED = 1
15+
16+
def setUp(self):
17+
super().setUp()
18+
19+
self.protector = ''.join(random.choice(string.ascii_letters) for _ in range(8))
20+
self.key_file = "/tmp/key"
21+
self.path = "dir/"
22+
23+
self.mount_a.run_shell_payload("sudo fscrypt --help")
24+
self.mount_a.run_shell_payload("sudo fscrypt setup --help")
25+
self.mount_a.run_shell_payload("sudo fscrypt setup --force --quiet")
26+
self.mount_a.run_shell_payload("sudo fscrypt status")
27+
self.mount_a.run_shell_payload(f"sudo fscrypt setup --quiet {self.mount_a.hostfs_mntpt}")
28+
self.mount_a.run_shell_payload("sudo fscrypt status")
29+
self.mount_a.run_shell_payload(f"sudo dd if=/dev/urandom of={self.key_file} bs=32 count=1")
30+
self.mount_a.run_shell_payload(f"mkdir -p {self.path}")
31+
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}")
32+
self.mount_a.run_shell_payload(f"sudo fscrypt unlock --quiet --key=/tmp/key {self.path}")
33+
34+
def tearDown(self):
35+
self.mount_a.run_shell_payload(f"sudo fscrypt purge --force --quiet {self.mount_a.hostfs_mntpt}")
36+
37+
super().tearDown()
38+
39+
class TestFSCrypt(FSCryptTestCase):
40+
41+
def test_fscrypt_basic_mount(self):
42+
"""
43+
That fscrypt can be setup and ingest files.
44+
"""
45+
46+
self.mount_a.run_shell_payload(f"cp -av /usr/include {self.path}/")
47+
48+
class TestFSCryptRecovery(FSCryptTestCase):
49+
50+
def test_fscrypt_journal_recovery(self):
51+
"""
52+
That alternate_name can be recovered from the journal.
53+
"""
54+
55+
file = ''.join(random.choice(string.ascii_letters) for _ in range(255))
56+
57+
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}")
58+
59+
def verify_alternate_name():
60+
J = self.fs.read_cache("/dir", depth=0)
61+
self.assertEqual(len(J), 1)
62+
inode = J[0]
63+
dirfrags = inode['dirfrags']
64+
self.assertEqual(len(dirfrags), 1)
65+
dirfrag = dirfrags[0]
66+
dentries = dirfrag['dentries']
67+
self.assertEqual(len(dentries), 1)
68+
# we don't know it's encrypted name, so we cannot verify that it's {file}
69+
dentry = dentries[0]
70+
name = basename(dentry['path'])
71+
# https://github.com/ceph/ceph-client/blob/fec50db7033ea478773b159e0e2efb135270e3b7/fs/ceph/crypto.h#L65-L90
72+
self.assertEqual(len(name), 240)
73+
alternate_name = dentry['alternate_name']
74+
self.assertGreater(len(alternate_name), 240)
75+
76+
verify_alternate_name()
77+
78+
self.fs.fail()
79+
80+
self.fs.journal_tool(['event', 'recover_dentries', 'list'], 0)
81+
self.fs.journal_tool(['journal', 'reset', '--yes-i-really-really-mean-it'], 0)
82+
83+
self.fs.set_joinable()
84+
self.fs.wait_for_daemons()
85+
86+
verify_alternate_name()
87+
88+
self.mount_a.run_shell_payload(f"cd {self.path} && find")
89+
self.mount_a.run_shell_payload(f"cd {self.path} && stat {file}")
90+
991

10-
class TestFscrypt(XFSTestsDev):
92+
class TestFSCryptXFS(XFSTestsDev):
1193

1294
def setup_xfsprogs_devs(self):
1395
self.install_xfsprogs = True

src/mds/CDentry.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ void CDentry::encode_remote(inodeno_t& ino, unsigned char d_type,
568568

569569
// marker, name, ino
570570
ENCODE_START(2, 1, bl);
571+
// WARNING: always put new fields at the end of bl
571572
encode(ino, bl);
572573
encode(d_type, bl);
573574
encode(alternate_name, bl);
@@ -599,6 +600,15 @@ void CDentry::dump(Formatter *f) const
599600
make_path(path);
600601

601602
f->dump_string("path", path.get_path());
603+
if (auto s = get_alternate_name(); !s.empty()) {
604+
bufferlist bl, b64;
605+
bl.append(s);
606+
bl.encode_base64(b64);
607+
auto encoded = std::string_view(b64.c_str(), b64.length());
608+
f->dump_string("alternate_name", encoded);
609+
} else {
610+
f->dump_string("alternate_name", "");
611+
}
602612
f->dump_unsigned("path_ino", path.get_ino().val);
603613
f->dump_unsigned("snap_first", first);
604614
f->dump_unsigned("snap_last", last);

src/mds/CDir.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2506,6 +2506,7 @@ void CDir::_omap_commit_ops(int r, int op_prio, int64_t metapool, version_t vers
25062506
bl.append('i'); // inode
25072507

25082508
ENCODE_START(2, 1, bl);
2509+
// WARNING: always put new fields at the end of bl
25092510
encode(item.alternate_name, bl);
25102511
_encode_primary_inode_base(item, dfts, bl);
25112512
ENCODE_FINISH(bl);

src/tools/cephfs/JournalTool.cc

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -888,14 +888,25 @@ int JournalTool::recover_dentries(
888888
}
889889

890890
if ((other_pool || write_dentry) && !dry_run) {
891-
dout(4) << "writing I dentry " << key << " into frag "
891+
dout(4) << "writing i dentry " << key << " into frag "
892892
<< frag_oid.name << dendl;
893+
dout(20) << " dnfirst = " << fb.dnfirst << dendl;
894+
if (!fb.alternate_name.empty()) {
895+
bufferlist bl, b64;
896+
bl.append(fb.alternate_name);
897+
bl.encode_base64(b64);
898+
auto encoded = std::string_view(b64.c_str(), b64.length());
899+
dout(20) << " alternate_name = b64:" << encoded << dendl;
900+
}
893901

894-
// Compose: Dentry format is dnfirst, [I|L], InodeStore(bare=true)
902+
// Compose: Dentry format is dnfirst, [i|l], InodeStore
895903
bufferlist dentry_bl;
896904
encode(fb.dnfirst, dentry_bl);
897-
encode('I', dentry_bl);
898-
encode_fullbit_as_inode(fb, true, &dentry_bl);
905+
encode('i', dentry_bl);
906+
ENCODE_START(2, 1, dentry_bl);
907+
encode(fb.alternate_name, dentry_bl);
908+
encode_fullbit_as_inode(fb, &dentry_bl);
909+
ENCODE_FINISH(dentry_bl);
899910

900911
// Record for writing to RADOS
901912
write_vals[key] = dentry_bl;
@@ -950,12 +961,15 @@ int JournalTool::recover_dentries(
950961
dout(4) << "writing L dentry " << key << " into frag "
951962
<< frag_oid.name << dendl;
952963

953-
// Compose: Dentry format is dnfirst, [I|L], InodeStore(bare=true)
964+
// Compose: Dentry format is dnfirst, [I|L], ino, d_type, alternate_name
954965
bufferlist dentry_bl;
955966
encode(rb.dnfirst, dentry_bl);
956-
encode('L', dentry_bl);
967+
encode('l', dentry_bl);
968+
ENCODE_START(2, 1, dentry_bl);
957969
encode(rb.ino, dentry_bl);
958970
encode(rb.d_type, dentry_bl);
971+
encode(rb.alternate_name, dentry_bl);
972+
ENCODE_FINISH(dentry_bl);
959973

960974
// Record for writing to RADOS
961975
write_vals[key] = dentry_bl;
@@ -1034,7 +1048,7 @@ int JournalTool::recover_dentries(
10341048
*/
10351049
for (const auto& fb : metablob.roots) {
10361050
inodeno_t ino = fb.inode->ino;
1037-
dout(4) << "updating root 0x" << std::hex << ino << std::dec << dendl;
1051+
dout(4) << "updating root " << ino << dendl;
10381052

10391053
object_t root_oid = InodeStore::get_object_name(ino, frag_t(), ".inode");
10401054
dout(4) << "object id " << root_oid.name << dendl;
@@ -1074,10 +1088,10 @@ int JournalTool::recover_dentries(
10741088
dout(4) << "writing root ino " << root_oid.name
10751089
<< " version " << fb.inode->version << dendl;
10761090

1077-
// Compose: root ino format is magic,InodeStore(bare=false)
1091+
// Compose: root ino format is magic,InodeStore
10781092
bufferlist new_root_ino_bl;
10791093
encode(std::string(CEPH_FS_ONDISK_MAGIC), new_root_ino_bl);
1080-
encode_fullbit_as_inode(fb, false, &new_root_ino_bl);
1094+
encode_fullbit_as_inode(fb, &new_root_ino_bl);
10811095

10821096
// Write to RADOS
10831097
r = output.write_full(root_oid.name, new_root_ino_bl);
@@ -1187,7 +1201,6 @@ int JournalTool::erase_region(JournalScanner const &js, uint64_t const pos, uint
11871201
*/
11881202
void JournalTool::encode_fullbit_as_inode(
11891203
const EMetaBlob::fullbit &fb,
1190-
const bool bare,
11911204
bufferlist *out_bl)
11921205
{
11931206
ceph_assert(out_bl != NULL);
@@ -1202,11 +1215,7 @@ void JournalTool::encode_fullbit_as_inode(
12021215
new_inode.old_inodes = fb.old_inodes;
12031216

12041217
// Serialize InodeStore
1205-
if (bare) {
1206-
new_inode.encode_bare(*out_bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
1207-
} else {
1208-
new_inode.encode(*out_bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
1209-
}
1218+
new_inode.encode(*out_bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
12101219
}
12111220

12121221
/**
@@ -1265,7 +1274,7 @@ int JournalTool::consume_inos(const std::set<inodeno_t> &inos)
12651274
{
12661275
const inodeno_t ino = *i;
12671276
if (ino_table.force_consume(ino)) {
1268-
dout(4) << "Used ino 0x" << std::hex << ino << std::dec
1277+
dout(4) << "Used ino " << ino
12691278
<< " requires inotable update" << dendl;
12701279
inotable_modified = true;
12711280
}

src/tools/cephfs/JournalTool.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ class JournalTool : public MDSUtility
7878
// Backing store helpers
7979
void encode_fullbit_as_inode(
8080
const EMetaBlob::fullbit &fb,
81-
const bool bare,
8281
bufferlist *out_bl);
8382
int consume_inos(const std::set<inodeno_t> &inos);
8483

0 commit comments

Comments
 (0)