From: Miklos Szeredi Date: Wed, 6 Jul 2005 09:14:20 +0000 (+0000) Subject: fix X-Git-Tag: fuse_2_4_0_pre2~57 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=437d81187fe3aac243a4b47f4a967a195fe9f22a;p=qemu-gpiodev%2Flibfuse.git fix --- diff --git a/ChangeLog b/ChangeLog index 65597a8..1855359 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2005-07-06 Miklos Szeredi + + * 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 * kernel: clean up 'direct_io' code diff --git a/README b/README index 5537ddb..fc1675e 100644 --- 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. diff --git a/include/fuse.h b/include/fuse.h index fb1c2d6..40c9b72 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -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 *); }; diff --git a/kernel/inode.c b/kernel/inode.c index ed14602..5dacb22 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -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; diff --git a/util/fusermount.c b/util/fusermount.c index 6e0abb2..ec2f39d 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -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();