mounting fixes
authorMiklos Szeredi <miklos@szeredi.hu>
Sun, 10 Dec 2006 00:13:53 +0000 (00:13 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Sun, 10 Dec 2006 00:13:53 +0000 (00:13 +0000)
ChangeLog
util/fusermount.c
util/mount.fuse

index a28a5b0f054a475c62b76dc1d6d80d27455bd46c..04154eb7b1449c8d2d4a3278cdd358663262e5de 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2006-12-10  Miklos Szeredi <miklos@szeredi.hu>
+
+       * mount.fuse: add "setuid=USER" option which does a "su - USER"
+       for the filesystem
+
+       * fusermount: use "/bin/mount -f" to add entry to /etc/mtab, and
+       "/bin/umount" to remove entry from /etc/mtab.  This gets rid of
+       the ugly code dealing with mtab, as well as a possible race
+       between fusermount and mount trying to modify /etc/mtab at the
+       same time
+
 2006-12-04  Miklos Szeredi <miklos@szeredi.hu>
 
        * Fix warnings with gcc-4.1 on 64bit archs.  Report from
index 2b84db540c0a5f25926aa6b60513030bd9f377bb..47f335c215cb7d254c82c83eedd3383751786285 100644 (file)
@@ -7,15 +7,6 @@
 */
 /* This program does the mounting and unmounting of FUSE filesystems */
 
-/*
- * NOTE: This program should be part of (or be called from) /bin/mount
- *
- * Unless that is done, operations on /etc/mtab are not under lock, and so
- * data in this file may be lost. (I will _not_ reimplement that locking,
- * and anyway that should be done in libc, if possible.  But probably it
- * isn't).
- */
-
 #include <config.h>
 
 #include <stdio.h>
 #include <pwd.h>
 #include <mntent.h>
 #include <dirent.h>
-#include <sys/param.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <sys/fsuid.h>
 #include <sys/socket.h>
-#include <sys/un.h>
 #include <sys/utsname.h>
-#include <sys/sysmacros.h>
-
-#ifdef HAVE_SELINUX_SELINUX_H
-#include <selinux/selinux.h>
-#endif
 
 #define FUSE_COMMFD_ENV         "_FUSE_COMMFD"
 
@@ -91,239 +75,119 @@ static void restore_privs(void)
 
 static int do_unmount(const char *mnt, int quiet, int lazy)
 {
-    int res = umount2(mnt, lazy ? 2 : 0);
-    if (res == -1) {
-        if (!quiet)
-            fprintf(stderr, "%s: failed to unmount %s: %s\n",
-                    progname, mnt, strerror(errno));
-    }
-    return res;
-}
-
-#ifndef IGNORE_MTAB
-/* use a lock file so that multiple fusermount processes don't try and
-   modify the mtab file at once! */
-static int lock_mtab(void)
-{
-    const char *mtab_lock = _PATH_MOUNTED ".fuselock";
-    int mtablock;
     int res;
-    struct stat mtab_stat;
+    int status;
 
-    /* /etc/mtab could be a symlink to /proc/mounts */
-    if (!lstat(_PATH_MOUNTED, &mtab_stat)) {
-        if (S_ISLNK(mtab_stat.st_mode))
-            return -1;
+    (void) quiet;
+    res = fork();
+    if (res == -1) {
+        perror("fork");
+        return -1;
     }
-
-    mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
-    if (mtablock >= 0) {
-        res = lockf(mtablock, F_LOCK, 0);
-        if (res < 0)
-            fprintf(stderr, "%s: error getting lock", progname);
-    } else
-        fprintf(stderr, "%s: unable to open fuse lock file\n", progname);
-
-    return mtablock;
-}
-
-static void unlock_mtab(int mtablock)
-{
-    if (mtablock >= 0) {
-       lockf(mtablock, F_ULOCK, 0);
-       close(mtablock);
+    if (res == 0) {
+        setuid(geteuid());
+        execl("/bin/umount", "/bin/umount", "-i", mnt, lazy ? "-l" : NULL,
+              NULL);
+        perror("execl /bin/umount");
+        exit(1);
     }
-}
-
-/* Glibc addmntent() doesn't encode '\n', misencodes '\t' as '\n'
-   (version 2.3.2), and encodes '\\' differently as mount(8).  So
-   let's not allow those characters, they are not all that usual in
-   filenames. */
-static int check_name(const char *name)
-{
-    const char *s;
-    for (s = "\n\t\\"; *s; s++) {
-        if (strchr(name, *s)) {
-            fprintf(stderr, "%s: illegal character 0x%02x in mount entry\n",
-                    progname, *s);
-            return -1;
-        }
+    res = waitpid(res, &status, 0);
+    if (res == -1) {
+        perror("waitpid");
+        return -1;
     }
+    if (status != 0)
+        return -1;
+
     return 0;
 }
 
+#ifndef IGNORE_MTAB
 static int add_mount(const char *fsname, const char *mnt, const char *type,
                      const char *opts)
 {
     int res;
-    const char *mtab = _PATH_MOUNTED;
-    struct mntent ent;
-    FILE *fp;
+    int status;
 
-    if (check_name(fsname) == -1 || check_name(mnt) == -1 ||
-        check_name(type) == -1 || check_name(opts) == -1)
-        return -1;
-
-    fp = setmntent(mtab, "a");
-    if (fp == NULL) {
-       fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
-               strerror(errno));
-       return -1;
-    }
-
-    ent.mnt_fsname = (char *) fsname;
-    ent.mnt_dir = (char *) mnt;
-    ent.mnt_type = (char *) type;
-    ent.mnt_opts = (char *) opts;
-    ent.mnt_freq = 0;
-    ent.mnt_passno = 0;
-    res = addmntent(fp, &ent);
-    if (res != 0) {
-        fprintf(stderr, "%s: failed to add entry to %s: %s\n", progname,
-                mtab, strerror(errno));
+    res = fork();
+    if (res == -1) {
+        perror("fork");
         return -1;
     }
-
-    endmntent(fp);
-    return 0;
-}
-
-static int unmount_rename(const char *mtab, const char *mtab_new)
-{
-    int res;
-    struct stat sbuf;
-
-    if (stat(mtab, &sbuf) == 0)
-        chown(mtab_new, sbuf.st_uid, sbuf.st_gid);
-
-#ifdef HAVE_LIBSELINUX
-    {
-        security_context_t filecon;
-
-        if (getfilecon(mtab, &filecon) > 0) {
-            setfilecon(mtab_new, filecon);
-            if (filecon != NULL)
-                freecon(filecon);
-        }
+    if (res == 0) {
+        setuid(geteuid());
+        execl("/bin/mount", "/bin/mount", "-i", "-f", "-t", type, "-o", opts,
+              fsname, mnt, NULL);
+        perror("execl /bin/mount");
+        exit(1);
     }
-#endif
-
-    res = rename(mtab_new, mtab);
+    res = waitpid(res, &status, 0);
     if (res == -1) {
-        fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname,
-                mtab_new, mtab, strerror(errno));
+        perror("waitpid");
         return -1;
     }
+    if (status != 0)
+        return -1;
+
     return 0;
 }
 
 static int unmount_fuse(const char *mnt, int quiet, int lazy)
 {
-    int res;
-    struct mntent *entp;
-    FILE *fp;
-    FILE *newfp = NULL;
-    const char *user = NULL;
-    char uidstr[32];
-    unsigned uidlen = 0;
-    int found;
-    int issymlink = 0;
-    struct stat stbuf;
-    const char *mtab = _PATH_MOUNTED;
-    const char *mtab_new = _PATH_MOUNTED "~fuse~";
-
-    if (lstat(mtab, &stbuf) != -1 && S_ISLNK(stbuf.st_mode))
-        issymlink = 1;
+    if (getuid() != 0) {
+        struct mntent *entp;
+        FILE *fp;
+        const char *user = NULL;
+        char uidstr[32];
+        unsigned uidlen = 0;
+        int found;
+        const char *mtab = _PATH_MOUNTED;
 
-    fp = setmntent(mtab, "r");
-    if (fp == NULL) {
-       fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
-               strerror(errno));
-       return -1;
-    }
+        user = get_user_name();
+        if (user == NULL)
+            return -1;
 
-    if (!issymlink) {
-        newfp = setmntent(mtab_new, "w");
-        if (newfp == NULL) {
-            fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab_new,
+        fp = setmntent(mtab, "r");
+        if (fp == NULL) {
+            fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
                     strerror(errno));
-            endmntent(fp);
             return -1;
         }
-    }
-
-    if (getuid() != 0) {
-        user = get_user_name();
-        if (user == NULL)
-            goto err_endmntent;
 
         uidlen = sprintf(uidstr, "%u", getuid());
-    }
 
-    found = 0;
-    while ((entp = getmntent(fp)) != NULL) {
-        int removed = 0;
-        if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
-            (strcmp(entp->mnt_type, "fuse") == 0 ||
-             strcmp(entp->mnt_type, "fuseblk") == 0)) {
-            if (user == NULL)
-                removed = 1;
-            else {
+        found = 0;
+        while ((entp = getmntent(fp)) != NULL) {
+            if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
+                (strcmp(entp->mnt_type, "fuse") == 0 ||
+                 strcmp(entp->mnt_type, "fuseblk") == 0)) {
                 char *p = strstr(entp->mnt_opts, "user=");
                 if (p && (p == entp->mnt_opts || *(p-1) == ',') &&
-                    strcmp(p + 5, user) == 0)
-                    removed = 1;
+                    strcmp(p + 5, user) == 0) {
+                    found = 1;
+                    break;
+                }
                 /* /etc/mtab is a link pointing to /proc/mounts: */
                 else if ((p = strstr(entp->mnt_opts, "user_id=")) &&
                          (p == entp->mnt_opts || *(p-1) == ',') &&
                          strncmp(p + 8, uidstr, uidlen) == 0 &&
-                         (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0'))
-                    removed = 1;
-            }
-        }
-        if (removed)
-            found = 1;
-        else if (!issymlink) {
-            res = addmntent(newfp, entp);
-            if (res != 0) {
-                fprintf(stderr, "%s: failed to add entry to %s: %s\n",
-                        progname, mtab_new, strerror(errno));
+                         (*(p+8+uidlen) == ',' || *(p+8+uidlen) == '\0')) {
+                    found = 1;
+                    break;
+                }
             }
         }
-    }
-
-    endmntent(fp);
-    if (!issymlink)
-        endmntent(newfp);
+        endmntent(fp);
 
-    if (!found) {
-        if (!quiet)
-            fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
-                    mnt, mtab);
-        goto err;
-    }
-
-    drop_privs();
-    res = do_unmount(mnt, quiet, lazy);
-    restore_privs();
-    if (res == -1)
-        goto err;
-
-    if (!issymlink) {
-        res = unmount_rename(mtab, mtab_new);
-        if (res == -1)
-            goto err;
+        if (!found) {
+            if (!quiet)
+                fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
+                        mnt, mtab);
+            return -1;
+        }
     }
-    return 0;
 
- err_endmntent:
-    if (!issymlink)
-        endmntent(newfp);
-    endmntent(fp);
- err:
-    if (!issymlink)
-        unlink(mtab_new);
-    return -1;
+    return do_unmount(mnt, quiet, lazy);
 }
 
 static int count_fuse_fs(void)
@@ -347,16 +211,6 @@ static int count_fuse_fs(void)
 
 
 #else /* IGNORE_MTAB */
-static int lock_mtab()
-{
-    return 0;
-}
-
-static void unlock_mtab(int mtablock)
-{
-    (void) mtablock;
-}
-
 static int count_fuse_fs()
 {
     return 0;
@@ -902,15 +756,11 @@ static int mount_fuse(const char *mnt, const char *opts)
     const char *real_mnt = mnt;
     int currdir_fd = -1;
     int mountpoint_fd = -1;
-    int mtablock = -1;
 
     fd = open_fuse_device(&dev);
     if (fd == -1)
         return -1;
 
-    if (geteuid() == 0)
-        mtablock = lock_mtab();
-
     drop_privs();
     read_conf();
 
@@ -919,7 +769,6 @@ static int mount_fuse(const char *mnt, const char *opts)
         if (mount_count >= mount_max) {
             fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
             close(fd);
-            unlock_mtab(mtablock);
             return -1;
         }
     }
@@ -943,13 +792,11 @@ static int mount_fuse(const char *mnt, const char *opts)
 
     if (res == -1) {
         close(fd);
-        unlock_mtab(mtablock);
         return -1;
     }
 
     if (geteuid() == 0) {
         res = add_mount(fsname, mnt, type, mnt_opts);
-        unlock_mtab(mtablock);
         if (res == -1) {
             umount2(mnt, 2); /* lazy umount */
             close(fd);
@@ -1166,12 +1013,14 @@ int main(int argc, char *argv[])
 
     umask(033);
     if (unmount) {
-        if (geteuid() == 0) {
-            int mtablock = lock_mtab();
+        if (geteuid() == 0)
             res = unmount_fuse(mnt, quiet, lazy);
-            unlock_mtab(mtablock);
-        } else
-            res = do_unmount(mnt, quiet, lazy);
+        else {
+            res = umount2(mnt, lazy ? 2 : 0);
+            if (res == -1 && !quiet)
+                fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
+                        mnt, strerror(errno));
+        }
         if (res == -1)
             exit(1);
         return 0;
index 0b111db399868ad002343a940dd130f5313fdc11..2a74449bc9b28ad04fdc2303330bb45c7fa476af 100644 (file)
@@ -45,8 +45,14 @@ MOUNTPOINT="$2"
 shift
 shift
 
-ignore_opts='\(user\|nouser\|users\|auto\|noauto\|_netdev\)'
+eval `echo $@ | sed -n "s/\([^,]*,\)*setuid=\([^,]*\).*/SETUID=\2/p"`
+
+ignore_opts='\(user\|nouser\|users\|auto\|noauto\|_netdev\|setuid=[^,]*\)'
 
 OPTIONS=`echo $@ | sed "s/,${ignore_opts}\|${ignore_opts},//g"`
 
-${FSTYPE} ${MOUNTPATH} ${MOUNTPOINT} ${OPTIONS}
+if test -z "$SETUID"; then
+    ${FSTYPE} ${MOUNTPATH} ${MOUNTPOINT} ${OPTIONS}
+else
+    su - "$SETUID" -c "${FSTYPE} ${MOUNTPATH} ${MOUNTPOINT} ${OPTIONS}"
+fi