umount(8) may only unmount the filesystem without disconnecting the NBD device.
Add `mount.erofs -u <DIR|BDEV>` to manually disconnect devices for maintenance. Signed-off-by: Gao Xiang <hsiang...@linux.alibaba.com> --- lib/backends/nbd.c | 10 ++++ lib/liberofs_nbd.h | 3 + mount/main.c | 134 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 138 insertions(+), 9 deletions(-) diff --git a/lib/backends/nbd.c b/lib/backends/nbd.c index 398a1e9..43630f0 100644 --- a/lib/backends/nbd.c +++ b/lib/backends/nbd.c @@ -24,6 +24,7 @@ #define NBD_DO_IT _IO( 0xab, 3 ) #define NBD_CLEAR_SOCK _IO( 0xab, 4 ) #define NBD_SET_SIZE_BLOCKS _IO( 0xab, 7 ) +#define NBD_DISCONNECT _IO( 0xab, 8 ) #define NBD_SET_TIMEOUT _IO( 0xab, 9 ) #define NBD_SET_FLAGS _IO( 0xab, 10) @@ -221,3 +222,12 @@ int erofs_nbd_send_reply_header(int skfd, __le64 cookie, int err) return 0; return ret < 0 ? -errno : -EIO; } + +int erofs_nbd_disconnect(int nbdfd) +{ + int err, err2; + + err = ioctl(nbdfd, NBD_DISCONNECT); + err2 = ioctl(nbdfd, NBD_CLEAR_SOCK); + return err ?: err2; +} diff --git a/lib/liberofs_nbd.h b/lib/liberofs_nbd.h index 6660df1..a68b7b1 100644 --- a/lib/liberofs_nbd.h +++ b/lib/liberofs_nbd.h @@ -7,6 +7,8 @@ #include "erofs/defs.h" +#define NBD_MAJOR 43 /* Network block device */ + /* Supported request types */ enum { EROFS_NBD_CMD_READ = 0, @@ -35,5 +37,6 @@ int erofs_nbd_connect(int nbdfd, int blkbits, u64 blocks); 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); +int erofs_nbd_disconnect(int nbdfd); #endif diff --git a/mount/main.c b/mount/main.c index 9cb203f..55181bc 100644 --- a/mount/main.c +++ b/mount/main.c @@ -6,6 +6,7 @@ #include <stdlib.h> #include <string.h> #include <sys/mount.h> +#include <sys/types.h> #include <pthread.h> #include <unistd.h> #include "erofs/config.h" @@ -28,6 +29,9 @@ struct loop_info { char pad1[120]; }; #endif +#ifdef HAVE_SYS_SYSMACROS_H +#include <sys/sysmacros.h> +#endif enum erofs_backend_drv { EROFSAUTO, @@ -44,6 +48,7 @@ static struct erofsmount_cfg { char *fstype; long flags; enum erofs_backend_drv backend; + bool umount; } mountcfg = { .full_options = "ro", .flags = MS_RDONLY, /* default mountflags */ @@ -120,7 +125,7 @@ static int erofsmount_parse_options(int argc, char **argv) char *dot; int opt; - while ((opt = getopt_long(argc, argv, "Nfno:st:v", + while ((opt = getopt_long(argc, argv, "Nfno:st:uv", long_options, NULL)) != -1) { switch (opt) { case 'o': @@ -146,20 +151,23 @@ static int erofsmount_parse_options(int argc, char **argv) } mountcfg.fstype = optarg; break; + case 'u': + mountcfg.umount = true; + break; default: return -EINVAL; } } + if (!mountcfg.umount) { + if (optind >= argc) { + erofs_err("missing argument: DEVICE"); + return -EINVAL; + } - if (optind >= argc) { - erofs_err("missing argument: DEVICE"); - return -EINVAL; + mountcfg.device = strdup(argv[optind++]); + if (!mountcfg.device) + return -ENOMEM; } - - mountcfg.device = strdup(argv[optind++]); - if (!mountcfg.device) - return -ENOMEM; - if (optind >= argc) { erofs_err("missing argument: MOUNTPOINT"); return -EINVAL; @@ -394,6 +402,106 @@ out_err: return -errno; } +int erofsmount_umount(char *target) +{ + char *device = NULL, *mountpoint = NULL; + struct stat st; + FILE *mounts; + int err, fd; + size_t n; + char *s; + bool isblk; + + target = realpath(target, NULL); + if (!target) + return -errno; + + err = lstat(target, &st); + if (err < 0) { + err = -errno; + goto err_out; + } + + if (S_ISBLK(st.st_mode)) { + isblk = true; + } else if (S_ISDIR(st.st_mode)) { + isblk = false; + } else { + err = -EINVAL; + goto err_out; + } + + mounts = fopen("/proc/mounts", "r"); + if (!mounts) { + err = -ENOENT; + goto err_out; + } + + for (s = NULL; (getline(&s, &n, mounts)) > 0;) { + bool hit = false; + char *f1, *f2, *end; + + f1 = s; + end = strchr(f1, ' '); + if (end) + *end = '\0'; + if (isblk && !strcmp(f1, target)) + hit = true; + if (end) { + f2 = end + 1; + end = strchr(f2, ' '); + if (end) + *end = '\0'; + if (!isblk && !strcmp(f2, target)) + hit = true; + } + if (hit) { + if (isblk) { + err = -EBUSY; + free(s); + fclose(mounts); + goto err_out; + } + free(device); + device = strdup(f1); + if (!mountpoint) + mountpoint = strdup(f2); + } + } + free(s); + fclose(mounts); + if (!isblk && !device) { + err = -ENOENT; + goto err_out; + } + + /* Avoid TOCTOU issue with NBD_CFLAG_DISCONNECT_ON_CLOSE */ + fd = open(isblk ? target : device, O_RDWR); + if (fd < 0) { + err = -errno; + goto err_out; + } + if (mountpoint) { + err = umount(mountpoint); + if (err) { + err = -errno; + close(fd); + goto err_out; + } + } + err = fstat(fd, &st); + if (err < 0) + err = -errno; + else if (S_ISBLK(st.st_mode) && major(st.st_rdev) == NBD_MAJOR) + err = erofs_nbd_disconnect(fd); + close(fd); +err_out: + free(device); + free(mountpoint); + free(target); + return err < 0 ? err : 0; +} + int main(int argc, char *argv[]) { int err; @@ -406,6 +514,14 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + if (mountcfg.umount) { + err = erofsmount_umount(mountcfg.mountpoint); + if (err < 0) + fprintf(stderr, "Failed to unmount %s: %s\n", + mountcfg.mountpoint, erofs_strerror(err)); + return err ? EXIT_FAILURE : EXIT_SUCCESS; + } + if (mountcfg.backend == EROFSFUSE) { err = erofsmount_fuse(mountcfg.device, mountcfg.mountpoint, mountcfg.fstype, mountcfg.full_options); -- 2.43.5