static int fuse_sync_callback(vnode_t vp, void *cargs) { int type; struct fuse_sync_cargs *args; struct fuse_vnode_data *fvdat; struct fuse_filehandle *fufh; struct fuse_data *data; mount_t mp; if (!vnode_hasdirtyblks(vp)) { return VNODE_RETURNED; } mp = vnode_mount(vp); if (fuse_isdeadfs_mp(mp)) { return VNODE_RETURNED_DONE; } data = fuse_get_mpdata(mp); if (!fuse_implemented(data, (vnode_isdir(vp)) ? FSESS_NOIMPLBIT(FSYNCDIR) : FSESS_NOIMPLBIT(FSYNC))) { return VNODE_RETURNED; } args = (struct fuse_sync_cargs *)cargs; fvdat = VTOFUD(vp); #if M_OSXFUSE_ENABLE_BIG_LOCK fuse_biglock_unlock(data->biglock); #endif cluster_push(vp, 0); #if M_OSXFUSE_ENABLE_BIG_LOCK fuse_biglock_lock(data->biglock); #endif for (type = 0; type < FUFH_MAXTYPE; type++) { fufh = &(fvdat->fufh[type]); if (FUFH_IS_VALID(fufh)) { (void)fuse_internal_fsync_fh(vp, args->context, fufh, FUSE_OP_FOREGROUNDED); } } /* * In general: * * - can use vnode_isinuse() if the need be * - vnode and UBC are in lock-step * - note that umount will call ubc_sync_range() */ return VNODE_RETURNED; }
static int fuse_sync_callback(vnode_t vp, void *cargs) { int type; struct fuse_sync_cargs *args; struct fuse_vnode_data *fvdat; struct fuse_dispatcher fdi; struct fuse_filehandle *fufh; struct fuse_data *data; mount_t mp; if (!vnode_hasdirtyblks(vp)) { return VNODE_RETURNED; } mp = vnode_mount(vp); if (fuse_isdeadfs(vp)) { return VNODE_RETURNED_DONE; } data = fuse_get_mpdata(mp); if (!fuse_implemented(data, (vnode_isdir(vp)) ? FSESS_NOIMPLBIT(FSYNCDIR) : FSESS_NOIMPLBIT(FSYNC))) { return VNODE_RETURNED; } args = (struct fuse_sync_cargs *)cargs; fvdat = VTOFUD(vp); cluster_push(vp, 0); fuse_dispatcher_init(&fdi, 0); for (type = 0; type < FUFH_MAXTYPE; type++) { fufh = &(fvdat->fufh[type]); if (FUFH_IS_VALID(fufh)) { (void)fuse_internal_fsync(vp, args->context, fufh, &fdi); } } /* * In general: * * - can use vnode_isinuse() if the need be * - vnode and UBC are in lock-step * - note that umount will call ubc_sync_range() */ return VNODE_RETURNED; }
static void handle_capabilities_and_attributes(mount_t mp, struct vfs_attr *attr) { struct fuse_data *data = fuse_get_mpdata(mp); if (!data) { panic("fuse4x: no private data for mount point?"); } attr->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 0 // | VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS /* * Note that we don't really have hard links in a fuse4x file system * unless the user file system daemon provides persistent/consistent * inode numbers. Maybe instead of returning the "wrong" answer here * we should just deny knowledge of this capability in the valid bits * below. */ | VOL_CAP_FMT_HARDLINKS // | VOL_CAP_FMT_JOURNAL // | VOL_CAP_FMT_JOURNAL_ACTIVE | VOL_CAP_FMT_NO_ROOT_TIMES // | VOL_CAP_FMT_SPARSE_FILES // | VOL_CAP_FMT_ZERO_RUNS // | VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING | VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_2TB_FILESIZE // | VOL_CAP_FMT_OPENDENYMODES // | VOL_CAP_FMT_HIDDEN_FILES // | VOL_CAP_FMT_PATH_FROM_ID ; attr->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0 | VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS | VOL_CAP_FMT_JOURNAL | VOL_CAP_FMT_JOURNAL_ACTIVE | VOL_CAP_FMT_NO_ROOT_TIMES | VOL_CAP_FMT_SPARSE_FILES | VOL_CAP_FMT_ZERO_RUNS | VOL_CAP_FMT_CASE_SENSITIVE | VOL_CAP_FMT_CASE_PRESERVING | VOL_CAP_FMT_FAST_STATFS | VOL_CAP_FMT_2TB_FILESIZE | VOL_CAP_FMT_OPENDENYMODES | VOL_CAP_FMT_HIDDEN_FILES | VOL_CAP_FMT_PATH_FROM_ID ; attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = 0 // | VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST // | VOL_CAP_INT_NFSEXPORT // | VOL_CAP_INT_READDIRATTR // | VOL_CAP_INT_EXCHANGEDATA // | VOL_CAP_INT_COPYFILE // | VOL_CAP_INT_ALLOCATE // | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK | VOL_CAP_INT_EXTENDED_SECURITY // | VOL_CAP_INT_USERACCESS // | VOL_CAP_INT_MANLOCK // | VOL_CAP_INT_EXTENDED_ATTR // | VOL_CAP_INT_NAMEDSTREAMS ; if (data->dataflags & FSESS_NATIVE_XATTR) { attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_ATTR; } /* Don't set the EXCHANGEDATA capability if it's known not to be * implemented in the FUSE daemon. */ if (fuse_implemented(data, FSESS_NOIMPLBIT(EXCHANGE))) { attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXCHANGEDATA; } attr->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = 0 | VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT | VOL_CAP_INT_READDIRATTR | VOL_CAP_INT_EXCHANGEDATA | VOL_CAP_INT_COPYFILE | VOL_CAP_INT_ALLOCATE | VOL_CAP_INT_VOL_RENAME | VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK | VOL_CAP_INT_EXTENDED_SECURITY | VOL_CAP_INT_USERACCESS | VOL_CAP_INT_MANLOCK | VOL_CAP_INT_EXTENDED_ATTR | VOL_CAP_INT_NAMEDSTREAMS ; attr->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0; attr->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0; attr->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0; attr->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0; VFSATTR_SET_SUPPORTED(attr, f_capabilities); attr->f_attributes.validattr.commonattr = 0 | ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_FSID | ATTR_CMN_OBJTYPE // | ATTR_CMN_OBJTAG | ATTR_CMN_OBJID // | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_PAROBJID // | ATTR_CMN_SCRIPT // | ATTR_CMN_CRTIME | ATTR_CMN_MODTIME // | ATTR_CMN_CHGTIME // | ATTR_CMN_ACCTIME // | ATTR_CMN_BKUPTIME // | ATTR_CMN_FNDRINFO | ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS // | ATTR_CMN_USERACCESS | ATTR_CMN_EXTENDED_SECURITY // | ATTR_CMN_UUID // | ATTR_CMN_GRPUUID // | ATTR_CMN_FILEID // | ATTR_CMN_PARENTID ; attr->f_attributes.validattr.volattr = 0 | ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | ATTR_VOL_SPACEAVAIL // | ATTR_VOL_MINALLOCATION // | ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE // | ATTR_VOL_OBJCOUNT | ATTR_VOL_FILECOUNT // | ATTR_VOL_DIRCOUNT // | ATTR_VOL_MAXOBJCOUNT | ATTR_VOL_MOUNTPOINT | ATTR_VOL_NAME | ATTR_VOL_MOUNTFLAGS | ATTR_VOL_MOUNTEDDEVICE // | ATTR_VOL_ENCODINGSUSED | ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES // | ATTR_VOL_INFO ; attr->f_attributes.validattr.dirattr = 0 | ATTR_DIR_LINKCOUNT // | ATTR_DIR_ENTRYCOUNT // | ATTR_DIR_MOUNTSTATUS ; attr->f_attributes.validattr.fileattr = 0 | ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | ATTR_FILE_DEVTYPE // | ATTR_FILE_FORKCOUNT // | ATTR_FILE_FORKLIST | ATTR_FILE_DATALENGTH | ATTR_FILE_DATAALLOCSIZE // | ATTR_FILE_RSRCLENGTH // | ATTR_FILE_RSRCALLOCSIZE ; attr->f_attributes.validattr.forkattr = 0; // | ATTR_FORK_TOTALSIZE // | ATTR_FORK_ALLOCSIZE ; // Handle some special cases if (!(data->dataflags & FSESS_CASE_INSENSITIVE)) { attr->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_CASE_SENSITIVE; } /* if (data->dataflags & FSESS_VOL_RENAME) { attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_VOL_RENAME; } else { fuse_clear_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME)); } */ /* Not yet. */ fuse_clear_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME)); if (data->dataflags & FSESS_XTIMES) { attr->f_attributes.validattr.commonattr |= (ATTR_CMN_BKUPTIME | ATTR_CMN_CHGTIME | ATTR_CMN_CRTIME); } else { fuse_clear_implemented(data, FSESS_NOIMPLBIT(GETXTIMES)); } // All attributes that we do support, we support natively. attr->f_attributes.nativeattr.commonattr = \ attr->f_attributes.validattr.commonattr; attr->f_attributes.nativeattr.volattr = \ attr->f_attributes.validattr.volattr; attr->f_attributes.nativeattr.dirattr = \ attr->f_attributes.validattr.dirattr; attr->f_attributes.nativeattr.fileattr = \ attr->f_attributes.validattr.fileattr; attr->f_attributes.nativeattr.forkattr = \ attr->f_attributes.validattr.forkattr; VFSATTR_SET_SUPPORTED(attr, f_attributes); }
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; }
__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; }