Skip to content

Commit b5ca6c1

Browse files
committed
Merge PR ceph#60726 into main
* refs/pull/60726/head: qa: add test checking 'journal import' from empty dump file cephfs-journal-tool: fix segfault during 'journal import' from invalid dump file Reviewed-by: Dhairya Parmar <[email protected]> Reviewed-by: Venky Shankar <[email protected]>
2 parents 1eccade + 2d45aa1 commit b5ca6c1

File tree

2 files changed

+84
-6
lines changed

2 files changed

+84
-6
lines changed

qa/tasks/cephfs/test_journal_repair.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import logging
88
from textwrap import dedent
99
import time
10+
import tempfile
1011

1112
from teuthology.exceptions import CommandFailedError, ConnectionLostError
1213
from tasks.cephfs.filesystem import ObjectNotFound, ROOT_INO
@@ -403,3 +404,49 @@ def test_journal_smoke(self):
403404
"timeout": "1h"
404405
})
405406

407+
def test_journal_import_from_empty_dump_file(self):
408+
"""
409+
That the 'journal import' recognizes empty file read and errors out.
410+
"""
411+
fname = tempfile.NamedTemporaryFile(delete=False).name
412+
self.mount_a.run_shell(["sudo", "touch", fname], omit_sudo=False)
413+
self.fs.fail()
414+
import_out = None
415+
try:
416+
import_out = self.fs.journal_tool(["journal", "import", fname], 0)
417+
except CommandFailedError as e:
418+
self.mount_a.run_shell(["sudo", "rm", fname], omit_sudo=False)
419+
raise RuntimeError(f"Unexpected journal import error: {str(e)}")
420+
self.fs.set_joinable()
421+
self.fs.wait_for_daemons()
422+
try:
423+
if import_out.endswith("done."):
424+
assert False
425+
except AssertionError:
426+
raise RuntimeError(f"Unexpected journal-tool result: '{import_out}'")
427+
finally:
428+
self.mount_a.run_shell(["sudo", "rm", fname], omit_sudo=False)
429+
430+
def test_journal_import_from_invalid_dump_file(self):
431+
"""
432+
That the 'journal import' recognizes invalid dump file and errors out.
433+
"""
434+
# Create an invalid dump file with partial header
435+
fname = tempfile.NamedTemporaryFile(delete=False).name
436+
self.mount_a.run_shell(["sudo", "sh", "-c", f'printf "Ceph mds0 journal dump\n\
437+
start offset 4194304 (0x400000)\n\
438+
length 940 (0x3ac)\nwrite_pos 4194304 (0x400000)\n" > {fname}'], omit_sudo=False)
439+
self.fs.fail()
440+
try:
441+
self.fs.journal_tool(["journal", "import", fname], 0)
442+
except CommandFailedError as e:
443+
self.fs.set_joinable()
444+
self.fs.wait_for_daemons()
445+
if e.exitstatus != 234:
446+
raise RuntimeError(f"Unexpected journal import error: {str(e)}")
447+
else:
448+
self.fs.set_joinable()
449+
self.fs.wait_for_daemons()
450+
raise RuntimeError("Expected journal import to fail")
451+
finally:
452+
self.mount_a.run_shell(["sudo", "rm", fname], omit_sudo=False)

src/tools/cephfs/Dumper.cc

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,23 +224,54 @@ int Dumper::undump(const char *dump_file, bool force)
224224
char buf[HEADER_LEN];
225225
r = safe_read(fd, buf, sizeof(buf));
226226
if (r < 0) {
227+
derr << "Error reading " << dump_file << dendl;
228+
VOID_TEMP_FAILURE_RETRY(::close(fd));
229+
return r;
230+
} else if (r == 0) {
231+
//Empty file read
232+
derr << "No-op since empty journal dump file: " << dump_file << dendl;
227233
VOID_TEMP_FAILURE_RETRY(::close(fd));
228234
return r;
229235
}
230236

231237
long long unsigned start, len, write_pos, format, trimmed_pos;
232238
long unsigned stripe_unit, stripe_count, object_size;
233-
sscanf(strstr(buf, "start offset"), "start offset %llu", &start);
234-
sscanf(strstr(buf, "length"), "length %llu", &len);
235-
sscanf(strstr(buf, "write_pos"), "write_pos %llu", &write_pos);
236-
sscanf(strstr(buf, "format"), "format %llu", &format);
239+
char *phdr = strstr(buf, "start offset");
240+
if (phdr == NULL) {
241+
derr << "Invalid header, no 'start offset' embedded" << dendl;
242+
::close(fd);
243+
return -EINVAL;
244+
}
245+
sscanf(phdr, "start offset %llu", &start);
246+
phdr = strstr(buf, "length");
247+
if (phdr == NULL) {
248+
derr << "Invalid header, no 'length' embedded" << dendl;
249+
::close(fd);
250+
return -EINVAL;
251+
}
252+
sscanf(phdr, "length %llu", &len);
253+
phdr = strstr(buf, "write_pos");
254+
if (phdr == NULL) {
255+
derr << "Invalid header, no 'write_pos' embedded" << dendl;
256+
::close(fd);
257+
return -EINVAL;
258+
}
259+
sscanf(phdr, "write_pos %llu", &write_pos);
260+
phdr = strstr(buf, "format");
261+
if (phdr == NULL) {
262+
derr << "Invalid header, no 'format' embedded" << dendl;
263+
::close(fd);
264+
return -EINVAL;
265+
}
266+
sscanf(phdr, "format %llu", &format);
237267

238268
if (!force) {
239269
// need to check if fsid match onlien cluster fsid
240-
if (strstr(buf, "fsid")) {
270+
phdr = strstr(buf, "fsid");
271+
if (phdr) {
241272
uuid_d fsid;
242273
char fsid_str[40];
243-
sscanf(strstr(buf, "fsid"), "fsid %39s", fsid_str);
274+
sscanf(phdr, "fsid %39s", fsid_str);
244275
r = fsid.parse(fsid_str);
245276
if (!r) {
246277
derr << "Invalid fsid" << dendl;

0 commit comments

Comments
 (0)