[PATCH v2 4/4] erofs-utils: mount: recover from detached NBD devices
Gao Xiang
hsiangkao at linux.alibaba.com
Wed Sep 3 12:18:58 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>
---
v2:
- fix nbd recovery with `backend` sysfs files.
include/erofs/defs.h | 3 +
lib/backends/nbd.c | 23 +++++++
lib/liberofs_nbd.h | 2 +
mount/main.c | 151 ++++++++++++++++++++++++++++++++++++++-----
4 files changed, 164 insertions(+), 15 deletions(-)
diff --git a/include/erofs/defs.h b/include/erofs/defs.h
index 6a1ac5c..71ca11b 100644
--- a/include/erofs/defs.h
+++ b/include/erofs/defs.h
@@ -384,6 +384,9 @@ unsigned long __roundup_pow_of_two(unsigned long n)
# define __erofs_fallthrough do {} while (0) /* fallthrough */
#endif
+#define __erofs_stringify_1(x...) #x
+#define __erofs_stringify(x...) __erofs_stringify_1(x)
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/backends/nbd.c b/lib/backends/nbd.c
index bf1b43c..682fc7b 100644
--- a/lib/backends/nbd.c
+++ b/lib/backends/nbd.c
@@ -194,6 +194,29 @@ char *erofs_nbd_get_identifier(int nbdnum)
return err ? ERR_PTR(err) : line;
}
+int erofs_nbd_get_index_from_minor(int minor)
+{
+ char s[32], *line = NULL;
+ int ret = -ENOENT;
+ size_t n;
+ FILE *f;
+
+ (void)snprintf(s, sizeof(s),
+ "/sys/dev/block/" __erofs_stringify(EROFS_NBD_MAJOR) ":%d/uevent", minor);
+ f = fopen(s, "r");
+ if (!f)
+ return -errno;
+
+ while (getline(&line, &n, f) >= 0) {
+ if (strncmp(line, "DEVNAME=nbd", sizeof("DEVNAME=nbd") - 1))
+ continue;
+ ret = strtoul(line + sizeof("DEVNAME=nbd") - 1, NULL, 10);
+ break;
+ }
+ free(line);
+ return ret;
+}
+
#if defined(HAVE_NETLINK_GENL_GENL_H) && defined(HAVE_LIBNL_GENL_3)
enum {
NBD_ATTR_UNSPEC,
diff --git a/lib/liberofs_nbd.h b/lib/liberofs_nbd.h
index 03886de..049c318 100644
--- a/lib/liberofs_nbd.h
+++ b/lib/liberofs_nbd.h
@@ -37,6 +37,8 @@ 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_get_index_from_minor(int minor);
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 95d31d9..f87979f 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) {
@@ -428,6 +441,105 @@ out_fork:
return num;
}
+static int erofsmount_reattach(const char *target)
+{
+ char *identifier, *line, *source, *recp = NULL;
+ struct erofsmount_nbd_ctx ctx = {};
+ 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 = erofs_nbd_get_index_from_minor(minor(st.st_rdev));
+ if (nbdnum < 0)
+ return nbdnum;
+ identifier = erofs_nbd_get_identifier(nbdnum);
+ if (IS_ERR(identifier))
+ identifier = NULL;
+ else if (identifier) {
+ n = strlen(identifier);
+ if (__erofs_unlikely(!n)) {
+ free(identifier);
+ identifier = NULL;
+ } else if (identifier[n - 1] == '\n') {
+ identifier[n - 1] = '\0';
+ }
+ }
+
+ 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)
@@ -446,6 +558,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;
@@ -649,37 +762,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.0
More information about the Linux-erofs
mailing list