summaryrefslogtreecommitdiffstats
path: root/mount
diff options
context:
space:
mode:
authorKarel Zak2011-03-29 10:19:56 +0200
committerKarel Zak2011-03-29 17:01:59 +0200
commitb7cdab2a4799de50cb6bcc387c5c602910f10d0b (patch)
tree4964510016f0514ad30d932f171679e82204b38e /mount
parentblkid: add -d option to print non-printable chars (diff)
downloadkernel-qcow2-util-linux-b7cdab2a4799de50cb6bcc387c5c602910f10d0b.tar.gz
kernel-qcow2-util-linux-b7cdab2a4799de50cb6bcc387c5c602910f10d0b.tar.xz
kernel-qcow2-util-linux-b7cdab2a4799de50cb6bcc387c5c602910f10d0b.zip
umount: use UMOUNT_NOFOLLOW for non-root users
The UMOUNT_NOFOLLOW umount2() flag is supported since kernel 2.6.34. Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'mount')
-rw-r--r--mount/umount.c98
1 files changed, 87 insertions, 11 deletions
diff --git a/mount/umount.c b/mount/umount.c
index 42671f465..347945c82 100644
--- a/mount/umount.c
+++ b/mount/umount.c
@@ -45,15 +45,22 @@ umount2(const char *path, int flags) {
}
#endif /* __NR_umount2 */
-#if !defined(MNT_FORCE)
-/* dare not try to include <linux/mount.h> -- lots of errors */
-#define MNT_FORCE 1
+#ifndef MNT_FORCE
+# define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */
#endif
#endif /* MNT_FORCE */
-#if !defined(MNT_DETACH)
-#define MNT_DETACH 2
+#ifndef MNT_DETACH
+# define MNT_DETACH 0x00000002 /* Just detach from the tree */
+#endif
+
+#ifndef UMOUNT_NOFOLLOW
+# define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+#ifndef UMOUNT_UNUSED
+# define UMOUNT_UNUSED 0x80000000 /* Flag guaranteed to be unused */
#endif
@@ -197,6 +204,61 @@ static void complain(int err, const char *dev) {
}
}
+/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
+static int umount_nofollow_support(void)
+{
+ int res = umount2("", UMOUNT_UNUSED);
+ if (res != -1 || errno != EINVAL)
+ return 0;
+
+ res = umount2("", UMOUNT_NOFOLLOW);
+ if (res != -1 || errno != ENOENT)
+ return 0;
+
+ return 1;
+}
+
+static const char *chdir_to_parent(const char *node, char **resbuf)
+{
+ char *tmp, *res;
+ const char *parent;
+ char buf[65536];
+
+ *resbuf = xstrdup(node);
+
+ tmp = strrchr(*resbuf, '/');
+ if (!tmp)
+ die (2, _("umount: internal error: invalid abs path: %s"), node);
+
+ if (tmp != *resbuf) {
+ *tmp = '\0';
+ res = tmp + 1;
+ parent = *resbuf;
+ } else if (tmp[1] != '\0') {
+ res = tmp + 1;
+ parent = "/";
+ } else {
+ res = ".";
+ parent = "/";
+ }
+
+ if (chdir(parent) == -1)
+ die (2, _("umount: failed to chdir to %s: %s"),
+ parent, strerror(errno));
+
+ if (!getcwd(buf, sizeof(buf)))
+ die (2, _("umount: failed to obtain current directory: %s"),
+ strerror(errno));
+
+ if (strcmp(buf, parent) != 0)
+ die (2, _("umount: mountpoint moved (%s -> %s)"), parent, buf);
+
+ if (verbose)
+ printf(_("current directory moved to %s\n"), res);
+
+ return res;
+}
+
/* Umount a single device. Return a status code, so don't exit
on a non-fatal error. We lock/unlock around each umount. */
static int
@@ -206,7 +268,9 @@ umount_one (const char *spec, const char *node, const char *type,
int isroot;
int res = 0;
int status;
- const char *loopdev;
+ int extra_flags = 0;
+ const char *loopdev, *target = node;
+ char *targetbuf = NULL;
int myloop = 0;
/* Special case for root. As of 0.99pl10 we can (almost) unmount root;
@@ -237,15 +301,23 @@ umount_one (const char *spec, const char *node, const char *type,
if (delloop && is_loop_device(spec))
myloop = 1;
+ if (restricted) {
+ if (umount_nofollow_support())
+ extra_flags |= UMOUNT_NOFOLLOW;
+
+ /* call umount(2) with relative path to avoid races */
+ target = chdir_to_parent(node, &targetbuf);
+ }
+
if (lazy) {
- res = umount2 (node, MNT_DETACH);
+ res = umount2 (target, MNT_DETACH | extra_flags);
if (res < 0)
umnt_err = errno;
goto writemtab;
}
if (force) { /* only supported for NFS */
- res = umount2 (node, MNT_FORCE);
+ res = umount2 (target, MNT_FORCE | extra_flags);
if (res == -1) {
int errsv = errno;
perror("umount2");
@@ -253,11 +325,15 @@ umount_one (const char *spec, const char *node, const char *type,
if (errno == ENOSYS) {
if (verbose)
printf(_("no umount2, trying umount...\n"));
- res = umount (node);
+ res = umount (target);
}
}
- } else
- res = umount (node);
+ } else if (extra_flags)
+ res = umount2 (target, extra_flags);
+ else
+ res = umount (target);
+
+ free(targetbuf);
if (res < 0)
umnt_err = errno;