fix
authorMiklos Szeredi <miklos@szeredi.hu>
Wed, 6 Jul 2005 09:14:20 +0000 (09:14 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Wed, 6 Jul 2005 09:14:20 +0000 (09:14 +0000)
ChangeLog
README
include/fuse.h
kernel/inode.c
util/fusermount.c

index 65597a8c98695c6eff89a53791ebf861d08845b6..1855359c595c0d146a0e39b5b800ba0d84d9b3c7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2005-07-06  Miklos Szeredi <miklos@szeredi.hu>
+
+       * fusermount: check if mountpoint is empty (only '.' and '..' for
+       directories, and size = 0 for regular files).  If "nonempty"
+       option is given, omit this check.  This is useful, so users don't
+       accidentally hide data (e.g. from backup programs).  Thanks to
+       Frank van Maarseveen for pointing this out.
+
+       * kernel: check if mandatory mount options ('fd', 'rootmode',
+       'user_id', 'group_id') are all given
+
 2005-07-03  Miklos Szeredi <miklos@szeredi.hu>
 
        * kernel: clean up 'direct_io' code
diff --git a/README b/README
index 5537ddbf7bcff03127ef6eeac1efb19ccea399ad..fc1675eea9af33d2ff00d0468c05223563f51a29 100644 (file)
--- a/README
+++ b/README
@@ -193,3 +193,9 @@ fsname=NAME
 
   Sets the filesystem name.  The default is the program name.
 
+nonempty
+
+  Allows mounts over a non-empty file or directory.  By default these
+  mounts are rejected (from version 2.3.1) to prevent accidental
+  covering up of data, which could for example prevent automatic
+  backup.
index fb1c2d6df9a28e6d6de3ebd8bcbd6cafc6726202..40c9b7208b3dc83347bb55d1bc60b40147d78b70 100644 (file)
@@ -157,6 +157,8 @@ struct fuse_operations {
      * is permitted for the given flags.  Optionally open may also
      * return an arbitary filehandle in the fuse_file_info structure,
      * which will be passed to all file operations.
+     * 
+     * Changed in version 2.2
      */
     int (*open) (const char *, struct fuse_file_info *);
 
@@ -168,6 +170,8 @@ struct fuse_operations {
      * 'direct_io' mount option is specified, in which case the return
      * value of the read system call will reflect the return value of
      * this operation.
+     *
+     * Changed in version 2.2
      */
     int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
 
@@ -176,6 +180,8 @@ struct fuse_operations {
      * Write should return exactly the number of bytes requested
      * except on error.  An exception to this is when the 'direct_io'
      * mount option is specified (see read operation).
+     *
+     * Changed in version 2.2
      */
     int (*write) (const char *, const char *, size_t, off_t,
                   struct fuse_file_info *);
@@ -203,6 +209,8 @@ struct fuse_operations {
      * not possible to determine if a flush is final, so each flush
      * should be treated equally.  Multiple write-flush sequences are
      * relatively rare, so this shouldn't be a problem.
+     * 
+     * Changed in version 2.2
      */
     int (*flush) (const char *, struct fuse_file_info *);
 
@@ -217,6 +225,8 @@ struct fuse_operations {
      * have a file opened more than once, in which case only the last
      * release will mean, that no more reads/writes will happen on the
      * file.  The return value of release is ignored.
+     *
+     * Changed in version 2.2
      */
     int (*release) (const char *, struct fuse_file_info *);
 
@@ -224,6 +234,8 @@ struct fuse_operations {
      *
      * If the datasync parameter is non-zero, then only the user data
      * should be flushed, not the meta data.
+     *
+     * Changed in version 2.2
      */
     int (*fsync) (const char *, int, struct fuse_file_info *);
 
@@ -243,6 +255,8 @@ struct fuse_operations {
      *
      * This method should check if the open operation is permitted for
      * this  directory
+     *
+     * Introduced in version 2.3
      */
     int (*opendir) (const char *, struct fuse_file_info *);
 
@@ -264,17 +278,24 @@ struct fuse_operations {
      * passes non-zero offset to the filler function.  When the buffer
      * is full (or an error happens) the filler function will return
      * '1'.
+     * 
+     * Introduced in version 2.3
      */
     int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
                     struct fuse_file_info *);
 
-    /** Release directory */
+    /** Release directory 
+     *
+     * Introduced in version 2.3
+     */
     int (*releasedir) (const char *, struct fuse_file_info *);
 
     /** Synchronize directory contents
      *
      * If the datasync parameter is non-zero, then only the user data
      * should be flushed, not the meta data
+     *
+     * Introduced in version 2.3
      */
     int (*fsyncdir) (const char *, int, struct fuse_file_info *);
 
@@ -284,6 +305,8 @@ struct fuse_operations {
      * The return value will passed in the private_data field of
      * fuse_context to all file operations and as a parameter to the
      * destroy() method.
+     * 
+     * Introduced in version 2.3
      */
     void *(*init) (void);
 
@@ -291,6 +314,8 @@ struct fuse_operations {
      * Clean up filesystem
      *
      * Called on filesystem exit.
+     * 
+     * Introduced in version 2.3
      */
     void (*destroy) (void *);
 };
index ed14602cc1e2d4e2cf20788c83885f5547ad27bd..5dacb220f18d8bfd547eed525d39f09d57bdd03e 100644 (file)
@@ -44,6 +44,10 @@ struct fuse_mount_data {
        unsigned rootmode;
        unsigned user_id;
        unsigned group_id;
+       unsigned fd_present : 1;
+       unsigned rootmode_present : 1;
+       unsigned user_id_present : 1;
+       unsigned group_id_present : 1;
        unsigned flags;
        unsigned max_read;
 };
@@ -350,7 +354,6 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
 {
        char *p;
        memset(d, 0, sizeof(struct fuse_mount_data));
-       d->fd = -1;
        d->max_read = ~0;
 
        while ((p = strsep(&opt, ",")) != NULL) {
@@ -366,24 +369,28 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
                        if (match_int(&args[0], &value))
                                return 0;
                        d->fd = value;
+                       d->fd_present = 1;
                        break;
 
                case OPT_ROOTMODE:
                        if (match_octal(&args[0], &value))
                                return 0;
                        d->rootmode = value;
+                       d->rootmode_present = 1;
                        break;
 
                case OPT_USER_ID:
                        if (match_int(&args[0], &value))
                                return 0;
                        d->user_id = value;
+                       d->user_id_present = 1;
                        break;
 
                case OPT_GROUP_ID:
                        if (match_int(&args[0], &value))
                                return 0;
                        d->group_id = value;
+                       d->group_id_present = 1;
                        break;
 
                case OPT_DEFAULT_PERMISSIONS:
@@ -417,7 +424,9 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
                        return 0;
                }
        }
-       if (d->fd == -1)
+       
+       if (!d->fd_present || !d->rootmode_present ||
+           !d->user_id_present || !d->group_id_present)
                return 0;
 
        return 1;
index 6e0abb2cf4218f8f919aa6ed966f3ef3a13740bc..ec2f39df3b78eb609ccfc6b8d769dc8521a74cd2 100644 (file)
@@ -27,6 +27,7 @@
 #include <fcntl.h>
 #include <pwd.h>
 #include <mntent.h>
+#include <dirent.h>
 #include <sys/param.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -474,9 +475,41 @@ static int opt_eq(const char *s, unsigned len, const char *opt)
         return 0;
 }
 
+static int check_mountpoint_empty(const char *mnt, mode_t rootmode,
+                                  off_t rootsize)
+{
+    int isempty = 1;
+
+    if (S_ISDIR(rootmode)) {
+        struct dirent *ent;
+        DIR *dp = opendir(mnt);
+        if (dp == NULL) {
+            fprintf(stderr, "%s: failed to mountpoint for reading: %s\n",
+                    progname, strerror(errno));
+            return -1;
+        }
+        while ((ent = readdir(dp)) != NULL) {
+            if (strcmp(ent->d_name, ".") != 0 &&
+                strcmp(ent->d_name, "..") != 0) {
+                isempty = 0;
+                break;
+            }
+        }
+        closedir(dp);
+    } else if (rootsize)
+        isempty = 0;
+
+    if (!isempty) {
+        fprintf(stderr, "%s: mountpoint is not empty\n", progname);
+        fprintf(stderr, "%s: if you are sure this is safe, use the 'nonempty' mount option\n", progname);
+        return -1;
+    }
+    return 0;
+}
+
 static int do_mount(const char *mnt, const char *type, mode_t rootmode,
                     int fd, const char *opts, const char *dev, char **fsnamep,
-                    char **mnt_optsp)
+                    char **mnt_optsp, off_t rootsize)
 {
     int res;
     int flags = MS_NOSUID | MS_NODEV;
@@ -485,6 +518,7 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode,
     const char *s;
     char *d;
     char *fsname = NULL;
+    int check_empty = 1;
 
     optbuf = malloc(strlen(opts) + 128);
     if (!optbuf) {
@@ -503,11 +537,12 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode,
             fsname = malloc(len - fsname_str_len + 1);
             if (!fsname) {
                 fprintf(stderr, "%s: failed to allocate memory\n", progname);
-                free(optbuf);
-                return -1;
+                goto err;
             }
             memcpy(fsname, s + fsname_str_len, len - fsname_str_len);
             fsname[len - fsname_str_len] = '\0';
+        } else if (opt_eq(s, len, "nonempty")) {
+            check_empty = 0;
         } else if (!begins_with(s, "fd=") &&
                    !begins_with(s, "rootmode=") &&
                    !begins_with(s, "user_id=") &&
@@ -530,8 +565,7 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode,
                 (opt_eq(s, len, "allow_other") ||
                  opt_eq(s, len, "allow_root"))) {
                 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
-                free(optbuf);
-                return -1;
+                goto err;
             }
             if (!skip_option) {
                 if (find_mount_flag(s, len, &on, &flag)) {
@@ -552,22 +586,22 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode,
     }
     *d = '\0';
     res = get_mnt_opts(flags, optbuf, &mnt_opts);
-    if (res == -1) {
-        free(mnt_opts);
-        free(optbuf);
-        return -1;
-    }
+    if (res == -1)
+        goto err;
+
     sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
             fd, rootmode, getuid(), getgid());
     if (fsname == NULL) {
         fsname = strdup(dev);
         if (!fsname) {
             fprintf(stderr, "%s: failed to allocate memory\n", progname);
-            free(optbuf);
-            return -1;
+            goto err;
         }
     }
 
+    if (check_empty && check_mountpoint_empty(mnt, rootmode, rootsize) == -1)
+        goto err;
+
     res = mount(fsname, mnt, type, flags, optbuf);
     if (res == -1 && errno == EINVAL) {
         /* It could be an old version not supporting group_id */
@@ -576,8 +610,7 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode,
     }
     if (res == -1) {
         fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
-        free(fsname);
-        free(mnt_opts);
+        goto err;
     } else {
         *fsnamep = fsname;
         *mnt_optsp = mnt_opts;
@@ -585,6 +618,12 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode,
     free(optbuf);
 
     return res;
+
+ err:
+    free(fsname);
+    free(mnt_opts);
+    free(optbuf);
+    return -1;
 }
 
 static int check_version(const char *dev)
@@ -822,7 +861,7 @@ static int mount_fuse(const char *mnt, const char *opts)
         restore_privs();
         if (res != -1)
             res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT, fd, opts,
-                           dev, &fsname, &mnt_opts);
+                           dev, &fsname, &mnt_opts, stbuf.st_size);
     } else
         restore_privs();