Exemple #1
0
int
fuse_io_dispatch(struct vnode *vp, struct uio *uio, int ioflag,
    struct ucred *cred)
{
	struct fuse_filehandle *fufh;
	int err, directio;

	MPASS(vp->v_type == VREG || vp->v_type == VDIR);

	err = fuse_filehandle_getrw(vp,
	    (uio->uio_rw == UIO_READ) ? FUFH_RDONLY : FUFH_WRONLY, &fufh);
	if (err) {
		printf("FUSE: io dispatch: filehandles are closed\n");
		return err;
	}
	/*
         * Ideally, when the daemon asks for direct io at open time, the
         * standard file flag should be set according to this, so that would
         * just change the default mode, which later on could be changed via
         * fcntl(2).
         * But this doesn't work, the O_DIRECT flag gets cleared at some point
         * (don't know where). So to make any use of the Fuse direct_io option,
         * we hardwire it into the file's private data (similarly to Linux,
         * btw.).
         */
	directio = (ioflag & IO_DIRECT) || !fsess_opt_datacache(vnode_mount(vp));

	switch (uio->uio_rw) {
	case UIO_READ:
		if (directio) {
			FS_DEBUG("direct read of vnode %ju via file handle %ju\n",
			    (uintmax_t)VTOILLU(vp), (uintmax_t)fufh->fh_id);
			err = fuse_read_directbackend(vp, uio, cred, fufh);
		} else {
			FS_DEBUG("buffered read of vnode %ju\n", 
			      (uintmax_t)VTOILLU(vp));
			err = fuse_read_biobackend(vp, uio, cred, fufh);
		}
		break;
	case UIO_WRITE:
		if (directio) {
			FS_DEBUG("direct write of vnode %ju via file handle %ju\n",
			    (uintmax_t)VTOILLU(vp), (uintmax_t)fufh->fh_id);
			err = fuse_write_directbackend(vp, uio, cred, fufh, ioflag);
		} else {
			FS_DEBUG("buffered write of vnode %ju\n", 
			      (uintmax_t)VTOILLU(vp));
			err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag);
		}
		break;
	default:
		panic("uninterpreted mode passed to fuse_io_dispatch");
	}

	return (err);
}
Exemple #2
0
int
fuse_vnode_savesize(struct vnode *vp, struct ucred *cred)
{
    struct fuse_vnode_data *fvdat = VTOFUD(vp);
    struct thread *td = curthread;
    struct fuse_filehandle *fufh = NULL;
    struct fuse_dispatcher  fdi;
    struct fuse_setattr_in *fsai;
    int err = 0;

    DEBUG("inode=%jd size=%jd\n", VTOI(vp), fvdat->filesize);
    ASSERT_VOP_ELOCKED(vp, "fuse_io_extend");

    if (fuse_isdeadfs(vp)) {
        return EBADF;
    }

    if (vnode_vtype(vp) == VDIR) {
        return EISDIR;
    }

    if (vfs_isrdonly(vnode_mount(vp))) {
        return EROFS;
    }

    if (cred == NULL) {
        cred = td->td_ucred;
    }

    fdisp_init(&fdi, sizeof(*fsai));
    fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
    fsai = fdi.indata;
    fsai->valid = 0;

    // Truncate to a new value.
    fsai->size = fvdat->filesize;
    fsai->valid |= FATTR_SIZE;

    fuse_filehandle_getrw(vp, FUFH_WRONLY, &fufh);
    if (fufh) {
        fsai->fh = fufh->fh_id;
        fsai->valid |= FATTR_FH;
    }

    err = fdisp_wait_answ(&fdi);
    fdisp_destroy(&fdi);
    if (err == 0)
        fvdat->flag &= ~FN_SIZECHANGE;

    fuse_invalidate_attr(vp);

    return err;
}
Exemple #3
0
int
fuse_io_strategy(struct vnode *vp, struct buf *bp)
{
	struct fuse_filehandle *fufh;
	struct fuse_vnode_data *fvdat = VTOFUD(vp);
	struct ucred *cred;
	struct uio *uiop;
	struct uio uio;
	struct iovec io;
	int error = 0;

	const int biosize = fuse_iosize(vp);

	MPASS(vp->v_type == VREG || vp->v_type == VDIR);
	MPASS(bp->b_iocmd == BIO_READ || bp->b_iocmd == BIO_WRITE);
	FS_DEBUG("inode=%ju offset=%jd resid=%ld\n",
	    (uintmax_t)VTOI(vp), (intmax_t)(((off_t)bp->b_blkno) * biosize),
	    bp->b_bcount);

	error = fuse_filehandle_getrw(vp,
	    (bp->b_iocmd == BIO_READ) ? FUFH_RDONLY : FUFH_WRONLY, &fufh);
	if (error) {
		printf("FUSE: strategy: filehandles are closed\n");
		bp->b_ioflags |= BIO_ERROR;
		bp->b_error = error;
		return (error);
	}
	cred = bp->b_iocmd == BIO_READ ? bp->b_rcred : bp->b_wcred;

	uiop = &uio;
	uiop->uio_iov = &io;
	uiop->uio_iovcnt = 1;
	uiop->uio_segflg = UIO_SYSSPACE;
	uiop->uio_td = curthread;

	/*
         * clear BIO_ERROR and B_INVAL state prior to initiating the I/O.  We
         * do this here so we do not have to do it in all the code that
         * calls us.
         */
	bp->b_flags &= ~B_INVAL;
	bp->b_ioflags &= ~BIO_ERROR;

	KASSERT(!(bp->b_flags & B_DONE),
	    ("fuse_io_strategy: bp %p already marked done", bp));
	if (bp->b_iocmd == BIO_READ) {
		io.iov_len = uiop->uio_resid = bp->b_bcount;
		io.iov_base = bp->b_data;
		uiop->uio_rw = UIO_READ;

		uiop->uio_offset = ((off_t)bp->b_blkno) * biosize;
		error = fuse_read_directbackend(vp, uiop, cred, fufh);

		if ((!error && uiop->uio_resid) ||
		    (fsess_opt_brokenio(vnode_mount(vp)) && error == EIO &&
		    uiop->uio_offset < fvdat->filesize && fvdat->filesize > 0 &&
		    uiop->uio_offset >= fvdat->cached_attrs.va_size)) {
			/*
	                 * If we had a short read with no error, we must have
	                 * hit a file hole.  We should zero-fill the remainder.
	                 * This can also occur if the server hits the file EOF.
	                 *
	                 * Holes used to be able to occur due to pending
	                 * writes, but that is not possible any longer.
	                 */
			int nread = bp->b_bcount - uiop->uio_resid;
			int left = uiop->uio_resid;

			if (error != 0) {
				printf("FUSE: Fix broken io: offset %ju, "
				       " resid %zd, file size %ju/%ju\n", 
				       (uintmax_t)uiop->uio_offset,
				    uiop->uio_resid, fvdat->filesize,
				    fvdat->cached_attrs.va_size);
				error = 0;
			}
			if (left > 0)
				bzero((char *)bp->b_data + nread, left);
			uiop->uio_resid = 0;
		}
		if (error) {
			bp->b_ioflags |= BIO_ERROR;
			bp->b_error = error;
		}
	} else {
		/*
	         * If we only need to commit, try to commit
	         */
		if (bp->b_flags & B_NEEDCOMMIT) {
			FS_DEBUG("write: B_NEEDCOMMIT flags set\n");
		}
		/*
	         * Setup for actual write
	         */
		if ((off_t)bp->b_blkno * biosize + bp->b_dirtyend > 
		    fvdat->filesize)
			bp->b_dirtyend = fvdat->filesize - 
				(off_t)bp->b_blkno * biosize;

		if (bp->b_dirtyend > bp->b_dirtyoff) {
			io.iov_len = uiop->uio_resid = bp->b_dirtyend
			    - bp->b_dirtyoff;
			uiop->uio_offset = (off_t)bp->b_blkno * biosize
			    + bp->b_dirtyoff;
			io.iov_base = (char *)bp->b_data + bp->b_dirtyoff;
			uiop->uio_rw = UIO_WRITE;

			error = fuse_write_directbackend(vp, uiop, cred, fufh, 0);

			if (error == EINTR || error == ETIMEDOUT
			    || (!error && (bp->b_flags & B_NEEDCOMMIT))) {

				bp->b_flags &= ~(B_INVAL | B_NOCACHE);
				if ((bp->b_flags & B_PAGING) == 0) {
					bdirty(bp);
					bp->b_flags &= ~B_DONE;
				}
				if ((error == EINTR || error == ETIMEDOUT) &&
				    (bp->b_flags & B_ASYNC) == 0)
					bp->b_flags |= B_EINTR;
			} else {
				if (error) {
					bp->b_ioflags |= BIO_ERROR;
					bp->b_flags |= B_INVAL;
					bp->b_error = error;
				}
				bp->b_dirtyoff = bp->b_dirtyend = 0;
			}
		} else {
			bp->b_resid = 0;
			bufdone(bp);
			return (0);
		}
	}
	bp->b_resid = uiop->uio_resid;
	bufdone(bp);
	return (error);
}
Exemple #4
0
/*
    struct vnop_setattr_args {
	struct vnode *a_vp;
	struct vattr *a_vap;
	struct ucred *a_cred;
	struct thread *a_td;
    };
*/
static int
fuse_vnop_setattr(struct vop_setattr_args *ap)
{
	struct vnode *vp = ap->a_vp;
	struct vattr *vap = ap->a_vap;
	struct ucred *cred = ap->a_cred;
	struct thread *td = curthread;

	struct fuse_dispatcher fdi;
	struct fuse_setattr_in *fsai;
	struct fuse_access_param facp;

	int err = 0;
	enum vtype vtyp;
	int sizechanged = 0;
	uint64_t newsize = 0;

	FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp));

	if (fuse_isdeadfs(vp)) {
		return ENXIO;
	}
	fdisp_init(&fdi, sizeof(*fsai));
	fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
	fsai = fdi.indata;
	fsai->valid = 0;

	bzero(&facp, sizeof(facp));

	facp.xuid = vap->va_uid;
	facp.xgid = vap->va_gid;

	if (vap->va_uid != (uid_t)VNOVAL) {
		facp.facc_flags |= FACCESS_CHOWN;
		fsai->uid = vap->va_uid;
		fsai->valid |= FATTR_UID;
	}
	if (vap->va_gid != (gid_t)VNOVAL) {
		facp.facc_flags |= FACCESS_CHOWN;
		fsai->gid = vap->va_gid;
		fsai->valid |= FATTR_GID;
	}
	if (vap->va_size != VNOVAL) {

		struct fuse_filehandle *fufh = NULL;

		/*Truncate to a new value. */
		    fsai->size = vap->va_size;
		sizechanged = 1;
		newsize = vap->va_size;
		fsai->valid |= FATTR_SIZE;

		fuse_filehandle_getrw(vp, FUFH_WRONLY, &fufh);
		if (fufh) {
			fsai->fh = fufh->fh_id;
			fsai->valid |= FATTR_FH;
		}
	}
	if (vap->va_atime.tv_sec != VNOVAL) {
		fsai->atime = vap->va_atime.tv_sec;
		fsai->atimensec = vap->va_atime.tv_nsec;
		fsai->valid |= FATTR_ATIME;
	}
	if (vap->va_mtime.tv_sec != VNOVAL) {
		fsai->mtime = vap->va_mtime.tv_sec;
		fsai->mtimensec = vap->va_mtime.tv_nsec;
		fsai->valid |= FATTR_MTIME;
	}
	if (vap->va_mode != (mode_t)VNOVAL) {
		fsai->mode = vap->va_mode & ALLPERMS;
		fsai->valid |= FATTR_MODE;
	}
	if (!fsai->valid) {
		goto out;
	}
	vtyp = vnode_vtype(vp);

	if (fsai->valid & FATTR_SIZE && vtyp == VDIR) {
		err = EISDIR;
		goto out;
	}
	if (vfs_isrdonly(vnode_mount(vp)) && (fsai->valid & ~FATTR_SIZE || vtyp == VREG)) {
		err = EROFS;
		goto out;
	}
	if (fsai->valid & ~FATTR_SIZE) {
	  /*err = fuse_internal_access(vp, VADMIN, context, &facp); */
	  /*XXX */
		    err = 0;
	}
	facp.facc_flags &= ~FACCESS_XQUERIES;

	if (err && !(fsai->valid & ~(FATTR_ATIME | FATTR_MTIME)) &&
	    vap->va_vaflags & VA_UTIMES_NULL) {
		err = fuse_internal_access(vp, VWRITE, &facp, td, cred);
	}
	if (err)
		goto out;
	if ((err = fdisp_wait_answ(&fdi)))
		goto out;
	vtyp = IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode);

	if (vnode_vtype(vp) != vtyp) {
		if (vnode_vtype(vp) == VNON && vtyp != VNON) {
			debug_printf("FUSE: Dang! vnode_vtype is VNON and vtype isn't.\n");
		} else {
			/*
	                 * STALE vnode, ditch
	                 *
	                 * The vnode has changed its type "behind our back". There's
	                 * nothing really we can do, so let us just force an internal
	                 * revocation and tell the caller to try again, if interested.
	                 */
			fuse_internal_vnode_disappear(vp);
			err = EAGAIN;
		}
	}
	if (!err && !sizechanged) {
		cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
	}
out:
	fdisp_destroy(&fdi);
	if (!err && sizechanged) {
		fuse_vnode_setsize(vp, cred, newsize);
		VTOFUD(vp)->flag &= ~FN_SIZECHANGE;
	}
	return err;
}