X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..e8c3f78193f1895ea514044358b93b1add9322f3:/bsd/vfs/kpi_vfs.c diff --git a/bsd/vfs/kpi_vfs.c b/bsd/vfs/kpi_vfs.c index 1a71f6b69..f09e98f74 100644 --- a/bsd/vfs/kpi_vfs.c +++ b/bsd/vfs/kpi_vfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Apple Inc. All rights reserved. + * Copyright (c) 2000-2017 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -103,10 +103,12 @@ #include #include #include +#include #include #include #include +#include #include @@ -120,6 +122,10 @@ #include #endif +#if NULLFS +#include +#endif + #include #define ESUCCESS 0 @@ -138,6 +144,8 @@ static void xattrfile_setattr(vnode_t dvp, const char * basename, struct vnode_attr * vap, vfs_context_t ctx); #endif /* CONFIG_APPLEDOUBLE */ +static errno_t post_rename(vnode_t fdvp, vnode_t fvp, vnode_t tdvp, vnode_t tvp); + /* * vnode_setneedinactive * @@ -233,7 +241,7 @@ VFS_UNMOUNT(mount_t mp, int flags, vfs_context_t ctx) * * The return codes documented above are those which may currently * be returned by HFS from hfs_vfs_root, which is a simple wrapper - * for a call to hfs_vget on the volume mount poit, not including + * for a call to hfs_vget on the volume mount point, not including * additional error codes which may be propagated from underlying * routines called by hfs_vget. */ @@ -336,7 +344,7 @@ VFS_VGET(mount_t mp, ino64_t ino, struct vnode **vpp, vfs_context_t ctx) } int -VFS_FHTOVP(mount_t mp, int fhlen, unsigned char * fhp, vnode_t * vpp, vfs_context_t ctx) +VFS_FHTOVP(mount_t mp, int fhlen, unsigned char *fhp, vnode_t *vpp, vfs_context_t ctx) { int error; @@ -353,7 +361,7 @@ VFS_FHTOVP(mount_t mp, int fhlen, unsigned char * fhp, vnode_t * vpp, vfs_contex } int -VFS_VPTOFH(struct vnode * vp, int *fhlenp, unsigned char * fhp, vfs_context_t ctx) +VFS_VPTOFH(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t ctx) { int error; @@ -369,6 +377,31 @@ VFS_VPTOFH(struct vnode * vp, int *fhlenp, unsigned char * fhp, vfs_context_t ct return(error); } +int VFS_IOCTL(struct mount *mp, u_long command, caddr_t data, + int flags, vfs_context_t context) +{ + if (mp == dead_mountp || !mp->mnt_op->vfs_ioctl) + return ENOTSUP; + + return mp->mnt_op->vfs_ioctl(mp, command, data, flags, + context ?: vfs_context_current()); +} + +int +VFS_VGET_SNAPDIR(mount_t mp, vnode_t *vpp, vfs_context_t ctx) +{ + int error; + + if ((mp == dead_mountp) || (mp->mnt_op->vfs_vget_snapdir == 0)) + return(ENOTSUP); + + if (ctx == NULL) + ctx = vfs_context_current(); + + error = (*mp->mnt_op->vfs_vget_snapdir)(mp, vpp, ctx); + + return (error); +} /* returns the cached throttle mask for the mount_t */ uint64_t @@ -379,7 +412,7 @@ vfs_throttle_mask(mount_t mp) /* returns a copy of vfs type name for the mount_t */ void -vfs_name(mount_t mp, char * buffer) +vfs_name(mount_t mp, char *buffer) { strncpy(buffer, mp->mnt_vtable->vfc_name, MFSNAMELEN); } @@ -599,6 +632,22 @@ vfs_clearextendedsecurity(mount_t mp) mount_unlock(mp); } +void +vfs_setnoswap(mount_t mp) +{ + mount_lock(mp); + mp->mnt_kern_flag |= MNTK_NOSWAP; + mount_unlock(mp); +} + +void +vfs_clearnoswap(mount_t mp) +{ + mount_lock(mp); + mp->mnt_kern_flag &= ~MNTK_NOSWAP; + mount_unlock(mp); +} + int vfs_extendedsecurity(mount_t mp) { @@ -736,8 +785,10 @@ vfs_devvp(mount_t mp) void vfs_ioattr(mount_t mp, struct vfsioattr *ioattrp) { - if (mp == NULL) { - ioattrp->io_maxreadcnt = MAXPHYS; + ioattrp->io_reserved[0] = NULL; + ioattrp->io_reserved[1] = NULL; + if (mp == NULL) { + ioattrp->io_maxreadcnt = MAXPHYS; ioattrp->io_maxwritecnt = MAXPHYS; ioattrp->io_segreadcnt = 32; ioattrp->io_segwritecnt = 32; @@ -745,8 +796,9 @@ vfs_ioattr(mount_t mp, struct vfsioattr *ioattrp) ioattrp->io_maxsegwritesize = MAXPHYS; ioattrp->io_devblocksize = DEV_BSIZE; ioattrp->io_flags = 0; + ioattrp->io_max_swappin_available = 0; } else { - ioattrp->io_maxreadcnt = mp->mnt_maxreadcnt; + ioattrp->io_maxreadcnt = mp->mnt_maxreadcnt; ioattrp->io_maxwritecnt = mp->mnt_maxwritecnt; ioattrp->io_segreadcnt = mp->mnt_segreadcnt; ioattrp->io_segwritecnt = mp->mnt_segwritecnt; @@ -754,9 +806,8 @@ vfs_ioattr(mount_t mp, struct vfsioattr *ioattrp) ioattrp->io_maxsegwritesize = mp->mnt_maxsegwritesize; ioattrp->io_devblocksize = mp->mnt_devblocksize; ioattrp->io_flags = mp->mnt_ioflags; + ioattrp->io_max_swappin_available = mp->mnt_max_swappin_available; } - ioattrp->io_reserved[0] = NULL; - ioattrp->io_reserved[1] = NULL; } @@ -776,6 +827,7 @@ vfs_setioattr(mount_t mp, struct vfsioattr * ioattrp) mp->mnt_maxsegwritesize = ioattrp->io_maxsegwritesize; mp->mnt_devblocksize = ioattrp->io_devblocksize; mp->mnt_ioflags = ioattrp->io_flags; + mp->mnt_max_swappin_available = ioattrp->io_max_swappin_available; } /* @@ -787,7 +839,7 @@ vfs_setioattr(mount_t mp, struct vfsioattr * ioattrp) typedef int (*PFI)(void *); extern int vfs_opv_numops; errno_t -vfs_fsadd(struct vfs_fsentry *vfe, vfstable_t * handle) +vfs_fsadd(struct vfs_fsentry *vfe, vfstable_t *handle) { struct vfstable *newvfstbl = NULL; int i,j; @@ -854,6 +906,10 @@ vfs_fsadd(struct vfs_fsentry *vfe, vfstable_t * handle) newvfstbl->vfc_vfsflags |= VFC_VFSNOMACLABEL; if (vfe->vfe_flags & VFS_TBLVNOP_NOUPDATEID_RENAME) newvfstbl->vfc_vfsflags |= VFC_VFSVNOP_NOUPDATEID_RENAME; + if (vfe->vfe_flags & VFS_TBLVNOP_SECLUDE_RENAME) + newvfstbl->vfc_vfsflags |= VFC_VFSVNOP_SECLUDE_RENAME; + if (vfe->vfe_flags & VFS_TBLCANMOUNTROOT) + newvfstbl->vfc_vfsflags |= VFC_VFSCANMOUNTROOT; /* * Allocate and init the vectors. @@ -886,6 +942,13 @@ vfs_fsadd(struct vfs_fsentry *vfe, vfstable_t * handle) for (j = 0; vfe->vfe_opvdescs[i]->opv_desc_ops[j].opve_op; j++) { opve_descp = &(vfe->vfe_opvdescs[i]->opv_desc_ops[j]); + /* Silently skip known-disabled operations */ + if (opve_descp->opve_op->vdesc_flags & VDESC_DISABLED) { + printf("vfs_fsadd: Ignoring reference in %p to disabled operation %s.\n", + vfe->vfe_opvdescs[i], opve_descp->opve_op->vdesc_name); + continue; + } + /* * Sanity check: is this operation listed * in the list of operations? We check this @@ -904,7 +967,7 @@ vfs_fsadd(struct vfs_fsentry *vfe, vfstable_t * handle) * list of supported operations. */ if (opve_descp->opve_op->vdesc_offset == 0 && - opve_descp->opve_op->vdesc_offset != VOFFSET(vnop_default)) { + opve_descp->opve_op != VDESC(vnop_default)) { printf("vfs_fsadd: operation %s not listed in %s.\n", opve_descp->opve_op->vdesc_name, "vfs_op_descs"); @@ -970,7 +1033,7 @@ vfs_fsadd(struct vfs_fsentry *vfe, vfstable_t * handle) * file system was added */ errno_t -vfs_fsremove(vfstable_t handle) +vfs_fsremove(vfstable_t handle) { struct vfstable * vfstbl = (struct vfstable *)handle; void *old_desc = NULL; @@ -1002,6 +1065,32 @@ vfs_fsremove(vfstable_t handle) return(err); } +void vfs_setowner(mount_t mp, uid_t uid, gid_t gid) +{ + mp->mnt_fsowner = uid; + mp->mnt_fsgroup = gid; +} + +/* + * Callers should be careful how they use this; accessing + * mnt_last_write_completed_timestamp is not thread-safe. Writing to + * it isn't either. Point is: be prepared to deal with strange values + * being returned. + */ +uint64_t vfs_idle_time(mount_t mp) +{ + if (mp->mnt_pending_write_size) + return 0; + + struct timeval now; + + microuptime(&now); + + return ((now.tv_sec + - mp->mnt_last_write_completed_timestamp.tv_sec) * 1000000 + + now.tv_usec - mp->mnt_last_write_completed_timestamp.tv_usec); +} + int vfs_context_pid(vfs_context_t ctx) { @@ -1295,6 +1384,11 @@ vfs_context_issuser(vfs_context_t ctx) return(kauth_cred_issuser(vfs_context_ucred(ctx))); } +int vfs_context_iskernel(vfs_context_t ctx) +{ + return ctx == &kerncontext; +} + /* * Given a context, for all fields of vfs_context_t which * are not held with a reference, set those fields to the @@ -1316,6 +1410,11 @@ vfs_context_bind(vfs_context_t ctx) return 0; } +int vfs_isswapmount(mount_t mnt) +{ + return mnt && ISSET(mnt->mnt_kern_flag, MNTK_SWAP_MOUNT) ? 1 : 0; +} + /* XXXXXXXXXXXXXX VNODE KAPIS XXXXXXXXXXXXXXXXXXXXXXXXX */ @@ -1502,12 +1601,16 @@ vfs_ctx_skipatime (vfs_context_t ctx) { if (proc->p_lflag & P_LRAGE_VNODES) { return 1; } - + if (ut) { - if (ut->uu_flag & UT_RAGE_VNODES) { + if (ut->uu_flag & (UT_RAGE_VNODES | UT_ATIME_UPDATE)) { return 1; } } + + if (proc->p_vfs_iopolicy & P_VFS_IOPOLICY_ATIME_UPDATES) { + return 1; + } } return 0; } @@ -1754,6 +1857,52 @@ vnode_clearnoreadahead(vnode_t vp) vnode_unlock(vp); } +int +vnode_isfastdevicecandidate(vnode_t vp) +{ + return ((vp->v_flag & VFASTDEVCANDIDATE)? 1 : 0); +} + +void +vnode_setfastdevicecandidate(vnode_t vp) +{ + vnode_lock_spin(vp); + vp->v_flag |= VFASTDEVCANDIDATE; + vnode_unlock(vp); +} + +void +vnode_clearfastdevicecandidate(vnode_t vp) +{ + vnode_lock_spin(vp); + vp->v_flag &= ~VFASTDEVCANDIDATE; + vnode_unlock(vp); +} + +int +vnode_isautocandidate(vnode_t vp) +{ + return ((vp->v_flag & VAUTOCANDIDATE)? 1 : 0); +} + +void +vnode_setautocandidate(vnode_t vp) +{ + vnode_lock_spin(vp); + vp->v_flag |= VAUTOCANDIDATE; + vnode_unlock(vp); +} + +void +vnode_clearautocandidate(vnode_t vp) +{ + vnode_lock_spin(vp); + vp->v_flag &= ~VAUTOCANDIDATE; + vnode_unlock(vp); +} + + + /* mark vnode_t to skip vflush() is SKIPSYSTEM */ void @@ -1833,7 +1982,7 @@ vnode_setname(vnode_t vp, char * name) void vnode_vfsname(vnode_t vp, char * buf) { - strncpy(buf, vp->v_mount->mnt_vtable->vfc_name, MFSNAMELEN); + strlcpy(buf, vp->v_mount->mnt_vtable->vfc_name, MFSNAMELEN); } /* return the FS type number */ @@ -2004,10 +2153,10 @@ vnode_get_filesec(vnode_t vp, kauth_filesec_t *fsecp, vfs_context_t ctx) fsec = NULL; fsec_uio = NULL; - error = 0; - + /* find out how big the EA is */ - if (vn_getxattr(vp, KAUTH_FILESEC_XATTR, NULL, &xsize, XATTR_NOSECURITY, ctx) != 0) { + error = vn_getxattr(vp, KAUTH_FILESEC_XATTR, NULL, &xsize, XATTR_NOSECURITY, ctx); + if (error != 0) { /* no EA, no filesec */ if ((error == ENOATTR) || (error == ENOENT) || (error == EJUSTRETURN)) error = 0; @@ -2029,6 +2178,11 @@ vnode_get_filesec(vnode_t vp, kauth_filesec_t *fsecp, vfs_context_t ctx) /* how many entries would fit? */ fsec_size = KAUTH_FILESEC_COUNT(xsize); + if (fsec_size > KAUTH_ACL_MAX_ENTRIES) { + KAUTH_DEBUG(" ERROR - Bogus (too large) kauth_fiilesec_t: %ld bytes", xsize); + error = 0; + goto out; + } /* get buffer and uio */ if (((fsec = kauth_filesec_alloc(fsec_size)) == NULL) || @@ -2175,6 +2329,7 @@ out: /* * Returns: 0 Success * ENOMEM Not enough space [only if has filesec] + * EINVAL Requested unknown attributes * VNOP_GETATTR: ??? * vnode_get_filesec: ??? * kauth_cred_guid2uid: ??? @@ -2190,6 +2345,12 @@ vnode_getattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx) uid_t nuid; gid_t ngid; + /* + * Reject attempts to fetch unknown attributes. + */ + if (vap->va_active & ~VNODE_ATTR_ALL) + return (EINVAL); + /* don't ask for extended security data if the filesystem doesn't support it */ if (!vfs_extendedsecurity(vnode_mount(vp))) { VATTR_CLEAR_ACTIVE(vap, va_acl); @@ -2416,7 +2577,18 @@ out: int vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx) { - int error, is_perm_change=0; + int error; +#if CONFIG_FSE + uint64_t active; + int is_perm_change = 0; + int is_stat_change = 0; +#endif + + /* + * Reject attempts to set unknown attributes. + */ + if (vap->va_active & ~VNODE_ATTR_ALL) + return (EINVAL); /* * Make sure the filesystem is mounted R/W. @@ -2426,6 +2598,20 @@ vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx) error = EROFS; goto out; } + +#if DEVELOPMENT || DEBUG + /* + * XXX VSWAP: Check for entitlements or special flag here + * so we can restrict access appropriately. + */ +#else /* DEVELOPMENT || DEBUG */ + + if (vnode_isswap(vp) && (ctx != vfs_context_kernel())) { + error = EPERM; + goto out; + } +#endif /* DEVELOPMENT || DEBUG */ + #if NAMEDSTREAMS /* For streams, va_data_size is the only setable attribute. */ if ((vp->v_flag & VISNAMEDSTREAM) && (vap->va_active != VNODE_ATTR_va_data_size)) { @@ -2433,6 +2619,25 @@ vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx) goto out; } #endif + /* Check for truncation */ + if(VATTR_IS_ACTIVE(vap, va_data_size)) { + switch(vp->v_type) { + case VREG: + /* For regular files it's ok */ + break; + case VDIR: + /* Not allowed to truncate directories */ + error = EISDIR; + goto out; + default: + /* For everything else we will clear the bit and let underlying FS decide on the rest */ + VATTR_CLEAR_ACTIVE(vap, va_data_size); + if (vap->va_active) + break; + /* If it was the only bit set, return success, to handle cases like redirect to /dev/null */ + return (0); + } + } /* * If ownership is being ignored on this volume, we silently discard @@ -2443,11 +2648,6 @@ vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx) VATTR_CLEAR_ACTIVE(vap, va_gid); } - if ( VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid) - || VATTR_IS_ACTIVE(vap, va_mode) || VATTR_IS_ACTIVE(vap, va_acl)) { - is_perm_change = 1; - } - /* * Make sure that extended security is enabled if we're going to try * to set any. @@ -2459,23 +2659,60 @@ vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx) goto out; } + /* Never allow the setting of any unsupported superuser flags. */ + if (VATTR_IS_ACTIVE(vap, va_flags)) { + vap->va_flags &= (SF_SUPPORTED | UF_SETTABLE); + } + +#if CONFIG_FSE + /* + * Remember all of the active attributes that we're + * attempting to modify. + */ + active = vap->va_active & ~VNODE_ATTR_RDONLY; +#endif + error = VNOP_SETATTR(vp, vap, ctx); if ((error == 0) && !VATTR_ALL_SUPPORTED(vap)) error = vnode_setattr_fallback(vp, vap, ctx); #if CONFIG_FSE - // only send a stat_changed event if this is more than - // just an access or backup time update - if (error == 0 && (vap->va_active != VNODE_ATTR_BIT(va_access_time)) && (vap->va_active != VNODE_ATTR_BIT(va_backup_time))) { +#define PERMISSION_BITS (VNODE_ATTR_BIT(va_uid) | VNODE_ATTR_BIT(va_uuuid) | \ + VNODE_ATTR_BIT(va_gid) | VNODE_ATTR_BIT(va_guuid) | \ + VNODE_ATTR_BIT(va_mode) | VNODE_ATTR_BIT(va_acl)) + + /* + * Now that we've changed them, decide whether to send an + * FSevent. + */ + if ((active & PERMISSION_BITS) & vap->va_supported) { + is_perm_change = 1; + } else { + /* + * We've already checked the permission bits, and we + * also want to filter out access time / backup time + * changes. + */ + active &= ~(PERMISSION_BITS | + VNODE_ATTR_BIT(va_access_time) | + VNODE_ATTR_BIT(va_backup_time)); + + /* Anything left to notify about? */ + if (active & vap->va_supported) + is_stat_change = 1; + } + + if (error == 0) { if (is_perm_change) { if (need_fsevent(FSE_CHOWN, vp)) { add_fsevent(FSE_CHOWN, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE); } - } else if(need_fsevent(FSE_STAT_CHANGED, vp)) { + } else if (is_stat_change && need_fsevent(FSE_STAT_CHANGED, vp)) { add_fsevent(FSE_STAT_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE); } } +#undef PERMISSION_BITS #endif out: @@ -2677,6 +2914,20 @@ vnode_ismonitored(vnode_t vp) { return (vp->v_knotes.slh_first != NULL); } +int +vnode_getbackingvnode(vnode_t in_vp, vnode_t* out_vpp) +{ + if (out_vpp) { + *out_vpp = NULLVP; + } +#if NULLFS + return nullfs_getbackingvnode(in_vp, out_vpp); +#else +#pragma unused(in_vp) + return ENOENT; +#endif +} + /* * Initialize a struct vnode_attr and activate the attributes required * by the vnode_notify() call. @@ -3326,7 +3577,7 @@ VNOP_IOCTL(vnode_t vp, u_long command, caddr_t data, int fflag, vfs_context_t ct * We have to be able to use the root filesystem's device vnode even when * devfs isn't mounted (yet/anymore), so we can't go looking at its mount * structure. If there is no data pointer, it doesn't matter whether - * the device is 64-bit ready. Any command (like DKIOCSYNCHRONIZECACHE) + * the device is 64-bit ready. Any command (like DKIOCSYNCHRONIZE) * which passes NULL for its data pointer can therefore be used during * mount or unmount of the root filesystem. * @@ -3711,7 +3962,7 @@ VNOP_LINK(vnode_t vp, vnode_t tdvp, struct componentname * cnp, vfs_context_t ct errno_t vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, struct vnode_attr *fvap, struct vnode *tdvp, struct vnode **tvpp, struct componentname *tcnp, struct vnode_attr *tvap, - uint32_t flags, vfs_context_t ctx) + vfs_rename_flags_t flags, vfs_context_t ctx) { int _err; struct nameidata *fromnd = NULL; @@ -3734,13 +3985,6 @@ vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, s panic("Not batched, and no fvp?"); } -#if CONFIG_SECLUDED_RENAME - if ((fcnp->cn_flags & CN_SECLUDE_RENAME) && - (((*fvpp)->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSVNOP_SECLUDE_RENAME) == 0)) { - return ENOTSUP; - } -#endif - #if CONFIG_APPLEDOUBLE /* * We need to preflight any potential AppleDouble file for the source file @@ -3783,37 +4027,35 @@ vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, s * in the rename syscall. It's OK if the source file does not exist, since this * is only for AppleDouble files. */ - if (xfromname != NULL) { - MALLOC(fromnd, struct nameidata *, sizeof (struct nameidata), M_TEMP, M_WAITOK); - NDINIT(fromnd, RENAME, OP_RENAME, NOFOLLOW | USEDVP | CN_NBMOUNTLOOK, - UIO_SYSSPACE, CAST_USER_ADDR_T(xfromname), ctx); - fromnd->ni_dvp = fdvp; - error = namei(fromnd); - - /* - * If there was an error looking up source attribute file, - * we'll behave as if it didn't exist. - */ + MALLOC(fromnd, struct nameidata *, sizeof (struct nameidata), M_TEMP, M_WAITOK); + NDINIT(fromnd, RENAME, OP_RENAME, NOFOLLOW | USEDVP | CN_NBMOUNTLOOK, + UIO_SYSSPACE, CAST_USER_ADDR_T(xfromname), ctx); + fromnd->ni_dvp = fdvp; + error = namei(fromnd); - if (error == 0) { - if (fromnd->ni_vp) { - /* src_attr_vp indicates need to call vnode_put / nameidone later */ - src_attr_vp = fromnd->ni_vp; - - if (fromnd->ni_vp->v_type != VREG) { - src_attr_vp = NULLVP; - vnode_put(fromnd->ni_vp); - } - } - /* - * Either we got an invalid vnode type (not a regular file) or the namei lookup - * suppressed ENOENT as a valid error since we're renaming. Either way, we don't - * have a vnode here, so we drop our namei buffer for the source attribute file - */ - if (src_attr_vp == NULLVP) { - nameidone(fromnd); + /* + * If there was an error looking up source attribute file, + * we'll behave as if it didn't exist. + */ + + if (error == 0) { + if (fromnd->ni_vp) { + /* src_attr_vp indicates need to call vnode_put / nameidone later */ + src_attr_vp = fromnd->ni_vp; + + if (fromnd->ni_vp->v_type != VREG) { + src_attr_vp = NULLVP; + vnode_put(fromnd->ni_vp); } } + /* + * Either we got an invalid vnode type (not a regular file) or the namei lookup + * suppressed ENOENT as a valid error since we're renaming. Either way, we don't + * have a vnode here, so we drop our namei buffer for the source attribute file + */ + if (src_attr_vp == NULLVP) { + nameidone(fromnd); + } } } #endif /* CONFIG_APPLEDOUBLE */ @@ -3824,13 +4066,18 @@ vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, s printf("VNOP_COMPOUND_RENAME() returned %d\n", _err); } } else { - _err = VNOP_RENAME(fdvp, *fvpp, fcnp, tdvp, *tvpp, tcnp, ctx); - } -#if CONFIG_MACF - if (_err == 0) { - mac_vnode_notify_rename(ctx, *fvpp, tdvp, tcnp); + if (flags) { + _err = VNOP_RENAMEX(fdvp, *fvpp, fcnp, tdvp, *tvpp, tcnp, flags, ctx); + if (_err == ENOTSUP && flags == VFS_RENAME_SECLUDE) { + // Legacy... + if ((*fvpp)->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSVNOP_SECLUDE_RENAME) { + fcnp->cn_flags |= CN_SECLUDE_RENAME; + _err = VNOP_RENAME(fdvp, *fvpp, fcnp, tdvp, *tvpp, tcnp, ctx); + } + } + } else + _err = VNOP_RENAME(fdvp, *fvpp, fcnp, tdvp, *tvpp, tcnp, ctx); } -#endif /* * If moved to a new directory that is restricted, @@ -3838,18 +4085,27 @@ vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, s */ if (_err == 0) { _err = vnode_flags(tdvp, &tdfflags, ctx); - if (_err == 0 && (tdfflags & SF_RESTRICTED)) { - uint32_t fflags; - _err = vnode_flags(*fvpp, &fflags, ctx); - if (_err == 0 && !(fflags & SF_RESTRICTED)) { - struct vnode_attr va; - VATTR_INIT(&va); - VATTR_SET(&va, va_flags, fflags | SF_RESTRICTED); - _err = vnode_setattr(*fvpp, &va, ctx); + if (_err == 0) { + uint32_t inherit_flags = tdfflags & (UF_DATAVAULT | SF_RESTRICTED); + if (inherit_flags) { + uint32_t fflags; + _err = vnode_flags(*fvpp, &fflags, ctx); + if (_err == 0 && fflags != (fflags | inherit_flags)) { + struct vnode_attr va; + VATTR_INIT(&va); + VATTR_SET(&va, va_flags, fflags | inherit_flags); + _err = vnode_setattr(*fvpp, &va, ctx); + } } } } +#if CONFIG_MACF + if (_err == 0) { + mac_vnode_notify_rename(ctx, *fvpp, tdvp, tcnp); + } +#endif + #if CONFIG_APPLEDOUBLE /* * Rename any associated extended attribute file (._ AppleDouble file). @@ -3996,7 +4252,6 @@ VNOP_RENAME(struct vnode *fdvp, struct vnode *fvp, struct componentname *fcnp, vfs_context_t ctx) { int _err = 0; - int events; struct vnop_rename_args a; a.a_desc = &vnop_rename_desc; @@ -4012,41 +4267,96 @@ VNOP_RENAME(struct vnode *fdvp, struct vnode *fvp, struct componentname *fcnp, _err = (*fdvp->v_op[vnop_rename_desc.vdesc_offset])(&a); DTRACE_FSINFO(rename, vnode_t, fdvp); - if (_err == 0) { - if (tvp && tvp != fvp) - vnode_setneedinactive(tvp); - } + if (_err) + return _err; - /* Wrote at least one directory. If transplanted a dir, also changed link counts */ - if (_err == 0) { - events = NOTE_WRITE; - if (vnode_isdir(fvp)) { - /* Link count on dir changed only if we are moving a dir and... - * --Moved to new dir, not overwriting there - * --Kept in same dir and DID overwrite - */ - if (((fdvp != tdvp) && (!tvp)) || ((fdvp == tdvp) && (tvp))) { - events |= NOTE_LINK; - } - } + return post_rename(fdvp, fvp, tdvp, tvp); +} - lock_vnode_and_post(fdvp, events); - if (fdvp != tdvp) { - lock_vnode_and_post(tdvp, events); - } +static errno_t +post_rename(vnode_t fdvp, vnode_t fvp, vnode_t tdvp, vnode_t tvp) +{ + if (tvp && tvp != fvp) + vnode_setneedinactive(tvp); - /* If you're replacing the target, post a deletion for it */ - if (tvp) - { - lock_vnode_and_post(tvp, NOTE_DELETE); + /* Wrote at least one directory. If transplanted a dir, also changed link counts */ + int events = NOTE_WRITE; + if (vnode_isdir(fvp)) { + /* Link count on dir changed only if we are moving a dir and... + * --Moved to new dir, not overwriting there + * --Kept in same dir and DID overwrite + */ + if (((fdvp != tdvp) && (!tvp)) || ((fdvp == tdvp) && (tvp))) { + events |= NOTE_LINK; } + } - lock_vnode_and_post(fvp, NOTE_RENAME); + lock_vnode_and_post(fdvp, events); + if (fdvp != tdvp) { + lock_vnode_and_post(tdvp, events); } - return (_err); + /* If you're replacing the target, post a deletion for it */ + if (tvp) + { + lock_vnode_and_post(tvp, NOTE_DELETE); + } + + lock_vnode_and_post(fvp, NOTE_RENAME); + + return 0; +} + +#if 0 +/* + *# + *#% renamex fdvp U U U + *#% renamex fvp U U U + *#% renamex tdvp L U U + *#% renamex tvp X U U + *# + */ +struct vnop_renamex_args { + struct vnodeop_desc *a_desc; + vnode_t a_fdvp; + vnode_t a_fvp; + struct componentname *a_fcnp; + vnode_t a_tdvp; + vnode_t a_tvp; + struct componentname *a_tcnp; + vfs_rename_flags_t a_flags; + vfs_context_t a_context; +}; +#endif /* 0*/ +errno_t +VNOP_RENAMEX(struct vnode *fdvp, struct vnode *fvp, struct componentname *fcnp, + struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp, + vfs_rename_flags_t flags, vfs_context_t ctx) +{ + int _err = 0; + struct vnop_renamex_args a; + + a.a_desc = &vnop_renamex_desc; + a.a_fdvp = fdvp; + a.a_fvp = fvp; + a.a_fcnp = fcnp; + a.a_tdvp = tdvp; + a.a_tvp = tvp; + a.a_tcnp = tcnp; + a.a_flags = flags; + a.a_context = ctx; + + /* do the rename of the main file. */ + _err = (*fdvp->v_op[vnop_renamex_desc.vdesc_offset])(&a); + DTRACE_FSINFO(renamex, vnode_t, fdvp); + + if (_err) + return _err; + + return post_rename(fdvp, fvp, tdvp, tvp); } + int VNOP_COMPOUND_RENAME( struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, struct vnode_attr *fvap, @@ -4891,11 +5201,16 @@ VNOP_ADVLOCK(struct vnode *vp, caddr_t id, int op, struct flock *fl, int flags, if ((vp->v_flag & VLOCKLOCAL)) { /* Advisory locking done at this layer */ _err = lf_advlock(&a); + } else if (flags & F_OFD_LOCK) { + /* Non-local locking doesn't work for OFD locks */ + _err = err_advlock(&a); } else { /* Advisory locking done by underlying filesystem */ _err = (*vp->v_op[vnop_advlock_desc.vdesc_offset])(&a); } DTRACE_FSINFO(advlock, vnode_t, vp); + if (op == F_UNLCK && flags == F_FLOCK) + post_event_if_success(vp, _err, NOTE_FUNLOCK); } return (_err); @@ -5126,6 +5441,64 @@ VNOP_COPYFILE(struct vnode *fvp, struct vnode *tdvp, struct vnode *tvp, struct c return (_err); } +#if 0 +struct vnop_clonefile_args { + struct vnodeop_desc *a_desc; + vnode_t a_fvp; + vnode_t a_dvp; + vnode_t *a_vpp; + struct componentname *a_cnp; + struct vnode_attr *a_vap; + uint32_t a_flags; + vfs_context_t a_context; + int (*a_dir_clone_authorizer)( /* Authorization callback */ + struct vnode_attr *vap, /* attribute to be authorized */ + kauth_action_t action, /* action for which attribute is to be authorized */ + struct vnode_attr *dvap, /* target directory attributes */ + vnode_t sdvp, /* source directory vnode pointer (optional) */ + mount_t mp, /* mount point of filesystem */ + dir_clone_authorizer_op_t vattr_op, /* specific operation requested : setup, authorization or cleanup */ + uint32_t flags; /* value passed in a_flags to the VNOP */ + vfs_context_t ctx, /* As passed to VNOP */ + void *reserved); /* Always NULL */ + void *a_reserved; /* Currently unused */ +}; +#endif /* 0 */ + +errno_t +VNOP_CLONEFILE(vnode_t fvp, vnode_t dvp, vnode_t *vpp, + struct componentname *cnp, struct vnode_attr *vap, uint32_t flags, + vfs_context_t ctx) +{ + int _err; + struct vnop_clonefile_args a; + a.a_desc = &vnop_clonefile_desc; + a.a_fvp = fvp; + a.a_dvp = dvp; + a.a_vpp = vpp; + a.a_cnp = cnp; + a.a_vap = vap; + a.a_flags = flags; + a.a_context = ctx; + + if (vnode_vtype(fvp) == VDIR) + a.a_dir_clone_authorizer = vnode_attr_authorize_dir_clone; + else + a.a_dir_clone_authorizer = NULL; + + _err = (*dvp->v_op[vnop_clonefile_desc.vdesc_offset])(&a); + + if (_err == 0 && *vpp) { + DTRACE_FSINFO(clonefile, vnode_t, *vpp); + if (kdebug_enable) + kdebug_lookup(*vpp, cnp); + } + + post_event_if_success(dvp, _err, NOTE_WRITE); + + return (_err); +} + errno_t VNOP_GETXATTR(vnode_t vp, const char *name, uio_t uio, size_t *size, int options, vfs_context_t ctx) {