[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