]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mount-tool: modernize umount and make sure it works for bind mounted files
authorLennart Poettering <lennart@poettering.net>
Fri, 7 Feb 2025 12:43:30 +0000 (13:43 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 18 Feb 2025 12:49:24 +0000 (13:49 +0100)
So far, "systemd-umount" executed on a bind mounted file would assume it
is supposed to unmount a loopback mounted file system. Let's address
that by instead checking if the file is a mount.

src/mount/mount-tool.c

index d1b4773ce8cb1d5d337112e9b879ef4d89e771d4..3dd34b9e7391ca2319b4d30746c1a2acffc60223 100644 (file)
@@ -1044,7 +1044,11 @@ static int action_umount(
                 int argc,
                 char **argv) {
 
-        int r, r2 = 0;
+        int r, ret = 0;
+
+        assert(bus);
+        assert(argv);
+        assert(argc > optind);
 
         if (arg_transport != BUS_TRANSPORT_LOCAL) {
                 for (int i = optind; i < argc; i++) {
@@ -1054,46 +1058,52 @@ static int action_umount(
                         if (r < 0)
                                 return r;
 
-                        r = stop_mounts(bus, p);
-                        if (r < 0)
-                                r2 = r;
+                        RET_GATHER(ret, stop_mounts(bus, p));
                 }
-                return r2;
+                return ret;
         }
 
         for (int i = optind; i < argc; i++) {
                 _cleanup_free_ char *u = NULL, *p = NULL;
-                struct stat st;
 
                 u = fstab_node_to_udev_node(argv[i]);
                 if (!u)
                         return log_oom();
 
-                r = chase(u, NULL, 0, &p, NULL);
+                _cleanup_close_ int fd = -EBADF;
+                r = chase(u, /* root= */ NULL, 0, &p, &fd);
                 if (r < 0) {
-                        r2 = log_error_errno(r, "Failed to make path %s absolute: %m", argv[i]);
+                        RET_GATHER(ret, log_error_errno(r, "Failed to make path %s absolute: %m", u));
                         continue;
                 }
 
-                if (stat(p, &st) < 0)
+                struct stat st;
+                if (fstat(fd, &st) < 0)
                         return log_error_errno(errno, "Can't stat %s (from %s): %m", p, argv[i]);
 
-                if (S_ISBLK(st.st_mode))
-                        r = umount_by_device_node(bus, p);
-                else if (S_ISREG(st.st_mode))
-                        r = umount_loop(bus, p);
-                else if (S_ISDIR(st.st_mode))
-                        r = stop_mounts(bus, p);
+                r = is_mount_point_at(fd, /* filename= */ NULL, /* flags= */ 0);
+                fd = safe_close(fd); /* before continuing make sure the dir is not keeping anything busy */
+                if (r > 0)
+                        RET_GATHER(ret, stop_mounts(bus, p));
                 else {
-                        log_error("Invalid file type: %s (from %s)", p, argv[i]);
-                        r = -EINVAL;
+                        /* This can realistically fail on pre-5.8 kernels that do not tell us via statx() if
+                         * something is a mount point, hence handle this gracefully, and go by type as we did
+                         * in pre-v258 times. */
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to determine if '%s' is a mount point, ignoring: %m", u);
+
+                        if (S_ISBLK(st.st_mode))
+                                RET_GATHER(ret, umount_by_device_node(bus, p));
+                        else if (S_ISREG(st.st_mode))
+                                RET_GATHER(ret, umount_loop(bus, p));
+                        else if (S_ISDIR(st.st_mode))
+                                RET_GATHER(ret, stop_mounts(bus, p));
+                        else
+                                RET_GATHER(ret, log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid file type: %s (from %s)", p, argv[i]));
                 }
-
-                if (r < 0)
-                        r2 = r;
         }
 
-        return r2;
+        return ret;
 }
 
 static int acquire_mount_type(sd_device *d) {