[PATCH 4/4] erofs-utils: mount: recover from detached NBD devices
Gao Xiang
hsiangkao at linux.alibaba.com
Wed Sep 3 01:06:10 AEST 2025
Add support for reconnecting to devices using recovery files, e.g.
$ sudo killall -9 mount.erofs
$ sudo mount.erofs --reattach /dev/nbdX
Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
lib/liberofs_nbd.h | 1 +
mount/main.c | 140 ++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 126 insertions(+), 15 deletions(-)
diff --git a/lib/liberofs_nbd.h b/lib/liberofs_nbd.h
index 03886de..cc80899 100644
--- a/lib/liberofs_nbd.h
+++ b/lib/liberofs_nbd.h
@@ -37,6 +37,7 @@ struct erofs_nbd_request {
long erofs_nbd_in_service(int nbdnum);
int erofs_nbd_devscan(void);
int erofs_nbd_connect(int nbdfd, int blkbits, u64 blocks);
+char *erofs_nbd_get_identifier(int nbdnum);
int erofs_nbd_do_it(int nbdfd);
int erofs_nbd_get_request(int skfd, struct erofs_nbd_request *rq);
int erofs_nbd_send_reply_header(int skfd, __le64 cookie, int err);
diff --git a/mount/main.c b/mount/main.c
index a534dd3..d2d292b 100644
--- a/mount/main.c
+++ b/mount/main.c
@@ -41,15 +41,21 @@ enum erofs_backend_drv {
EROFSNBD,
};
+enum erofsmount_mode {
+ EROFSMOUNT_MODE_MOUNT,
+ EROFSMOUNT_MODE_UMOUNT,
+ EROFSMOUNT_MODE_REATTACH,
+};
+
static struct erofsmount_cfg {
char *device;
- char *mountpoint;
+ char *target;
char *options;
char *full_options; /* used for erofsfuse */
char *fstype;
long flags;
enum erofs_backend_drv backend;
- bool umount;
+ enum erofsmount_mode mountmode;
} mountcfg = {
.full_options = "ro",
.flags = MS_RDONLY, /* default mountflags */
@@ -121,6 +127,7 @@ static int erofsmount_parse_options(int argc, char **argv)
{
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
+ {"reattach", no_argument, 0, 512},
{0, 0, 0, 0},
};
char *dot;
@@ -153,13 +160,16 @@ static int erofsmount_parse_options(int argc, char **argv)
mountcfg.fstype = optarg;
break;
case 'u':
- mountcfg.umount = true;
+ mountcfg.mountmode = EROFSMOUNT_MODE_UMOUNT;
+ break;
+ case 512:
+ mountcfg.mountmode = EROFSMOUNT_MODE_REATTACH;
break;
default:
return -EINVAL;
}
}
- if (!mountcfg.umount) {
+ if (mountcfg.mountmode == EROFSMOUNT_MODE_MOUNT) {
if (optind >= argc) {
erofs_err("missing argument: DEVICE");
return -EINVAL;
@@ -170,12 +180,15 @@ static int erofsmount_parse_options(int argc, char **argv)
return -ENOMEM;
}
if (optind >= argc) {
- erofs_err("missing argument: MOUNTPOINT");
+ if (mountcfg.mountmode == EROFSMOUNT_MODE_MOUNT)
+ erofs_err("missing argument: MOUNTPOINT");
+ else
+ erofs_err("missing argument: TARGET");
return -EINVAL;
}
- mountcfg.mountpoint = strdup(argv[optind++]);
- if (!mountcfg.mountpoint)
+ mountcfg.target = strdup(argv[optind++]);
+ if (!mountcfg.target)
return -ENOMEM;
if (optind < argc) {
@@ -418,6 +431,94 @@ out_fork:
return num;
}
+static int erofsmount_reattach(const char *target)
+{
+ struct erofsmount_nbd_ctx ctx = {};
+ char *identifier, *line, *source, *recp;
+ int nbdnum, err;
+ struct stat st;
+ size_t n;
+ FILE *f;
+
+ err = lstat(target, &st);
+ if (err < 0)
+ return -errno;
+
+ if (!S_ISBLK(st.st_mode) || major(st.st_rdev) != EROFS_NBD_MAJOR)
+ return -ENOTBLK;
+ nbdnum = minor(st.st_rdev);
+
+ identifier = erofs_nbd_get_identifier(nbdnum);
+ if (IS_ERR(identifier))
+ identifier = NULL;
+
+ if (!identifier &&
+ (asprintf(&recp, "/var/run/erofs/mountnbd_nbd%d", nbdnum) <= 0)) {
+ err = -ENOMEM;
+ goto err_identifier;
+ }
+
+ f = fopen(identifier ?: recp, "r");
+ if (!f) {
+ err = -errno;
+ free(recp);
+ goto err_identifier;
+ }
+ free(recp);
+
+ line = NULL;
+ if ((err = getline(&line, &n, f)) <= 0) {
+ err = -errno;
+ fclose(f);
+ goto err_identifier;
+ }
+ fclose(f);
+ if (err && line[err - 1] == '\n')
+ line[err - 1] = '\0';
+
+ source = strchr(line, ' ');
+ if (!source) {
+ erofs_err("invalid source recorded in recovery file: %s", line);
+ err = -EINVAL;
+ goto err_line;
+ } else {
+ *(source++) = '\0';
+ }
+
+ if (strcmp(line, "LOCAL")) {
+ err = -EOPNOTSUPP;
+ erofs_err("unsupported source type %s recorded in recovery file", line);
+ goto err_line;
+ }
+
+ err = open(source, O_RDONLY);
+ if (err < 0) {
+ err = -errno;
+ goto err_line;
+ }
+ ctx.vd.fd = err;
+
+ err = erofs_nbd_nl_reconnect(nbdnum, identifier);
+ if (err >= 0) {
+ ctx.sk.fd = err;
+ if (fork() == 0) {
+ free(line);
+ free(identifier);
+ if ((uintptr_t)erofsmount_nbd_loopfn(&ctx))
+ return EXIT_FAILURE;
+ return EXIT_SUCCESS;
+ }
+ close(ctx.sk.fd);
+ err = 0;
+ }
+ close(ctx.vd.fd);
+err_line:
+ free(line);
+err_identifier:
+ free(identifier);
+ return err;
+}
+
static int erofsmount_nbd(const char *source, const char *mountpoint,
const char *fstype, int flags,
const char *options)
@@ -436,6 +537,7 @@ static int erofsmount_nbd(const char *source, const char *mountpoint,
err = erofsmount_startnbd_nl(&pid, source);
if (err < 0) {
+ erofs_info("Fall back to ioctl-based NBD; failover is unsupported");
num = erofs_nbd_devscan();
if (num < 0)
return num;
@@ -639,37 +741,45 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
- if (mountcfg.umount) {
- err = erofsmount_umount(mountcfg.mountpoint);
+ if (mountcfg.mountmode == EROFSMOUNT_MODE_UMOUNT) {
+ err = erofsmount_umount(mountcfg.target);
if (err < 0)
fprintf(stderr, "Failed to unmount %s: %s\n",
- mountcfg.mountpoint, erofs_strerror(err));
+ mountcfg.target, erofs_strerror(err));
+ return err ? EXIT_FAILURE : EXIT_SUCCESS;
+ }
+
+ if (mountcfg.mountmode == EROFSMOUNT_MODE_REATTACH) {
+ err = erofsmount_reattach(mountcfg.target);
+ if (err < 0)
+ fprintf(stderr, "Failed to reattach %s: %s\n",
+ mountcfg.target, erofs_strerror(err));
return err ? EXIT_FAILURE : EXIT_SUCCESS;
}
if (mountcfg.backend == EROFSFUSE) {
- err = erofsmount_fuse(mountcfg.device, mountcfg.mountpoint,
+ err = erofsmount_fuse(mountcfg.device, mountcfg.target,
mountcfg.fstype, mountcfg.full_options);
goto exit;
}
if (mountcfg.backend == EROFSNBD) {
- err = erofsmount_nbd(mountcfg.device, mountcfg.mountpoint,
+ err = erofsmount_nbd(mountcfg.device, mountcfg.target,
mountcfg.fstype, mountcfg.flags,
mountcfg.options);
goto exit;
}
- err = mount(mountcfg.device, mountcfg.mountpoint, mountcfg.fstype,
+ err = mount(mountcfg.device, mountcfg.target, mountcfg.fstype,
mountcfg.flags, mountcfg.options);
if (err < 0)
err = -errno;
if ((err == -ENODEV || err == -EPERM) && mountcfg.backend == EROFSAUTO)
- err = erofsmount_fuse(mountcfg.device, mountcfg.mountpoint,
+ err = erofsmount_fuse(mountcfg.device, mountcfg.target,
mountcfg.fstype, mountcfg.full_options);
else if (err == -ENOTBLK)
- err = erofsmount_loopmount(mountcfg.device, mountcfg.mountpoint,
+ err = erofsmount_loopmount(mountcfg.device, mountcfg.target,
mountcfg.fstype, mountcfg.flags,
mountcfg.options);
exit:
--
2.43.5
More information about the Linux-erofs
mailing list