From a25d4c2e7ea3741c5cf44d17d955c9bae91ca128 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 23 Nov 2004 22:32:16 +0000 Subject: [PATCH] various fixes --- ChangeLog | 15 ++ kernel/dev.c | 15 +- kernel/dir.c | 2 + kernel/fuse_i.h | 30 ++-- kernel/inode.c | 17 ++- kernel/linux/fuse.h | 6 + lib/fuse.c | 29 +++- lib/mount.c | 4 + util/fusermount.c | 330 +++++++++++++++++++++++++++----------------- 9 files changed, 289 insertions(+), 159 deletions(-) diff --git a/ChangeLog b/ChangeLog index 61d8293..e8d2cac 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2004-11-23 Miklos Szeredi + + * More cleanups in the kernel + + * The 10,229 charater device number has been assigned for FUSE + + * Version file checking fix (reported by Christian Magnusson) + + * fusermount: opening the fuse device now doesn't need /sys. + + * Optimize reading by controlling the maximum readahead based on + the 'max_read' mount option + + * fixes for UCLIBC (Christian Magnusson) + 2004-11-19 Miklos Szeredi * Cleaned up kernel in preparation for merge into mainline: diff --git a/kernel/dev.c b/kernel/dev.c index 7261770..da41e59 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -8,6 +8,7 @@ #include "fuse_i.h" +#include #include #include #ifdef KERNEL_2_6 @@ -261,7 +262,6 @@ static ssize_t fuse_dev_read(struct file *file, char __user *buf, else if (!list_empty(&fc->pending)) { req = list_entry(fc->pending.next, struct fuse_req, list); list_del_init(&req->list); - req->locked = 1; } spin_unlock(&fuse_lock); if (!fc) @@ -275,12 +275,9 @@ static ssize_t fuse_dev_read(struct file *file, char __user *buf, if (ret < 0) { req->out.h.error = -EPROTO; req->finished = 1; - } else { + } else list_add_tail(&req->list, &fc->processing); - req->sent = 1; - } - req->locked = 0; - if (ret < 0 || req->interrupted) + if (ret < 0) /* Unlocks fuse_lock: */ request_end(fc, req); else @@ -471,10 +468,8 @@ static ssize_t fuse_dev_write(struct file *file, const char __user *buf, spin_lock(&fuse_lock); req = request_find(fc, oh.unique); - if (req != NULL) { + if (req != NULL) list_del_init(&req->list); - req->locked = 1; - } spin_unlock(&fuse_lock); if (!req) return -ENOENT; @@ -491,7 +486,6 @@ static ssize_t fuse_dev_write(struct file *file, const char __user *buf, process_getdir(req); } req->finished = 1; - req->locked = 0; /* Unlocks fuse_lock: */ request_end(fc, req); @@ -565,7 +559,6 @@ struct file_operations fuse_dev_operations = { }; #ifdef KERNEL_2_6 -#define FUSE_MINOR MISC_DYNAMIC_MINOR #ifndef FUSE_MAINLINE static decl_subsys(fs, NULL, NULL); diff --git a/kernel/dir.c b/kernel/dir.c index 124bf86..fe8e803 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -111,6 +111,7 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, int generation, struct fuse_attr *attr, int version) { struct inode *inode; + struct fuse_conn *fc = SB_FC(sb); inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid); if (!inode) @@ -118,6 +119,7 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, if ((inode->i_state & I_NEW)) { inode->i_generation = generation; + inode->i_data.backing_dev_info = &fc->bdi; fuse_init_inode(inode, attr); unlock_new_inode(inode); } else if (inode->i_generation != generation) diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index e454fd0..c5b8e23 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -42,6 +42,10 @@ #include #include #include +#ifdef KERNEL_2_6 +#include +#include +#endif #include #ifndef BUG_ON @@ -152,15 +156,6 @@ struct fuse_req { /** True if the request has reply */ unsigned int isreply:1; - /** The request is locked */ - unsigned int locked:1; - - /** The request has been interrupted while it was locked */ - unsigned int interrupted:1; - - /* The request has been sent to the client */ - unsigned int sent:1; - /* The request is preallocated */ unsigned int preallocated:1; @@ -264,6 +259,11 @@ struct fuse_conn { /** Is removexattr not implemented by fs? */ unsigned int no_removexattr : 1; + +#ifdef KERNEL_2_6 + /** Backing dev info */ + struct backing_dev_info bdi; +#endif }; struct fuse_getdir_out_i { @@ -283,7 +283,16 @@ struct fuse_getdir_out_i { extern struct file_operations fuse_dev_operations; /** - * The lock to protect fuses structures + * This is the single global spinlock which protects FUSE's structures + * + * The following data is protected by this lock: + * + * - the private_data field of the device file + * - the s_fs_info field of the super block + * - unused_list, pending, processing lists in fuse_conn + * - the unique request ID counter reqctr in fuse_conn + * - the sb (super_block) field in fuse_conn + * - the file (device file) field in fuse_conn */ extern spinlock_t fuse_lock; @@ -336,7 +345,6 @@ int fuse_fs_init(void); */ void fuse_fs_cleanup(void); - /** * Allocate a request */ diff --git a/kernel/inode.c b/kernel/inode.c index 55a283b..8cae834 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -178,7 +178,9 @@ enum { OPT_ALLOW_OTHER, OPT_ALLOW_ROOT, OPT_KERNEL_CACHE, +#ifndef FUSE_MAINLINE OPT_LARGE_READ, +#endif OPT_DIRECT_IO, OPT_MAX_READ, OPT_ERR @@ -192,7 +194,9 @@ static match_table_t tokens = { {OPT_ALLOW_OTHER, "allow_other"}, {OPT_ALLOW_ROOT, "allow_root"}, {OPT_KERNEL_CACHE, "kernel_cache"}, +#ifndef FUSE_MAINLINE {OPT_LARGE_READ, "large_read"}, +#endif {OPT_DIRECT_IO, "direct_io"}, {OPT_MAX_READ, "max_read=%u"}, {OPT_ERR, NULL} @@ -247,7 +251,8 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) case OPT_KERNEL_CACHE: d->flags |= FUSE_KERNEL_CACHE; break; - + +#ifndef FUSE_MAINLINE case OPT_LARGE_READ: #ifndef KERNEL_2_6 d->flags |= FUSE_LARGE_READ; @@ -261,7 +266,7 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) } #endif break; - +#endif case OPT_DIRECT_IO: d->flags |= FUSE_DIRECT_IO; break; @@ -351,6 +356,10 @@ static struct fuse_conn *new_conn(void) req->preallocated = 1; list_add(&req->list, &fc->unused_list); } +#ifdef KERNEL_2_6 + fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; + fc->bdi.unplug_io_fn = default_unplug_io_fn; +#endif fc->reqctr = 1; } return fc; @@ -502,6 +511,10 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent) fc->flags = d.flags; fc->uid = d.uid; fc->max_read = d.max_read; +#ifdef KERNEL_2_6 + if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages) + fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE; +#endif fc->max_write = FUSE_MAX_IN / 2; SB_FC(sb) = fc; diff --git a/kernel/linux/fuse.h b/kernel/linux/fuse.h index f5f7405..ea640e9 100644 --- a/kernel/linux/fuse.h +++ b/kernel/linux/fuse.h @@ -17,6 +17,12 @@ /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 +/** The major number of the fuse character device */ +#define FUSE_MAJOR 10 + +/** The minor number of the fuse character device */ +#define FUSE_MINOR 229 + /** Opening this will yield a new control file */ #define FUSE_DEV "/dev/fuse" diff --git a/lib/fuse.c b/lib/fuse.c index b29db9d..59f8b45 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -17,6 +17,9 @@ #include #include +#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version" +#define FUSE_DEV_OLD "/proc/fs/fuse/dev" + #define FUSE_MAX_PATH 4096 #define PARAM(inarg) (((char *)(inarg)) + sizeof(*inarg)) @@ -1722,12 +1725,18 @@ static int check_version(struct fuse *f) const char *version_file = FUSE_VERSION_FILE; FILE *vf = fopen(version_file, "r"); if (vf == NULL) { - version_file = "/sys/fs/fuse/version"; + version_file = FUSE_VERSION_FILE_OLD; vf = fopen(version_file, "r"); if (vf == NULL) { - fprintf(stderr, "fuse: kernel interface too old, need >= %i.%i\n", - FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); - return -1; + struct stat tmp; + if (stat(FUSE_DEV_OLD, &tmp) != -1) { + fprintf(stderr, "fuse: kernel interface too old, need >= %i.%i\n", + FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); + return -1; + } else { + fprintf(stderr, "fuse: warning: version of kernel interface unknown\n"); + return 0; + } } } res = fscanf(vf, "%i.%i", &f->majorver, &f->minorver); @@ -1817,7 +1826,17 @@ struct fuse *fuse_new(int fd, const char *opts, const struct fuse_operations *op if (f->id_table == NULL) goto out_free_name_table; - pthread_mutex_init(&f->lock, NULL); +#ifndef USE_UCLIBC + pthread_mutex_init(&f->lock, NULL); +#else + { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); + pthread_mutex_init(&f->lock, &attr); + pthread_mutexattr_destroy(&attr); + } +#endif f->numworker = 0; f->numavail = 0; f->op = *op; diff --git a/lib/mount.c b/lib/mount.c index 05dcb59..a84dbee 100644 --- a/lib/mount.c +++ b/lib/mount.c @@ -91,7 +91,11 @@ int fuse_mount(const char *mountpoint, const char *opts) return -1; } +#ifndef USE_UCLIBC pid = fork(); +#else + pid = vfork(); +#endif if(pid == -1) { perror("fuse: fork() failed"); close(fds[0]); diff --git a/util/fusermount.c b/util/fusermount.c index 98a705b..a7d26ed 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -38,8 +38,8 @@ #define FUSE_COMMFD_ENV "_FUSE_COMMFD" #define FUSE_DEV_OLD "/proc/fs/fuse/dev" -#define FUSE_DEV_NEW "/dev/fuse" #define FUSE_SYS_DEV "/sys/class/misc/fuse/dev" +#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version" const char *progname; @@ -54,66 +54,7 @@ static const char *get_user_name() } } -/* use a lock file so that multiple fusermount processes don't try and - modify the mtab file at once! */ -static int lock_mtab() -{ - const char *mtab_lock = _PATH_MOUNTED ".fuselock"; - int mtablock; - int res; - - mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600); - if (mtablock >= 0) { - res = lockf(mtablock, F_LOCK, 0); - if (res < 0) - perror("error getting lock"); - } else - fprintf(stderr, "unable to open fuse lock file, continuing anyway\n"); - - return mtablock; -} - -static void unlock_mtab(int mtablock) -{ - if (mtablock >= 0) { - lockf(mtablock, F_ULOCK, 0); - close(mtablock); - } -} - -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; - - 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)); - return -1; - } - - endmntent(fp); - return 0; -} - - +#ifndef USE_UCLIBC /* Until there is a nice interface for capabilities in _libc_, this will remain here. I don't think it is fair to expect users to compile libcap for this program. And anyway what's all this fuss about versioning the @@ -140,7 +81,7 @@ static uid_t oldfsuid; static gid_t oldfsgid; static struct __user_cap_data_struct oldcaps; -static int drop_privs() +static int drop_privs(void) { int res; struct __user_cap_header_struct head; @@ -174,7 +115,7 @@ static int drop_privs() return 0; } -static void restore_privs() +static void restore_privs(void) { struct __user_cap_header_struct head; int res; @@ -189,12 +130,81 @@ static void restore_privs() setfsuid(oldfsuid); setfsgid(oldfsgid); } +#else /* USE_UCLIBC */ +static int drop_privs(void) +{ + return 0; +} +static void restore_privs(void) +{ +} +#endif /* USE_UCLIBC */ + + +#ifndef USE_UCLIBC +/* use a lock file so that multiple fusermount processes don't try and + modify the mtab file at once! */ +static int lock_mtab() +{ + const char *mtab_lock = _PATH_MOUNTED ".fuselock"; + int mtablock; + int res; + + mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600); + if (mtablock >= 0) { + res = lockf(mtablock, F_LOCK, 0); + if (res < 0) + perror("error getting lock"); + } else + fprintf(stderr, "unable to open fuse lock file, continuing anyway\n"); + + return mtablock; +} + +static void unlock_mtab(int mtablock) +{ + if (mtablock >= 0) { + lockf(mtablock, F_ULOCK, 0); + close(mtablock); + } +} -static int remove_mount(const char *mnt, int quiet, int lazy) +static int add_mount(const char *fsname, const char *mnt, const char *type, + const char *opts) { int res; const char *mtab = _PATH_MOUNTED; - const char *mtab_new = _PATH_MOUNTED "~fuse~"; + struct mntent ent; + FILE *fp; + + 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)); + return -1; + } + + endmntent(fp); + return 0; +} + +static int remove_mount(const char *mnt, int quiet, const char *mtab, + const char *mtab_new) +{ + int res; struct mntent *entp; FILE *fp; FILE *newfp; @@ -248,46 +258,67 @@ static int remove_mount(const char *mnt, int quiet, int lazy) endmntent(fp); endmntent(newfp); - - if (found) { - if (getuid() != 0) { - res = drop_privs(); - if (res == -1) { - unlink(mtab_new); - return -1; - } - } - res = umount2(mnt, lazy ? 2 : 0); - if (res == -1) { - if (!quiet) - fprintf(stderr, "%s: failed to unmount %s: %s\n", - progname, mnt,strerror(errno)); - found = -1; - } - if (getuid() != 0) - restore_privs(); + if (!found) { + if (!quiet) + fprintf(stderr, "%s: entry for %s not found in %s\n", progname, + mnt, mtab); + unlink(mtab_new); + return -1; } - if (found == 1) { - res = rename(mtab_new, mtab); - if (res == -1) { - fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname, - mtab_new, mtab, strerror(errno)); + return 0; +} + + +static int do_unmount(const char *mnt, int quiet, int lazy, const char *mtab, + const char *mtab_new) +{ + int res; + + if (getuid() != 0) { + res = drop_privs(); + if (res == -1) return -1; - } } - else { - if (!found && !quiet) - fprintf(stderr, "%s: entry for %s not found in %s\n", progname, - mnt, mtab); - unlink(mtab_new); + 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 -1; } + if (getuid() != 0) + restore_privs(); + res = rename(mtab_new, mtab); + if (res == -1) { + fprintf(stderr, "%s: failed to rename %s to %s: %s\n", progname, + mtab_new, mtab, strerror(errno)); + return -1; + } return 0; } +static int unmount_fuse(const char *mnt, int quiet, int lazy) +{ + int res; + const char *mtab = _PATH_MOUNTED; + const char *mtab_new = _PATH_MOUNTED "~fuse~"; + + res = remove_mount(mnt, quiet, mtab, mtab_new); + if (res == -1) + return -1; + + res = do_unmount(mnt, quiet, lazy, mtab, mtab_new); + if (res == -1) { + unlink(mtab_new); + return -1; + } + return 0; +} +#endif /* USE_UCLIBC */ + static int begins_with(const char *s, const char *beg) { if (strncmp(s, beg, strlen(beg)) == 0) @@ -475,20 +506,32 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode, return res; } -static int check_version(void) +static int check_version(const char *dev) { int res; int majorver; int minorver; - const char *version_file = FUSE_VERSION_FILE; - FILE *vf = fopen(version_file, "r"); + const char *version_file; + int isold = 0; + FILE *vf; + + if (strcmp(dev, FUSE_DEV_OLD) == 0) + isold = 1; + + if (isold) + version_file = FUSE_VERSION_FILE_OLD; + else + version_file = FUSE_VERSION_FILE; + + vf = fopen(version_file, "r"); if (vf == NULL) { - version_file = "/sys/fs/fuse/version"; - vf = fopen(version_file, "r"); - if (vf == NULL) { + if (isold) { fprintf(stderr, "%s: kernel interface too old\n", progname); return -1; - } + } else + /* If /sys/fs/fuse/version doesn't exist, just skip + version checking */ + return 0; } res = fscanf(vf, "%i.%i", &majorver, &minorver); fclose(vf); @@ -606,7 +649,7 @@ static int try_open_new_temp(unsigned devnum, char **devp) return fd; } -static int try_open_new(char **devp) +static int try_open_new(char **devp, int final) { const char *dev; unsigned minor; @@ -616,8 +659,18 @@ static int try_open_new(char **devp) unsigned devnum; char buf[256]; int fd = open(FUSE_SYS_DEV, O_RDONLY); - if (fd == -1) - return -2; + if (fd == -1) { + if (!final) + return -2; + fd = try_open(FUSE_DEV, devp, 0); + if (fd != -1) + return fd; + if (errno == ENODEV) + return -2; + fprintf(stderr, "%s: failed to open %s: %s\n", progname, + FUSE_DEV, strerror(errno)); + return -1; + } res = read(fd, buf, sizeof(buf)-1); close(fd); @@ -635,10 +688,17 @@ static int try_open_new(char **devp) } devnum = (major << 8) + (minor & 0xff) + ((minor & 0xff00) << 12); - dev = FUSE_DEV_NEW; + dev = FUSE_DEV; res = stat(dev, &stbuf); - if (res == -1) - return try_open_new_temp(devnum, devp); + if (res == -1) { + if (major == FUSE_MAJOR && minor == FUSE_MINOR) + return try_open_new_temp(devnum, devp); + else { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, + dev, strerror(errno)); + return -1; + } + } if ((stbuf.st_mode & S_IFMT) != S_IFCHR || stbuf.st_rdev != devnum) { fprintf(stderr, "%s: %s exists but has wrong attributes\n", progname, @@ -652,21 +712,27 @@ static int open_fuse_device(char **devp) { int fd; - fd = try_open_new(devp); - if (fd != -2) - return fd; - - fd = try_open(FUSE_DEV_OLD, devp, 1); - if (fd != -1) - return fd; - if (1 #ifndef AUTO_MODPROBE && getuid() == 0 #endif ) { int status; - pid_t pid = fork(); + pid_t pid; + + fd = try_open(FUSE_DEV_OLD, devp, 1); + if (fd != -1) + return fd; + + fd = try_open_new(devp, 0); + if (fd != -2) + return fd; + +#ifndef USE_UCLIBC + pid = fork(); +#else + pid = vfork(); +#endif if (pid == 0) { setuid(0); execl("/sbin/modprobe", "/sbin/modprobe", "fuse", NULL); @@ -674,16 +740,15 @@ static int open_fuse_device(char **devp) } if (pid != -1) waitpid(pid, &status, 0); + } - fd = try_open_new(devp); - if (fd != -2) - return fd; + fd = try_open(FUSE_DEV_OLD, devp, 1); + if (fd != -1) + return fd; - fd = try_open(FUSE_DEV_OLD, devp, 1); - if (fd != -1) - return fd; - - } + fd = try_open_new(devp, 1); + if (fd != -2) + return fd; fprintf(stderr, "fuse device not found, try 'modprobe fuse' first\n"); return -1; @@ -697,7 +762,6 @@ static int mount_fuse(const char *mnt, const char *opts) char *dev; const char *type = "fuse"; struct stat stbuf; - int mtablock; char *fsname; char *mnt_opts; const char *real_mnt = mnt; @@ -713,7 +777,7 @@ static int mount_fuse(const char *mnt, const char *opts) return -1; } - res = check_version(); + res = check_version(dev); if (res != -1) { res = check_perm(&real_mnt, &stbuf, &currdir_fd); if (res != -1) @@ -731,9 +795,10 @@ static int mount_fuse(const char *mnt, const char *opts) fchdir(currdir_fd); close(currdir_fd); } - + +#ifndef USE_UCLIBC if (geteuid() == 0) { - mtablock = lock_mtab(); + int mtablock = lock_mtab(); res = add_mount(fsname, mnt, type, mnt_opts); unlock_mtab(mtablock); if (res == -1) { @@ -741,6 +806,8 @@ static int mount_fuse(const char *mnt, const char *opts) return -1; } } +#endif + free(fsname); free(mnt_opts); free(dev); @@ -928,11 +995,14 @@ int main(int argc, char *argv[]) restore_privs(); if (unmount) { +#ifndef USE_UCLIBC if (geteuid() == 0) { int mtablock = lock_mtab(); - res = remove_mount(mnt, quiet, lazy); + res = unmount_fuse(mnt, quiet, lazy); unlock_mtab(mtablock); - } else { + } else +#endif + { res = umount2(mnt, lazy ? 2 : 0); if (res == -1) { if (!quiet) -- 2.30.2