__private_extern__ int fuse_internal_remove(vnode_t dvp, vnode_t vp, struct componentname *cnp, enum fuse_opcode op, vfs_context_t context) { struct fuse_dispatcher fdi; struct vnode_attr *vap = VTOVA(vp); int need_invalidate = 0; uint64_t target_nlink = 0; mount_t mp = vnode_mount(vp); int err = 0; fdisp_init(&fdi, cnp->cn_namelen + 1); fdisp_make_vp(&fdi, op, dvp, context); memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen); ((char *)fdi.indata)[cnp->cn_namelen] = '\0'; if ((vap->va_nlink > 1) && vnode_isreg(vp)) { need_invalidate = 1; target_nlink = vap->va_nlink; } if (!(err = fdisp_wait_answ(&fdi))) { fuse_ticket_drop(fdi.tick); } fuse_invalidate_attr(dvp); fuse_invalidate_attr(vp); /* * XXX: M_MACFUSE_INVALIDATE_CACHED_VATTRS_UPON_UNLINK * * Consider the case where vap->va_nlink > 1 for the entity being * removed. In our world, other in-memory vnodes that share a link * count each with this one may not know right way that this one just * got deleted. We should let them know, say, through a vnode_iterate() * here and a callback that does fuse_invalidate_attr(vp) on each * relevant vnode. */ if (need_invalidate && !err) { if (!vfs_busy(mp, LK_NOWAIT)) { vnode_iterate(mp, 0, fuse_internal_remove_callback, (void *)&target_nlink); vfs_unbusy(mp); } else { IOLog("MacFUSE: skipping link count fixup upon remove\n"); } } return err; }
int fuse_internal_remove(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, enum fuse_opcode op) { struct fuse_dispatcher fdi; struct vattr *vap = VTOVA(vp); #if INVALIDATE_CACHED_VATTRS_UPON_UNLINK int need_invalidate = 0; uint64_t target_nlink = 0; #endif int err = 0; debug_printf("dvp=%p, cnp=%p, op=%d\n", vp, cnp, op); fdisp_init(&fdi, cnp->cn_namelen + 1); fdisp_make_vp(&fdi, op, dvp, cnp->cn_thread, cnp->cn_cred); memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen); ((char *)fdi.indata)[cnp->cn_namelen] = '\0'; #if INVALIDATE_CACHED_VATTRS_UPON_UNLINK if (vap->va_nlink > 1) { need_invalidate = 1; target_nlink = vap->va_nlink; } #endif err = fdisp_wait_answ(&fdi); fdisp_destroy(&fdi); fuse_invalidate_attr(dvp); fuse_invalidate_attr(vp); #ifdef XXXIP /* * XXX: INVALIDATE_CACHED_VATTRS_UPON_UNLINK * * Consider the case where vap->va_nlink > 1 for the entity being * removed. In our world, other in-memory vnodes that share a link * count each with this one may not know right way that this one just * got deleted. We should let them know, say, through a vnode_iterate() * here and a callback that does fuse_invalidate_attr(vp) on each * relevant vnode. */ if (need_invalidate && !err) { vnode_iterate(vnode_mount(vp), 0, fuse_internal_remove_callback, (void *)&target_nlink); } #endif return err; }
static int fuse_internal_remove_callback(struct vnode *vp, void *cargs) { struct vattr *vap; uint64_t target_nlink; vap = VTOVA(vp); target_nlink = *(uint64_t *)cargs; /* somewhat lame "heuristics", but you got better ideas? */ if ((vap->va_nlink == target_nlink) && vnode_isreg(vp)) { fuse_invalidate_attr(vp); } return 0; }
/* struct vnop_link_args { struct vnode *a_tdvp; struct vnode *a_vp; struct componentname *a_cnp; }; */ static int fuse_vnop_link(struct vop_link_args *ap) { struct vnode *vp = ap->a_vp; struct vnode *tdvp = ap->a_tdvp; struct componentname *cnp = ap->a_cnp; struct vattr *vap = VTOVA(vp); struct fuse_dispatcher fdi; struct fuse_entry_out *feo; struct fuse_link_in fli; int err; fuse_trace_printf_vnop(); if (fuse_isdeadfs(vp)) { return ENXIO; } if (vnode_mount(tdvp) != vnode_mount(vp)) { return EXDEV; } if (vap->va_nlink >= FUSE_LINK_MAX) { return EMLINK; } fli.oldnodeid = VTOI(vp); fdisp_init(&fdi, 0); fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp, FUSE_LINK, &fli, sizeof(fli), &fdi); if ((err = fdisp_wait_answ(&fdi))) { goto out; } feo = fdi.answ; err = fuse_internal_checkentry(feo, vnode_vtype(vp)); fuse_invalidate_attr(tdvp); fuse_invalidate_attr(vp); out: fdisp_destroy(&fdi); return err; }
int fuse_internal_remove(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, enum fuse_opcode op) { struct fuse_dispatcher fdi; struct vattr *vap = VTOVA(vp); #if INVALIDATE_CACHED_VATTRS_UPON_UNLINK int need_invalidate = 0; uint64_t target_nlink = 0; #endif int err = 0; debug_printf("dvp=%p, cnp=%p, op=%d\n", vp, cnp, op); fdisp_init(&fdi, cnp->cn_namelen + 1); fdisp_make_vp(&fdi, op, dvp, cnp->cn_thread, cnp->cn_cred); memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen); ((char *)fdi.indata)[cnp->cn_namelen] = '\0'; #if INVALIDATE_CACHED_VATTRS_UPON_UNLINK if (vap->va_nlink > 1) { need_invalidate = 1; target_nlink = vap->va_nlink; } #endif err = fdisp_wait_answ(&fdi); fdisp_destroy(&fdi); return err; }
/* struct vnop_getattr_args { struct vnode *a_vp; struct vattr *a_vap; struct ucred *a_cred; struct thread *a_td; }; */ static int fuse_vnop_getattr(struct vop_getattr_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_vnode_data *fvdat = VTOFUD(vp); int err = 0; int dataflags; struct fuse_dispatcher fdi; FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp)); dataflags = fuse_get_mpdata(vnode_mount(vp))->dataflags; /* Note that we are not bailing out on a dead file system just yet. */ if (!(dataflags & FSESS_INITED)) { if (!vnode_isvroot(vp)) { fdata_set_dead(fuse_get_mpdata(vnode_mount(vp))); err = ENOTCONN; debug_printf("fuse_getattr b: returning ENOTCONN\n"); return err; } else { goto fake; } } fdisp_init(&fdi, 0); if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, td, cred))) { if ((err == ENOTCONN) && vnode_isvroot(vp)) { /* see comment at similar place in fuse_statfs() */ fdisp_destroy(&fdi); goto fake; } if (err == ENOENT) { fuse_internal_vnode_disappear(vp); } goto out; } cache_attrs(vp, (struct fuse_attr_out *)fdi.answ); if (vap != VTOVA(vp)) { memcpy(vap, VTOVA(vp), sizeof(*vap)); } if (vap->va_type != vnode_vtype(vp)) { fuse_internal_vnode_disappear(vp); err = ENOENT; goto out; } if ((fvdat->flag & FN_SIZECHANGE) != 0) vap->va_size = fvdat->filesize; if (vnode_isreg(vp) && (fvdat->flag & FN_SIZECHANGE) == 0) { /* * This is for those cases when the file size changed without us * knowing, and we want to catch up. */ off_t new_filesize = ((struct fuse_attr_out *) fdi.answ)->attr.size; if (fvdat->filesize != new_filesize) { fuse_vnode_setsize(vp, cred, new_filesize); } } debug_printf("fuse_getattr e: returning 0\n"); out: fdisp_destroy(&fdi); return err; fake: bzero(vap, sizeof(*vap)); vap->va_type = vnode_vtype(vp); return 0; }
/* getattr sidekicks */ __private_extern__ int fuse_internal_loadxtimes(vnode_t vp, struct vnode_attr *out_vap, vfs_context_t context) { struct vnode_attr *in_vap = VTOVA(vp); struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp)); struct fuse_dispatcher fdi; struct fuse_getxtimes_out *fgxo = NULL; int isvroot = vnode_isvroot(vp); struct timespec t = { 0, 0 }; const struct timespec kZeroTime = { 0, 0 }; int err = 0; if (!(data->dataflags & FSESS_XTIMES)) { /* We don't return anything. */ goto out; } if (VTOFUD(vp)->c_flag & C_XTIMES_VALID) { VATTR_RETURN(out_vap, va_backup_time, in_vap->va_backup_time); VATTR_RETURN(out_vap, va_create_time, in_vap->va_create_time); goto out; } if (!fuse_implemented(data, FSESS_NOIMPLBIT(GETXTIMES))) { goto fake; } if (fuse_isdeadfs(vp) && isvroot) { goto fake; } if (!(data->dataflags & FSESS_INITED) && isvroot) { goto fake; } err = fdisp_simple_putget_vp(&fdi, FUSE_GETXTIMES, vp, context); if (err) { /* We don't ever treat this as a hard error. */ err = 0; goto fake; } fgxo = (struct fuse_getxtimes_out *)fdi.answ; t.tv_sec = (time_t)fgxo->bkuptime; /* XXX: truncation */ t.tv_nsec = fgxo->bkuptimensec; VATTR_RETURN(in_vap, va_backup_time, t); VATTR_RETURN(out_vap, va_backup_time, t); t.tv_sec = (time_t)fgxo->crtime; /* XXX: truncation */ t.tv_nsec = fgxo->crtimensec; VATTR_RETURN(in_vap, va_create_time, t); VATTR_RETURN(out_vap, va_create_time, t); fuse_ticket_drop(fdi.tick); VTOFUD(vp)->c_flag |= C_XTIMES_VALID; goto out; fake: VATTR_RETURN(out_vap, va_backup_time, kZeroTime); VATTR_RETURN(out_vap, va_create_time, kZeroTime); out: return err; }