mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-12-08 00:00:12 +03:00
bcachefs undump
Some checks failed
build / bcachefs-tools-deb (ubuntu-24.04) (push) Has been cancelled
build / bcachefs-tools-rpm (push) Has been cancelled
build / bcachefs-tools-msrv (push) Has been cancelled
Nix Flake actions / nix-matrix (push) Has been cancelled
Nix Flake actions / ${{ matrix.name }} (${{ matrix.system }}) (push) Has been cancelled
Some checks failed
build / bcachefs-tools-deb (ubuntu-24.04) (push) Has been cancelled
build / bcachefs-tools-rpm (push) Has been cancelled
build / bcachefs-tools-msrv (push) Has been cancelled
Nix Flake actions / nix-matrix (push) Has been cancelled
Nix Flake actions / ${{ matrix.name }} (${{ matrix.system }}) (push) Has been cancelled
Add our own version of 'qemu-img convert', which doesn't have the l1 table size limit or require fixing our missing reflink table. Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
parent
da8f1d04e3
commit
e65dd86e41
@ -96,6 +96,7 @@ void bcachefs_usage(void)
|
||||
"Debug:\n"
|
||||
"These commands work on offline, unmounted filesystems\n"
|
||||
" dump Dump filesystem metadata to a qcow2 image\n"
|
||||
" undump Convert qcow2 metadata dumps to sparse raw files\n"
|
||||
" list List filesystem metadata in textual form\n"
|
||||
" list_journal List contents of journal\n"
|
||||
"\n"
|
||||
|
||||
@ -386,3 +386,85 @@ int cmd_dump(int argc, char *argv[])
|
||||
darray_exit(&dev_names);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void undump_usage(void)
|
||||
{
|
||||
puts("bcachefs undump - turn qcow2 images from 'bcachefs dump' back into sparse raw images\n"
|
||||
"Usage: bcachefs undump [OPTION]... <files>\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -f, --force Force; overwrite when needed\n"
|
||||
" -h, --help Display this help and exit\n"
|
||||
"Report bugs to <linux-bcachefs@vger.kernel.org>");
|
||||
}
|
||||
|
||||
struct undump {
|
||||
char *in, *out;
|
||||
int infd, outfd;
|
||||
};
|
||||
|
||||
int cmd_undump(int argc, char *argv[])
|
||||
{
|
||||
static const struct option longopts[] = {
|
||||
{ "force", no_argument, NULL, 'f' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL }
|
||||
};
|
||||
bool force = false;
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "fh",
|
||||
longopts, NULL)) != -1)
|
||||
switch (opt) {
|
||||
case 'f':
|
||||
force = true;
|
||||
break;
|
||||
case 'h':
|
||||
undump_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
args_shift(optind);
|
||||
|
||||
if (!argc) {
|
||||
undump_usage();
|
||||
die("Please supply file(s) to convert");
|
||||
}
|
||||
|
||||
DARRAY(struct undump) files = {};
|
||||
|
||||
for (unsigned i = 0; i < argc; i++) {
|
||||
unsigned len = strlen(argv[i]);
|
||||
const char *suffix = ".qcow2";
|
||||
unsigned suffixlen = strlen(suffix);
|
||||
|
||||
if (len <= suffixlen ||
|
||||
strcmp(suffix, argv[i] + len - suffixlen)) {
|
||||
die("%s not a qcow2 image?", argv[i]);
|
||||
}
|
||||
|
||||
char *out = strdup(argv[i]);
|
||||
out[len - suffixlen] = '\0';
|
||||
|
||||
if (!force && !access(out, F_OK))
|
||||
die("%s already exists", out);
|
||||
|
||||
darray_push(&files, ((struct undump) {
|
||||
.in = argv[i],
|
||||
.out = out,
|
||||
.infd = xopen(argv[i], O_RDONLY),
|
||||
}));
|
||||
}
|
||||
|
||||
darray_for_each(files, i)
|
||||
i->outfd = xopen(i->out, O_WRONLY|O_CREAT|(!force ? O_EXCL : 0), 0600);
|
||||
|
||||
darray_for_each(files, i) {
|
||||
qcow2_to_raw(i->infd, i->outfd);
|
||||
close(i->infd);
|
||||
close(i->outfd);
|
||||
free(i->out);
|
||||
}
|
||||
|
||||
darray_exit(&files);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ int cmd_fsck(int argc, char *argv[]);
|
||||
int cmd_recovery_pass(int argc, char *argv[]);
|
||||
|
||||
int cmd_dump(int argc, char *argv[]);
|
||||
int cmd_undump(int argc, char *argv[]);
|
||||
|
||||
int cmd_list_journal(int argc, char *argv[]);
|
||||
int cmd_kill_btree_node(int argc, char *argv[]);
|
||||
|
||||
|
||||
@ -143,8 +143,7 @@ void qcow2_image_finish(struct qcow2_image *img)
|
||||
|
||||
memset(buf, 0, img->block_size);
|
||||
memcpy(buf, &hdr, sizeof(hdr));
|
||||
xpwrite(img->outfd, buf, img->block_size, 0,
|
||||
"qcow2 header");
|
||||
xpwrite(img->outfd, buf, img->block_size, 0, "qcow2 header");
|
||||
|
||||
free(img->l2_table);
|
||||
free(img->l1_table);
|
||||
@ -160,3 +159,51 @@ void qcow2_write_image(int infd, int outfd, ranges *data,
|
||||
qcow2_write_ranges(&img, data);
|
||||
qcow2_image_finish(&img);
|
||||
}
|
||||
|
||||
void qcow2_to_raw(int infd, int outfd)
|
||||
{
|
||||
struct qcow2_hdr hdr;
|
||||
|
||||
xpread(infd, &hdr, sizeof(hdr), 0);
|
||||
|
||||
if (hdr.magic != cpu_to_be32(QCOW_MAGIC))
|
||||
die("not a qcow2 image");
|
||||
|
||||
if (hdr.version != cpu_to_be32(QCOW_VERSION))
|
||||
die("incorrect qcow2 version");
|
||||
|
||||
ftruncate(outfd, be64_to_cpu(hdr.size));
|
||||
|
||||
unsigned block_size = 1U << be32_to_cpu(hdr.block_bits);
|
||||
|
||||
unsigned l1_size = be32_to_cpu(hdr.l1_size);
|
||||
unsigned l2_size = block_size / sizeof(u64);
|
||||
|
||||
__be64 *l1_table = xcalloc(l1_size, sizeof(u64));
|
||||
__be64 *l2_table = xmalloc(block_size);
|
||||
void *data_buf = xmalloc(block_size);
|
||||
|
||||
xpread(infd, l1_table, l1_size * sizeof(u64), be64_to_cpu(hdr.l1_table_offset));
|
||||
|
||||
for (u64 i = 0; i < l1_size; i++) {
|
||||
if (!l1_table[i])
|
||||
continue;
|
||||
|
||||
xpread(infd, l2_table, block_size, be64_to_cpu(l1_table[i]) & ~QCOW_OFLAG_COPIED);
|
||||
|
||||
for (unsigned j = 0; j < l2_size; j++) {
|
||||
u64 src_offset = be64_to_cpu(l2_table[j]) & ~QCOW_OFLAG_COPIED;
|
||||
if (!src_offset)
|
||||
continue;
|
||||
|
||||
u64 dst_offset = (i * l2_size + j) * block_size;
|
||||
|
||||
xpread(infd, data_buf, block_size, src_offset);
|
||||
xpwrite(outfd, data_buf, block_size, dst_offset, "qcow2 data");
|
||||
}
|
||||
}
|
||||
|
||||
free(data_buf);
|
||||
free(l2_table);
|
||||
free(l1_table);
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
#include <linux/types.h>
|
||||
#include "tools-util.h"
|
||||
|
||||
#define QCOW2_L1_MAX (4ULL << 20)
|
||||
|
||||
struct qcow2_image {
|
||||
int infd;
|
||||
int outfd;
|
||||
@ -25,4 +27,6 @@ void qcow2_image_finish(struct qcow2_image *);
|
||||
|
||||
void qcow2_write_image(int, int, ranges *, unsigned);
|
||||
|
||||
void qcow2_to_raw(int, int);
|
||||
|
||||
#endif /* _QCOW2_H */
|
||||
|
||||
@ -52,15 +52,15 @@ char *mprintf(const char *fmt, ...)
|
||||
return str;
|
||||
}
|
||||
|
||||
void xpread(int fd, void *buf, size_t count, off_t offset)
|
||||
void __xpread(int fd, void *buf, size_t count, off_t offset, const char *file, unsigned line)
|
||||
{
|
||||
while (count) {
|
||||
ssize_t r = pread(fd, buf, count, offset);
|
||||
|
||||
if (r < 0)
|
||||
die("read error: %m");
|
||||
die("read error: %m at %s:%u", file, line);
|
||||
if (!r)
|
||||
die("pread error: unexpected eof");
|
||||
die("pread error: unexpected eof at %s:%u", file, line);
|
||||
count -= r;
|
||||
offset += r;
|
||||
}
|
||||
|
||||
@ -28,8 +28,13 @@ void die(const char *, ...)
|
||||
__attribute__ ((format (printf, 1, 2))) noreturn;
|
||||
char *mprintf(const char *, ...)
|
||||
__attribute__ ((format (printf, 1, 2)));
|
||||
void xpread(int, void *, size_t, off_t);
|
||||
|
||||
void __xpread(int, void *, size_t, off_t, const char *, unsigned);
|
||||
#define xpread(_fd, _buf, _count, _offset) \
|
||||
__xpread(_fd, _buf, _count, _offset, __FILE__, __LINE__)
|
||||
|
||||
void xpwrite(int, const void *, size_t, off_t, const char *);
|
||||
|
||||
struct stat xfstatat(int, const char *, int);
|
||||
struct stat xfstat(int);
|
||||
struct stat xstat(const char *);
|
||||
|
||||
@ -48,6 +48,7 @@ fn handle_c_command(mut argv: Vec<String>, symlink_cmd: Option<&str>) -> i32 {
|
||||
"data" => c::data_cmds(argc, argv),
|
||||
"device" => c::device_cmds(argc, argv),
|
||||
"dump" => c::cmd_dump(argc, argv),
|
||||
"undump" => c::cmd_undump(argc, argv),
|
||||
"format" => c::cmd_format(argc, argv),
|
||||
"fs" => c::fs_cmds(argc, argv),
|
||||
"fsck" => c::cmd_fsck(argc, argv),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user