Changeset View
Changeset View
Standalone View
Standalone View
sys/fs/fuse/fuse_vnops.c
Show First 20 Lines • Show All 312 Lines • ▼ Show 20 Lines | |||||
/* Close wrapper for fifos. */ | /* Close wrapper for fifos. */ | ||||
static int | static int | ||||
fuse_fifo_close(struct vop_close_args *ap) | fuse_fifo_close(struct vop_close_args *ap) | ||||
{ | { | ||||
return (fifo_specops.vop_close(ap)); | return (fifo_specops.vop_close(ap)); | ||||
} | } | ||||
/* Invalidate a range of cached data, whether dirty of not */ | |||||
static int | |||||
fuse_inval_buf_range(struct vnode *vp, off_t filesize, off_t start, off_t end) | |||||
{ | |||||
struct buf *bp; | |||||
daddr_t left_lbn, end_lbn, right_lbn; | |||||
off_t new_filesize; | |||||
int iosize, left_on, right_on, right_blksize; | |||||
iosize = fuse_iosize(vp); | |||||
left_lbn = start / iosize; | |||||
end_lbn = howmany(end, iosize); | |||||
left_on = start & (iosize - 1); | |||||
if (left_on != 0) { | |||||
bp = getblk(vp, left_lbn, iosize, PCATCH, 0, 0); | |||||
if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyend >= left_on) { | |||||
/* | |||||
* Flush the dirty buffer, because we don't have a | |||||
* byte-granular way to record which parts of the | |||||
* buffer are valid. | |||||
*/ | |||||
bwrite(bp); | |||||
if (bp->b_error) | |||||
return (bp->b_error); | |||||
} else { | |||||
brelse(bp); | |||||
} | |||||
} | |||||
right_on = end & (iosize - 1); | |||||
if (right_on != 0) { | |||||
right_lbn = end / iosize; | |||||
new_filesize = MAX(filesize, end); | |||||
right_blksize = MIN(iosize, new_filesize - iosize * right_lbn); | |||||
bp = getblk(vp, right_lbn, right_blksize, PCATCH, 0, 0); | |||||
if ((bp->b_flags & B_CACHE) != 0 && bp->b_dirtyoff < right_on) { | |||||
/* | |||||
* Flush the dirty buffer, because we don't have a | |||||
* byte-granular way to record which parts of the | |||||
* buffer are valid. | |||||
*/ | |||||
bwrite(bp); | |||||
if (bp->b_error) | |||||
return (bp->b_error); | |||||
} else { | |||||
brelse(bp); | |||||
} | |||||
} | |||||
v_inval_buf_range(vp, left_lbn, end_lbn, iosize); | |||||
return (0); | |||||
} | |||||
/* Send FUSE_LSEEK for this node */ | /* Send FUSE_LSEEK for this node */ | ||||
static int | static int | ||||
fuse_vnop_do_lseek(struct vnode *vp, struct thread *td, struct ucred *cred, | fuse_vnop_do_lseek(struct vnode *vp, struct thread *td, struct ucred *cred, | ||||
pid_t pid, off_t *offp, int whence) | pid_t pid, off_t *offp, int whence) | ||||
{ | { | ||||
struct fuse_dispatcher fdi; | struct fuse_dispatcher fdi; | ||||
struct fuse_filehandle *fufh; | struct fuse_filehandle *fufh; | ||||
struct fuse_lseek_in *flsi; | struct fuse_lseek_in *flsi; | ||||
▲ Show 20 Lines • Show All 333 Lines • ▼ Show 20 Lines | fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap) | ||||
struct fuse_dispatcher fdi; | struct fuse_dispatcher fdi; | ||||
struct fuse_filehandle *infufh, *outfufh; | struct fuse_filehandle *infufh, *outfufh; | ||||
struct fuse_copy_file_range_in *fcfri; | struct fuse_copy_file_range_in *fcfri; | ||||
struct ucred *incred = ap->a_incred; | struct ucred *incred = ap->a_incred; | ||||
struct ucred *outcred = ap->a_outcred; | struct ucred *outcred = ap->a_outcred; | ||||
struct fuse_write_out *fwo; | struct fuse_write_out *fwo; | ||||
struct thread *td; | struct thread *td; | ||||
struct uio io; | struct uio io; | ||||
off_t outfilesize; | |||||
pid_t pid; | pid_t pid; | ||||
int err; | int err; | ||||
if (mp != vnode_mount(outvp)) | if (mp != vnode_mount(outvp)) | ||||
goto fallback; | goto fallback; | ||||
if (incred->cr_uid != outcred->cr_uid) | if (incred->cr_uid != outcred->cr_uid) | ||||
goto fallback; | goto fallback; | ||||
Show All 39 Lines | fuse_vnop_copy_file_range(struct vop_copy_file_range_args *ap) | ||||
if (ap->a_fsizetd) { | if (ap->a_fsizetd) { | ||||
io.uio_offset = *ap->a_outoffp; | io.uio_offset = *ap->a_outoffp; | ||||
io.uio_resid = *ap->a_lenp; | io.uio_resid = *ap->a_lenp; | ||||
err = vn_rlimit_fsize(outvp, &io, ap->a_fsizetd); | err = vn_rlimit_fsize(outvp, &io, ap->a_fsizetd); | ||||
if (err) | if (err) | ||||
goto unlock; | goto unlock; | ||||
} | } | ||||
err = fuse_vnode_size(outvp, &outfilesize, outcred, curthread); | |||||
if (err) | |||||
goto unlock; | |||||
err = fuse_inval_buf_range(outvp, outfilesize, *ap->a_outoffp, | |||||
*ap->a_outoffp + *ap->a_lenp); | |||||
if (err) | |||||
goto unlock; | |||||
fdisp_init(&fdi, sizeof(*fcfri)); | fdisp_init(&fdi, sizeof(*fcfri)); | ||||
fdisp_make_vp(&fdi, FUSE_COPY_FILE_RANGE, invp, td, incred); | fdisp_make_vp(&fdi, FUSE_COPY_FILE_RANGE, invp, td, incred); | ||||
fcfri = fdi.indata; | fcfri = fdi.indata; | ||||
fcfri->fh_in = infufh->fh_id; | fcfri->fh_in = infufh->fh_id; | ||||
fcfri->off_in = *ap->a_inoffp; | fcfri->off_in = *ap->a_inoffp; | ||||
fcfri->nodeid_out = VTOI(outvp); | fcfri->nodeid_out = VTOI(outvp); | ||||
fcfri->fh_out = outfufh->fh_id; | fcfri->fh_out = outfufh->fh_id; | ||||
fcfri->off_out = *ap->a_outoffp; | fcfri->off_out = *ap->a_outoffp; | ||||
▲ Show 20 Lines • Show All 849 Lines • ▼ Show 20 Lines | if (fsess_is_impl(mp, FUSE_LSEEK)) { | ||||
*/ | */ | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
default: | default: | ||||
return (vop_stdpathconf(ap)); | return (vop_stdpathconf(ap)); | ||||
} | } | ||||
} | } | ||||
SDT_PROBE_DEFINE3(fusefs, , vnops, filehandles_closed, "struct vnode*", | |||||
"struct uio*", "struct ucred*"); | |||||
/* | /* | ||||
struct vnop_read_args { | struct vnop_read_args { | ||||
struct vnode *a_vp; | struct vnode *a_vp; | ||||
struct uio *a_uio; | struct uio *a_uio; | ||||
int a_ioflag; | int a_ioflag; | ||||
struct ucred *a_cred; | struct ucred *a_cred; | ||||
}; | }; | ||||
*/ | */ | ||||
static int | static int | ||||
fuse_vnop_read(struct vop_read_args *ap) | fuse_vnop_read(struct vop_read_args *ap) | ||||
{ | { | ||||
struct vnode *vp = ap->a_vp; | struct vnode *vp = ap->a_vp; | ||||
struct uio *uio = ap->a_uio; | struct uio *uio = ap->a_uio; | ||||
int ioflag = ap->a_ioflag; | int ioflag = ap->a_ioflag; | ||||
struct ucred *cred = ap->a_cred; | struct ucred *cred = ap->a_cred; | ||||
pid_t pid = curthread->td_proc->p_pid; | pid_t pid = curthread->td_proc->p_pid; | ||||
struct fuse_filehandle *fufh; | |||||
int err; | |||||
bool closefufh = false, directio; | |||||
MPASS(vp->v_type == VREG || vp->v_type == VDIR); | |||||
if (fuse_isdeadfs(vp)) { | if (fuse_isdeadfs(vp)) { | ||||
return ENXIO; | return ENXIO; | ||||
} | } | ||||
if (VTOFUD(vp)->flag & FN_DIRECTIO) { | if (VTOFUD(vp)->flag & FN_DIRECTIO) { | ||||
ioflag |= IO_DIRECT; | ioflag |= IO_DIRECT; | ||||
} | } | ||||
return fuse_io_dispatch(vp, uio, ioflag, cred, pid); | err = fuse_filehandle_getrw(vp, FREAD, &fufh, cred, pid); | ||||
if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) { | |||||
/* | |||||
* nfsd will do I/O without first doing VOP_OPEN. We | |||||
* must implicitly open the file here | |||||
*/ | |||||
err = fuse_filehandle_open(vp, FREAD, &fufh, curthread, cred); | |||||
closefufh = true; | |||||
} | } | ||||
if (err) { | |||||
SDT_PROBE3(fusefs, , vnops, filehandles_closed, vp, uio, cred); | |||||
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)); | |||||
fuse_vnode_update(vp, FN_ATIMECHANGE); | |||||
if (directio) { | |||||
SDT_PROBE2(fusefs, , vnops, trace, 1, "direct read of vnode"); | |||||
err = fuse_read_directbackend(vp, uio, cred, fufh); | |||||
} else { | |||||
SDT_PROBE2(fusefs, , vnops, trace, 1, "buffered read of vnode"); | |||||
err = fuse_read_biobackend(vp, uio, ioflag, cred, fufh, pid); | |||||
} | |||||
if (closefufh) | |||||
fuse_filehandle_close(vp, fufh, curthread, cred); | |||||
return (err); | |||||
} | |||||
/* | |||||
struct vnop_readdir_args { | struct vnop_readdir_args { | ||||
struct vnode *a_vp; | struct vnode *a_vp; | ||||
struct uio *a_uio; | struct uio *a_uio; | ||||
struct ucred *a_cred; | struct ucred *a_cred; | ||||
int *a_eofflag; | int *a_eofflag; | ||||
int *a_ncookies; | int *a_ncookies; | ||||
u_long **a_cookies; | u_long **a_cookies; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 533 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
fuse_vnop_write(struct vop_write_args *ap) | fuse_vnop_write(struct vop_write_args *ap) | ||||
{ | { | ||||
struct vnode *vp = ap->a_vp; | struct vnode *vp = ap->a_vp; | ||||
struct uio *uio = ap->a_uio; | struct uio *uio = ap->a_uio; | ||||
int ioflag = ap->a_ioflag; | int ioflag = ap->a_ioflag; | ||||
struct ucred *cred = ap->a_cred; | struct ucred *cred = ap->a_cred; | ||||
pid_t pid = curthread->td_proc->p_pid; | pid_t pid = curthread->td_proc->p_pid; | ||||
struct fuse_filehandle *fufh; | |||||
int err; | |||||
bool closefufh = false, directio; | |||||
MPASS(vp->v_type == VREG || vp->v_type == VDIR); | |||||
if (fuse_isdeadfs(vp)) { | if (fuse_isdeadfs(vp)) { | ||||
return ENXIO; | return ENXIO; | ||||
} | } | ||||
if (VTOFUD(vp)->flag & FN_DIRECTIO) { | if (VTOFUD(vp)->flag & FN_DIRECTIO) { | ||||
ioflag |= IO_DIRECT; | ioflag |= IO_DIRECT; | ||||
} | } | ||||
return fuse_io_dispatch(vp, uio, ioflag, cred, pid); | err = fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid); | ||||
if (err == EBADF && vnode_mount(vp)->mnt_flag & MNT_EXPORTED) { | |||||
/* | |||||
* nfsd will do I/O without first doing VOP_OPEN. We | |||||
* must implicitly open the file here | |||||
*/ | |||||
err = fuse_filehandle_open(vp, FWRITE, &fufh, curthread, cred); | |||||
closefufh = true; | |||||
} | |||||
if (err) { | |||||
SDT_PROBE3(fusefs, , vnops, filehandles_closed, vp, uio, cred); | |||||
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)); | |||||
fuse_vnode_update(vp, FN_MTIMECHANGE | FN_CTIMECHANGE); | |||||
if (directio) { | |||||
off_t start, end, filesize; | |||||
bool pages = (ioflag & IO_VMIO) != 0; | |||||
SDT_PROBE2(fusefs, , vnops, trace, 1, "direct write of vnode"); | |||||
err = fuse_vnode_size(vp, &filesize, cred, curthread); | |||||
if (err) | |||||
goto out; | |||||
start = uio->uio_offset; | |||||
end = start + uio->uio_resid; | |||||
if (!pages) { | |||||
err = fuse_inval_buf_range(vp, filesize, start, | |||||
end); | |||||
if (err) | |||||
goto out; | |||||
} | |||||
err = fuse_write_directbackend(vp, uio, cred, fufh, | |||||
filesize, ioflag, pages); | |||||
} else { | |||||
SDT_PROBE2(fusefs, , vnops, trace, 1, | |||||
"buffered write of vnode"); | |||||
if (!fsess_opt_writeback(vnode_mount(vp))) | |||||
ioflag |= IO_SYNC; | |||||
err = fuse_write_biobackend(vp, uio, cred, fufh, ioflag, pid); | |||||
} | |||||
fuse_internal_clear_suid_on_write(vp, cred, uio->uio_td); | |||||
out: | |||||
if (closefufh) | |||||
fuse_filehandle_close(vp, fufh, curthread, cred); | |||||
return (err); | |||||
} | } | ||||
static daddr_t | static daddr_t | ||||
fuse_gbp_getblkno(struct vnode *vp, vm_ooffset_t off) | fuse_gbp_getblkno(struct vnode *vp, vm_ooffset_t off) | ||||
{ | { | ||||
const int biosize = fuse_iosize(vp); | const int biosize = fuse_iosize(vp); | ||||
return (off / biosize); | return (off / biosize); | ||||
▲ Show 20 Lines • Show All 553 Lines • Show Last 20 Lines |