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 <hsiang...@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


Reply via email to