aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2005-07-06 09:14:20 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2005-07-06 09:14:20 +0000
commit437d81187fe3aac243a4b47f4a967a195fe9f22a (patch)
tree7aef267c8c4f79bc6e8ff42dd3337359775de4ef
parentac1b49d854abbbe01147d3ae652055b09e27357a (diff)
downloadlibfuse-437d81187fe3aac243a4b47f4a967a195fe9f22a.tar.gz
fix
-rw-r--r--ChangeLog11
-rw-r--r--README6
-rw-r--r--include/fuse.h27
-rw-r--r--kernel/inode.c13
-rw-r--r--util/fusermount.c69
5 files changed, 108 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index 65597a8..1855359 100644
--- 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 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 <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();