[PATCH] erofs-utils: mount: add unmount support
Gao Xiang
hsiangkao at linux.alibaba.com
Thu Aug 28 18:42:50 AEST 2025
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 <hsiangkao at 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
More information about the Linux-erofs
mailing list