Date: Sun, 11 Sep 2005 17:08:14 GMT From: soc-chenk <soc-chenk@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 83408 for review Message-ID: <200509111708.j8BH8E3c060295@repoman.freebsd.org>
next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=83408 Change 83408 by soc-chenk@soc-chenk_leavemealone on 2005/09/11 17:07:48 Code state tagged as "version 0.01.1 Affected files ... .. //depot/projects/soc2005/fuse4bsd2/COPYRIGHT#1 add .. //depot/projects/soc2005/fuse4bsd2/Changelog#1 add .. //depot/projects/soc2005/fuse4bsd2/README#2 edit .. //depot/projects/soc2005/fuse4bsd2/fuse_module/Makefile#2 edit .. //depot/projects/soc2005/fuse4bsd2/fuse_module/fuse.c#2 edit .. //depot/projects/soc2005/fuse4bsd2/fuse_module/fuse_subr.c#1 add .. //depot/projects/soc2005/fuse4bsd2/mount_fusefs/mount_fusefs.c#2 edit Differences ... ==== //depot/projects/soc2005/fuse4bsd2/README#2 (text+ko) ==== @@ -1,21 +1,162 @@ -This is a FreeBSD kernel module to support the Fuse userspace virtual -filesystem framework (http://fuse.sourceforge.net). +Fuse module for FreeBSD. + +Fuse is an userspace filesystem framework, originally for Linux, +see http://fuse.sourceforge.net. + +This distributions contains a port of Fuse to FreeBSD. + +Mainly this consists of writing a compatible kernel module for FreeBSD. +The userspace part is pretty portable. You'll find here a patch to get +that compiled, and brief instruvctions to get that running. + +The module was written for and tested with CURRENT, aka FreeBSD-7.0. +I'd guess it will work fine with RELENG 6 too, but currently its not usable +with 5.x (or lower) versions. + +Waht can be considered as a public homepage for the project is + + http://wikitest.freebsd.org/moin.cgi/FuseFilesystem + +see updtates, further info there. Get in contact with me at +soc-chenk@freebsd.org. + +Installation + +I'll describe here the installation of the core Fuse components and a Fuse +based filesystem, sshfs, which lets you mount a machine remotely by means +of an ssh conncetion. + +I'll describe a non-privileged installation. + +Downloads + +You need to get a bunch of things. + + * Fuse itself. Get Fuse 2.4.0-pre1 from their homepage, + http://fuse.sourceforge.net + + * This module. The latest version is available via Darcs, you can fetch it by + the + + darcs get http://creo.hu/~csaba/darcs-repos/fuse4bsd + + command. These instructions are appropriate for the version in place (with + which this README ships). + + * Fuse sshfs. The latest release (1.2) will do, fetch the sshfs-fuse package from + [WWW]http://sourceforge.net/projects/fuse. + +Compilation + +Let's start with the module. + + * From your Fuse distribuiton copy over kernel/fuse_kernel.h as + fuse_module/fuse_kernel.h.orig. + + * Type make. If you want normal quantity of debug output, use + DEBUG2G=1, if you want tons of debug output, use DEBUG=1. + +Now go for the fuse userspace. + + * We will assume that you have my module at ../fuse4bsd + + * Apply the patch with + + patch -Np1 < ../fuse4bsd/fuselib/fuselib-2.4.0-pre1.diff + + * Do + + cp ../fuse4bsd/fuselib/fusermount.c util/ && + cp ../fuse4bsd/fuse_module/fuse_kernel.h include/ && + cp ../fuse4bsd/fuse_module/linux_compat.h include/ + + (the first command replaces fusermount.c with a trimmed down version + without the mount support code, and the other two dynamically customize the + header file defining the kernel-userland interface; as these are needed in + the module as well, they are handled separately from the userspace patch). + + * We will do a non-privileged install, I'll use ~/meta/fuse-2.4.0-pre1 as the + prefix. Configure fuse with + + /configure --prefix ~/meta/fuse-2.4.0-pre1 --disable-kernel-module MOUNT_FUSE_PATH=/tmp + + * Now type + + make && + ln -s /usr/bin/true chown && + ln -s /usr/bin/true mknod && + ln -s /usr/bin/true chmod && + env PATH=`pwd`:$PATH make install + + +You have successfully installed fuse libs. Go on to sshfs. + + * Edit sshfs.c: add a #include <sys/socket.h> line to its includes. + + * Type + env PKG_CONFIG_PATH=~/meta/fuse-2.4.0-pre1/lib/pkgconfig/ ./configure && + make + +Congratulations, you have all components prepared! + +In the following, you'll need to act as superuser, or enable the vfs.usermount +sysctl. + +First, of course, load fuse_module/fuse.ko (for this you definitely need to be a +superuser). + +Then pick your favourite ssh accessible account (though maybe you'd better +stick to servers running OpenSSH -- I've seen commits in sshfs' CVS for better +interoperability with other servers, which show there might occur problems with +them), say, it's foo@bar.baz. + +Firing up a Fuse daemon + +... sshfs, more concretely. + +Go to sshfs' directory. Type something along the following line: + +env PATH=$PATH:~csaba/meta/fuse-2.4.0-pre1/bin/ \ + LD_LIBRARY_PATH=~csaba/meta/fuse-2.4.0-pre1/lib/ \ + ./sshfs foo@bar.baz / -d + +Note that common Fuse arguments include the mountpoint -- in our case, that's +just ignored, but required nevertheless --, and "-d" which doesn't let the +daemon fork to background and enables debug messages. + +Mounting a Fuse filesystem + +Mounting is nnpfs style, in contrast to Linux Fuse style. + +In Linux, when you start a Fuse daemon, it first connects to the (unique) Fuse +device, and then calls the mount system call with the appropriate parameters, +so as a user, you can't separate the stages of Fuse mounting. + +In FreeBSD, you first start the daemon as above, then it gets a shiny fresh new +dedicated fuse device (like /dev/fuse0), and you have to do the mounting +manually, like + +mount_fusefs /dev/fuse0 /mnt/fuse + +(Find mount_fusefs in the mount_fusefs/ subdir of the fuse4bsd source tree. +If you run it without arguments, it prints the available options.) + +You can ask: how to find out the device number? If you start your first daemon, +by all chance it will be /dev/fuse0. But in general, you can do a fstat /dev/ +fuse* to see which device is in use. -It's not yet ready, but you can test those parts which has already been -completed. +If you mounted it successfully, you can use the filesystem. -See http://wikitest.freebsd.org/moin.cgi/FuseFilesystem for more info. +It supports common POSIX fucntionality (mmap is read-only). -Some of the stuff needed for compiling the userspace component for -FreeBSD (those ones namely which don't require active development, just -synchronization with Fuse sources from time to time) are available from +When you finished, umount it with the "-f" siwtch. -http://creo.hu/~csaba/projects/fuse4bsd +It's a bug that you have to use that, but harmless... in the 95% +percent of cases. If you had intensive I/O which has been interrupted, +then there will remain dirty buffers, and in this case forced umount +may result in a panic. -The latest version of this module is available from the FreeBSD Perforce -server (htpp://perforce.freebsd.org) at the path -//depot/projects/soc2005/fuse4bsd/, and by Darcs, via the +Any feedbakc is highly welcome. -darcs get http://creo.hu/~csaba/darcs-repos/fuse4bsd +Csaba Henk -command. ==== //depot/projects/soc2005/fuse4bsd2/fuse_module/Makefile#2 (text+ko) ==== @@ -1,4 +1,4 @@ -SRCS=fuse.c +SRCS=fuse.c fuse_subr.c .if defined(FMASTER) SRCS+= fmaster.c ==== //depot/projects/soc2005/fuse4bsd2/fuse_module/fuse.c#2 (text+ko) ==== @@ -21,7 +21,6 @@ #include <sys/stat.h> #include <sys/unistd.h> /* pathconf */ #include <sys/filedesc.h> /* open */ -//#include <sys/ucred.h> /* open */ #include <sys/file.h> /* open */ #include <sys/fcntl.h> /* open */ #include <sys/dirent.h> /* readdir */ @@ -32,13 +31,7 @@ #include <vm/vm_extern.h> #include <vm/vnode_pager.h> -//#include <vm/vnode_pager.h> #include <vm/vm_object.h> -//#include <vm/vm_page.h> -//#include <vm/vm_pager.h> -//#include <vm/vm_map.h> -//#include <vm/vnode_pager.h> -//extern int vnode_pbuf_freecnt; #include "fuse.h" @@ -1186,7 +1179,7 @@ if (! fdip->tick) fdip->tick = ticket_fetch(fdip->data); - FUSE_DIMALLOC2(&fdip->tick->msgn.msg, fdip->finh, fdip->indata, fdip->iosize); + FUSE_DIMALLOC(&fdip->tick->msgn.msg, fdip->finh, fdip->indata, fdip->iosize); fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, td, cred); } @@ -1353,6 +1346,11 @@ int err = 0; DEBUG("sending FORGET with %llu lookups\n", nlookup); +#if 0 & _DEBUG2G + DEBUG2G("=============>\n"); + kdb_backtrace(); + DEBUG2G("<=============\n"); +#endif fdip->iosize = sizeof(*ffi); if (fdip->tick) { @@ -1457,6 +1455,7 @@ .vfs_root = fuse_root, .vfs_statfs = fuse_statfs, //.vfs_vget = fuse_vget, + //.vfs_sync = vfs_stdsync, }; #if __FreeBSD_version >= 600000 @@ -1623,9 +1622,24 @@ return (ENXIO); } - MALLOC(fmnt, struct fuse_mnt_data *, sizeof(*fmnt), M_FUSEFS, M_WAITOK); + MALLOC(fmnt, struct fuse_mnt_data *, sizeof(*fmnt), M_FUSEFS, M_WAITOK| M_ZERO); fmnt->fdev = fdev; + vfs_flagopt(opts, "allow_other", &fmnt->mntopts, FUSEFS_DAEMON_CAN_SPY); + if (fmnt->mntopts & FUSEFS_DAEMON_CAN_SPY && suser(td)) { + uprintf("only root can use \"allow_other\"\n"); + free(fmnt, M_FUSEFS); + return (EPERM); + } + vfs_flagopt(opts, "direct_io", &fmnt->mntopts, FUSEFS_DIRECTIO); + vfs_flagopt(opts, "kernel_cache", &fmnt->mntopts, FUSEFS_KEEPCACHE); +#if ! REALTIME_TRACK_UNPRIVPROCDBG + fmnt->mntopts &= ~FUSEFS_UNPRIVPROCDBG; + fmnt->mntopts |= get_unprivileged_proc_debug(td) ? FUSEFS_UNPRIVPROCDBG : 0; +#endif + + DEBUG2G("mntopts 0x%x\n", fmnt->mntopts); + sx_slock(slock); /* Sanity + permission checks */ @@ -1644,7 +1658,7 @@ } if ((! err) && suser(td)) { - if (td->td_ucred->cr_ruid != data->daemoncred->cr_ruid) { + if (td->td_ucred->cr_uid != data->daemoncred->cr_uid) { err = EPERM; uprintf("attempt to mount other user's daemon\n"); } @@ -1729,7 +1743,10 @@ flags |= FORCECLOSE; #if _DEBUG - vn_printf(((struct fuse_mnt_data *)mp->mnt_data)->rvp, DEBLABEL "root node before execution\n"); + //vn_printf(((struct fuse_mnt_data *)mp->mnt_data)->rvp, DEBLABEL "root node before execution\n"); + DEBUG2G("=============>\n"); + kdb_backtrace(); + DEBUG2G("<=============\n"); #endif /* Flush files -> vflush */ /* There is 1 extra root vnode reference (mp->mnt_data). */ @@ -1890,6 +1907,7 @@ } + /****************** * * >>> Vnode ops @@ -1990,6 +2008,12 @@ struct thread *td = ap->a_td; DEBUG("pfft...\n"); +#if _DEBUG + DEBUG2G("=============>\n"); + kdb_backtrace(); + vn_printf(vp, " "); + DEBUG2G("<=============\n"); +#endif fuse_filehandle_gc(vp, td, NULL); vnode_destroy_vobject(vp); return (fuse_recyc_backend(vp, td)); @@ -2004,6 +2028,12 @@ int err; DEBUG("getting at vnode of ino %d\n", VTOI(vp)); +#if _DEBUG + DEBUG2G("=============>\n"); + kdb_backtrace(); + vn_printf(vp, " "); + DEBUG2G("<=============\n"); +#endif fuse_filehandle_gc(vp, td, NULL); @@ -2107,37 +2137,43 @@ struct ucred *cred = ap->a_cred; struct thread *td = ap->a_td; + struct fuse_mnt_data *fmnt = vp->v_mount->mnt_data; struct fuse_dispatcher fdi; int err = 0; if ((err = fdisp_simple_putget(&fdi, FUSE_GETATTR, vp, td, cred))) return (err); - - /* XXX we discard timeout related info, * just go for the attributes */ fat2vat(vp->v_mount, &((struct fuse_attr_out *)fdi.answ)->attr, vap); ticket_drop(fdi.tick); -#if ! DAEMON_CAN_SPY - /* The default policy is to forbid a user from using the filesystem, - * if she is *more* privileged than the daemon. This is to protect - * the power user from the daemon spying on her I/O operations. - * Yes, it is a crude and blunt protection... - * XXX there should be a sysctl interface to this. Ideally not just a - * "daemon can spy bit", but some clever data which determines in a - * compact but flexible way whose daemon can spy on whom. - */ + if (! (fmnt->mntopts & FUSEFS_DAEMON_CAN_SPY)) { + /* The policy is to forbid a user from using the filesystem, + * if she is *more* privileged than the daemon. This is to protect + * the power user from the daemon spying on her I/O operations. + * Yes, it is a crude and blunt protection... + * XXX there should be a sysctl interface to this. Ideally not just a + * "daemon can spy bit", but some clever data which determines in a + * compact but flexible way whose daemon can spy on whom. + */ - if (cr_cansee(fusedev_get_data(fdi.fdev)->daemoncred, cred)) { - sx_sunlock(fdi.slock); - uprintf("Your access is blocked in order to prevent the fuse daemon spying on you\n"); - return (EPERM); + //DEBUG2G("mntopts 0x%x\n", ((struct fuse_mnt_data *)vp->v_mount->mnt_data)->mntopts); + if (cr_candebug( +#if REALTIME_TRACK_UNPRIVPROCDBG + get_unprivileged_proc_debug(td), +#else + ((struct fuse_mnt_data *)vp->v_mount->mnt_data)->mntopts & FUSEFS_UNPRIVPROCDBG, +#endif + fusedev_get_data(fdi.fdev)->daemoncred, cred)) + { + sx_sunlock(fdi.slock); + uprintf("Your access is blocked in order to prevent the fuse daemon spying on you\n (consider mounting with \"allow_other\")\n"); + return (EPERM); + } } -#endif - sx_sunlock(fdi.slock); if (vp->v_type != vap->va_type) { @@ -2633,6 +2669,7 @@ static int fuse_standard_metrics(struct vnode *vp, struct thread *td, struct ucred *cred, int mode, struct fuse_filehandle *fufh, void *param) { + DEBUG2G("fufh->mode, %x mode %x\n", fufh->mode, mode); if (fufh->cred->cr_uid == cred->cr_uid && fufh->cred->cr_rgid == cred->cr_rgid && fufh->pid == td->td_proc->p_pid && @@ -2711,7 +2748,7 @@ struct vnode *vp = ap->a_vp; int fdidx = ap->a_fdidx; struct ucred *cred = ap->a_cred; - int mode = OFLAGS(ap->a_mode); + int mode = ap->a_mode; struct fuse_filehandle *fufh; struct fuse_vnode_data *fvdat = vp->v_data; @@ -2724,6 +2761,7 @@ int err = 0; struct file *fp = NULL; int keep_cache; + struct fuse_mnt_data *fmnt = vp->v_mount->mnt_data; if (! vp->v_object) /* The "if" here is just to avoid needless getattr'ing */ @@ -2753,12 +2791,13 @@ if (! fp) panic("nonneg file desc passed to us but no file there"); + DEBUG2G("fp->f_flag 0x%x, open mode %d\n", fp->f_flag, mode); fdi.iosize = sizeof(*foi); if ((err = fdisp_prepare_all(&fdi, (vp->v_type == VDIR) ? FUSE_OPENDIR : FUSE_OPEN, vp, td, cred))) return (err); foi = fdi.indata; - foi->flags = mode; + foi->flags = OFLAGS(mode); if ((err = fdisp_wait_answ(&fdi))) return (err); @@ -2768,14 +2807,41 @@ fufh->fh_id = foo->fh; +#if 0 + /* + * We ignore the following flags sent from userspace, as they are in + * fact mount options... just for some bizarre reason in Linux they + * are kept at the daemon an she sends them down all the time. + * + * Well, that bizarre reason might be that this way daemons can + * interpret/evaluate/override mount options. Yet you shouldn't + * worry too mich about that happenning, as its accessible only + * via the low-level API (filesystem writers are not encouraged to + * fiddle with it). + * + * This implementation gives the mount opts to the mount util, which in turn + * passes them to the kernel, not to the daemon, so our mount info + * is authoritative, not the flags as seen by the daemon. So we ignore + * them. + */ + if (foo->open_flags & FOPEN_DIRECT_IO) fp->f_flag |= O_DIRECT; keep_cache = foo->open_flags & FOPEN_KEEP_CACHE; +#endif ticket_drop(fdi.tick); sx_sunlock(fdi.slock); + fp->f_flag = mode; + if (fmnt->mntopts & FUSEFS_DIRECTIO) + fp->f_flag |= O_DIRECT; + + + DEBUG2G("fp->f_flag 0x%x\n", fp->f_flag); + keep_cache = fmnt->mntopts & FUSEFS_KEEPCACHE; + #if DIRECTIO_FOR_DIRS if (vp->v_type == VDIR) { DEBUG("coloring file #%d\n", fdidx); @@ -2900,7 +2966,7 @@ fri = fdi.indata; fri->fh = fufh->fh_id; - fri->flags = flags; + fri->flags = OFLAGS(flags); fuse_insert_callback(fdi.tick, NULL); fuse_insert_message(fdi.tick); @@ -2963,7 +3029,7 @@ fp->f_data = NULL; if (fufh->useco == 0) - err = fuse_send_release(fp->f_vnode, td, NULL, fufh, OFLAGS(fp->f_flag) & ~O_EXCL); + err = fuse_send_release(fp->f_vnode, td, NULL, fufh, fp->f_flag & ~O_EXCL); //DEBUG2G("closing vnode #%d, opencount now is %d\n", VTOI(fp->f_vnode), --(fvdat->opencount)); return (err); @@ -3132,6 +3198,7 @@ { int err = 0; struct fuse_filehandle *fufh = fp->f_data; + struct fuse_mnt_data *fmnt = fp->f_vnode->v_mount->mnt_data; BREAK_IF_BAD(fp); @@ -3141,7 +3208,16 @@ if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; - if (fp->f_flag & O_DIRECT) { + DEBUG2G("fp->f_flag 0x%x\n", fp->f_flag); + + /* The great idea is to use the directio mnt opt such that it just + * controls the default mode of the file, later on it can be tuned + * by fcntl. Alas, that doesn't work -- the system always stripes + * out the O_DIRECT bit from my customized flag. So we fall back + * to the hardcoded behaviour (similar to Linux's, btw). + */ + //if (fp->f_flag & O_DIRECT) { + if (fmnt->mntopts & FUSEFS_DIRECTIO) { DEBUG2G("direct read of vnode %d via file handle %llu\n", VTOI(fp->f_vnode), fufh->fh_id); err = fuse_read_directbackend(fp->f_vnode, fufh->fh_id, uio, cred, td, FUSE_READ, fuse_std_buffeater, NULL); } else { @@ -4366,6 +4442,7 @@ { struct fuse_filehandle *fufh = fp->f_data; struct vattr va; + struct fuse_mnt_data *fmnt = fp->f_vnode->v_mount->mnt_data; int err = 0; BREAK_IF_BAD(fp); @@ -4387,7 +4464,8 @@ } else if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; - if (fp->f_flag & O_DIRECT) { + //if (fp->f_flag & O_DIRECT) { + if (fmnt->mntopts & FUSEFS_DIRECTIO) { DEBUG2G("direct write of vnode %d via file handle %llu\n", VTOI(fp->f_vnode), fufh->fh_id); err = fuse_write_directbackend(fp->f_vnode, fufh->fh_id, uio, cred, td); } else { @@ -4454,7 +4532,7 @@ #if _DEBUG vn_printf(vp, DEBLABEL "fuse_strategy: looking for fufh for vnode #%d, to read from block #%d\n", VTOI(vp), (int)bp->b_blkno); #endif - if (! (fufh = get_filehandle(vp, NULL, cred, bp->b_iocmd == BIO_READ ? O_RDONLY : O_WRONLY))) + if (! (fufh = get_filehandle(vp, NULL, cred, bp->b_iocmd == BIO_READ ? FREAD : FWRITE))) err = EIO; if (! err) @@ -4608,7 +4686,7 @@ { struct fuse_vnode_data *fvdat = ap->a_vp->v_data; - printf("nodeid: %d, fh_counter: %d\n", VTOI(ap->a_vp), fvdat->fh_counter); + printf("nodeid: %d, fh_counter: %d, nlookup: %llu\n", VTOI(ap->a_vp), fvdat->fh_counter, fvdat->nlookup); return (0); } ==== //depot/projects/soc2005/fuse4bsd2/mount_fusefs/mount_fusefs.c#2 (text+ko) ==== @@ -39,29 +39,30 @@ #include "mntopts.h" -struct mntopt mopts[] = { - MOPT_STDOPTS, - { NULL } -}; - void usage(void); int main(int argc, char *argv[]) { -#if __FreeBSD_version >= 600000 struct iovec *iov; -#else - struct iovec iov[6]; -#endif int ch, mntflags, iovlen; char *dev, *dir, mntpath[MAXPATHLEN]; + iov = NULL; + iovlen = 0; mntflags = 0; while ((ch = getopt(argc, argv, "o:")) != -1) { switch(ch) { case 'o': - getmntopts(optarg, mopts, &mntflags, 0); + if (strcmp(optarg,"fspath") && + strcmp(optarg,"from") && + strcmp(optarg,"allow_other") && + strcmp(optarg,"kernel_cache") && + strcmp(optarg,"direct_io")) { + usage(); + return (1); + } + build_iovec(&iov, &iovlen, optarg, "", -1); break; case '?': default: @@ -86,30 +87,11 @@ /* Prepare the options vector for nmount(). build_iovec() is declared * in mntopts.h. */ -#if __FreeBSD_version >= 600000 - iov = NULL; - iovlen = 0; build_iovec(&iov, &iovlen, "fstype", "fusefs", -1); build_iovec(&iov, &iovlen, "fspath", mntpath, -1); build_iovec(&iov, &iovlen, "from", dev, -1); - if (nmount(iov, iovlen, mntflags) < 0) -#else - iov[0].iov_base = "fstype"; - iov[0].iov_len = sizeof("fstype"); - iov[1].iov_base = "fusefs"; - iov[1].iov_len = strlen(iov[1].iov_base) + 1; - iov[2].iov_base = "fspath"; - iov[2].iov_len = sizeof("fspath"); - iov[3].iov_base = mntpath; - iov[3].iov_len = strlen(mntpath) + 1; - iov[4].iov_base = "from"; - iov[4].iov_len = sizeof("from"); - iov[5].iov_base = dev; - iov[5].iov_len = strlen(dev) + 1; - - if (nmount(iov, 6, mntflags) < 0) -#endif + if (nmount(iov, iovlen, 0) < 0) err(EX_OSERR, "%s", dev); exit(0); @@ -119,6 +101,9 @@ usage(void) { fprintf(stderr, - "usage: mount_fusefs [-o options] special node\n"); + "usage: mount_fusefs [-o option...] special node\n" + "known options: allow_other kernel_cache direct_io\n" + "(multiple options require separate \"-o\"-s)\n"); + exit(EX_USAGE); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200509111708.j8BH8E3c060295>