static int fuse_device_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct fuse_data *data; struct fuse_ticket *tick; int error; error = devfs_get_cdevpriv((void **)&data); if (error != 0) return (error); if (!data) panic("no fuse data upon fuse device close"); fdata_set_dead(data); FUSE_LOCK(); fuse_lck_mtx_lock(data->aw_mtx); /* wakup poll()ers */ selwakeuppri(&data->ks_rsel, PZERO + 1); /* Don't let syscall handlers wait in vain */ while ((tick = fuse_aw_pop(data))) { fuse_lck_mtx_lock(tick->tk_aw_mtx); fticket_set_answered(tick); tick->tk_aw_errno = ENOTCONN; wakeup(tick); fuse_lck_mtx_unlock(tick->tk_aw_mtx); FUSE_ASSERT_AW_DONE(tick); fuse_ticket_drop(tick); } fuse_lck_mtx_unlock(data->aw_mtx); FUSE_UNLOCK(); SDT_PROBE2(fuse, , device, trace, 1, "device close"); return (0); }
int fuse_filehandle_put(vnode_t vp, vfs_context_t context, fufh_type_t fufh_type) { struct fuse_dispatcher fdi; struct fuse_release_in *fri; struct fuse_vnode_data *fvdat = VTOFUD(vp); struct fuse_filehandle *fufh = NULL; int err = 0; int isdir = 0; int op = FUSE_RELEASE; const bool wait_for_completion = true; fuse_trace_printf("fuse_filehandle_put(vp=%p, fufh_type=%d)\n", vp, fufh_type); fufh = &(fvdat->fufh[fufh_type]); if (FUFH_IS_VALID(fufh)) { panic("fuse4x: filehandle_put called on a valid fufh (type=%d)", fufh_type); /* NOTREACHED */ } if (fuse_isdeadfs(vp)) { goto out; } if (vnode_isdir(vp)) { op = FUSE_RELEASEDIR; isdir = 1; } fdisp_init(&fdi, sizeof(*fri)); fdisp_make_vp(&fdi, op, vp, context); fri = fdi.indata; fri->fh = fufh->fh_id; fri->flags = fufh->open_flags; if (wait_for_completion) { if ((err = fdisp_wait_answ(&fdi))) { goto out; } else { fuse_ticket_drop(fdi.tick); } } else { fuse_insert_callback(fdi.tick, NULL); fuse_insert_message(fdi.tick); } out: OSAddAtomic(-1, (SInt32 *)&fuse_fh_current); fuse_invalidate_attr(vp); return err; }
__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; }
__private_extern__ int fuse_internal_readdir(vnode_t vp, uio_t uio, vfs_context_t context, struct fuse_filehandle *fufh, struct fuse_iov *cookediov, int *numdirent) { int err = 0; struct fuse_dispatcher fdi; struct fuse_read_in *fri; struct fuse_data *data; if (uio_resid(uio) == 0) { return 0; } fdisp_init(&fdi, 0); /* Note that we DO NOT have a UIO_SYSSPACE here (so no need for p2p I/O). */ while (uio_resid(uio) > 0) { fdi.iosize = sizeof(*fri); fdisp_make_vp(&fdi, FUSE_READDIR, vp, context); fri = fdi.indata; fri->fh = fufh->fh_id; fri->offset = uio_offset(uio); data = fuse_get_mpdata(vnode_mount(vp)); fri->size = (typeof(fri->size))min((size_t)uio_resid(uio), data->iosize); if ((err = fdisp_wait_answ(&fdi))) { goto out; } if ((err = fuse_internal_readdir_processdata(vp, uio, fri->size, fdi.answ, fdi.iosize, cookediov, numdirent))) { break; } } /* done: */ fuse_ticket_drop(fdi.tick); out: return ((err == -1) ? 0 : err); }
__private_extern__ int fuse_internal_fsync(vnode_t vp, vfs_context_t context, struct fuse_filehandle *fufh, void *param, fuse_op_waitfor_t waitfor) { int err = 0; int op = FUSE_FSYNC; struct fuse_fsync_in *ffsi; struct fuse_dispatcher *fdip = param; fuse_trace_printf_func(); fdip->iosize = sizeof(*ffsi); fdip->tick = NULL; if (vnode_isdir(vp)) { op = FUSE_FSYNCDIR; } fdisp_make_vp(fdip, op, vp, context); ffsi = fdip->indata; ffsi->fh = fufh->fh_id; ffsi->fsync_flags = 1; /* datasync */ if (waitfor == FUSE_OP_FOREGROUNDED) { if ((err = fdisp_wait_answ(fdip))) { if (err == ENOSYS) { if (op == FUSE_FSYNC) { fuse_clear_implemented(fdip->tick->tk_data, FSESS_NOIMPLBIT(FSYNC)); } else if (op == FUSE_FSYNCDIR) { fuse_clear_implemented(fdip->tick->tk_data, FSESS_NOIMPLBIT(FSYNCDIR)); } } goto out; } else { fuse_ticket_drop(fdip->tick); } } else { fuse_insert_callback(fdip->tick, fuse_internal_fsync_callback); fuse_insert_message(fdip->tick); } out: return err; }
__private_extern__ int fuse_internal_fsync_callback(struct fuse_ticket *ftick, __unused uio_t uio) { fuse_trace_printf_func(); if (ftick->tk_aw_ohead.error == ENOSYS) { if (fticket_opcode(ftick) == FUSE_FSYNC) { fuse_clear_implemented(ftick->tk_data, FSESS_NOIMPLBIT(FSYNC)); } else if (fticket_opcode(ftick) == FUSE_FSYNCDIR) { fuse_clear_implemented(ftick->tk_data, FSESS_NOIMPLBIT(FSYNCDIR)); } else { IOLog("MacFUSE: unexpected opcode in sync handling\n"); } } fuse_ticket_drop(ftick); return 0; }
int fuse_filehandle_put(vnode_t vp, vfs_context_t context, fufh_type_t fufh_type) { struct fuse_dispatcher fdi; struct fuse_release_in *fri; struct fuse_vnode_data *fvdat = VTOFUD(vp); struct fuse_filehandle *fufh = NULL; fuse_trace_printf("fuse_filehandle_put(vp=%p, fufh_type=%d)\n", vp, fufh_type); fufh = &(fvdat->fufh[fufh_type]); if (FUFH_IS_VALID(fufh)) { panic("fuse4x: filehandle_put called on a valid fufh (type=%d)", fufh_type); /* NOTREACHED */ } if (fuse_isdeadfs(vp)) { goto out; } int op = vnode_isdir(vp) ? FUSE_RELEASEDIR : FUSE_RELEASE; fuse_dispatcher_init(&fdi, sizeof(*fri)); fuse_dispatcher_make_vp(&fdi, op, vp, context); fri = fdi.indata; fri->fh = fufh->fh_id; fri->flags = fufh->open_flags; int err = fuse_dispatcher_wait_answer(&fdi); if (!err) { fuse_ticket_drop(fdi.ticket); } out: OSDecrementAtomic((SInt32 *)&fuse_fh_current); fuse_invalidate_attr(vp); return err; }
__private_extern__ int fuse_internal_rename(vnode_t fdvp, __unused vnode_t fvp, struct componentname *fcnp, vnode_t tdvp, __unused vnode_t tvp, struct componentname *tcnp, vfs_context_t context) { struct fuse_dispatcher fdi; struct fuse_rename_in *fri; int err = 0; fdisp_init(&fdi, sizeof(*fri) + fcnp->cn_namelen + tcnp->cn_namelen + 2); fdisp_make_vp(&fdi, FUSE_RENAME, fdvp, context); fri = fdi.indata; fri->newdir = VTOI(tdvp); memcpy((char *)fdi.indata + sizeof(*fri), fcnp->cn_nameptr, fcnp->cn_namelen); ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen] = '\0'; memcpy((char *)fdi.indata + sizeof(*fri) + fcnp->cn_namelen + 1, tcnp->cn_nameptr, tcnp->cn_namelen); ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen + tcnp->cn_namelen + 1] = '\0'; if (!(err = fdisp_wait_answ(&fdi))) { fuse_ticket_drop(fdi.tick); } if (err == 0) { fuse_invalidate_attr(fdvp); if (tdvp != fdvp) { fuse_invalidate_attr(tdvp); } } return err; }
__private_extern__ int fuse_internal_newentry_core(vnode_t dvp, vnode_t *vpp, struct componentname *cnp, enum vtype vtyp, struct fuse_dispatcher *fdip, vfs_context_t context) { int err = 0; struct fuse_entry_out *feo; mount_t mp = vnode_mount(dvp); if ((err = fdisp_wait_answ(fdip))) { return err; } feo = fdip->answ; if ((err = fuse_internal_checkentry(feo, vtyp))) { goto out; } err = fuse_vget_i(vpp, 0 /* flags */, feo, cnp, dvp, mp, context); if (err) { fuse_internal_forget_send(mp, context, feo->nodeid, 1, fdip); return err; } cache_attrs(*vpp, feo); out: fuse_ticket_drop(fdip->tick); return err; }
static errno_t fuse_vfsop_getattr(mount_t mp, struct vfs_attr *attr, vfs_context_t context) { int err = 0; bool deading = false, faking = false; struct fuse_dispatcher fdi; struct fuse_statfs_out *fsfo; struct fuse_statfs_out faked; struct fuse_data *data; fuse_trace_printf_vfsop(); data = fuse_get_mpdata(mp); if (!data) { panic("fuse4x: no private data for mount point?"); } if (!(data->dataflags & FSESS_INITED)) { // coreservices process requests ATTR_VOL_CAPABILITIES on the mountpoint right before // returning from mount() syscall. We need to fake the output because daemon might // not be ready to response yet (and deadlock will happen). faking = true; goto dostatfs; } fdisp_init(&fdi, 0); fdisp_make(&fdi, FUSE_STATFS, mp, FUSE_ROOT_ID, context); if ((err = fdisp_wait_answ(&fdi))) { // If we cannot communicate with the daemon (most likely because // it's dead), we still want to portray that we are a bonafide // file system so that we can be gracefully unmounted. if (err == ENOTCONN) { deading = faking = true; goto dostatfs; } return err; } dostatfs: if (faking) { bzero(&faked, sizeof(faked)); fsfo = &faked; } else { fsfo = fdi.answ; } if (fsfo->st.bsize == 0) { fsfo->st.bsize = FUSE_DEFAULT_IOSIZE; } if (fsfo->st.frsize == 0) { fsfo->st.frsize = FUSE_DEFAULT_BLOCKSIZE; } /* optimal transfer block size; will go into f_iosize in the kernel */ fsfo->st.bsize = fuse_round_size(fsfo->st.bsize, FUSE_MIN_IOSIZE, FUSE_MAX_IOSIZE); /* file system fragment size; will go into f_bsize in the kernel */ fsfo->st.frsize = fuse_round_size(fsfo->st.frsize, FUSE_MIN_BLOCKSIZE, FUSE_MAX_BLOCKSIZE); /* We must have: f_iosize >= f_bsize (fsfo->st.bsize >= fsfo->st_frsize) */ if (fsfo->st.bsize < fsfo->st.frsize) { fsfo->st.bsize = fsfo->st.frsize; } /* * TBD: Possibility: * * For actual I/O to fuse4x's "virtual" storage device, we use * data->blocksize and data->iosize. These are really meant to be * constant across the lifetime of a single mount. If necessary, we * can experiment by updating the mount point's stat with the frsize * and bsize values we come across here. */ /* * FUSE user daemon will (might) give us this: * * __u64 blocks; // total data blocks in the file system * __u64 bfree; // free blocks in the file system * __u64 bavail; // free blocks available to non-superuser * __u64 files; // total file nodes in the file system * __u64 ffree; // free file nodes in the file system * __u32 bsize; // preferred/optimal file system block size * __u32 namelen; // maximum length of filenames * __u32 frsize; // fundamental file system block size * * On Mac OS X, we will map this data to struct vfs_attr as follows: * * Mac OS X FUSE * -------- ---- * uint64_t f_supported <- // handled here * uint64_t f_active <- // handled here * uint64_t f_objcount <- - * uint64_t f_filecount <- files * uint64_t f_dircount <- - * uint32_t f_bsize <- frsize * size_t f_iosize <- bsize * uint64_t f_blocks <- blocks * uint64_t f_bfree <- bfree * uint64_t f_bavail <- bavail * uint64_t f_bused <- blocks - bfree * uint64_t f_files <- files * uint64_t f_ffree <- ffree * fsid_t f_fsid <- // handled elsewhere * uid_t f_owner <- // handled elsewhere * ... capabilities <- // handled here * ... attributes <- // handled here * f_create_time <- - * f_modify_time <- - * f_access_time <- - * f_backup_time <- - * uint32_t f_fssubtype <- // daemon provides * char *f_vol_name <- // handled here * uint16_t f_signature <- // handled here * uint16_t f_carbon_fsid <- // handled here */ VFSATTR_RETURN(attr, f_filecount, fsfo->st.files); VFSATTR_RETURN(attr, f_bsize, fsfo->st.frsize); VFSATTR_RETURN(attr, f_iosize, fsfo->st.bsize); VFSATTR_RETURN(attr, f_blocks, fsfo->st.blocks); VFSATTR_RETURN(attr, f_bfree, fsfo->st.bfree); VFSATTR_RETURN(attr, f_bavail, fsfo->st.bavail); VFSATTR_RETURN(attr, f_bused, (fsfo->st.blocks - fsfo->st.bfree)); VFSATTR_RETURN(attr, f_files, fsfo->st.files); VFSATTR_RETURN(attr, f_ffree, fsfo->st.ffree); /* f_fsid and f_owner handled elsewhere. */ /* Handle capabilities and attributes. */ handle_capabilities_and_attributes(mp, attr); VFSATTR_RETURN(attr, f_create_time, kZeroTime); VFSATTR_RETURN(attr, f_modify_time, kZeroTime); VFSATTR_RETURN(attr, f_access_time, kZeroTime); VFSATTR_RETURN(attr, f_backup_time, kZeroTime); if (deading) { VFSATTR_RETURN(attr, f_fssubtype, (uint32_t)FUSE_FSSUBTYPE_INVALID); } else { VFSATTR_RETURN(attr, f_fssubtype, data->fssubtype); } /* Daemon needs to pass this. */ if (VFSATTR_IS_ACTIVE(attr, f_vol_name)) { if (data->volname[0] != 0) { strncpy(attr->f_vol_name, data->volname, MAXPATHLEN); attr->f_vol_name[MAXPATHLEN - 1] = 0; VFSATTR_SET_SUPPORTED(attr, f_vol_name); } } VFSATTR_RETURN(attr, f_signature, OSSwapBigToHostInt16(FUSEFS_SIGNATURE)); VFSATTR_RETURN(attr, f_carbon_fsid, 0); if (!faking) fuse_ticket_drop(fdi.tick); return 0; }
static errno_t fuse_vfsop_unmount(mount_t mp, int mntflags, vfs_context_t context) { int err = 0; int flags = 0; fuse_device_t fdev; struct fuse_data *data; struct fuse_dispatcher fdi; vnode_t fuse_rootvp = NULLVP; fuse_trace_printf_vfsop(); if (mntflags & MNT_FORCE) { flags |= FORCECLOSE; } data = fuse_get_mpdata(mp); if (!data) { panic("fuse4x: no mount private data in vfs_unmount"); } #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_lock(data->biglock); #endif fdev = data->fdev; if (fdata_dead_get(data)) { /* * If the file system daemon is dead, it's pointless to try to do * any unmount-time operations that go out to user space. Therefore, * we pretend that this is a force unmount. However, this isn't of much * use. That's because if any non-root vnode is in use, the vflush() * that the kernel does before calling our VFS_UNMOUNT will fail * if the original unmount wasn't forcible already. That earlier * vflush is called with SKIPROOT though, so it wouldn't bail out * on the root vnode being in use. * * If we want, we could set FORCECLOSE here so that a non-forced * unmount will be "upgraded" to a forced unmount if the root vnode * is busy (you are cd'd to the mount point, for example). It's not * quite pure to do that though. * * flags |= FORCECLOSE; * log("fuse4x: forcing unmount on a dead file system\n"); */ } else if (!(data->dataflags & FSESS_INITED)) { flags |= FORCECLOSE; log("fuse4x: forcing unmount on not-yet-alive file system\n"); fdata_set_dead(data); } fuse_rootvp = data->rootvp; fuse_trace_printf("%s: Calling vflush(mp, fuse_rootvp, flags=0x%X);\n", __FUNCTION__, flags); #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_unlock(data->biglock); #endif err = vflush(mp, fuse_rootvp, flags); #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_lock(data->biglock); #endif fuse_trace_printf("%s: Done.\n", __FUNCTION__); if (err) { #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_unlock(data->biglock); #endif return err; } if (vnode_isinuse(fuse_rootvp, 1) && !(flags & FORCECLOSE)) { #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_unlock(data->biglock); #endif return EBUSY; } if (fdata_dead_get(data)) { goto alreadydead; } fdisp_init(&fdi, 0 /* no data to send along */); fdisp_make(&fdi, FUSE_DESTROY, mp, FUSE_ROOT_ID, context); fuse_trace_printf("%s: Waiting for reply from FUSE_DESTROY.\n", __FUNCTION__); err = fdisp_wait_answ(&fdi); fuse_trace_printf("%s: Reply received.\n", __FUNCTION__); if (!err) { fuse_ticket_drop(fdi.tick); } /* * Note that dounmount() signals a VQ_UNMOUNT VFS event. */ fdata_set_dead(data); alreadydead: fuse_trace_printf("%s: Calling vnode_rele(fuse_rootp);\n", __FUNCTION__); #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_unlock(data->biglock); #endif vnode_rele(fuse_rootvp); /* We got this reference in fuse_vfsop_mount(). */ #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_lock(data->biglock); #endif fuse_trace_printf("%s: Done.\n", __FUNCTION__); data->rootvp = NULLVP; fuse_trace_printf("%s: Calling vflush(mp, NULLVP, FORCECLOSE);\n", __FUNCTION__); #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_unlock(data->biglock); #endif (void)vflush(mp, NULLVP, FORCECLOSE); #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_lock(data->biglock); #endif fuse_trace_printf("%s: Done.\n", __FUNCTION__); fuse_lck_mtx_lock(fdev->mtx); vfs_setfsprivate(mp, NULL); data->dataflags &= ~FSESS_MOUNTED; OSAddAtomic(-1, (SInt32 *)&fuse_mount_count); #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_unlock(data->biglock); #endif if (!(data->dataflags & FSESS_OPENED)) { /* fdev->data was left for us to clean up */ fuse_device_close_final(fdev); /* fdev->data is gone now */ } fuse_lck_mtx_unlock(fdev->mtx); return 0; }
static errno_t fuse_vfsop_setattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t context) { int error = 0; fuse_trace_printf_vfsop(); kauth_cred_t cred = vfs_context_ucred(context); if (!fuse_vfs_context_issuser(context) && (kauth_cred_getuid(cred) != vfs_statfs(mp)->f_owner)) { return EACCES; } struct fuse_data *data = fuse_get_mpdata(mp); if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) { if (!fuse_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME))) { error = ENOTSUP; goto out; } if (fsap->f_vol_name[0] == 0) { error = EINVAL; goto out; } size_t namelen = strlen(fsap->f_vol_name); if (namelen >= MAXPATHLEN) { error = ENAMETOOLONG; goto out; } vnode_t root_vp; error = fuse_vfsop_root(mp, &root_vp, context); if (error) { goto out; } struct fuse_dispatcher fdi; fdisp_init(&fdi, namelen + 1); fdisp_make_vp(&fdi, FUSE_SETVOLNAME, root_vp, context); memcpy((char *)fdi.indata, fsap->f_vol_name, namelen); ((char *)fdi.indata)[namelen] = '\0'; if (!(error = fdisp_wait_answ(&fdi))) { fuse_ticket_drop(fdi.tick); } (void)vnode_put(root_vp); if (error) { if (error == ENOSYS) { error = ENOTSUP; fuse_clear_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME)); } goto out; } copystr(fsap->f_vol_name, data->volname, MAXPATHLEN - 1, &namelen); bzero(data->volname + namelen, MAXPATHLEN - namelen); VFSATTR_SET_SUPPORTED(fsap, f_vol_name); } out: return error; }
/* * Because of the vagaries of how a filehandle can be used, we try not to * be too smart in here (we try to be smart elsewhere). It is required that * you come in here only if you really do not have the said filehandle--else * we panic. * * This function should be called with fufh_mtx mutex locked. */ int fuse_filehandle_get(vnode_t vp, vfs_context_t context, fufh_type_t fufh_type, int mode) { struct fuse_dispatcher fdi; struct fuse_open_in *foi; struct fuse_open_out *foo; struct fuse_filehandle *fufh; struct fuse_vnode_data *fvdat = VTOFUD(vp); int err = 0; int oflags = 0; int op = FUSE_OPEN; fuse_trace_printf("fuse_filehandle_get(vp=%p, fufh_type=%d, mode=%x)\n", vp, fufh_type, mode); fufh = &(fvdat->fufh[fufh_type]); if (FUFH_IS_VALID(fufh)) { panic("fuse4x: filehandle_get called despite valid fufh (type=%d)", fufh_type); /* NOTREACHED */ } /* * Note that this means we are effectively FILTERING OUT open() flags. */ oflags = fuse_filehandle_xlate_to_oflags(fufh_type); if (vnode_isdir(vp)) { op = FUSE_OPENDIR; if (fufh_type != FUFH_RDONLY) { log("fuse4x: non-rdonly fufh requested for directory\n"); fufh_type = FUFH_RDONLY; } } fuse_dispatcher_init(&fdi, sizeof(*foi)); fuse_dispatcher_make_vp(&fdi, op, vp, context); if (vnode_islnk(vp) && (mode & O_SYMLINK)) { oflags |= O_SYMLINK; } foi = fdi.indata; foi->flags = oflags; OSIncrementAtomic((SInt32 *)&fuse_fh_upcall_count); if ((err = fuse_dispatcher_wait_answer(&fdi))) { const char *vname = vnode_getname(vp); if (err == ENOENT) { /* * See comment in fuse_vnop_reclaim(). */ cache_purge(vp); } log("fuse4x: filehandle_get: failed for %s " "(type=%d, err=%d, caller=%p)\n", (vname) ? vname : "?", fufh_type, err, __builtin_return_address(0)); if (vname) { vnode_putname(vname); } if (err == ENOENT) { fuse_vncache_purge(vp); } return err; } OSIncrementAtomic((SInt32 *)&fuse_fh_current); foo = fdi.answer; fufh->fh_id = foo->fh; fufh->open_count = 1; fufh->open_flags = oflags; fufh->fuse_open_flags = foo->open_flags; fuse_ticket_drop(fdi.ticket); return 0; }
__private_extern__ int fuse_internal_strategy(vnode_t vp, buf_t bp) { size_t biosize; size_t chunksize; size_t respsize; int mapped = FALSE; int mode; int op; int vtype = vnode_vtype(vp); int err = 0; caddr_t bufdat; off_t left; off_t offset; int32_t bflags = buf_flags(bp); fufh_type_t fufh_type; struct fuse_dispatcher fdi; struct fuse_data *data; struct fuse_vnode_data *fvdat = VTOFUD(vp); struct fuse_filehandle *fufh = NULL; mount_t mp = vnode_mount(vp); data = fuse_get_mpdata(mp); biosize = data->blocksize; if (!(vtype == VREG || vtype == VDIR)) { return ENOTSUP; } if (bflags & B_READ) { mode = FREAD; fufh_type = FUFH_RDONLY; /* FUFH_RDWR will also do */ } else { mode = FWRITE; fufh_type = FUFH_WRONLY; /* FUFH_RDWR will also do */ } if (fvdat->flag & FN_CREATING) { fuse_lck_mtx_lock(fvdat->createlock); if (fvdat->flag & FN_CREATING) { (void)fuse_msleep(fvdat->creator, fvdat->createlock, PDROP | PINOD | PCATCH, "fuse_internal_strategy", NULL); } else { fuse_lck_mtx_unlock(fvdat->createlock); } } fufh = &(fvdat->fufh[fufh_type]); if (!FUFH_IS_VALID(fufh)) { fufh_type = FUFH_RDWR; fufh = &(fvdat->fufh[fufh_type]); if (!FUFH_IS_VALID(fufh)) { fufh = NULL; } else { /* We've successfully fallen back to FUFH_RDWR. */ } } if (!fufh) { if (mode == FREAD) { fufh_type = FUFH_RDONLY; } else { fufh_type = FUFH_RDWR; } /* * Lets NOT do the filehandle preflight check here. */ err = fuse_filehandle_get(vp, NULL, fufh_type, 0 /* mode */); if (!err) { fufh = &(fvdat->fufh[fufh_type]); FUFH_AUX_INC(fufh); /* We've created a NEW fufh of type fufh_type. open_count is 1. */ } } else { /* good fufh */ FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count); /* We're using an existing fufh of type fufh_type. */ } if (err) { /* A more typical error case. */ if ((err == ENOTCONN) || fuse_isdeadfs(vp)) { buf_seterror(bp, EIO); buf_biodone(bp); return EIO; } IOLog("MacFUSE: strategy failed to get fh " "(vtype=%d, fufh_type=%d, err=%d)\n", vtype, fufh_type, err); if (!vfs_issynchronous(mp)) { IOLog("MacFUSE: asynchronous write failed!\n"); } buf_seterror(bp, EIO); buf_biodone(bp); return EIO; } if (!fufh) { panic("MacFUSE: tried everything but still no fufh"); /* NOTREACHED */ } #define B_INVAL 0x00040000 /* Does not contain valid info. */ #define B_ERROR 0x00080000 /* I/O error occurred. */ if (bflags & B_INVAL) { IOLog("MacFUSE: buffer does not contain valid information\n"); } if (bflags & B_ERROR) { IOLog("MacFUSE: an I/O error has occured\n"); } if (buf_count(bp) == 0) { return 0; } fdisp_init(&fdi, 0); if (mode == FREAD) { struct fuse_read_in *fri; buf_setresid(bp, buf_count(bp)); offset = (off_t)((off_t)buf_blkno(bp) * biosize); if (offset >= fvdat->filesize) { /* Trying to read at/after EOF? */ if (offset != fvdat->filesize) { /* Trying to read after EOF? */ buf_seterror(bp, EINVAL); } buf_biodone(bp); return 0; } /* Note that we just made sure that offset < fvdat->filesize. */ if ((offset + buf_count(bp)) > fvdat->filesize) { /* Trimming read */ buf_setcount(bp, (uint32_t)(fvdat->filesize - offset)); } if (buf_map(bp, &bufdat)) { IOLog("MacFUSE: failed to map buffer in strategy\n"); return EFAULT; } else { mapped = TRUE; } while (buf_resid(bp) > 0) { chunksize = min((size_t)buf_resid(bp), data->iosize); fdi.iosize = sizeof(*fri); op = FUSE_READ; if (vtype == VDIR) { op = FUSE_READDIR; } fdisp_make_vp(&fdi, op, vp, (vfs_context_t)0); fri = fdi.indata; fri->fh = fufh->fh_id; /* * Historical note: * * fri->offset = ((off_t)(buf_blkno(bp))) * biosize; * * This wasn't being incremented!? */ fri->offset = offset; fri->size = (typeof(fri->size))chunksize; fdi.tick->tk_aw_type = FT_A_BUF; fdi.tick->tk_aw_bufdata = bufdat; if ((err = fdisp_wait_answ(&fdi))) { /* There was a problem with reading. */ goto out; } respsize = fdi.tick->tk_aw_bufsize; if (respsize < 0) { /* Cannot really happen... */ err = EIO; goto out; } buf_setresid(bp, (uint32_t)(buf_resid(bp) - respsize)); bufdat += respsize; offset += respsize; /* Did we hit EOF before being done? */ if ((respsize == 0) && (buf_resid(bp) > 0)) { /* * Historical note: * If we don't get enough data, just fill the rest with zeros. * In NFS context, this would mean a hole in the file. */ /* Zero-pad the incomplete buffer. */ bzero(bufdat, buf_resid(bp)); buf_setresid(bp, 0); break; } } /* while (buf_resid(bp) > 0) */ } else { /* write */ struct fuse_write_in *fwi; struct fuse_write_out *fwo; int merr = 0; off_t diff; if (buf_map(bp, &bufdat)) { IOLog("MacFUSE: failed to map buffer in strategy\n"); return EFAULT; } else { mapped = TRUE; } /* Write begin */ buf_setresid(bp, buf_count(bp)); offset = (off_t)((off_t)buf_blkno(bp) * biosize); /* XXX: TBD -- Check here for extension (writing past end) */ left = buf_count(bp); while (left) { fdi.iosize = sizeof(*fwi); op = FUSE_WRITE; fdisp_make_vp(&fdi, op, vp, (vfs_context_t)0); chunksize = min((size_t)left, data->iosize); fwi = fdi.indata; fwi->fh = fufh->fh_id; fwi->offset = offset; fwi->size = (typeof(fwi->size))chunksize; fdi.tick->tk_ms_type = FT_M_BUF; fdi.tick->tk_ms_bufdata = bufdat; fdi.tick->tk_ms_bufsize = chunksize; /* About to write <chunksize> at <offset> */ if ((err = fdisp_wait_answ(&fdi))) { merr = 1; break; } fwo = fdi.answ; diff = chunksize - fwo->size; if (diff < 0) { err = EINVAL; break; } left -= fwo->size; bufdat += fwo->size; offset += fwo->size; buf_setresid(bp, buf_resid(bp) - fwo->size); } if (merr) { goto out; } } if (fdi.tick) { fuse_ticket_drop(fdi.tick); } else { /* No ticket upon leaving */ } out: if (err) { buf_seterror(bp, err); } if (mapped == TRUE) { buf_unmap(bp); } buf_biodone(bp); return err; }
/* * Because of the vagaries of how a filehandle can be used, we try not to * be too smart in here (we try to be smart elsewhere). It is required that * you come in here only if you really do not have the said filehandle--else * we panic. */ int fuse_filehandle_get(vnode_t vp, vfs_context_t context, fufh_type_t fufh_type, int mode) { struct fuse_dispatcher fdi; struct fuse_open_in *foi; struct fuse_open_out *foo; struct fuse_filehandle *fufh; struct fuse_vnode_data *fvdat = VTOFUD(vp); int err = 0; int isdir = 0; int oflags = 0; int op = FUSE_OPEN; fuse_trace_printf("fuse_filehandle_get(vp=%p, fufh_type=%d, mode=%x)\n", vp, fufh_type, mode); fufh = &(fvdat->fufh[fufh_type]); if (FUFH_IS_VALID(fufh)) { panic("fuse4x: filehandle_get called despite valid fufh (type=%d)", fufh_type); /* NOTREACHED */ } /* * Note that this means we are effectively FILTERING OUT open() flags. */ (void)mode; oflags = fuse_filehandle_xlate_to_oflags(fufh_type); if (vnode_isdir(vp)) { isdir = 1; op = FUSE_OPENDIR; if (fufh_type != FUFH_RDONLY) { log("fuse4x: non-rdonly fufh requested for directory\n"); fufh_type = FUFH_RDONLY; } } fdisp_init(&fdi, sizeof(*foi)); fdisp_make_vp(&fdi, op, vp, context); if (vnode_islnk(vp) && (mode & O_SYMLINK)) { oflags |= O_SYMLINK; } foi = fdi.indata; foi->flags = oflags; OSAddAtomic(1, (SInt32 *)&fuse_fh_upcall_count); if ((err = fdisp_wait_answ(&fdi))) { #if M_FUSE4X_ENABLE_UNSUPPORTED const char *vname = vnode_getname(vp); #endif /* M_FUSE4X_ENABLE_UNSUPPORTED */ if (err == ENOENT) { /* * See comment in fuse_vnop_reclaim(). */ cache_purge(vp); } #if M_FUSE4X_ENABLE_UNSUPPORTED log("fuse4x: filehandle_get: failed for %s " "(type=%d, err=%d, caller=%p)\n", (vname) ? vname : "?", fufh_type, err, __builtin_return_address(0)); if (vname) { vnode_putname(vname); } #endif /* M_FUSE4X_ENABLE_UNSUPPORTED */ if (err == ENOENT) { #if M_FUSE4X_ENABLE_BIGLOCK struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp)); fuse_biglock_unlock(data->biglock); #endif fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT); #if M_FUSE4X_ENABLE_BIGLOCK fuse_biglock_lock(data->biglock); #endif } return err; } OSAddAtomic(1, (SInt32 *)&fuse_fh_current); foo = fdi.answ; fufh->fh_id = foo->fh; fufh->open_count = 1; fufh->open_flags = oflags; fufh->fuse_open_flags = foo->open_flags; fufh->aux_count = 0; fuse_ticket_drop(fdi.tick); return 0; }
__private_extern__ int fuse_internal_access(vnode_t vp, int action, vfs_context_t context, struct fuse_access_param *facp) { int err = 0; int default_error = 0; uint32_t mask = 0; int dataflags; mount_t mp; struct fuse_dispatcher fdi; struct fuse_access_in *fai; struct fuse_data *data; fuse_trace_printf_func(); mp = vnode_mount(vp); data = fuse_get_mpdata(mp); dataflags = data->dataflags; /* Allow for now; let checks be handled inline later. */ if (fuse_isdeferpermissions_mp(mp)) { return 0; } if (facp->facc_flags & FACCESS_FROM_VNOP) { default_error = ENOTSUP; } /* * (action & KAUTH_VNODE_GENERIC_WRITE_BITS) on a read-only file system * would have been handled by higher layers. */ if (!fuse_implemented(data, FSESS_NOIMPLBIT(ACCESS))) { return default_error; } /* Unless explicitly permitted, deny everyone except the fs owner. */ if (!vnode_isvroot(vp) && !(facp->facc_flags & FACCESS_NOCHECKSPY)) { if (!(dataflags & FSESS_ALLOW_OTHER)) { int denied = fuse_match_cred(data->daemoncred, vfs_context_ucred(context)); if (denied) { return EPERM; } } facp->facc_flags |= FACCESS_NOCHECKSPY; } if (!(facp->facc_flags & FACCESS_DO_ACCESS)) { return default_error; } if (vnode_isdir(vp)) { if (action & (KAUTH_VNODE_LIST_DIRECTORY | KAUTH_VNODE_READ_EXTATTRIBUTES)) { mask |= R_OK; } if (action & (KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD)) { mask |= W_OK; } if (action & KAUTH_VNODE_SEARCH) { mask |= X_OK; } } else { if (action & (KAUTH_VNODE_READ_DATA | KAUTH_VNODE_READ_EXTATTRIBUTES)) { mask |= R_OK; } if (action & (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA)) { mask |= W_OK; } if (action & KAUTH_VNODE_EXECUTE) { mask |= X_OK; } } if (action & (KAUTH_VNODE_WRITE_ATTRIBUTES | KAUTH_VNODE_WRITE_EXTATTRIBUTES | KAUTH_VNODE_WRITE_SECURITY)) { mask |= W_OK; } bzero(&fdi, sizeof(fdi)); fdisp_init(&fdi, sizeof(*fai)); fdisp_make_vp(&fdi, FUSE_ACCESS, vp, context); fai = fdi.indata; fai->mask = F_OK; fai->mask |= mask; if (!(err = fdisp_wait_answ(&fdi))) { fuse_ticket_drop(fdi.tick); } if (err == ENOSYS) { /* * Make sure we don't come in here again. */ vfs_clearauthopaque(mp); fuse_clear_implemented(data, FSESS_NOIMPLBIT(ACCESS)); err = default_error; } if (err == ENOENT) { const char *vname = NULL; #if M_MACFUSE_ENABLE_UNSUPPORTED vname = vnode_getname(vp); #endif /* M_MACFUSE_ENABLE_UNSUPPORTED */ IOLog("MacFUSE: disappearing vnode %p (name=%s type=%d action=%x)\n", vp, (vname) ? vname : "?", vnode_vtype(vp), action); #if M_MACFUSE_ENABLE_UNSUPPORTED if (vname) { vnode_putname(vname); } #endif /* M_MACFUSE_ENABLE_UNSUPPORTED */ /* * On 10.4, I think I can get Finder to lock because of /.Trashes/<uid> * unless I use REVOKE_NONE here. */ #if M_MACFUSE_ENABLE_INTERIM_FSNODE_LOCK && !M_MACFUSE_ENABLE_HUGE_LOCK fuse_biglock_unlock(data->biglock); #endif fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT); #if M_MACFUSE_ENABLE_INTERIM_FSNODE_LOCK && !M_MACFUSE_ENABLE_HUGE_LOCK fuse_biglock_lock(data->biglock); #endif } return err; }
/* 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; }
__private_extern__ int fuse_internal_exchange(vnode_t fvp, const char *fname, size_t flen, vnode_t tvp, const char *tname, size_t tlen, int options, vfs_context_t context) { struct fuse_dispatcher fdi; struct fuse_exchange_in *fei; struct fuse_vnode_data *ffud = VTOFUD(fvp); struct fuse_vnode_data *tfud = VTOFUD(tvp); vnode_t fdvp = ffud->parentvp; vnode_t tdvp = tfud->parentvp; int err = 0; fdisp_init(&fdi, sizeof(*fei) + flen + tlen + 2); fdisp_make_vp(&fdi, FUSE_EXCHANGE, fvp, context); fei = fdi.indata; fei->olddir = VTOI(fdvp); fei->newdir = VTOI(tdvp); fei->options = (uint64_t)options; memcpy((char *)fdi.indata + sizeof(*fei), fname, flen); ((char *)fdi.indata)[sizeof(*fei) + flen] = '\0'; memcpy((char *)fdi.indata + sizeof(*fei) + flen + 1, tname, tlen); ((char *)fdi.indata)[sizeof(*fei) + flen + tlen + 1] = '\0'; ubc_msync(fvp, (off_t)0, (off_t)ffud->filesize, (off_t*)0, UBC_PUSHALL | UBC_INVALIDATE | UBC_SYNC); ubc_msync(tvp, (off_t)0, (off_t)tfud->filesize, (off_t*)0, UBC_PUSHALL | UBC_INVALIDATE | UBC_SYNC); if (!(err = fdisp_wait_answ(&fdi))) { fuse_ticket_drop(fdi.tick); } if (err == 0) { if (fdvp) { fuse_invalidate_attr(fdvp); } if (tdvp != fdvp) { if (tdvp) { fuse_invalidate_attr(tdvp); } } fuse_invalidate_attr(fvp); fuse_invalidate_attr(tvp); cache_purge(fvp); cache_purge(tvp); /* Swap sizes */ off_t tmpfilesize = ffud->filesize; ffud->filesize = tfud->filesize; tfud->filesize = tmpfilesize; ubc_setsize(fvp, (off_t)ffud->filesize); ubc_setsize(tvp, (off_t)tfud->filesize); fuse_kludge_exchange(fvp, tvp); /* * Another approach (will need additional kernel support to work): * vnode_t tmpvp = ffud->vp; ffud->vp = tfud->vp; tfud->vp = tmpvp; vnode_t tmpparentvp = ffud->parentvp; ffud->parentvp = tfud->parentvp; tfud->parentvp = tmpparentvp; off_t tmpfilesize = ffud->filesize; ffud->filesize = tfud->filesize; tfud->filesize = tmpfilesize; struct fuse_vnode_data tmpfud; memcpy(&tmpfud, ffud, sizeof(struct fuse_vnode_data)); memcpy(ffud, tfud, sizeof(struct fuse_vnode_data)); memcpy(tfud, &tmpfud, sizeof(struct fuse_vnode_data)); HNodeExchangeFromFSNode(ffud, tfud); * */ } return err; }
__private_extern__ int fuse_internal_init_synchronous(struct fuse_ticket *ftick) { int err = 0; struct fuse_init_out *fiio; struct fuse_data *data = ftick->tk_data; if ((err = ftick->tk_aw_ohead.error)) { goto out; } fiio = fticket_resp(ftick)->base; if ((fiio->major < MACFUSE_MIN_USER_VERSION_MAJOR) || (fiio->minor < MACFUSE_MIN_USER_VERSION_MINOR)){ IOLog("MacFUSE: user-space library has too low a version\n"); err = EPROTONOSUPPORT; goto out; } data->fuse_libabi_major = fiio->major; data->fuse_libabi_minor = fiio->minor; if (fuse_libabi_geq(data, MACFUSE_MIN_USER_VERSION_MAJOR, MACFUSE_MIN_USER_VERSION_MINOR)) { if (fticket_resp(ftick)->len == sizeof(struct fuse_init_out)) { data->max_write = fiio->max_write; } else { err = EINVAL; } } else { /* Old fix values */ data->max_write = 4096; } if (fiio->flags & FUSE_CASE_INSENSITIVE) { data->dataflags |= FSESS_CASE_INSENSITIVE; } if (fiio->flags & FUSE_VOL_RENAME) { data->dataflags |= FSESS_VOL_RENAME; } if (fiio->flags & FUSE_XTIMES) { data->dataflags |= FSESS_XTIMES; } out: fuse_ticket_drop(ftick); if (err) { fdata_set_dead(data); } fuse_lck_mtx_lock(data->ticket_mtx); data->dataflags |= FSESS_INITED; fuse_wakeup(&data->ticketer); fuse_lck_mtx_unlock(data->ticket_mtx); return 0; }