cmd_fsck: Create loopback device for kernel fsck when necessary

fstests generic/648 now passes

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2024-10-12 15:13:41 -04:00
parent 282331defa
commit 4f9293b045

View File

@ -160,6 +160,45 @@ static bool should_use_kernel_fsck(darray_str devs)
return ret;
}
static bool is_blockdev(const char *path)
{
struct stat s;
if (stat(path, &s))
return true;
return S_ISBLK(s.st_mode);
}
static void loopdev_free(const char *path)
{
char *cmd = mprintf("losetup -d %s", path);
system(cmd);
free(cmd);
}
static char *loopdev_alloc(const char *path)
{
char *cmd = mprintf("losetup --show -f %s", path);
FILE *f = popen(cmd, "r");
free(cmd);
if (!f) {
fprintf(stderr, "error executing losetup: %m\n");
return NULL;
}
char *line = NULL;
size_t n = 0;
getline(&line, &n, f);
int ret = pclose(f);
if (ret) {
fprintf(stderr, "error executing losetup: %i\n", ret);
free(line);
return NULL;
}
strim(line);
return line;
}
int cmd_fsck(int argc, char *argv[])
{
static const struct option longopts[] = {
@ -243,19 +282,35 @@ int cmd_fsck(int argc, char *argv[])
struct printbuf parse_later = PRINTBUF;
if (kernel_probed) {
darray_str loopdevs = {};
int fsck_fd = -1;
printf("Running in-kernel offline fsck\n");
struct bch_ioctl_fsck_offline *fsck = calloc(sizeof(*fsck) +
sizeof(u64) * devs.nr, 1);
struct bch_ioctl_fsck_offline *fsck = calloc(sizeof(*fsck) + sizeof(u64) * devs.nr, 1);
fsck->opts = (unsigned long)opts_str.buf;
darray_for_each(devs, i)
darray_for_each(devs, i) {
if (is_blockdev(*i)) {
fsck->devs[i - devs.data] = (unsigned long) *i;
} else {
char *l = loopdev_alloc(*i);
if (!l)
goto kernel_fsck_err;
darray_push(&loopdevs, l);
fsck->devs[i - devs.data] = (unsigned long) l;
}
}
fsck->nr_devs = devs.nr;
int ctl_fd = bcachectl_open();
int fsck_fd = ioctl(ctl_fd, BCH_IOCTL_FSCK_OFFLINE, fsck);
fsck_fd = ioctl(ctl_fd, BCH_IOCTL_FSCK_OFFLINE, fsck);
kernel_fsck_err:
free(fsck);
darray_for_each(loopdevs, i)
loopdev_free(*i);
darray_exit(&loopdevs);
if (fsck_fd < 0 && kernel < 0)
goto userland_fsck;