X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6601e61aa18bf4f09af135ff61fc7f4771d23b06..060df5ea7c632b1ac8cc8aac1fb59758165c2084:/bsd/vfs/vfs_syscalls.c diff --git a/bsd/vfs/vfs_syscalls.c b/bsd/vfs/vfs_syscalls.c index 07725bb98..02c3c39af 100644 --- a/bsd/vfs/vfs_syscalls.c +++ b/bsd/vfs/vfs_syscalls.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 1995-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1995-2008 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright (c) 1989, 1993 @@ -58,6 +64,12 @@ * * @(#)vfs_syscalls.c 8.41 (Berkeley) 6/15/95 */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + */ #include #include @@ -82,12 +94,16 @@ #include #include #include +#include +#include #include +#include #include #include #include +#include -#include +#include #include #include @@ -96,28 +112,69 @@ #include -#include #include +#include +#if CONFIG_MACF +#include +#include +#endif -/* - * The currently logged-in user, for ownership of files/directories whose on-disk - * permissions are ignored: - */ -uid_t console_user; +#if CONFIG_FSE +#define GET_PATH(x) \ + (x) = get_pathbuff(); +#define RELEASE_PATH(x) \ + release_pathbuff(x); +#else +#define GET_PATH(x) \ + MALLOC_ZONE((x), char *, MAXPATHLEN, M_NAMEI, M_WAITOK); +#define RELEASE_PATH(x) \ + FREE_ZONE((x), MAXPATHLEN, M_NAMEI); +#endif /* CONFIG_FSE */ + +/* struct for checkdirs iteration */ +struct cdirargs { + vnode_t olddp; + vnode_t newdp; +}; +/* callback for checkdirs iteration */ +static int checkdirs_callback(proc_t p, void * arg); static int change_dir(struct nameidata *ndp, vfs_context_t ctx); static int checkdirs(vnode_t olddp, vfs_context_t ctx); void enablequotas(struct mount *mp, vfs_context_t ctx); static int getfsstat_callback(mount_t mp, void * arg); static int getutimes(user_addr_t usrtvp, struct timespec *tsp); -static int setutimes(vfs_context_t ctx, struct vnode *vp, const struct timespec *ts, int nullflag); +static int setutimes(vfs_context_t ctx, vnode_t vp, const struct timespec *ts, int nullflag); static int sync_callback(mount_t, void *); static int munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, user_addr_t bufp, int *sizep, boolean_t is_64_bit, boolean_t partial_copy); +static int statfs64_common(struct mount *mp, struct vfsstatfs *sfsp, + user_addr_t bufp); +static int fsync_common(proc_t p, struct fsync_args *uap, int flags); + +#ifdef CONFIG_IMGSRC_ACCESS +static int prepare_coveredvp(vnode_t vp, vfs_context_t ctx, struct componentname *cnp, const char *fsname); +static int authorize_devpath_and_update_mntfromname(mount_t mp, user_addr_t devpath, vnode_t *devvpp, vfs_context_t ctx); +static int place_mount_and_checkdirs(mount_t mp, vnode_t vp, vfs_context_t ctx); +static void undo_place_on_covered_vp(mount_t mp, vnode_t vp); +static int mount_begin_update(mount_t mp, vfs_context_t ctx, int flags); +static void mount_end_update(mount_t mp); +static int relocate_imageboot_source(vnode_t vp, struct componentname *cnp, const char *fsname, vfs_context_t ctx, boolean_t is64bit, user_addr_t fsmountargs); +#endif /* CONFIG_IMGSRC_ACCESS */ + +int (*union_dircheckp)(struct vnode **, struct fileproc *, vfs_context_t); + +__private_extern__ +int sync_internal(void); + +__private_extern__ +int open1(vfs_context_t, struct nameidata *, int, struct vnode_attr *, int32_t *); + +__private_extern__ +int unlink1(vfs_context_t, struct nameidata *, int); -__private_extern__ int sync_internal(void); #ifdef __APPLE_API_OBSOLETE struct fstatv_args { @@ -131,34 +188,32 @@ struct lstatv_args { struct mkcomplex_args { const char *path; /* pathname of the file to be created */ mode_t mode; /* access mode for the newly created file */ - u_long type; /* format of the complex file */ + u_int32_t type; /* format of the complex file */ }; struct statv_args { const char *path; /* pathname of the target file */ struct vstat *vsb; /* vstat structure for returned info */ }; -int fstatv(struct proc *p, struct fstatv_args *uap, register_t *retval); -int lstatv(struct proc *p, struct lstatv_args *uap, register_t *retval); -int mkcomplex(struct proc *p, struct mkcomplex_args *uap, register_t *retval); -int statv(struct proc *p, struct statv_args *uap, register_t *retval); +int fstatv(proc_t p, struct fstatv_args *uap, int32_t *retval); +int lstatv(proc_t p, struct lstatv_args *uap, int32_t *retval); +int mkcomplex(proc_t p, struct mkcomplex_args *uap, int32_t *retval); +int statv(proc_t p, struct statv_args *uap, int32_t *retval); #endif /* __APPLE_API_OBSOLETE */ -#if UNION -extern int (**union_vnodeop_p)(void *); -extern struct vnode *union_dircache(struct vnode*, struct proc*); -#endif /* UNION */ +/* + * incremented each time a mount or unmount operation occurs + * used to invalidate the cached value of the rootvp in the + * mount structure utilized by cache_lookup_path + */ +uint32_t mount_generation = 0; /* counts number of mount and unmount operations */ unsigned int vfs_nummntops=0; extern struct fileops vnops; - -extern void mount_list_add(mount_t mp); -extern void mount_list_remove(mount_t mp); -extern int mount_refdrain(mount_t mp); -extern int vcount(struct vnode *vp); +extern errno_t rmdir_remove_orphaned_appleDouble(vnode_t, vfs_context_t, int *); /* @@ -170,16 +225,51 @@ extern int vcount(struct vnode *vp); */ /* ARGSUSED */ int -mount(struct proc *p, register struct mount_args *uap, __unused register_t *retval) +mount(proc_t p, struct mount_args *uap, __unused int32_t *retval) { - struct vnode *vp; + struct __mac_mount_args muap; + + muap.type = uap->type; + muap.path = uap->path; + muap.flags = uap->flags; + muap.data = uap->data; + muap.mac_p = USER_ADDR_NULL; + return (__mac_mount(p, &muap, retval)); +} + +/* + * __mac_mount: + * Mount a file system taking into account MAC label behavior. + * See mount(2) man page for more information + * + * Parameters: p Process requesting the mount + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->type Filesystem type + * uap->path Path to mount + * uap->data Mount arguments + * uap->mac_p MAC info + * uap->flags Mount flags + * + * + * Returns: 0 Success + * !0 Not success + */ +int +__mac_mount(struct proc *p, register struct __mac_mount_args *uap, __unused int32_t *retval) +{ + struct vnode *vp, *pvp; struct vnode *devvp = NULLVP; struct vnode *device_vnode = NULLVP; +#if CONFIG_MACF + struct vnode *rvp; +#endif struct mount *mp; struct vfstable *vfsp = (struct vfstable *)0; int error, flag = 0; struct vnode_attr va; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); struct nameidata nd; struct nameidata nd1; char fstypename[MFSNAMELEN]; @@ -188,30 +278,45 @@ mount(struct proc *p, register struct mount_args *uap, __unused register_t *retv user_addr_t fsmountargs = uap->data; int ronly = 0; int mntalloc = 0; + boolean_t vfsp_ref = FALSE; mode_t accessmode; boolean_t is_64bit; boolean_t is_rwlock_locked = FALSE; + boolean_t did_rele = FALSE; + boolean_t have_usecount = FALSE; AUDIT_ARG(fflags, uap->flags); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); is_64bit = proc_is64bit(p); /* * Get vnode to be covered */ - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + NDINIT(&nd, LOOKUP, NOTRIGGER | FOLLOW | AUDITVNPATH1 | WANTPARENT, + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); vp = nd.ni_vp; + pvp = nd.ni_dvp; if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_ROOTFS)) uap->flags |= MNT_UPDATE; + + error = copyinstr(uap->type, fstypename, MFSNAMELEN, &dummy); + if (error) + goto out1; +#ifdef CONFIG_IMGSRC_ACCESS + if (uap->flags == MNT_IMGSRC) { + error = relocate_imageboot_source(vp, &nd.ni_cnd, fstypename, ctx, is_64bit, fsmountargs); + vnode_put(pvp); + vnode_put(vp); + return error; + } +#endif /* CONFIG_IMGSRC_ACCESS */ + if (uap->flags & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { error = EINVAL; @@ -220,7 +325,7 @@ mount(struct proc *p, register struct mount_args *uap, __unused register_t *retv mp = vp->v_mount; /* unmount in progress return error */ - mount_lock(mp); + mount_lock_spin(mp); if (mp->mnt_lflag & MNT_LUNMOUNT) { mount_unlock(mp); error = EBUSY; @@ -238,19 +343,37 @@ mount(struct proc *p, register struct mount_args *uap, __unused register_t *retv error = ENOTSUP; goto out1; } + +#ifdef CONFIG_IMGSRC_ACCESS + /* Can't downgrade the backer of the root FS */ + if ((mp->mnt_kern_flag & MNTK_BACKS_ROOT) && + (!vfs_isrdonly(mp)) && (uap->flags & MNT_RDONLY)) + { + error = ENOTSUP; + goto out1; + } +#endif /* CONFIG_IMGSRC_ACCESS */ + /* * Only root, or the user that did the original mount is * permitted to update it. */ - if (mp->mnt_vfsstat.f_owner != kauth_cred_getuid(context.vc_ucred) && - (error = suser(context.vc_ucred, &p->p_acflag))) { + if (mp->mnt_vfsstat.f_owner != kauth_cred_getuid(vfs_context_ucred(ctx)) && + (error = suser(vfs_context_ucred(ctx), &p->p_acflag))) { goto out1; } +#if CONFIG_MACF + error = mac_mount_check_remount(ctx, mp); + if (error != 0) { + lck_rw_done(&mp->mnt_rwlock); + goto out1; + } +#endif /* * For non-root users, silently enforce MNT_NOSUID and MNT_NODEV, * and MNT_NOEXEC if mount point is already MNT_NOEXEC. */ - if (suser(context.vc_ucred, NULL)) { + if (suser(vfs_context_ucred(ctx), NULL)) { uap->flags |= MNT_NOSUID | MNT_NODEV; if (mp->mnt_flag & MNT_NOEXEC) uap->flags |= MNT_NOEXEC; @@ -269,21 +392,21 @@ mount(struct proc *p, register struct mount_args *uap, __unused register_t *retv */ VATTR_INIT(&va); VATTR_WANTED(&va, va_uid); - if ((error = vnode_getattr(vp, &va, &context)) || - (va.va_uid != kauth_cred_getuid(context.vc_ucred) && - (error = suser(context.vc_ucred, &p->p_acflag)))) { + if ((error = vnode_getattr(vp, &va, ctx)) || + (va.va_uid != kauth_cred_getuid(vfs_context_ucred(ctx)) && + (error = suser(vfs_context_ucred(ctx), &p->p_acflag)))) { goto out1; } /* * For non-root users, silently enforce MNT_NOSUID and MNT_NODEV, and * MNT_NOEXEC if mount point is already MNT_NOEXEC. */ - if (suser(context.vc_ucred, NULL)) { + if (suser(vfs_context_ucred(ctx), NULL)) { uap->flags |= MNT_NOSUID | MNT_NODEV; if (vp->v_mount->mnt_flag & MNT_NOEXEC) uap->flags |= MNT_NOEXEC; } - if ( (error = VNOP_FSYNC(vp, MNT_WAIT, &context)) ) + if ( (error = VNOP_FSYNC(vp, MNT_WAIT, ctx)) ) goto out1; if ( (error = buf_invalidateblks(vp, BUF_WRITE_DATA, 0, 0)) ) @@ -293,34 +416,41 @@ mount(struct proc *p, register struct mount_args *uap, __unused register_t *retv error = ENOTDIR; goto out1; } - if ( (error = copyinstr(uap->type, fstypename, MFSNAMELEN, &dummy)) ) - goto out1; /* XXXAUDIT: Should we capture the type on the error path as well? */ AUDIT_ARG(text, fstypename); mount_list_lock(); for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) - if (!strcmp(vfsp->vfc_name, fstypename)) + if (!strncmp(vfsp->vfc_name, fstypename, MFSNAMELEN)) { + vfsp->vfc_refcount++; + vfsp_ref = TRUE; break; + } mount_list_unlock(); if (vfsp == NULL) { error = ENODEV; goto out1; } +#if CONFIG_MACF + error = mac_mount_check_mount(ctx, vp, + &nd.ni_cnd, vfsp->vfc_name); + if (error != 0) + goto out1; +#endif if (ISSET(vp->v_flag, VMOUNT) && (vp->v_mountedhere != NULL)) { error = EBUSY; goto out1; } - vnode_lock(vp); + vnode_lock_spin(vp); SET(vp->v_flag, VMOUNT); vnode_unlock(vp); /* * Allocate and initialize the filesystem. */ - MALLOC_ZONE(mp, struct mount *, (u_long)sizeof(struct mount), + MALLOC_ZONE(mp, struct mount *, (u_int32_t)sizeof(struct mount), M_MOUNT, M_WAITOK); - bzero((char *)mp, (u_long)sizeof(struct mount)); + bzero((char *)mp, (u_int32_t)sizeof(struct mount)); mntalloc = 1; /* Initialize the default IO constraints */ @@ -329,6 +459,12 @@ mount(struct proc *p, register struct mount_args *uap, __unused register_t *retv mp->mnt_maxsegreadsize = mp->mnt_maxreadcnt; mp->mnt_maxsegwritesize = mp->mnt_maxwritecnt; mp->mnt_devblocksize = DEV_BSIZE; + mp->mnt_alignmentmask = PAGE_MASK; + mp->mnt_ioqueue_depth = MNT_DEFAULT_IOQUEUE_DEPTH; + mp->mnt_ioscale = 1; + mp->mnt_ioflags = 0; + mp->mnt_realrootvp = NULLVP; + mp->mnt_authcache_ttl = CACHED_LOOKUP_RIGHT_TTL; TAILQ_INIT(&mp->mnt_vnodelist); TAILQ_INIT(&mp->mnt_workerqueue); @@ -338,15 +474,13 @@ mount(struct proc *p, register struct mount_args *uap, __unused register_t *retv is_rwlock_locked = TRUE; mp->mnt_op = vfsp->vfc_vfsops; mp->mnt_vtable = vfsp; - mount_list_lock(); - vfsp->vfc_refcount++; - mount_list_unlock(); //mp->mnt_stat.f_type = vfsp->vfc_typenum; mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK; strncpy(mp->mnt_vfsstat.f_fstypename, vfsp->vfc_name, MFSTYPENAMELEN); strncpy(mp->mnt_vfsstat.f_mntonname, nd.ni_cnd.cn_pnbuf, MAXPATHLEN); mp->mnt_vnodecovered = vp; - mp->mnt_vfsstat.f_owner = kauth_cred_getuid(context.vc_ucred); + mp->mnt_vfsstat.f_owner = kauth_cred_getuid(vfs_context_ucred(ctx)); + mp->mnt_devbsdunit = LOWPRI_MAX_NUM_DEV - 1; /* XXX 3762912 hack to support HFS filesystem 'owner' - filesystem may update later */ vfs_setowner(mp, KAUTH_UID_NONE, KAUTH_GID_NONE); @@ -359,13 +493,26 @@ update: mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_kern_flag |= MNTK_WANTRDWR; + mp->mnt_flag &= ~(MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | - MNT_UNKNOWNPERMISSIONS | MNT_DONTBROWSE | MNT_AUTOMOUNTED); + MNT_UNKNOWNPERMISSIONS | MNT_DONTBROWSE | MNT_AUTOMOUNTED | + MNT_DEFWRITE | MNT_NOATIME | MNT_QUARANTINE | MNT_CPROTECT ); + mp->mnt_flag |= uap->flags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_UNKNOWNPERMISSIONS | MNT_DONTBROWSE | MNT_AUTOMOUNTED | - MNT_DEFWRITE); + MNT_DEFWRITE | MNT_NOATIME | MNT_QUARANTINE | MNT_CPROTECT ); + +#if CONFIG_MACF + if (uap->flags & MNT_MULTILABEL) { + if (vfsp->vfc_vfsflags & VFC_VFSNOMACLABEL) { + error = EINVAL; + goto out1; + } + mp->mnt_flag |= MNT_MULTILABEL; + } +#endif if (vfsp->vfc_vfsflags & VFC_VFSLOCALARGS) { if (is_64bit) { @@ -373,7 +520,7 @@ update: goto out1; fsmountargs += sizeof(devpath); } else { - char *tmp; + user32_addr_t tmp; if ( (error = copyin(fsmountargs, (caddr_t)&tmp, sizeof(tmp))) ) goto out1; /* munge into LP64 addr */ @@ -383,7 +530,7 @@ update: /* if it is not update and device name needs to be parsed */ if ((devpath)) { - NDINIT(&nd1, LOOKUP, FOLLOW, UIO_USERSPACE, devpath, &context); + NDINIT(&nd1, LOOKUP, FOLLOW, UIO_USERSPACE, devpath, ctx); if ( (error = namei(&nd1)) ) goto out1; @@ -404,11 +551,11 @@ update: * If mount by non-root, then verify that user has necessary * permissions on the device. */ - if (suser(context.vc_ucred, NULL) != 0) { + if (suser(vfs_context_ucred(ctx), NULL) != 0) { accessmode = KAUTH_VNODE_READ_DATA; if ((mp->mnt_flag & MNT_RDONLY) == 0) accessmode |= KAUTH_VNODE_WRITE_DATA; - if ((error = vnode_authorize(devvp, NULL, accessmode, &context)) != 0) + if ((error = vnode_authorize(devvp, NULL, accessmode, ctx)) != 0) goto out2; } } @@ -428,7 +575,7 @@ update: error = EBUSY; goto out3; } - if ( (error = VNOP_FSYNC(devvp, MNT_WAIT, &context)) ) { + if ( (error = VNOP_FSYNC(devvp, MNT_WAIT, ctx)) ) { error = ENOTBLK; goto out3; } @@ -436,33 +583,110 @@ update: goto out3; ronly = (mp->mnt_flag & MNT_RDONLY) != 0; - if ( (error = VNOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, &context)) ) +#if CONFIG_MACF + error = mac_vnode_check_open(ctx, + devvp, + ronly ? FREAD : FREAD|FWRITE); + if (error) + goto out3; +#endif /* MAC */ + if ( (error = VNOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, ctx)) ) goto out3; mp->mnt_devvp = devvp; device_vnode = devvp; } else { if ((mp->mnt_flag & MNT_RDONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) { + dev_t dev; + int maj; /* * If upgrade to read-write by non-root, then verify * that user has necessary permissions on the device. */ device_vnode = mp->mnt_devvp; - if (device_vnode && suser(context.vc_ucred, NULL)) { - if ((error = vnode_authorize(device_vnode, NULL, - KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, &context)) != 0) + + if (device_vnode) { + vnode_getalways(device_vnode); + + if (suser(vfs_context_ucred(ctx), NULL)) { + if ((error = vnode_authorize(device_vnode, NULL, + KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, ctx)) != 0) { + vnode_put(device_vnode); + goto out2; + } + } + + /* Tell the device that we're upgrading */ + dev = (dev_t)device_vnode->v_rdev; + maj = major(dev); + + if ((u_int)maj >= (u_int)nblkdev) + panic("Volume mounted on a device with invalid major number.\n"); + + error = bdevsw[maj].d_open(dev, FREAD | FWRITE, S_IFBLK, p); + + vnode_put(device_vnode); + if (error != 0) { goto out2; + } } } device_vnode = NULLVP; } } +#if CONFIG_MACF + if ((uap->flags & MNT_UPDATE) == 0) { + mac_mount_label_init(mp); + mac_mount_label_associate(ctx, mp); + } + if (uap->mac_p != USER_ADDR_NULL) { + struct user_mac mac; + char *labelstr = NULL; + size_t ulen = 0; + if ((uap->flags & MNT_UPDATE) != 0) { + error = mac_mount_check_label_update( + ctx, mp); + if (error != 0) + goto out3; + } + if (is_64bit) { + error = copyin(uap->mac_p, &mac, sizeof(mac)); + } else { + struct mac mac32; + error = copyin(uap->mac_p, &mac32, sizeof(mac32)); + mac.m_buflen = mac32.m_buflen; + mac.m_string = CAST_USER_ADDR_T(mac32.m_string); + } + if (error != 0) + goto out3; + if ((mac.m_buflen > MAC_MAX_LABEL_BUF_LEN) || + (mac.m_buflen < 2)) { + error = EINVAL; + goto out3; + } + MALLOC(labelstr, char *, mac.m_buflen, M_MACTEMP, M_WAITOK); + error = copyinstr(mac.m_string, labelstr, mac.m_buflen, &ulen); + if (error != 0) { + FREE(labelstr, M_MACTEMP); + goto out3; + } + AUDIT_ARG(mac_string, labelstr); + error = mac_mount_label_internalize(mp->mnt_mntlabel, labelstr); + FREE(labelstr, M_MACTEMP); + if (error != 0) + goto out3; + } +#endif + if (device_vnode != NULL) { + VNOP_IOCTL(device_vnode, DKIOCGETBSDUNIT, (caddr_t)&mp->mnt_devbsdunit, 0, NULL); + mp->mnt_devbsdunit %= LOWPRI_MAX_NUM_DEV; + } /* * Mount the filesystem. */ - error = VFS_MOUNT(mp, device_vnode, fsmountargs, &context); + error = VFS_MOUNT(mp, device_vnode, fsmountargs, ctx); if (uap->flags & MNT_UPDATE) { if (mp->mnt_kern_flag & MNTK_WANTRDWR) @@ -476,21 +700,57 @@ update: lck_rw_done(&mp->mnt_rwlock); is_rwlock_locked = FALSE; if (!error) - enablequotas(mp,&context); + enablequotas(mp, ctx); goto out2; } /* * Put the new filesystem on the mount list after root. */ if (error == 0) { - vnode_lock(vp); + struct vfs_attr vfsattr; +#if CONFIG_MACF + if (vfs_flags(mp) & MNT_MULTILABEL) { + error = VFS_ROOT(mp, &rvp, ctx); + if (error) { + printf("%s() VFS_ROOT returned %d\n", __func__, error); + goto out3; + } + error = vnode_label(mp, NULL, rvp, NULL, 0, ctx); + /* + * drop reference provided by VFS_ROOT + */ + vnode_put(rvp); + + if (error) + goto out3; + } +#endif /* MAC */ + + vnode_lock_spin(vp); CLR(vp->v_flag, VMOUNT); vp->v_mountedhere = mp; vnode_unlock(vp); - vnode_ref(vp); + /* + * taking the name_cache_lock exclusively will + * insure that everyone is out of the fast path who + * might be trying to use a now stale copy of + * vp->v_mountedhere->mnt_realrootvp + * bumping mount_generation causes the cached values + * to be invalidated + */ + name_cache_lock(); + mount_generation++; + name_cache_unlock(); + + error = vnode_ref(vp); + if (error != 0) { + goto out4; + } + + have_usecount = TRUE; - error = checkdirs(vp, &context); + error = checkdirs(vp, ctx); if (error != 0) { /* Unmount the filesystem as cdir/rdirs cannot be updated */ goto out4; @@ -499,15 +759,51 @@ update: * there is no cleanup code here so I have made it void * we need to revisit this */ - (void)VFS_START(mp, 0, &context); + (void)VFS_START(mp, 0, ctx); + + error = mount_list_add(mp); + if (error != 0) { + goto out4; + } - mount_list_add(mp); lck_rw_done(&mp->mnt_rwlock); is_rwlock_locked = FALSE; + /* Check if this mounted file system supports EAs or named streams. */ + /* Skip WebDAV file systems for now since they hang in VFS_GETATTR here. */ + VFSATTR_INIT(&vfsattr); + VFSATTR_WANTED(&vfsattr, f_capabilities); + if (strncmp(mp->mnt_vfsstat.f_fstypename, "webdav", sizeof("webdav")) != 0 && + vfs_getattr(mp, &vfsattr, ctx) == 0 && + VFSATTR_IS_SUPPORTED(&vfsattr, f_capabilities)) { + if ((vfsattr.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_EXTENDED_ATTR) && + (vfsattr.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_EXTENDED_ATTR)) { + mp->mnt_kern_flag |= MNTK_EXTENDED_ATTRS; + } +#if NAMEDSTREAMS + if ((vfsattr.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_NAMEDSTREAMS) && + (vfsattr.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_NAMEDSTREAMS)) { + mp->mnt_kern_flag |= MNTK_NAMED_STREAMS; + } +#endif + /* Check if this file system supports path from id lookups. */ + if ((vfsattr.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_PATH_FROM_ID) && + (vfsattr.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_PATH_FROM_ID)) { + mp->mnt_kern_flag |= MNTK_PATH_FROM_ID; + } else if (mp->mnt_flag & MNT_DOVOLFS) { + /* Legacy MNT_DOVOLFS flag also implies path from id lookups. */ + mp->mnt_kern_flag |= MNTK_PATH_FROM_ID; + } + } + if (mp->mnt_vtable->vfc_vfsflags & VFC_VFSNATIVEXATTR) { + mp->mnt_kern_flag |= MNTK_EXTENDED_ATTRS; + } + if (mp->mnt_vtable->vfc_vfsflags & VFC_VFSPREFLIGHT) { + mp->mnt_kern_flag |= MNTK_UNMOUNT_PREFLIGHT; + } /* increment the operations count */ - OSAddAtomic(1, (SInt32 *)&vfs_nummntops); - enablequotas(mp,&context); + OSAddAtomic(1, &vfs_nummntops); + enablequotas(mp, ctx); if (device_vnode) { device_vnode->v_specflags |= SI_MOUNTEDON; @@ -524,7 +820,7 @@ update: /* Now that mount is setup, notify the listeners */ vfs_event_signal(NULL, VQ_MOUNT, (intptr_t)NULL); } else { - vnode_lock(vp); + vnode_lock_spin(vp); CLR(vp->v_flag, VMOUNT); vnode_unlock(vp); mount_list_lock(); @@ -532,12 +828,15 @@ update: mount_list_unlock(); if (device_vnode ) { - VNOP_CLOSE(device_vnode, ronly ? FREAD : FREAD|FWRITE, &context); vnode_rele(device_vnode); + VNOP_CLOSE(device_vnode, ronly ? FREAD : FREAD|FWRITE, ctx); } lck_rw_done(&mp->mnt_rwlock); is_rwlock_locked = FALSE; mount_lock_destroy(mp); +#if CONFIG_MACF + mac_mount_label_destroy(mp); +#endif FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT); } nameidone(&nd); @@ -550,18 +849,30 @@ update: vnode_put(devvp); vnode_put(vp); + /* Note that we've changed something in the parent directory */ + post_event_if_success(pvp, error, NOTE_WRITE); + vnode_put(pvp); + return(error); + out4: - (void)VFS_UNMOUNT(mp, MNT_FORCE, &context); + (void)VFS_UNMOUNT(mp, MNT_FORCE, ctx); if (device_vnode != NULLVP) { - VNOP_CLOSE(device_vnode, mp->mnt_flag & MNT_RDONLY ? FREAD : FREAD|FWRITE, &context); + vnode_rele(device_vnode); + VNOP_CLOSE(device_vnode, mp->mnt_flag & MNT_RDONLY ? FREAD : FREAD|FWRITE, + ctx); + did_rele = TRUE; } - vnode_lock(vp); + vnode_lock_spin(vp); vp->v_mountedhere = (mount_t) 0; vnode_unlock(vp); - vnode_rele(vp); + + if (have_usecount) { + vnode_rele(vp); + } out3: - vnode_rele(devvp); + if (devpath && ((uap->flags & MNT_UPDATE) == 0) && (!did_rele)) + vnode_rele(devvp); out2: if (devpath && devvp) vnode_put(devvp); @@ -571,199 +882,614 @@ out1: lck_rw_done(&mp->mnt_rwlock); } if (mntalloc) { +#if CONFIG_MACF + mac_mount_label_destroy(mp); +#endif + FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT); + } + + if (vfsp_ref) { mount_list_lock(); vfsp->vfc_refcount--; mount_list_unlock(); - FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT); } vnode_put(vp); + vnode_put(pvp); nameidone(&nd); return(error); - -} - -void -enablequotas(struct mount *mp, vfs_context_t context) -{ - struct nameidata qnd; - int type; - char qfpath[MAXPATHLEN]; - const char *qfname = QUOTAFILENAME; - const char *qfopsname = QUOTAOPSNAME; - const char *qfextension[] = INITQFNAMES; - - if ((strcmp(mp->mnt_vfsstat.f_fstypename, "hfs") != 0 ) - && (strcmp( mp->mnt_vfsstat.f_fstypename, "ufs") != 0)) - return; - - /* - * Enable filesystem disk quotas if necessary. - * We ignore errors as this should not interfere with final mount - */ - for (type=0; type < MAXQUOTAS; type++) { - sprintf(qfpath, "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfopsname, qfextension[type]); - NDINIT(&qnd, LOOKUP, FOLLOW, UIO_SYSSPACE32, CAST_USER_ADDR_T(qfpath), context); - if (namei(&qnd) != 0) - continue; /* option file to trigger quotas is not present */ - vnode_put(qnd.ni_vp); - nameidone(&qnd); - sprintf(qfpath, "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfname, qfextension[type]); - - (void) VFS_QUOTACTL(mp, QCMD(Q_QUOTAON, type), 0, qfpath, context); - } - return; } -/* - * Scan all active processes to see if any of them have a current - * or root directory onto which the new filesystem has just been - * mounted. If so, replace them with the new mount point. +#ifdef CONFIG_IMGSRC_ACCESS +/* + * Flush in-core data, check for competing mount attempts, + * and set VMOUNT */ static int -checkdirs(olddp, context) - struct vnode *olddp; - vfs_context_t context; +prepare_coveredvp(vnode_t vp, vfs_context_t ctx, struct componentname *cnp, const char *fsname) { - struct filedesc *fdp; - struct vnode *newdp; - struct proc *p; - struct vnode *tvp; - struct vnode *fdp_cvp; - struct vnode *fdp_rvp; - int cdir_changed = 0; - int rdir_changed = 0; - boolean_t funnel_state; - int err; + struct vnode_attr va; + int error; - if (olddp->v_usecount == 1) - return(0); - err = VFS_ROOT(olddp->v_mountedhere, &newdp, context); - if (err) { -#if DIAGNOSTIC - panic("mount: lost mount"); -#endif - return(err); + /* + * If the user is not root, ensure that they own the directory + * onto which we are attempting to mount. + */ + VATTR_INIT(&va); + VATTR_WANTED(&va, va_uid); + if ((error = vnode_getattr(vp, &va, ctx)) || + (va.va_uid != kauth_cred_getuid(vfs_context_ucred(ctx)) && + (!vfs_context_issuser(ctx)))) { + error = EPERM; + goto out; } - funnel_state = thread_funnel_set(kernel_flock, TRUE); - for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { - proc_fdlock(p); - fdp = p->p_fd; - if (fdp == (struct filedesc *)0) { - proc_fdunlock(p); - continue; - } - fdp_cvp = fdp->fd_cdir; - fdp_rvp = fdp->fd_rdir; - proc_fdunlock(p); + if ( (error = VNOP_FSYNC(vp, MNT_WAIT, ctx)) ) + goto out; - if (fdp_cvp == olddp) { - vnode_ref(newdp); - tvp = fdp->fd_cdir; - fdp_cvp = newdp; - cdir_changed = 1; - vnode_rele(tvp); - } - if (fdp_rvp == olddp) { - vnode_ref(newdp); - tvp = fdp->fd_rdir; - fdp_rvp = newdp; - rdir_changed = 1; - vnode_rele(tvp); - } - if (cdir_changed || rdir_changed) { - proc_fdlock(p); - fdp->fd_cdir = fdp_cvp; - fdp->fd_rdir = fdp_rvp; - proc_fdunlock(p); - } + if ( (error = buf_invalidateblks(vp, BUF_WRITE_DATA, 0, 0)) ) + goto out; + + if (vp->v_type != VDIR) { + error = ENOTDIR; + goto out; } - if (rootvnode == olddp) { - vnode_ref(newdp); - tvp = rootvnode; - rootvnode = newdp; - vnode_rele(tvp); + + if (ISSET(vp->v_flag, VMOUNT) && (vp->v_mountedhere != NULL)) { + error = EBUSY; + goto out; } - thread_funnel_set(kernel_flock, funnel_state); - vnode_put(newdp); - return(0); +#if CONFIG_MACF + error = mac_mount_check_mount(ctx, vp, + cnp, fsname); + if (error != 0) + goto out; +#endif + + vnode_lock_spin(vp); + SET(vp->v_flag, VMOUNT); + vnode_unlock(vp); + +out: + return error; } -/* - * Unmount a file system. - * - * Note: unmount takes a path to the vnode mounted on as argument, - * not special file (as before). - */ -/* ARGSUSED */ -int -unmount(struct proc *p, register struct unmount_args *uap, __unused register_t *retval) +static int +authorize_devpath_and_update_mntfromname(mount_t mp, user_addr_t devpath, vnode_t *devvpp, vfs_context_t ctx) { - register struct vnode *vp; - struct mount *mp; - int error; struct nameidata nd; - struct vfs_context context; + vnode_t vp; + mode_t accessmode; + int error; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, devpath, ctx); + if ( (error = namei(&nd)) ) + return error; - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); - error = namei(&nd); - if (error) - return (error); + strncpy(mp->mnt_vfsstat.f_mntfromname, nd.ni_cnd.cn_pnbuf, MAXPATHLEN); vp = nd.ni_vp; - mp = vp->v_mount; nameidone(&nd); + if (vp->v_type != VBLK) { + error = ENOTBLK; + goto out; + } + if (major(vp->v_rdev) >= nblkdev) { + error = ENXIO; + goto out; + } /* - * Must be the root of the filesystem + * If mount by non-root, then verify that user has necessary + * permissions on the device. */ - if ((vp->v_flag & VROOT) == 0) { + if (!vfs_context_issuser(ctx)) { + accessmode = KAUTH_VNODE_READ_DATA; + if ((mp->mnt_flag & MNT_RDONLY) == 0) + accessmode |= KAUTH_VNODE_WRITE_DATA; + if ((error = vnode_authorize(vp, NULL, accessmode, ctx)) != 0) + goto out; + } + + *devvpp = vp; +out: + if (error) { vnode_put(vp); - return (EINVAL); } - mount_ref(mp, 0); - vnode_put(vp); - /* safedounmount consumes the mount ref */ - return (safedounmount(mp, uap->flags, p)); + + return error; } /* - * The mount struct comes with a mount ref which will be consumed. - * Do the actual file system unmount, prevent some common foot shooting. - * - * XXX Should take a "vfs_context_t" instead of a "struct proc *" + * Clear VMOUNT, set v_mountedhere, and mnt_vnodecovered, ref the vnode, + * and call checkdirs() */ -int -safedounmount(mp, flags, p) - struct mount *mp; - int flags; - struct proc *p; +static int +place_mount_and_checkdirs(mount_t mp, vnode_t vp, vfs_context_t ctx) { int error; + mp->mnt_vnodecovered = vp; /* XXX This is normally only set at init-time ... */ + + vnode_lock_spin(vp); + CLR(vp->v_flag, VMOUNT); + vp->v_mountedhere = mp; + vnode_unlock(vp); + /* - * Only root, or the user that did the original mount is - * permitted to unmount this filesystem. + * taking the name_cache_lock exclusively will + * insure that everyone is out of the fast path who + * might be trying to use a now stale copy of + * vp->v_mountedhere->mnt_realrootvp + * bumping mount_generation causes the cached values + * to be invalidated */ - if ((mp->mnt_vfsstat.f_owner != kauth_cred_getuid(kauth_cred_get())) && - (error = suser(kauth_cred_get(), &p->p_acflag))) { + name_cache_lock(); + mount_generation++; + name_cache_unlock(); + + error = vnode_ref(vp); + if (error != 0) { goto out; } - /* + error = checkdirs(vp, ctx); + if (error != 0) { + /* Unmount the filesystem as cdir/rdirs cannot be updated */ + vnode_rele(vp); + goto out; + } + +out: + if (error != 0) { + mp->mnt_vnodecovered = NULLVP; + } + return error; +} + +static void +undo_place_on_covered_vp(mount_t mp, vnode_t vp) +{ + vnode_rele(vp); + vnode_lock_spin(vp); + vp->v_mountedhere = (mount_t)NULL; + vnode_unlock(vp); + + mp->mnt_vnodecovered = NULLVP; +} + +static int +mount_begin_update(mount_t mp, vfs_context_t ctx, int flags) +{ + int error; + + /* unmount in progress return error */ + mount_lock_spin(mp); + if (mp->mnt_lflag & MNT_LUNMOUNT) { + mount_unlock(mp); + return EBUSY; + } + mount_unlock(mp); + lck_rw_lock_exclusive(&mp->mnt_rwlock); + + /* + * We only allow the filesystem to be reloaded if it + * is currently mounted read-only. + */ + if ((flags & MNT_RELOAD) && + ((mp->mnt_flag & MNT_RDONLY) == 0)) { + error = ENOTSUP; + goto out; + } + + /* + * Only root, or the user that did the original mount is + * permitted to update it. + */ + if (mp->mnt_vfsstat.f_owner != kauth_cred_getuid(vfs_context_ucred(ctx)) && + (!vfs_context_issuser(ctx))) { + error = EPERM; + goto out; + } +#if CONFIG_MACF + error = mac_mount_check_remount(ctx, mp); + if (error != 0) { + goto out; + } +#endif + +out: + if (error) { + lck_rw_done(&mp->mnt_rwlock); + } + + return error; +} + +static void +mount_end_update(mount_t mp) +{ + lck_rw_done(&mp->mnt_rwlock); +} + +static int +relocate_imageboot_source(vnode_t vp, struct componentname *cnp, + const char *fsname, vfs_context_t ctx, + boolean_t is64bit, user_addr_t fsmountargs) +{ + int error; + mount_t mp; + boolean_t placed = FALSE; + vnode_t devvp; + struct vfstable *vfsp; + user_addr_t devpath; + char *old_mntonname; + + /* If we didn't imageboot, nothing to move */ + if (imgsrc_rootvnode == NULLVP) { + return EINVAL; + } + + /* Only root can do this */ + if (!vfs_context_issuser(ctx)) { + return EPERM; + } + + error = vnode_get(imgsrc_rootvnode); + if (error != 0) { + return error; + } + + MALLOC(old_mntonname, char*, MAXPATHLEN, M_TEMP, M_WAITOK); + + /* Can only move once */ + mp = vnode_mount(imgsrc_rootvnode); + if ((mp->mnt_kern_flag & MNTK_HAS_MOVED) == MNTK_HAS_MOVED) { + error = EBUSY; + goto out0; + } + + /* Get exclusive rwlock on mount, authorize update on mp */ + error = mount_begin_update(mp , ctx, 0); + if (error != 0) { + goto out0; + } + + /* + * It can only be moved once. Flag is set under the rwlock, + * so we're now safe to proceed. + */ + if ((mp->mnt_kern_flag & MNTK_HAS_MOVED) == MNTK_HAS_MOVED) { + goto out1; + } + + /* Mark covered vnode as mount in progress, authorize placing mount on top */ + error = prepare_coveredvp(vp, ctx, cnp, fsname); + if (error != 0) { + goto out1; + } + + /* Sanity check the name caller has provided */ + vfsp = mp->mnt_vtable; + if (strncmp(vfsp->vfc_name, fsname, MFSNAMELEN) != 0) { + error = EINVAL; + goto out2; + } + + /* Check the device vnode and update mount-from name, for local filesystems */ + if (vfsp->vfc_vfsflags & VFC_VFSLOCALARGS) { + if (is64bit) { + if ( (error = copyin(fsmountargs, (caddr_t)&devpath, sizeof(devpath))) ) + goto out2; + fsmountargs += sizeof(devpath); + } else { + user32_addr_t tmp; + if ( (error = copyin(fsmountargs, (caddr_t)&tmp, sizeof(tmp))) ) + goto out2; + /* munge into LP64 addr */ + devpath = CAST_USER_ADDR_T(tmp); + fsmountargs += sizeof(tmp); + } + + if (devpath != USER_ADDR_NULL) { + error = authorize_devpath_and_update_mntfromname(mp, devpath, &devvp, ctx); + if (error) { + goto out2; + } + + vnode_put(devvp); + } + } + + /* + * Place mp on top of vnode, ref the vnode, call checkdirs(), + * and increment the name cache's mount generation + */ + error = place_mount_and_checkdirs(mp, vp, ctx); + if (error != 0) { + goto out2; + } + + placed = TRUE; + + strncpy(old_mntonname, mp->mnt_vfsstat.f_mntonname, MAXPATHLEN); + strncpy(mp->mnt_vfsstat.f_mntonname, cnp->cn_pnbuf, MAXPATHLEN); + + /* Forbid future moves */ + mount_lock(mp); + mp->mnt_kern_flag |= MNTK_HAS_MOVED; + mount_unlock(mp); + + /* Finally, add to mount list, completely ready to go */ + error = mount_list_add(mp); + if (error != 0) { + goto out3; + } + + mount_end_update(mp); + vnode_put(imgsrc_rootvnode); + FREE(old_mntonname, M_TEMP); + + return 0; +out3: + strncpy(mp->mnt_vfsstat.f_mntonname, old_mntonname, MAXPATHLEN); + + mount_lock(mp); + mp->mnt_kern_flag &= ~(MNTK_HAS_MOVED); + mount_unlock(mp); + +out2: + /* + * Placing the mp on the vnode clears VMOUNT, + * so cleanup is different after that point + */ + if (placed) { + /* Rele the vp, clear VMOUNT and v_mountedhere */ + undo_place_on_covered_vp(mp, vp); + } else { + vnode_lock_spin(vp); + CLR(vp->v_flag, VMOUNT); + vnode_unlock(vp); + } +out1: + mount_end_update(mp); + +out0: + vnode_put(imgsrc_rootvnode); + FREE(old_mntonname, M_TEMP); + return error; +} + +#endif /* CONFIG_IMGSRC_ACCESS */ + +void +enablequotas(struct mount *mp, vfs_context_t ctx) +{ + struct nameidata qnd; + int type; + char qfpath[MAXPATHLEN]; + const char *qfname = QUOTAFILENAME; + const char *qfopsname = QUOTAOPSNAME; + const char *qfextension[] = INITQFNAMES; + + /* XXX Shoulkd be an MNTK_ flag, instead of strncmp()'s */ + if (strncmp(mp->mnt_vfsstat.f_fstypename, "hfs", sizeof("hfs")) != 0 ) { + return; + } + /* + * Enable filesystem disk quotas if necessary. + * We ignore errors as this should not interfere with final mount + */ + for (type=0; type < MAXQUOTAS; type++) { + snprintf(qfpath, sizeof(qfpath), "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfopsname, qfextension[type]); + NDINIT(&qnd, LOOKUP, FOLLOW, UIO_SYSSPACE, CAST_USER_ADDR_T(qfpath), ctx); + if (namei(&qnd) != 0) + continue; /* option file to trigger quotas is not present */ + vnode_put(qnd.ni_vp); + nameidone(&qnd); + snprintf(qfpath, sizeof(qfpath), "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfname, qfextension[type]); + + (void) VFS_QUOTACTL(mp, QCMD(Q_QUOTAON, type), 0, qfpath, ctx); + } + return; +} + + +static int +checkdirs_callback(proc_t p, void * arg) +{ + struct cdirargs * cdrp = (struct cdirargs * )arg; + vnode_t olddp = cdrp->olddp; + vnode_t newdp = cdrp->newdp; + struct filedesc *fdp; + vnode_t tvp; + vnode_t fdp_cvp; + vnode_t fdp_rvp; + int cdir_changed = 0; + int rdir_changed = 0; + + /* + * XXX Also needs to iterate each thread in the process to see if it + * XXX is using a per-thread current working directory, and, if so, + * XXX update that as well. + */ + + proc_fdlock(p); + fdp = p->p_fd; + if (fdp == (struct filedesc *)0) { + proc_fdunlock(p); + return(PROC_RETURNED); + } + fdp_cvp = fdp->fd_cdir; + fdp_rvp = fdp->fd_rdir; + proc_fdunlock(p); + + if (fdp_cvp == olddp) { + vnode_ref(newdp); + tvp = fdp->fd_cdir; + fdp_cvp = newdp; + cdir_changed = 1; + vnode_rele(tvp); + } + if (fdp_rvp == olddp) { + vnode_ref(newdp); + tvp = fdp->fd_rdir; + fdp_rvp = newdp; + rdir_changed = 1; + vnode_rele(tvp); + } + if (cdir_changed || rdir_changed) { + proc_fdlock(p); + fdp->fd_cdir = fdp_cvp; + fdp->fd_rdir = fdp_rvp; + proc_fdunlock(p); + } + return(PROC_RETURNED); +} + + + +/* + * Scan all active processes to see if any of them have a current + * or root directory onto which the new filesystem has just been + * mounted. If so, replace them with the new mount point. + */ +static int +checkdirs(vnode_t olddp, vfs_context_t ctx) +{ + vnode_t newdp; + vnode_t tvp; + int err; + struct cdirargs cdr; + struct uthread * uth = get_bsdthread_info(current_thread()); + + if (olddp->v_usecount == 1) + return(0); + if (uth != (struct uthread *)0) + uth->uu_notrigger = 1; + err = VFS_ROOT(olddp->v_mountedhere, &newdp, ctx); + if (uth != (struct uthread *)0) + uth->uu_notrigger = 0; + + if (err != 0) { +#if DIAGNOSTIC + panic("mount: lost mount: error %d", err); +#endif + return(err); + } + + cdr.olddp = olddp; + cdr.newdp = newdp; + /* do not block for exec/fork trans as the vp in cwd & rootdir are not changing */ + proc_iterate(PROC_ALLPROCLIST | PROC_NOWAITTRANS, checkdirs_callback, (void *)&cdr, NULL, NULL); + + if (rootvnode == olddp) { + vnode_ref(newdp); + tvp = rootvnode; + rootvnode = newdp; + vnode_rele(tvp); + } + + vnode_put(newdp); + return(0); +} + +/* + * Unmount a file system. + * + * Note: unmount takes a path to the vnode mounted on as argument, + * not special file (as before). + */ +/* ARGSUSED */ +int +unmount(__unused proc_t p, struct unmount_args *uap, __unused int32_t *retval) +{ + vnode_t vp; + struct mount *mp; + int error; + struct nameidata nd; + vfs_context_t ctx = vfs_context_current(); + + NDINIT(&nd, LOOKUP, NOTRIGGER | FOLLOW | AUDITVNPATH1, + UIO_USERSPACE, uap->path, ctx); + error = namei(&nd); + if (error) + return (error); + vp = nd.ni_vp; + mp = vp->v_mount; + nameidone(&nd); + +#if CONFIG_MACF + error = mac_mount_check_umount(ctx, mp); + if (error != 0) { + vnode_put(vp); + return (error); + } +#endif + /* + * Must be the root of the filesystem + */ + if ((vp->v_flag & VROOT) == 0) { + vnode_put(vp); + return (EINVAL); + } + mount_ref(mp, 0); + vnode_put(vp); + /* safedounmount consumes the mount ref */ + return (safedounmount(mp, uap->flags, ctx)); +} + +int +vfs_unmountbyfsid(fsid_t * fsid, int flags, vfs_context_t ctx) +{ + mount_t mp; + + mp = mount_list_lookupby_fsid(fsid, 0, 1); + if (mp == (mount_t)0) { + return(ENOENT); + } + mount_ref(mp, 0); + mount_iterdrop(mp); + /* safedounmount consumes the mount ref */ + return(safedounmount(mp, flags, ctx)); +} + + +/* + * The mount struct comes with a mount ref which will be consumed. + * Do the actual file system unmount, prevent some common foot shooting. + */ +int +safedounmount(struct mount *mp, int flags, vfs_context_t ctx) +{ + int error; + proc_t p = vfs_context_proc(ctx); + + /* + * Only root, or the user that did the original mount is + * permitted to unmount this filesystem. + */ + if ((mp->mnt_vfsstat.f_owner != kauth_cred_getuid(kauth_cred_get())) && + (error = suser(kauth_cred_get(), &p->p_acflag))) + goto out; + + /* * Don't allow unmounting the root file system. */ if (mp->mnt_flag & MNT_ROOTFS) { - error = EBUSY; + error = EBUSY; /* the root is always busy */ + goto out; + } + +#ifdef CONFIG_IMGSRC_ACCESS + if (mp->mnt_kern_flag & MNTK_BACKS_ROOT) { + error = EBUSY; goto out; } +#endif /* CONFIG_IMGSRC_ACCESS */ + + return (dounmount(mp, flags, 1, ctx)); - return (dounmount(mp, flags, 1, p)); out: mount_drop(mp, 0); return(error); @@ -773,21 +1499,14 @@ out: * Do the actual file system unmount. */ int -dounmount(mp, flags, withref, p) - register struct mount *mp; - int flags; - int withref; - struct proc *p; +dounmount(struct mount *mp, int flags, int withref, vfs_context_t ctx) { - struct vnode *coveredvp = (vnode_t)0; + vnode_t coveredvp = (vnode_t)0; int error; int needwakeup = 0; - struct vfs_context context; int forcedunmount = 0; int lflags = 0; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + struct vnode *devvp = NULLVP; if (flags & MNT_FORCE) forcedunmount = 1; @@ -799,9 +1518,9 @@ dounmount(mp, flags, withref, p) } if (mp->mnt_lflag & MNT_LUNMOUNT) { mp->mnt_lflag |= MNT_LWAIT; - if (withref != 0) + if(withref != 0) mount_drop(mp, 1); - msleep((caddr_t)mp, &mp->mnt_mlock, (PVFS | PDROP), "dounmount", 0 ); + msleep((caddr_t)mp, &mp->mnt_mlock, (PVFS | PDROP), "dounmount", NULL); /* * The prior unmount attempt has probably succeeded. * Do not dereference mp here - returning EBUSY is safest. @@ -811,16 +1530,39 @@ dounmount(mp, flags, withref, p) mp->mnt_kern_flag |= MNTK_UNMOUNT; mp->mnt_lflag |= MNT_LUNMOUNT; mp->mnt_flag &=~ MNT_ASYNC; + /* + * anyone currently in the fast path that + * trips over the cached rootvp will be + * dumped out and forced into the slow path + * to regenerate a new cached value + */ + mp->mnt_realrootvp = NULLVP; mount_unlock(mp); + + /* + * taking the name_cache_lock exclusively will + * insure that everyone is out of the fast path who + * might be trying to use a now stale copy of + * vp->v_mountedhere->mnt_realrootvp + * bumping mount_generation causes the cached values + * to be invalidated + */ + name_cache_lock(); + mount_generation++; + name_cache_unlock(); + + lck_rw_lock_exclusive(&mp->mnt_rwlock); if (withref != 0) mount_drop(mp, 0); +#if CONFIG_FSE fsevent_unmount(mp); /* has to come first! */ +#endif error = 0; if (forcedunmount == 0) { ubc_umount(mp); /* release cached vnodes */ if ((mp->mnt_flag & MNT_RDONLY) == 0) { - error = VFS_SYNC(mp, MNT_WAIT, &context); + error = VFS_SYNC(mp, MNT_WAIT, ctx); if (error) { mount_lock(mp); mp->mnt_kern_flag &= ~MNTK_UNMOUNT; @@ -845,7 +1587,7 @@ dounmount(mp, flags, withref, p) /* make sure there are no one in the mount iterations or lookup */ mount_iterdrain(mp); - error = VFS_UNMOUNT(mp, flags, &context); + error = VFS_UNMOUNT(mp, flags, ctx); if (error) { mount_iterreset(mp); mount_lock(mp); @@ -857,13 +1599,17 @@ dounmount(mp, flags, withref, p) /* increment the operations count */ if (!error) - OSAddAtomic(1, (SInt32 *)&vfs_nummntops); + OSAddAtomic(1, &vfs_nummntops); if ( mp->mnt_devvp && mp->mnt_vtable->vfc_vfsflags & VFC_VFSLOCALARGS) { - mp->mnt_devvp->v_specflags &= ~SI_MOUNTEDON; - VNOP_CLOSE(mp->mnt_devvp, mp->mnt_flag & MNT_RDONLY ? FREAD : FREAD|FWRITE, - &context); - vnode_rele(mp->mnt_devvp); + /* hold an io reference and drop the usecount before close */ + devvp = mp->mnt_devvp; + vnode_getalways(devvp); + vnode_rele(devvp); + VNOP_CLOSE(devvp, mp->mnt_flag & MNT_RDONLY ? FREAD : FREAD|FWRITE, + ctx); + vnode_clearmountedon(devvp); + vnode_put(devvp); } lck_rw_done(&mp->mnt_rwlock); mount_list_remove(mp); @@ -872,7 +1618,7 @@ dounmount(mp, flags, withref, p) /* mark the mount point hook in the vp but not drop the ref yet */ if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { vnode_getwithref(coveredvp); - vnode_lock(coveredvp); + vnode_lock_spin(coveredvp); coveredvp->v_mountedhere = (struct mount *)0; vnode_unlock(coveredvp); vnode_put(coveredvp); @@ -911,20 +1657,34 @@ out: wakeup((caddr_t)mp); if (!error) { if ((coveredvp != NULLVP)) { + vnode_t pvp; + vnode_getwithref(coveredvp); + pvp = vnode_getparent(coveredvp); vnode_rele(coveredvp); - vnode_lock(coveredvp); + vnode_lock_spin(coveredvp); if(mp->mnt_crossref == 0) { vnode_unlock(coveredvp); mount_lock_destroy(mp); +#if CONFIG_MACF + mac_mount_label_destroy(mp); +#endif FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT); } else { coveredvp->v_lflag |= VL_MOUNTDEAD; vnode_unlock(coveredvp); } vnode_put(coveredvp); + + if (pvp) { + lock_vnode_and_post(pvp, NOTE_WRITE); + vnode_put(pvp); + } } else if (mp->mnt_flag & MNT_ROOTFS) { mount_lock_destroy(mp); +#if CONFIG_MACF + mac_mount_label_destroy(mp); +#endif FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT); } else panic("dounmount: no coveredvp"); @@ -945,6 +1705,9 @@ mount_dropcrossref(mount_t mp, vnode_t dp, int need_put) vnode_put_locked(dp); vnode_unlock(dp); mount_lock_destroy(mp); +#if CONFIG_MACF + mac_mount_label_destroy(mp); +#endif FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT); return; } @@ -965,19 +1728,14 @@ struct ctldebug debug0 = { "syncprt", &syncprt }; int print_vmpage_stat=0; static int -sync_callback(mount_t mp, __unused void * arg) +sync_callback(mount_t mp, void * arg) { - struct proc * p = current_proc(); int asyncflag; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); if ((mp->mnt_flag & MNT_RDONLY) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; - VFS_SYNC(mp, MNT_NOWAIT, &context); + VFS_SYNC(mp, arg ? MNT_WAIT : MNT_NOWAIT, vfs_context_current()); if (asyncflag) mp->mnt_flag |= MNT_ASYNC; } @@ -985,20 +1743,29 @@ sync_callback(mount_t mp, __unused void * arg) } -extern unsigned int vp_pagein, vp_pgodirty, vp_pgoclean; -extern unsigned int dp_pgins, dp_pgouts; +#include + +clock_sec_t sync_wait_time = 0; /* ARGSUSED */ int -sync(__unused struct proc *p, __unused struct sync_args *uap, __unused register_t *retval) +sync(__unused proc_t p, __unused struct sync_args *uap, __unused int32_t *retval) { + clock_nsec_t nsecs; vfs_iterate(LK_NOWAIT, sync_callback, (void *)0); + + { + static fsid_t fsid = { { 0, 0 } }; + + clock_get_calendar_microtime(&sync_wait_time, &nsecs); + vfs_event_signal(&fsid, VQ_SYNCEVENT, (intptr_t)NULL); + wakeup((caddr_t)&sync_wait_time); + } + { if(print_vmpage_stat) { vm_countdirtypages(); - printf("VP: %d: %d: %d: %d: %d\n", vp_pgodirty, vp_pgoclean, vp_pagein, - dp_pgins, dp_pgouts); } } #if DIAGNOSTIC @@ -1011,25 +1778,36 @@ sync(__unused struct proc *p, __unused struct sync_args *uap, __unused register_ /* * Change filesystem quotas. */ -/* ARGSUSED */ +#if QUOTA +static int quotactl_funneled(proc_t p, struct quotactl_args *uap, int32_t *retval); + int -quotactl(struct proc *p, register struct quotactl_args *uap, __unused register_t *retval) +quotactl(proc_t p, struct quotactl_args *uap, int32_t *retval) { - register struct mount *mp; + boolean_t funnel_state; + int error; + + funnel_state = thread_funnel_set(kernel_flock, TRUE); + error = quotactl_funneled(p, uap, retval); + thread_funnel_set(kernel_flock, funnel_state); + return(error); +} + +static int +quotactl_funneled(proc_t p, struct quotactl_args *uap, __unused int32_t *retval) +{ + struct mount *mp; int error, quota_cmd, quota_status; caddr_t datap; size_t fnamelen; struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); struct dqblk my_dqblk; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - AUDIT_ARG(uid, uap->uid, 0, 0, 0); + AUDIT_ARG(uid, uap->uid); AUDIT_ARG(cmd, uap->cmd); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); @@ -1076,7 +1854,7 @@ quotactl(struct proc *p, register struct quotactl_args *uap, __unused register_t } /* switch */ if (error == 0) { - error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, datap, &context); + error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, datap, ctx); } switch (quota_cmd) { @@ -1109,26 +1887,143 @@ quotactl(struct proc *p, register struct quotactl_args *uap, __unused register_t return (error); } +#else +int +quotactl(__unused proc_t p, __unused struct quotactl_args *uap, __unused int32_t *retval) +{ + return (EOPNOTSUPP); +} +#endif /* QUOTA */ + +/* + * Get filesystem statistics. + * + * Returns: 0 Success + * namei:??? + * vfs_update_vfsstat:??? + * munge_statfs:EFAULT + */ +/* ARGSUSED */ +int +statfs(__unused proc_t p, struct statfs_args *uap, __unused int32_t *retval) +{ + struct mount *mp; + struct vfsstatfs *sp; + int error; + struct nameidata nd; + vfs_context_t ctx = vfs_context_current(); + vnode_t vp; + + NDINIT(&nd, LOOKUP, NOTRIGGER | FOLLOW | AUDITVNPATH1, + UIO_USERSPACE, uap->path, ctx); + error = namei(&nd); + if (error) + return (error); + vp = nd.ni_vp; + mp = vp->v_mount; + sp = &mp->mnt_vfsstat; + nameidone(&nd); + + error = vfs_update_vfsstat(mp, ctx, VFS_USER_EVENT); + vnode_put(vp); + if (error != 0) + return (error); + + error = munge_statfs(mp, sp, uap->buf, NULL, IS_64BIT_PROCESS(p), TRUE); + return (error); +} + +/* + * Get filesystem statistics. + */ +/* ARGSUSED */ +int +fstatfs(__unused proc_t p, struct fstatfs_args *uap, __unused int32_t *retval) +{ + vnode_t vp; + struct mount *mp; + struct vfsstatfs *sp; + int error; + + AUDIT_ARG(fd, uap->fd); + + if ( (error = file_vnode(uap->fd, &vp)) ) + return (error); + + error = vnode_getwithref(vp); + if (error) { + file_drop(uap->fd); + return (error); + } + + AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1); + + mp = vp->v_mount; + if (!mp) { + error = EBADF; + goto out; + } + sp = &mp->mnt_vfsstat; + if ((error = vfs_update_vfsstat(mp,vfs_context_current(),VFS_USER_EVENT)) != 0) { + goto out; + } + + error = munge_statfs(mp, sp, uap->buf, NULL, IS_64BIT_PROCESS(p), TRUE); + +out: + file_drop(uap->fd); + vnode_put(vp); + + return (error); +} -/* - * Get filesystem statistics. +/* + * Common routine to handle copying of statfs64 data to user space + */ +static int +statfs64_common(struct mount *mp, struct vfsstatfs *sfsp, user_addr_t bufp) +{ + int error; + struct statfs64 sfs; + + bzero(&sfs, sizeof(sfs)); + + sfs.f_bsize = sfsp->f_bsize; + sfs.f_iosize = (int32_t)sfsp->f_iosize; + sfs.f_blocks = sfsp->f_blocks; + sfs.f_bfree = sfsp->f_bfree; + sfs.f_bavail = sfsp->f_bavail; + sfs.f_files = sfsp->f_files; + sfs.f_ffree = sfsp->f_ffree; + sfs.f_fsid = sfsp->f_fsid; + sfs.f_owner = sfsp->f_owner; + sfs.f_type = mp->mnt_vtable->vfc_typenum; + sfs.f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + sfs.f_fssubtype = sfsp->f_fssubtype; + strlcpy(&sfs.f_fstypename[0], &sfsp->f_fstypename[0], MFSTYPENAMELEN); + strlcpy(&sfs.f_mntonname[0], &sfsp->f_mntonname[0], MAXPATHLEN); + strlcpy(&sfs.f_mntfromname[0], &sfsp->f_mntfromname[0], MAXPATHLEN); + + error = copyout((caddr_t)&sfs, bufp, sizeof(sfs)); + + return(error); +} + +/* + * Get file system statistics in 64-bit mode */ -/* ARGSUSED */ int -statfs(struct proc *p, register struct statfs_args *uap, __unused register_t *retval) +statfs64(__unused struct proc *p, struct statfs64_args *uap, __unused int32_t *retval) { struct mount *mp; struct vfsstatfs *sp; int error; struct nameidata nd; - struct vfs_context context; + vfs_context_t ctxp = vfs_context_current(); vnode_t vp; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + NDINIT(&nd, LOOKUP, NOTRIGGER | FOLLOW | AUDITVNPATH1, + UIO_USERSPACE, uap->path, ctxp); error = namei(&nd); if (error) return (error); @@ -1137,58 +2032,62 @@ statfs(struct proc *p, register struct statfs_args *uap, __unused register_t *re sp = &mp->mnt_vfsstat; nameidone(&nd); - error = vfs_update_vfsstat(mp, &context); + error = vfs_update_vfsstat(mp, ctxp, VFS_USER_EVENT); vnode_put(vp); if (error != 0) return (error); - error = munge_statfs(mp, sp, uap->buf, NULL, IS_64BIT_PROCESS(p), TRUE); + error = statfs64_common(mp, sp, uap->buf); + return (error); } -/* - * Get filesystem statistics. +/* + * Get file system statistics in 64-bit mode */ -/* ARGSUSED */ int -fstatfs(struct proc *p, register struct fstatfs_args *uap, __unused register_t *retval) +fstatfs64(__unused struct proc *p, struct fstatfs64_args *uap, __unused int32_t *retval) { struct vnode *vp; struct mount *mp; struct vfsstatfs *sp; int error; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); AUDIT_ARG(fd, uap->fd); if ( (error = file_vnode(uap->fd, &vp)) ) return (error); + error = vnode_getwithref(vp); + if (error) { + file_drop(uap->fd); + return (error); + } + AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1); mp = vp->v_mount; if (!mp) { - file_drop(uap->fd); - return (EBADF); + error = EBADF; + goto out; } sp = &mp->mnt_vfsstat; - if ((error = vfs_update_vfsstat(mp, &context)) != 0) { - file_drop(uap->fd); - return (error); + if ((error = vfs_update_vfsstat(mp, vfs_context_current(), VFS_USER_EVENT)) != 0) { + goto out; } - file_drop(uap->fd); - error = munge_statfs(mp, sp, uap->buf, NULL, IS_64BIT_PROCESS(p), TRUE); + error = statfs64_common(mp, sp, uap->buf); + +out: + file_drop(uap->fd); + vnode_put(vp); return (error); } - struct getfsstat_struct { user_addr_t sfsp; + user_addr_t *mp; int count; int maxcount; int flags; @@ -1202,21 +2101,18 @@ getfsstat_callback(mount_t mp, void * arg) struct getfsstat_struct *fstp = (struct getfsstat_struct *)arg; struct vfsstatfs *sp; - struct proc * p = current_proc(); int error, my_size; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vfs_context_t ctx = vfs_context_current(); if (fstp->sfsp && fstp->count < fstp->maxcount) { sp = &mp->mnt_vfsstat; /* * If MNT_NOWAIT is specified, do not refresh the - * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. + * fsstat cache. MNT_WAIT/MNT_DWAIT overrides MNT_NOWAIT. */ - if (((fstp->flags & MNT_NOWAIT) == 0 || (fstp->flags & MNT_WAIT)) && - (error = vfs_update_vfsstat(mp, &context))) { + if (((fstp->flags & MNT_NOWAIT) == 0 || (fstp->flags & (MNT_WAIT | MNT_DWAIT))) && + (error = vfs_update_vfsstat(mp, ctx, + VFS_USER_EVENT))) { KAUTH_DEBUG("vfs_update_vfsstat returned %d", error); return(VFS_RETURNED); } @@ -1224,12 +2120,21 @@ getfsstat_callback(mount_t mp, void * arg) /* * Need to handle LP64 version of struct statfs */ - error = munge_statfs(mp, sp, fstp->sfsp, &my_size, IS_64BIT_PROCESS(p), FALSE); + error = munge_statfs(mp, sp, fstp->sfsp, &my_size, IS_64BIT_PROCESS(vfs_context_proc(ctx)), FALSE); if (error) { fstp->error = error; return(VFS_RETURNED_DONE); } fstp->sfsp += my_size; + + if (fstp->mp) { + error = mac_mount_label_get(mp, *fstp->mp); + if (error) { + fstp->error = error; + return(VFS_RETURNED_DONE); + } + fstp->mp++; + } } fstp->count++; return(VFS_RETURNED); @@ -1240,21 +2145,100 @@ getfsstat_callback(mount_t mp, void * arg) */ int getfsstat(__unused proc_t p, struct getfsstat_args *uap, int *retval) +{ + struct __mac_getfsstat_args muap; + + muap.buf = uap->buf; + muap.bufsize = uap->bufsize; + muap.mac = USER_ADDR_NULL; + muap.macsize = 0; + muap.flags = uap->flags; + + return (__mac_getfsstat(p, &muap, retval)); +} + +/* + * __mac_getfsstat: Get MAC-related file system statistics + * + * Parameters: p (ignored) + * uap User argument descriptor (see below) + * retval Count of file system statistics (N stats) + * + * Indirect: uap->bufsize Buffer size + * uap->macsize MAC info size + * uap->buf Buffer where information will be returned + * uap->mac MAC info + * uap->flags File system flags + * + * + * Returns: 0 Success + * !0 Not success + * + */ +int +__mac_getfsstat(__unused proc_t p, struct __mac_getfsstat_args *uap, int *retval) { user_addr_t sfsp; - int count, maxcount; + user_addr_t *mp; + size_t count, maxcount, bufsize, macsize; struct getfsstat_struct fst; + bufsize = (size_t) uap->bufsize; + macsize = (size_t) uap->macsize; + if (IS_64BIT_PROCESS(p)) { - maxcount = uap->bufsize / sizeof(struct user_statfs); + maxcount = bufsize / sizeof(struct user64_statfs); } else { - maxcount = uap->bufsize / sizeof(struct statfs); + maxcount = bufsize / sizeof(struct user32_statfs); } sfsp = uap->buf; count = 0; + mp = NULL; + +#if CONFIG_MACF + if (uap->mac != USER_ADDR_NULL) { + u_int32_t *mp0; + int error; + unsigned int i; + + count = (macsize / (IS_64BIT_PROCESS(p) ? 8 : 4)); + if (count != maxcount) + return (EINVAL); + + /* Copy in the array */ + MALLOC(mp0, u_int32_t *, macsize, M_MACTEMP, M_WAITOK); + if (mp0 == NULL) { + return (ENOMEM); + } + + error = copyin(uap->mac, mp0, macsize); + if (error) { + FREE(mp0, M_MACTEMP); + return (error); + } + + /* Normalize to an array of user_addr_t */ + MALLOC(mp, user_addr_t *, count * sizeof(user_addr_t), M_MACTEMP, M_WAITOK); + if (mp == NULL) { + FREE(mp0, M_MACTEMP); + return (ENOMEM); + } + + for (i = 0; i < count; i++) { + if (IS_64BIT_PROCESS(p)) + mp[i] = ((user_addr_t *)mp0)[i]; + else + mp[i] = (user_addr_t)mp0[i]; + } + FREE(mp0, M_MACTEMP); + } +#endif + + fst.sfsp = sfsp; + fst.mp = mp; fst.flags = uap->flags; fst.count = 0; fst.error = 0; @@ -1263,6 +2247,9 @@ getfsstat(__unused proc_t p, struct getfsstat_args *uap, int *retval) vfs_iterate(0, getfsstat_callback, &fst); + if (mp) + FREE(mp, M_MACTEMP); + if (fst.error ) { KAUTH_DEBUG("ERROR - %s gets %d", p->p_comm, fst.error); return(fst.error); @@ -1275,31 +2262,112 @@ getfsstat(__unused proc_t p, struct getfsstat_args *uap, int *retval) return (0); } -#if COMPAT_GETFSSTAT -ogetfsstat(p, uap, retval) - struct proc *p; - register struct getfsstat_args *uap; - register_t *retval; +static int +getfsstat64_callback(mount_t mp, void * arg) { - return (ENOTSUP); + struct getfsstat_struct *fstp = (struct getfsstat_struct *)arg; + struct vfsstatfs *sp; + int error; + + if (fstp->sfsp && fstp->count < fstp->maxcount) { + sp = &mp->mnt_vfsstat; + /* + * If MNT_NOWAIT is specified, do not refresh the fsstat + * cache. MNT_WAIT overrides MNT_NOWAIT. + * + * We treat MNT_DWAIT as MNT_WAIT for all instances of + * getfsstat, since the constants are out of the same + * namespace. + */ + if (((fstp->flags & MNT_NOWAIT) == 0 || + (fstp->flags & (MNT_WAIT | MNT_DWAIT))) && + (error = vfs_update_vfsstat(mp, vfs_context_current(), VFS_USER_EVENT))) { + KAUTH_DEBUG("vfs_update_vfsstat returned %d", error); + return(VFS_RETURNED); + } + + error = statfs64_common(mp, sp, fstp->sfsp); + if (error) { + fstp->error = error; + return(VFS_RETURNED_DONE); + } + fstp->sfsp += sizeof(struct statfs64); + } + fstp->count++; + return(VFS_RETURNED); +} + +/* + * Get statistics on all file systems in 64 bit mode. + */ +int +getfsstat64(__unused proc_t p, struct getfsstat64_args *uap, int *retval) +{ + user_addr_t sfsp; + int count, maxcount; + struct getfsstat_struct fst; + + maxcount = uap->bufsize / sizeof(struct statfs64); + + sfsp = uap->buf; + count = 0; + + fst.sfsp = sfsp; + fst.flags = uap->flags; + fst.count = 0; + fst.error = 0; + fst.maxcount = maxcount; + + vfs_iterate(0, getfsstat64_callback, &fst); + + if (fst.error ) { + KAUTH_DEBUG("ERROR - %s gets %d", p->p_comm, fst.error); + return(fst.error); + } + + if (fst.sfsp && fst.count > fst.maxcount) + *retval = fst.maxcount; + else + *retval = fst.count; + + return (0); } -#endif /* * Change current working directory to a given file descriptor. */ /* ARGSUSED */ -int -fchdir(struct proc *p, struct fchdir_args *uap, __unused register_t *retval) +static int +common_fchdir(proc_t p, struct fchdir_args *uap, int per_thread) { - register struct filedesc *fdp = p->p_fd; - struct vnode *vp, *tdp, *tvp; + struct filedesc *fdp = p->p_fd; + vnode_t vp; + vnode_t tdp; + vnode_t tvp; struct mount *mp; int error; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + AUDIT_ARG(fd, uap->fd); + if (per_thread && uap->fd == -1) { + /* + * Switching back from per-thread to per process CWD; verify we + * in fact have one before proceeding. The only success case + * for this code path is to return 0 preemptively after zapping + * the thread structure contents. + */ + thread_t th = vfs_context_thread(ctx); + if (th) { + uthread_t uth = get_bsdthread_info(th); + tvp = uth->uu_cdir; + uth->uu_cdir = NULLVP; + if (tvp != NULLVP) { + vnode_rele(tvp); + return (0); + } + } + return (EBADF); + } if ( (error = file_vnode(uap->fd, &vp)) ) return(error); @@ -1310,16 +2378,26 @@ fchdir(struct proc *p, struct fchdir_args *uap, __unused register_t *retval) AUDIT_ARG(vnpath, vp, ARG_VNODE1); - if (vp->v_type != VDIR) + if (vp->v_type != VDIR) { error = ENOTDIR; - else - error = vnode_authorize(vp, NULL, KAUTH_VNODE_SEARCH, &context); + goto out; + } + +#if CONFIG_MACF + error = mac_vnode_check_chdir(ctx, vp); + if (error) + goto out; +#endif + error = vnode_authorize(vp, NULL, KAUTH_VNODE_SEARCH, ctx); + if (error) + goto out; + while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, LK_NOWAIT)) { error = EACCES; goto out; } - error = VFS_ROOT(mp, &tdp, &context); + error = VFS_ROOT(mp, &tdp, ctx); vfs_unbusy(mp); if (error) break; @@ -1332,10 +2410,23 @@ fchdir(struct proc *p, struct fchdir_args *uap, __unused register_t *retval) goto out; vnode_put(vp); - proc_fdlock(p); - tvp = fdp->fd_cdir; - fdp->fd_cdir = vp; - proc_fdunlock(p); + if (per_thread) { + thread_t th = vfs_context_thread(ctx); + if (th) { + uthread_t uth = get_bsdthread_info(th); + tvp = uth->uu_cdir; + uth->uu_cdir = vp; + OSBitOrAtomic(P_THCWD, &p->p_flag); + } else { + vnode_rele(vp); + return (ENOENT); + } + } else { + proc_fdlock(p); + tvp = fdp->fd_cdir; + fdp->fd_cdir = vp; + proc_fdunlock(p); + } if (tvp) vnode_rele(tvp); @@ -1349,25 +2440,39 @@ out: return(error); } +int +fchdir(proc_t p, struct fchdir_args *uap, __unused int32_t *retval) +{ + return common_fchdir(p, uap, 0); +} + +int +__pthread_fchdir(proc_t p, struct __pthread_fchdir_args *uap, __unused int32_t *retval) +{ + return common_fchdir(p, (void *)uap, 1); +} + /* - * Change current working directory (``.''). + * Change current working directory ("."). + * + * Returns: 0 Success + * change_dir:ENOTDIR + * change_dir:??? + * vnode_ref:ENOENT No such file or directory */ /* ARGSUSED */ -int -chdir(struct proc *p, struct chdir_args *uap, __unused register_t *retval) +static int +common_chdir(proc_t p, struct chdir_args *uap, int per_thread) { - register struct filedesc *fdp = p->p_fd; + struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; - struct vnode *tvp; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vnode_t tvp; + vfs_context_t ctx = vfs_context_current(); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); - error = change_dir(&nd, &context); + UIO_USERSPACE, uap->path, ctx); + error = change_dir(&nd, ctx); if (error) return (error); if ( (error = vnode_ref(nd.ni_vp)) ) { @@ -1379,10 +2484,23 @@ chdir(struct proc *p, struct chdir_args *uap, __unused register_t *retval) */ vnode_put(nd.ni_vp); - proc_fdlock(p); - tvp = fdp->fd_cdir; - fdp->fd_cdir = nd.ni_vp; - proc_fdunlock(p); + if (per_thread) { + thread_t th = vfs_context_thread(ctx); + if (th) { + uthread_t uth = get_bsdthread_info(th); + tvp = uth->uu_cdir; + uth->uu_cdir = nd.ni_vp; + OSBitOrAtomic(P_THCWD, &p->p_flag); + } else { + vnode_rele(nd.ni_vp); + return (ENOENT); + } + } else { + proc_fdlock(p); + tvp = fdp->fd_cdir; + fdp->fd_cdir = nd.ni_vp; + proc_fdunlock(p); + } if (tvp) vnode_rele(tvp); @@ -1390,43 +2508,85 @@ chdir(struct proc *p, struct chdir_args *uap, __unused register_t *retval) return (0); } + +/* + * chdir + * + * Change current working directory (".") for the entire process + * + * Parameters: p Process requesting the call + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect parameters: uap->path Directory path + * + * Returns: 0 Success + * common_chdir: ENOTDIR + * common_chdir: ENOENT No such file or directory + * common_chdir: ??? + * + */ +int +chdir(proc_t p, struct chdir_args *uap, __unused int32_t *retval) +{ + return common_chdir(p, (void *)uap, 0); +} + +/* + * __pthread_chdir + * + * Change current working directory (".") for a single thread + * + * Parameters: p Process requesting the call + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect parameters: uap->path Directory path + * + * Returns: 0 Success + * common_chdir: ENOTDIR + * common_chdir: ENOENT No such file or directory + * common_chdir: ??? + * + */ +int +__pthread_chdir(proc_t p, struct __pthread_chdir_args *uap, __unused int32_t *retval) +{ + return common_chdir(p, (void *)uap, 1); +} + + /* * Change notion of root (``/'') directory. */ /* ARGSUSED */ int -chroot(struct proc *p, struct chroot_args *uap, __unused register_t *retval) +chroot(proc_t p, struct chroot_args *uap, __unused int32_t *retval) { - register struct filedesc *fdp = p->p_fd; + struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; - boolean_t shared_regions_active; - struct vnode *tvp; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vnode_t tvp; + vfs_context_t ctx = vfs_context_current(); if ((error = suser(kauth_cred_get(), &p->p_acflag))) return (error); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); - error = change_dir(&nd, &context); + UIO_USERSPACE, uap->path, ctx); + error = change_dir(&nd, ctx); if (error) return (error); - if(p->p_flag & P_NOSHLIB) { - shared_regions_active = FALSE; - } else { - shared_regions_active = TRUE; - } - if ((error = clone_system_shared_regions(shared_regions_active, - TRUE, /* chain_regions */ - (int)nd.ni_vp))) { +#if CONFIG_MACF + error = mac_vnode_check_chroot(ctx, nd.ni_vp, + &nd.ni_cnd); + if (error) { vnode_put(nd.ni_vp); return (error); } +#endif + if ( (error = vnode_ref(nd.ni_vp)) ) { vnode_put(nd.ni_vp); return (error); @@ -1447,23 +2607,41 @@ chroot(struct proc *p, struct chroot_args *uap, __unused register_t *retval) /* * Common routine for chroot and chdir. + * + * Returns: 0 Success + * ENOTDIR Not a directory + * namei:??? [anything namei can return] + * vnode_authorize:??? [anything vnode_authorize can return] */ static int change_dir(struct nameidata *ndp, vfs_context_t ctx) { - struct vnode *vp; + vnode_t vp; int error; if ((error = namei(ndp))) return (error); nameidone(ndp); vp = ndp->ni_vp; - if (vp->v_type != VDIR) - error = ENOTDIR; - else - error = vnode_authorize(vp, NULL, KAUTH_VNODE_SEARCH, ctx); - if (error) + + if (vp->v_type != VDIR) { + vnode_put(vp); + return (ENOTDIR); + } + +#if CONFIG_MACF + error = mac_vnode_check_chdir(ctx, vp); + if (error) { + vnode_put(vp); + return (error); + } +#endif + + error = vnode_authorize(vp, NULL, KAUTH_VNODE_SEARCH, ctx); + if (error) { vnode_put(vp); + return (error); + } return (error); } @@ -1471,21 +2649,36 @@ change_dir(struct nameidata *ndp, vfs_context_t ctx) /* * Check permissions, allocate an open file structure, * and call the device open routine if any. + * + * Returns: 0 Success + * EINVAL + * EINTR + * falloc:ENFILE + * falloc:EMFILE + * falloc:ENOMEM + * vn_open_auth:??? + * dupfdopen:??? + * VNOP_ADVLOCK:??? + * vnode_setsize:??? + * + * XXX Need to implement uid, gid */ - -#warning XXX implement uid, gid -static int -open1(vfs_context_t ctx, user_addr_t upath, int uflags, struct vnode_attr *vap, register_t *retval) +int +open1(vfs_context_t ctx, struct nameidata *ndp, int uflags, struct vnode_attr *vap, int32_t *retval) { - struct proc *p = vfs_context_proc(ctx); - register struct filedesc *fdp = p->p_fd; - register struct fileproc *fp; - register struct vnode *vp; + proc_t p = vfs_context_proc(ctx); + uthread_t uu = get_bsdthread_info(vfs_context_thread(ctx)); + struct filedesc *fdp = p->p_fd; + struct fileproc *fp; + vnode_t vp; int flags, oflags; struct fileproc *nfp; int type, indx, error; struct flock lf; - struct nameidata nd; + int no_controlling_tty = 0; + int deny_controlling_tty = 0; + struct session *sessp = SESSION_NULL; + struct vfs_context context = *vfs_context_current(); /* local copy */ oflags = uflags; @@ -1496,19 +2689,43 @@ open1(vfs_context_t ctx, user_addr_t upath, int uflags, struct vnode_attr *vap, AUDIT_ARG(fflags, oflags); AUDIT_ARG(mode, vap->va_mode); - if ( (error = falloc(p, &nfp, &indx)) ) { + if ( (error = falloc(p, &nfp, &indx, ctx)) ) { return (error); } fp = nfp; - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, upath, ctx); - p->p_dupfd = -indx - 1; /* XXX check for fdopen */ + uu->uu_dupfd = -indx - 1; + + if (!(p->p_flag & P_CONTROLT)) { + sessp = proc_session(p); + no_controlling_tty = 1; + /* + * If conditions would warrant getting a controlling tty if + * the device being opened is a tty (see ttyopen in tty.c), + * but the open flags deny it, set a flag in the session to + * prevent it. + */ + if (SESS_LEADER(p, sessp) && + sessp->s_ttyvp == NULL && + (flags & O_NOCTTY)) { + session_lock(sessp); + sessp->s_flags |= S_NOCTTY; + session_unlock(sessp); + deny_controlling_tty = 1; + } + } - if ((error = vn_open_auth(&nd, &flags, vap))) { - if ((error == ENODEV || error == ENXIO) && (p->p_dupfd >= 0)) { /* XXX from fdopen */ - if ((error = dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { - fp_drop(p, indx, 0, 0); + if ((error = vn_open_auth(ndp, &flags, vap))) { + if ((error == ENODEV || error == ENXIO) && (uu->uu_dupfd >= 0)){ /* XXX from fdopen */ + if ((error = dupfdopen(fdp, indx, uu->uu_dupfd, flags, error)) == 0) { + fp_drop(p, indx, NULL, 0); *retval = indx; + if (deny_controlling_tty) { + session_lock(sessp); + sessp->s_flags &= ~S_NOCTTY; + session_unlock(sessp); + } + if (sessp != SESSION_NULL) + session_rele(sessp); return (0); } } @@ -1516,10 +2733,17 @@ open1(vfs_context_t ctx, user_addr_t upath, int uflags, struct vnode_attr *vap, error = EINTR; fp_free(p, indx, fp); + if (deny_controlling_tty) { + session_lock(sessp); + sessp->s_flags &= ~S_NOCTTY; + session_unlock(sessp); + } + if (sessp != SESSION_NULL) + session_rele(sessp); return (error); } - p->p_dupfd = 0; - vp = nd.ni_vp; + uu->uu_dupfd = 0; + vp = ndp->ni_vp; fp->f_fglob->fg_flag = flags & (FMASK | O_EVTONLY); fp->f_fglob->fg_type = DTYPE_VNODE; @@ -1537,6 +2761,12 @@ open1(vfs_context_t ctx, user_addr_t upath, int uflags, struct vnode_attr *vap, type = F_FLOCK; if ((flags & FNONBLOCK) == 0) type |= F_WAIT; +#if CONFIG_MACF + error = mac_file_check_lock(vfs_context_ucred(ctx), fp->f_fglob, + F_SETLK, &lf); + if (error) + goto bad; +#endif if ((error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob, F_SETLK, &lf, type, ctx))) goto bad; fp->f_fglob->fg_flag |= FHASLOCK; @@ -1546,6 +2776,35 @@ open1(vfs_context_t ctx, user_addr_t upath, int uflags, struct vnode_attr *vap, if ((flags & O_TRUNC) && ((error = vnode_setsize(vp, (off_t)0, 0, ctx)) != 0)) goto bad; + /* + * If the open flags denied the acquisition of a controlling tty, + * clear the flag in the session structure that prevented the lower + * level code from assigning one. + */ + if (deny_controlling_tty) { + session_lock(sessp); + sessp->s_flags &= ~S_NOCTTY; + session_unlock(sessp); + } + + /* + * If a controlling tty was set by the tty line discipline, then we + * want to set the vp of the tty into the session structure. We have + * a race here because we can't get to the vp for the tp in ttyopen, + * because it's not passed as a parameter in the open path. + */ + if (no_controlling_tty && (p->p_flag & P_CONTROLT)) { + vnode_t ttyvp; + vnode_ref(vp); + session_lock(sessp); + ttyvp = sessp->s_ttyvp; + sessp->s_ttyvp = vp; + sessp->s_ttyvid = vnode_vid(vp); + session_unlock(sessp); + if (ttyvp != NULLVP) + vnode_rele(ttyvp); + } + vnode_put(vp); proc_fdlock(p); @@ -1555,9 +2814,22 @@ open1(vfs_context_t ctx, user_addr_t upath, int uflags, struct vnode_attr *vap, *retval = indx; + if (sessp != SESSION_NULL) + session_rele(sessp); return (0); bad: - vn_close(vp, fp->f_fglob->fg_flag, fp->f_fglob->fg_cred, p); + if (deny_controlling_tty) { + session_lock(sessp); + sessp->s_flags &= ~S_NOCTTY; + session_unlock(sessp); + } + if (sessp != SESSION_NULL) + session_rele(sessp); + + /* Modify local copy (to not damage thread copy) */ + context.vc_ucred = fp->f_fglob->fg_cred; + + vn_close(vp, fp->f_fglob->fg_flag, &context); vnode_put(vp); fp_free(p, indx, fp); @@ -1565,24 +2837,46 @@ bad: } +/* + * open_extended: open a file given a path name; with extended argument list (including extended security (ACL)). + * + * Parameters: p Process requesting the open + * uap User argument descriptor (see below) + * retval Pointer to an area to receive the + * return calue from the system call + * + * Indirect: uap->path Path to open (same as 'open') + * uap->flags Flags to open (same as 'open' + * uap->uid UID to set, if creating + * uap->gid GID to set, if creating + * uap->mode File mode, if creating (same as 'open') + * uap->xsecurity ACL to set, if creating + * + * Returns: 0 Success + * !0 errno value + * + * Notes: The kauth_filesec_t in 'va', if any, is in host byte order. + * + * XXX: We should enummerate the possible errno values here, and where + * in the code they originated. + */ int -open_extended(struct proc *p, struct open_extended_args *uap, register_t *retval) +open_extended(proc_t p, struct open_extended_args *uap, int32_t *retval) { - struct vfs_context context; - register struct filedesc *fdp = p->p_fd; + struct filedesc *fdp = p->p_fd; int ciferror; kauth_filesec_t xsecdst; struct vnode_attr va; + struct nameidata nd; int cmode; + AUDIT_ARG(owner, uap->uid, uap->gid); + xsecdst = NULL; if ((uap->xsecurity != USER_ADDR_NULL) && ((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0)) return ciferror; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - VATTR_INIT(&va); cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT; VATTR_SET(&va, va_mode, cmode); @@ -1593,7 +2887,9 @@ open_extended(struct proc *p, struct open_extended_args *uap, register_t *retval if (xsecdst != NULL) VATTR_SET(&va, va_acl, &xsecdst->fsec_acl); - ciferror = open1(&context, uap->path, uap->flags, &va, retval); + NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, UIO_USERSPACE, uap->path, vfs_context_current()); + + ciferror = open1(vfs_context_current(), &nd, uap->flags, &va, retval); if (xsecdst != NULL) kauth_filesec_free(xsecdst); @@ -1601,22 +2897,28 @@ open_extended(struct proc *p, struct open_extended_args *uap, register_t *retval } int -open(struct proc *p, struct open_args *uap, register_t *retval) +open(proc_t p, struct open_args *uap, int32_t *retval) { - struct vfs_context context; - register struct filedesc *fdp = p->p_fd; + __pthread_testcancel(1); + return(open_nocancel(p, (struct open_nocancel_args *)uap, retval)); +} + +int +open_nocancel(proc_t p, struct open_nocancel_args *uap, int32_t *retval) +{ + struct filedesc *fdp = p->p_fd; struct vnode_attr va; + struct nameidata nd; int cmode; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - VATTR_INIT(&va); /* Mask off all but regular access permissions */ cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT; VATTR_SET(&va, va_mode, cmode & ACCESSPERMS); - return(open1(&context, uap->path, uap->flags, &va, retval)); + NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, UIO_USERSPACE, uap->path, vfs_context_current()); + + return(open1(vfs_context_current(), &nd, uap->flags, &va, retval)); } @@ -1626,33 +2928,30 @@ open(struct proc *p, struct open_args *uap, register_t *retval) static int mkfifo1(vfs_context_t ctx, user_addr_t upath, struct vnode_attr *vap); int -mknod(struct proc *p, register struct mknod_args *uap, __unused register_t *retval) +mknod(proc_t p, struct mknod_args *uap, __unused int32_t *retval) { struct vnode_attr va; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error; int whiteout = 0; struct nameidata nd; vnode_t vp, dvp; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - VATTR_INIT(&va); VATTR_SET(&va, va_mode, (uap->mode & ALLPERMS) & ~p->p_fd->fd_cmask); VATTR_SET(&va, va_rdev, uap->dev); /* If it's a mknod() of a FIFO, call mkfifo1() instead */ if ((uap->mode & S_IFMT) == S_IFIFO) - return(mkfifo1(&context, uap->path, &va)); + return(mkfifo1(ctx, uap->path, &va)); AUDIT_ARG(mode, uap->mode); - AUDIT_ARG(dev, uap->dev); + AUDIT_ARG(value32, uap->dev); - if ((error = suser(context.vc_ucred, &p->p_acflag))) + if ((error = suser(vfs_context_ucred(ctx), &p->p_acflag))) return (error); NDINIT(&nd, CREATE, LOCKPARENT | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); @@ -1664,9 +2963,6 @@ mknod(struct proc *p, register struct mknod_args *uap, __unused register_t *retv goto out; } - if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context)) != 0) - goto out; - switch (uap->mode & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ VATTR_SET(&va, va_type, VBAD); @@ -1684,10 +2980,23 @@ mknod(struct proc *p, register struct mknod_args *uap, __unused register_t *retv error = EINVAL; goto out; } + +#if CONFIG_MACF + if (!whiteout) { + error = mac_vnode_check_create(ctx, + nd.ni_dvp, &nd.ni_cnd, &va); + if (error) + goto out; + } +#endif + + if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx)) != 0) + goto out; + if (whiteout) { - error = VNOP_WHITEOUT(dvp, &nd.ni_cnd, CREATE, &context); + error = VNOP_WHITEOUT(dvp, &nd.ni_cnd, CREATE, ctx); } else { - error = vn_create(dvp, &vp, &nd.ni_cnd, &va, 0, &context); + error = vn_create(dvp, &vp, &nd.ni_cnd, &va, 0, ctx); } if (error) goto out; @@ -1704,9 +3013,11 @@ mknod(struct proc *p, register struct mknod_args *uap, __unused register_t *retv if (update_flags) vnode_update_identity(vp, dvp, nd.ni_cnd.cn_nameptr, nd.ni_cnd.cn_namelen, nd.ni_cnd.cn_hash, update_flags); - add_fsevent(FSE_CREATE_FILE, &context, +#if CONFIG_FSE + add_fsevent(FSE_CREATE_FILE, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE); +#endif } out: @@ -1725,6 +3036,12 @@ out: /* * Create a named pipe. + * + * Returns: 0 Success + * EEXIST + * namei:??? + * vnode_authorize:??? + * vn_create:??? */ static int mkfifo1(vfs_context_t ctx, user_addr_t upath, struct vnode_attr *vap) @@ -1746,10 +3063,19 @@ mkfifo1(vfs_context_t ctx, user_addr_t upath, struct vnode_attr *vap) error = EEXIST; goto out; } + VATTR_SET(vap, va_type, VFIFO); + +#if CONFIG_MACF + error = mac_vnode_check_create(ctx, nd.ni_dvp, + &nd.ni_cnd, vap); + if (error) + goto out; +#endif + + if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx)) != 0) goto out; - VATTR_SET(vap, va_type, VFIFO); error = vn_create(dvp, &vp, &nd.ni_cnd, vap, 0, ctx); out: @@ -1766,23 +3092,43 @@ out: return error; } + +/* + * mkfifo_extended: Create a named pipe; with extended argument list (including extended security (ACL)). + * + * Parameters: p Process requesting the open + * uap User argument descriptor (see below) + * retval (Ignored) + * + * Indirect: uap->path Path to fifo (same as 'mkfifo') + * uap->uid UID to set + * uap->gid GID to set + * uap->mode File mode to set (same as 'mkfifo') + * uap->xsecurity ACL to set, if creating + * + * Returns: 0 Success + * !0 errno value + * + * Notes: The kauth_filesec_t in 'va', if any, is in host byte order. + * + * XXX: We should enummerate the possible errno values here, and where + * in the code they originated. + */ int -mkfifo_extended(struct proc *p, struct mkfifo_extended_args *uap, __unused register_t *retval) +mkfifo_extended(proc_t p, struct mkfifo_extended_args *uap, __unused int32_t *retval) { int ciferror; kauth_filesec_t xsecdst; - struct vfs_context context; struct vnode_attr va; + AUDIT_ARG(owner, uap->uid, uap->gid); + xsecdst = KAUTH_FILESEC_NONE; if (uap->xsecurity != USER_ADDR_NULL) { if ((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0) return ciferror; } - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - VATTR_INIT(&va); VATTR_SET(&va, va_mode, (uap->mode & ALLPERMS) & ~p->p_fd->fd_cmask); if (uap->uid != KAUTH_UID_NONE) @@ -1792,7 +3138,7 @@ mkfifo_extended(struct proc *p, struct mkfifo_extended_args *uap, __unused regis if (xsecdst != KAUTH_FILESEC_NONE) VATTR_SET(&va, va_acl, &xsecdst->fsec_acl); - ciferror = mkfifo1(&context, uap->path, &va); + ciferror = mkfifo1(vfs_context_current(), uap->path, &va); if (xsecdst != KAUTH_FILESEC_NONE) kauth_filesec_free(xsecdst); @@ -1801,41 +3147,124 @@ mkfifo_extended(struct proc *p, struct mkfifo_extended_args *uap, __unused regis /* ARGSUSED */ int -mkfifo(struct proc *p, register struct mkfifo_args *uap, __unused register_t *retval) +mkfifo(proc_t p, struct mkfifo_args *uap, __unused int32_t *retval) { - struct vfs_context context; struct vnode_attr va; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - VATTR_INIT(&va); VATTR_SET(&va, va_mode, (uap->mode & ALLPERMS) & ~p->p_fd->fd_cmask); - return(mkfifo1(&context, uap->path, &va)); + return(mkfifo1(vfs_context_current(), uap->path, &va)); +} + + +static char * +my_strrchr(char *p, int ch) +{ + char *save; + + for (save = NULL;; ++p) { + if (*p == ch) + save = p; + if (!*p) + return(save); + } + /* NOTREACHED */ +} + +extern int safe_getpath(struct vnode *dvp, char *leafname, char *path, int _len, int *truncated_path); + +int +safe_getpath(struct vnode *dvp, char *leafname, char *path, int _len, int *truncated_path) +{ + int ret, len = _len; + + *truncated_path = 0; + ret = vn_getpath(dvp, path, &len); + if (ret == 0 && len < (MAXPATHLEN - 1)) { + if (leafname) { + path[len-1] = '/'; + len += strlcpy(&path[len], leafname, MAXPATHLEN-len) + 1; + if (len > MAXPATHLEN) { + char *ptr; + + // the string got truncated! + *truncated_path = 1; + ptr = my_strrchr(path, '/'); + if (ptr) { + *ptr = '\0'; // chop off the string at the last directory component + } + len = strlen(path) + 1; + } + } + } else if (ret == 0) { + *truncated_path = 1; + } else if (ret != 0) { + struct vnode *mydvp=dvp; + + if (ret != ENOSPC) { + printf("safe_getpath: failed to get the path for vp %p (%s) : err %d\n", + dvp, dvp->v_name ? dvp->v_name : "no-name", ret); + } + *truncated_path = 1; + + do { + if (mydvp->v_parent != NULL) { + mydvp = mydvp->v_parent; + } else if (mydvp->v_mount) { + strlcpy(path, mydvp->v_mount->mnt_vfsstat.f_mntonname, _len); + break; + } else { + // no parent and no mount point? only thing is to punt and say "/" changed + strlcpy(path, "/", _len); + len = 2; + mydvp = NULL; + } + + if (mydvp == NULL) { + break; + } + + len = _len; + ret = vn_getpath(mydvp, path, &len); + } while (ret == ENOSPC); + } + + return len; } + /* * Make a hard file link. + * + * Returns: 0 Success + * EPERM + * EEXIST + * EXDEV + * namei:??? + * vnode_authorize:??? + * VNOP_LINK:??? */ /* ARGSUSED */ int -link(struct proc *p, register struct link_args *uap, __unused register_t *retval) +link(__unused proc_t p, struct link_args *uap, __unused int32_t *retval) { vnode_t vp, dvp, lvp; struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error; +#if CONFIG_FSE fse_info finfo; +#endif int need_event, has_listeners; + char *target_path = NULL; + int truncated=0; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); vp = dvp = lvp = NULLVP; /* look up the object we are linking to */ NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); @@ -1843,25 +3272,49 @@ link(struct proc *p, register struct link_args *uap, __unused register_t *retval nameidone(&nd); - /* we're not allowed to link to directories */ + /* + * Normally, linking to directories is not supported. + * However, some file systems may have limited support. + */ if (vp->v_type == VDIR) { - error = EPERM; /* POSIX */ - goto out; + if (!(vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSDIRLINKS)) { + error = EPERM; /* POSIX */ + goto out; + } + /* Linking to a directory requires ownership. */ + if (!kauth_cred_issuser(vfs_context_ucred(ctx))) { + struct vnode_attr dva; + + VATTR_INIT(&dva); + VATTR_WANTED(&dva, va_uid); + if (vnode_getattr(vp, &dva, ctx) != 0 || + !VATTR_IS_SUPPORTED(&dva, va_uid) || + (dva.va_uid != kauth_cred_getuid(vfs_context_ucred(ctx)))) { + error = EACCES; + goto out; + } + } } - /* or to anything that kauth doesn't want us to (eg. immutable items) */ - if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, &context)) != 0) - goto out; - /* lookup the target node */ nd.ni_cnd.cn_nameiop = CREATE; - nd.ni_cnd.cn_flags = LOCKPARENT | AUDITVNPATH2; + nd.ni_cnd.cn_flags = LOCKPARENT | AUDITVNPATH2 | CN_NBMOUNTLOOK; nd.ni_dirp = uap->link; error = namei(&nd); if (error != 0) goto out; dvp = nd.ni_dvp; lvp = nd.ni_vp; + +#if CONFIG_MACF + if ((error = mac_vnode_check_link(ctx, dvp, vp, &nd.ni_cnd)) != 0) + goto out2; +#endif + + /* or to anything that kauth doesn't want us to (eg. immutable items) */ + if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx)) != 0) + goto out2; + /* target node must not exist */ if (lvp != NULLVP) { error = EEXIST; @@ -1874,55 +3327,76 @@ link(struct proc *p, register struct link_args *uap, __unused register_t *retval } /* authorize creation of the target note */ - if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context)) != 0) + if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx)) != 0) goto out2; /* and finally make the link */ - error = VNOP_LINK(vp, dvp, &nd.ni_cnd, &context); + error = VNOP_LINK(vp, dvp, &nd.ni_cnd, ctx); if (error) goto out2; +#if CONFIG_FSE need_event = need_fsevent(FSE_CREATE_FILE, dvp); +#else + need_event = 0; +#endif has_listeners = kauth_authorize_fileop_has_listeners(); if (need_event || has_listeners) { - char *target_path = NULL; char *link_to_path = NULL; int len, link_name_len; /* build the path to the new link file */ - target_path = get_pathbuff(); - len = MAXPATHLEN; - vn_getpath(dvp, target_path, &len); - target_path[len-1] = '/'; - strcpy(&target_path[len], nd.ni_cnd.cn_nameptr); - len += nd.ni_cnd.cn_namelen; + GET_PATH(target_path); + if (target_path == NULL) { + error = ENOMEM; + goto out2; + } + + len = safe_getpath(dvp, nd.ni_cnd.cn_nameptr, target_path, MAXPATHLEN, &truncated); if (has_listeners) { /* build the path to file we are linking to */ - link_to_path = get_pathbuff(); + GET_PATH(link_to_path); + if (link_to_path == NULL) { + error = ENOMEM; + goto out2; + } + link_name_len = MAXPATHLEN; vn_getpath(vp, link_to_path, &link_name_len); - /* call out to allow 3rd party notification of rename. + /* + * Call out to allow 3rd party notification of rename. * Ignore result of kauth_authorize_fileop call. */ - kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_LINK, + kauth_authorize_fileop(vfs_context_ucred(ctx), KAUTH_FILEOP_LINK, (uintptr_t)link_to_path, (uintptr_t)target_path); - if (link_to_path != NULL) - release_pathbuff(link_to_path); + if (link_to_path != NULL) { + RELEASE_PATH(link_to_path); + } } +#if CONFIG_FSE if (need_event) { /* construct fsevent */ - if (get_fse_info(vp, &finfo, &context) == 0) { + if (get_fse_info(vp, &finfo, ctx) == 0) { + if (truncated) { + finfo.mode |= FSE_TRUNCATED_PATH; + } + // build the path to the destination of the link - add_fsevent(FSE_CREATE_FILE, &context, + add_fsevent(FSE_CREATE_FILE, ctx, FSE_ARG_STRING, len, target_path, FSE_ARG_FINFO, &finfo, FSE_ARG_DONE); } + if (vp->v_parent) { + add_fsevent(FSE_STAT_CHANGED, ctx, + FSE_ARG_VNODE, vp->v_parent, + FSE_ARG_DONE); + } } - release_pathbuff(target_path); +#endif } out2: /* @@ -1930,6 +3404,9 @@ out2: * since it may need to release the fs_nodelock on the dvp */ nameidone(&nd); + if (target_path != NULL) { + RELEASE_PATH(target_path); + } out: if (lvp) vnode_put(lvp); @@ -1946,19 +3423,16 @@ out: */ /* ARGSUSED */ int -symlink(struct proc *p, register struct symlink_args *uap, __unused register_t *retval) +symlink(proc_t p, struct symlink_args *uap, __unused int32_t *retval) { struct vnode_attr va; char *path; int error; struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); vnode_t vp, dvp; size_t dummy=0; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - MALLOC_ZONE(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); error = copyinstr(uap->path, path, MAXPATHLEN, &dummy); if (error) @@ -1966,80 +3440,94 @@ symlink(struct proc *p, register struct symlink_args *uap, __unused register_t * AUDIT_ARG(text, path); /* This is the link string */ NDINIT(&nd, CREATE, LOCKPARENT | AUDITVNPATH1, - UIO_USERSPACE, uap->link, &context); + UIO_USERSPACE, uap->link, ctx); error = namei(&nd); if (error) goto out; dvp = nd.ni_dvp; vp = nd.ni_vp; - if (vp == NULL) { - VATTR_INIT(&va); - VATTR_SET(&va, va_type, VLNK); - VATTR_SET(&va, va_mode, ACCESSPERMS & ~p->p_fd->fd_cmask); - - /* authorize */ - error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context); - /* get default ownership, etc. */ - if (error == 0) - error = vnode_authattr_new(dvp, &va, 0, &context); - if (error == 0) - error = VNOP_SYMLINK(dvp, &vp, &nd.ni_cnd, &va, path, &context); + VATTR_INIT(&va); + VATTR_SET(&va, va_type, VLNK); + VATTR_SET(&va, va_mode, ACCESSPERMS & ~p->p_fd->fd_cmask); +#if CONFIG_MACF + error = mac_vnode_check_create(ctx, + dvp, &nd.ni_cnd, &va); +#endif + if (error != 0) { + goto skipit; + } - /* do fallback attribute handling */ - if (error == 0) - error = vnode_setattr_fallback(vp, &va, &context); + if (vp != NULL) { + error = EEXIST; + goto skipit; + } + + /* authorize */ + if (error == 0) + error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx); + /* get default ownership, etc. */ + if (error == 0) + error = vnode_authattr_new(dvp, &va, 0, ctx); + if (error == 0) + error = VNOP_SYMLINK(dvp, &vp, &nd.ni_cnd, &va, path, ctx); + + /* do fallback attribute handling */ + if (error == 0) + error = vnode_setattr_fallback(vp, &va, ctx); - if (error == 0) { - int update_flags = 0; + if (error == 0) { + int update_flags = 0; - if (vp == NULL) { - nd.ni_cnd.cn_nameiop = LOOKUP; - nd.ni_cnd.cn_flags = 0; - error = namei(&nd); - vp = nd.ni_vp; + if (vp == NULL) { + nd.ni_cnd.cn_nameiop = LOOKUP; + nd.ni_cnd.cn_flags = 0; + error = namei(&nd); + vp = nd.ni_vp; - if (vp == NULL) - goto skipit; - } + if (vp == NULL) + goto skipit; + } #if 0 /* XXX - kauth_todo - is KAUTH_FILEOP_SYMLINK needed? */ - /* call out to allow 3rd party notification of rename. - * Ignore result of kauth_authorize_fileop call. - */ - if (kauth_authorize_fileop_has_listeners() && - namei(&nd) == 0) { - char *new_link_path = NULL; - int len; + /* call out to allow 3rd party notification of rename. + * Ignore result of kauth_authorize_fileop call. + */ + if (kauth_authorize_fileop_has_listeners() && + namei(&nd) == 0) { + char *new_link_path = NULL; + int len; - /* build the path to the new link file */ - new_link_path = get_pathbuff(); - len = MAXPATHLEN; - vn_getpath(dvp, new_link_path, &len); + /* build the path to the new link file */ + new_link_path = get_pathbuff(); + len = MAXPATHLEN; + vn_getpath(dvp, new_link_path, &len); + if ((len + 1 + nd.ni_cnd.cn_namelen + 1) < MAXPATHLEN) { new_link_path[len - 1] = '/'; - strcpy(&new_link_path[len], nd.ni_cnd.cn_nameptr); - - kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_SYMLINK, - (uintptr_t)path, (uintptr_t)new_link_path); - if (new_link_path != NULL) - release_pathbuff(new_link_path); + strlcpy(&new_link_path[len], nd.ni_cnd.cn_nameptr, MAXPATHLEN-len); } + + kauth_authorize_fileop(vfs_context_ucred(ctx), KAUTH_FILEOP_SYMLINK, + (uintptr_t)path, (uintptr_t)new_link_path); + if (new_link_path != NULL) + release_pathbuff(new_link_path); + } #endif - // Make sure the name & parent pointers are hooked up - if (vp->v_name == NULL) - update_flags |= VNODE_UPDATE_NAME; - if (vp->v_parent == NULLVP) - update_flags |= VNODE_UPDATE_PARENT; - - if (update_flags) - vnode_update_identity(vp, dvp, nd.ni_cnd.cn_nameptr, nd.ni_cnd.cn_namelen, nd.ni_cnd.cn_hash, update_flags); + // Make sure the name & parent pointers are hooked up + if (vp->v_name == NULL) + update_flags |= VNODE_UPDATE_NAME; + if (vp->v_parent == NULLVP) + update_flags |= VNODE_UPDATE_PARENT; + + if (update_flags) + vnode_update_identity(vp, dvp, nd.ni_cnd.cn_nameptr, nd.ni_cnd.cn_namelen, nd.ni_cnd.cn_hash, update_flags); - add_fsevent(FSE_CREATE_FILE, &context, - FSE_ARG_VNODE, vp, - FSE_ARG_DONE); - } - } else - error = EEXIST; +#if CONFIG_FSE + add_fsevent(FSE_CREATE_FILE, ctx, + FSE_ARG_VNODE, vp, + FSE_ARG_DONE); +#endif + } skipit: /* @@ -2059,22 +3547,18 @@ out: /* * Delete a whiteout from the filesystem. + * XXX authorization not implmented for whiteouts */ -/* ARGSUSED */ -#warning XXX authorization not implmented for whiteouts int -undelete(struct proc *p, register struct undelete_args *uap, __unused register_t *retval) +undelete(__unused proc_t p, struct undelete_args *uap, __unused int32_t *retval) { int error; struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); vnode_t vp, dvp; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT|AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); @@ -2082,7 +3566,7 @@ undelete(struct proc *p, register struct undelete_args *uap, __unused register_t vp = nd.ni_vp; if (vp == NULLVP && (nd.ni_cnd.cn_flags & ISWHITEOUT)) { - error = VNOP_WHITEOUT(dvp, &nd.ni_cnd, DELETE, &context); + error = VNOP_WHITEOUT(dvp, &nd.ni_cnd, DELETE, ctx); } else error = EEXIST; @@ -2099,78 +3583,159 @@ undelete(struct proc *p, register struct undelete_args *uap, __unused register_t return (error); } + /* * Delete a name from the filesystem. */ /* ARGSUSED */ -static int -_unlink(struct proc *p, struct unlink_args *uap, __unused register_t *retval, int nodelbusy) +int +unlink1(vfs_context_t ctx, struct nameidata *ndp, int nodelbusy) { vnode_t vp, dvp; int error; - struct nameidata nd; - struct vfs_context context; struct componentname *cnp; + char *path = NULL; + int len=0; +#if CONFIG_FSE + fse_info finfo; +#endif int flags = 0; + int need_event = 0; + int has_listeners = 0; + int truncated_path=0; +#if NAMEDRSRCFORK + /* unlink or delete is allowed on rsrc forks and named streams */ + ndp->ni_cnd.cn_flags |= CN_ALLOWRSRCFORK; +#endif - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + ndp->ni_cnd.cn_flags |= LOCKPARENT; + cnp = &ndp->ni_cnd; + + error = namei(ndp); + if (error) + return (error); - NDINIT(&nd, DELETE, LOCKPARENT | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); - cnp = &nd.ni_cnd; + dvp = ndp->ni_dvp; + vp = ndp->ni_vp; /* With Carbon delete semantics, busy files cannot be deleted */ - if (nodelbusy) + if (nodelbusy) { flags |= VNODE_REMOVE_NODELETEBUSY; + } - error = namei(&nd); - if (error) - return (error); - dvp = nd.ni_dvp; - vp = nd.ni_vp; - - if (vp->v_type == VDIR) { + /* + * Normally, unlinking of directories is not supported. + * However, some file systems may have limited support. + */ + if ((vp->v_type == VDIR) && + !(vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSDIRLINKS)) { error = EPERM; /* POSIX */ - } else { - /* - * The root of a mounted filesystem cannot be deleted. - */ - if (vp->v_flag & VROOT) { - error = EBUSY; - } } + + /* + * The root of a mounted filesystem cannot be deleted. + */ + if (vp->v_flag & VROOT) { + error = EBUSY; + } + if (error) + goto out; + + /* authorize the delete operation */ +#if CONFIG_MACF + if (!error) + error = mac_vnode_check_unlink(ctx, + dvp, vp, cnp); +#endif /* MAC */ if (!error) - error = vnode_authorize(vp, nd.ni_dvp, KAUTH_VNODE_DELETE, &context); + error = vnode_authorize(vp, ndp->ni_dvp, KAUTH_VNODE_DELETE, ctx); + if (error) + goto out; +#if CONFIG_FSE + need_event = need_fsevent(FSE_DELETE, dvp); + if (need_event) { + if ((vp->v_flag & VISHARDLINK) == 0) { + get_fse_info(vp, &finfo, ctx); + } + } +#endif + has_listeners = kauth_authorize_fileop_has_listeners(); + if (need_event || has_listeners) { + GET_PATH(path); + if (path == NULL) { + error = ENOMEM; + goto out; + } + + len = safe_getpath(dvp, ndp->ni_cnd.cn_nameptr, path, MAXPATHLEN, &truncated_path); + } + +#if NAMEDRSRCFORK + if (ndp->ni_cnd.cn_flags & CN_WANTSRSRCFORK) + error = vnode_removenamedstream(dvp, vp, XATTR_RESOURCEFORK_NAME, 0, ctx); + else +#endif + error = VNOP_REMOVE(dvp, vp, &ndp->ni_cnd, flags, ctx); + + /* + * Call out to allow 3rd party notification of delete. + * Ignore result of kauth_authorize_fileop call. + */ if (!error) { - char *path = NULL; - int len; - fse_info finfo; + if (has_listeners) { + kauth_authorize_fileop(vfs_context_ucred(ctx), + KAUTH_FILEOP_DELETE, + (uintptr_t)vp, + (uintptr_t)path); + } - if (need_fsevent(FSE_DELETE, dvp)) { - path = get_pathbuff(); - len = MAXPATHLEN; - vn_getpath(vp, path, &len); - get_fse_info(vp, &finfo, &context); + if (vp->v_flag & VISHARDLINK) { + // + // if a hardlink gets deleted we want to blow away the + // v_parent link because the path that got us to this + // instance of the link is no longer valid. this will + // force the next call to get the path to ask the file + // system instead of just following the v_parent link. + // + vnode_update_identity(vp, NULL, NULL, 0, 0, VNODE_UPDATE_PARENT); } - error = VNOP_REMOVE(dvp, vp, &nd.ni_cnd, flags, &context); - if ( !error && path != NULL) { - add_fsevent(FSE_DELETE, &context, - FSE_ARG_STRING, len, path, - FSE_ARG_FINFO, &finfo, - FSE_ARG_DONE); - } - if (path != NULL) - release_pathbuff(path); +#if CONFIG_FSE + if (need_event) { + if (vp->v_flag & VISHARDLINK) { + get_fse_info(vp, &finfo, ctx); + } + if (truncated_path) { + finfo.mode |= FSE_TRUNCATED_PATH; + } + add_fsevent(FSE_DELETE, ctx, + FSE_ARG_STRING, len, path, + FSE_ARG_FINFO, &finfo, + FSE_ARG_DONE); + } +#endif } + if (path != NULL) + RELEASE_PATH(path); + /* * nameidone has to happen before we vnode_put(dvp) * since it may need to release the fs_nodelock on the dvp */ - nameidone(&nd); +out: +#if NAMEDRSRCFORK + /* recycle the deleted rsrc fork vnode to force a reclaim, which + * will cause its shadow file to go away if necessary. + */ + if ((vnode_isnamedstream(ndp->ni_vp)) && + (ndp->ni_vp->v_parent != NULLVP) && + vnode_isshadow(ndp->ni_vp)) { + vnode_recycle(ndp->ni_vp); + } +#endif + nameidone(ndp); vnode_put(dvp); vnode_put(vp); return (error); @@ -2180,38 +3745,37 @@ _unlink(struct proc *p, struct unlink_args *uap, __unused register_t *retval, in * Delete a name from the filesystem using POSIX semantics. */ int -unlink(p, uap, retval) - struct proc *p; - struct unlink_args *uap; - register_t *retval; +unlink(__unused proc_t p, struct unlink_args *uap, __unused int32_t *retval) { - return _unlink(p, uap, retval, 0); + struct nameidata nd; + vfs_context_t ctx = vfs_context_current(); + + NDINIT(&nd, DELETE, AUDITVNPATH1, UIO_USERSPACE, uap->path, ctx); + return unlink1(ctx, &nd, 0); } /* * Delete a name from the filesystem using Carbon semantics. */ int -delete(p, uap, retval) - struct proc *p; - struct delete_args *uap; - register_t *retval; +delete(__unused proc_t p, struct delete_args *uap, __unused int32_t *retval) { - return _unlink(p, (struct unlink_args *)uap, retval, 1); + struct nameidata nd; + vfs_context_t ctx = vfs_context_current(); + + NDINIT(&nd, DELETE, AUDITVNPATH1, UIO_USERSPACE, uap->path, ctx); + return unlink1(ctx, &nd, 1); } /* * Reposition read/write file offset. */ int -lseek(p, uap, retval) - struct proc *p; - register struct lseek_args *uap; - off_t *retval; +lseek(proc_t p, struct lseek_args *uap, off_t *retval) { struct fileproc *fp; - struct vnode *vp; - struct vfs_context context; + vnode_t vp; + struct vfs_context *ctx; off_t offset = uap->offset, file_size; int error; @@ -2224,6 +3788,21 @@ lseek(p, uap, retval) file_drop(uap->fd); return(ESPIPE); } + + + ctx = vfs_context_current(); +#if CONFIG_MACF + if (uap->whence == L_INCR && uap->offset == 0) + error = mac_file_check_get_offset(vfs_context_ucred(ctx), + fp->f_fglob); + else + error = mac_file_check_change_offset(vfs_context_ucred(ctx), + fp->f_fglob); + if (error) { + file_drop(uap->fd); + return (error); + } +#endif if ( (error = vnode_getwithref(vp)) ) { file_drop(uap->fd); return(error); @@ -2234,9 +3813,7 @@ lseek(p, uap, retval) offset += fp->f_fglob->fg_offset; break; case L_XTND: - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - if ((error = vnode_size(vp, &file_size, &context)) != 0) + if ((error = vnode_size(vp, &file_size, ctx)) != 0) break; offset += file_size; break; @@ -2265,6 +3842,12 @@ lseek(p, uap, retval) } } } + + /* + * An lseek can affect whether data is "available to read." Use + * hint of NOTE_NONE so no EVFILT_VNODE events fire + */ + post_event_if_success(vp, error, NOTE_NONE); (void)vnode_put(vp); file_drop(uap->fd); return (error); @@ -2273,6 +3856,9 @@ lseek(p, uap, retval) /* * Check access permissions. + * + * Returns: 0 Success + * vnode_authorize:??? */ static int access1(vnode_t vp, vnode_t dvp, int uflags, vfs_context_t ctx) @@ -2309,6 +3895,12 @@ access1(vnode_t vp, vnode_t dvp, int uflags, vfs_context_t ctx) action = uflags >> 8; } +#if CONFIG_MACF + error = mac_vnode_check_access(ctx, vp, uflags); + if (error) + return (error); +#endif /* MAC */ + /* action == 0 means only check for existence */ if (action != 0) { error = vnode_authorize(vp, dvp, action | KAUTH_VNODE_ACCESS, ctx); @@ -2321,95 +3913,194 @@ access1(vnode_t vp, vnode_t dvp, int uflags, vfs_context_t ctx) -/* XXX need to support the check-as uid argument */ +/* + * access_extended: Check access permissions in bulk. + * + * Description: uap->entries Pointer to an array of accessx + * descriptor structs, plus one or + * more NULL terminated strings (see + * "Notes" section below). + * uap->size Size of the area pointed to by + * uap->entries. + * uap->results Pointer to the results array. + * + * Returns: 0 Success + * ENOMEM Insufficient memory + * EINVAL Invalid arguments + * namei:EFAULT Bad address + * namei:ENAMETOOLONG Filename too long + * namei:ENOENT No such file or directory + * namei:ELOOP Too many levels of symbolic links + * namei:EBADF Bad file descriptor + * namei:ENOTDIR Not a directory + * namei:??? + * access1: + * + * Implicit returns: + * uap->results Array contents modified + * + * Notes: The uap->entries are structured as an arbitrary length array + * of accessx descriptors, followed by one or more NULL terminated + * strings + * + * struct accessx_descriptor[0] + * ... + * struct accessx_descriptor[n] + * char name_data[0]; + * + * We determine the entry count by walking the buffer containing + * the uap->entries argument descriptor. For each descriptor we + * see, the valid values for the offset ad_name_offset will be + * in the byte range: + * + * [ uap->entries + sizeof(struct accessx_descriptor) ] + * to + * [ uap->entries + uap->size - 2 ] + * + * since we must have at least one string, and the string must + * be at least one character plus the NULL terminator in length. + * + * XXX: Need to support the check-as uid argument + */ int -access_extended(__unused struct proc *p, struct access_extended_args *uap, __unused register_t *retval) +access_extended(__unused proc_t p, struct access_extended_args *uap, __unused int32_t *retval) { - struct accessx_descriptor *input; - errno_t *result; - int error, limit, nent, i, j, wantdelete; + struct accessx_descriptor *input = NULL; + errno_t *result = NULL; + errno_t error = 0; + int wantdelete = 0; + unsigned int desc_max, desc_actual, i, j; struct vfs_context context; struct nameidata nd; int niopts; - vnode_t vp, dvp; + vnode_t vp = NULL; + vnode_t dvp = NULL; +#define ACCESSX_MAX_DESCR_ON_STACK 10 + struct accessx_descriptor stack_input[ACCESSX_MAX_DESCR_ON_STACK]; - input = NULL; - result = NULL; - error = 0; - vp = NULL; - dvp = NULL; context.vc_ucred = NULL; - /* check input size and fetch descriptor array into allocated storage */ + /* + * Validate parameters; if valid, copy the descriptor array and string + * arguments into local memory. Before proceeding, the following + * conditions must have been met: + * + * o The total size is not permitted to exceed ACCESSX_MAX_TABLESIZE + * o There must be sufficient room in the request for at least one + * descriptor and a one yte NUL terminated string. + * o The allocation of local storage must not fail. + */ if (uap->size > ACCESSX_MAX_TABLESIZE) return(ENOMEM); - if (uap->size < sizeof(struct accessx_descriptor)) + if (uap->size < (sizeof(struct accessx_descriptor) + 2)) return(EINVAL); + if (uap->size <= sizeof (stack_input)) { + input = stack_input; + } else { MALLOC(input, struct accessx_descriptor *, uap->size, M_TEMP, M_WAITOK); if (input == NULL) { error = ENOMEM; goto out; } + } error = copyin(uap->entries, input, uap->size); if (error) goto out; + AUDIT_ARG(opaque, input, uap->size); + /* - * Access is defined as checking against the process' - * real identity, even if operations are checking the - * effective identity. So we need to tweak the credential - * in the context. + * Force NUL termination of the copyin buffer to avoid nami() running + * off the end. If the caller passes us bogus data, they may get a + * bogus result. + */ + ((char *)input)[uap->size - 1] = 0; + + /* + * Access is defined as checking against the process' real identity, + * even if operations are checking the effective identity. This + * requires that we use a local vfs context. */ context.vc_ucred = kauth_cred_copy_real(kauth_cred_get()); - context.vc_proc = current_proc(); + context.vc_thread = current_thread(); /* - * Find out how many entries we have, so we can allocate the result array. + * Find out how many entries we have, so we can allocate the result + * array by walking the list and adjusting the count downward by the + * earliest string offset we see. */ - limit = uap->size / sizeof(struct accessx_descriptor); - nent = limit; - wantdelete = 0; - for (i = 0; i < nent; i++) { + desc_max = (uap->size - 2) / sizeof(struct accessx_descriptor); + desc_actual = desc_max; + for (i = 0; i < desc_actual; i++) { /* - * Take the offset to the name string for this entry and convert to an - * input array index, which would be one off the end of the array if this - * was the lowest-addressed name string. + * Take the offset to the name string for this entry and + * convert to an input array index, which would be one off + * the end of the array if this entry was the lowest-addressed + * name string. */ j = input[i].ad_name_offset / sizeof(struct accessx_descriptor); - /* bad input */ - if (j > limit) { + + /* + * An offset greater than the max allowable offset is an error. + * It is also an error for any valid entry to point + * to a location prior to the end of the current entry, if + * it's not a reference to the string of the previous entry. + */ + if (j > desc_max || (j != 0 && j <= i)) { error = EINVAL; goto out; } - /* implicit reference to previous name, not a real offset */ + + /* + * An offset of 0 means use the previous descriptor's offset; + * this is used to chain multiple requests for the same file + * to avoid multiple lookups. + */ if (j == 0) { - /* first entry must have a name string */ + /* This is not valid for the first entry */ if (i == 0) { error = EINVAL; goto out; } continue; } - if (j < nent) - nent = j; + + /* + * If the offset of the string for this descriptor is before + * what we believe is the current actual last descriptor, + * then we need to adjust our estimate downward; this permits + * the string table following the last descriptor to be out + * of order relative to the descriptor list. + */ + if (j < desc_actual) + desc_actual = j; } - if (nent > ACCESSX_MAX_DESCRIPTORS) { + + /* + * We limit the actual number of descriptors we are willing to process + * to a hard maximum of ACCESSX_MAX_DESCRIPTORS. If the number being + * requested does not exceed this limit, + */ + if (desc_actual > ACCESSX_MAX_DESCRIPTORS) { error = ENOMEM; goto out; } - MALLOC(result, errno_t *, nent * sizeof(errno_t), M_TEMP, M_WAITOK); + MALLOC(result, errno_t *, desc_actual * sizeof(errno_t), M_TEMP, M_WAITOK); if (result == NULL) { error = ENOMEM; goto out; } /* - * Do the work. + * Do the work by iterating over the descriptor entries we know to + * at least appear to contain valid data. */ error = 0; - for (i = 0; i < nent; i++) { + for (i = 0; i < desc_actual; i++) { /* - * Looking up a new name? + * If the ad_name_offset is 0, then we use the previous + * results to make the check; otherwise, we are looking up + * a new file name. */ if (input[i].ad_name_offset != 0) { /* discard old vnodes */ @@ -2422,19 +4113,26 @@ access_extended(__unused struct proc *p, struct access_extended_args *uap, __unu dvp = NULL; } - /* scan forwards to see if we need the parent this time */ + /* + * Scan forward in the descriptor list to see if we + * need the parent vnode. We will need it if we are + * deleting, since we must have rights to remove + * entries in the parent directory, as well as the + * rights to delete the object itself. + */ wantdelete = input[i].ad_flags & _DELETE_OK; - for (j = i + 1; (j < nent) && (input[j].ad_name_offset == 0); j++) + for (j = i + 1; (j < desc_actual) && (input[j].ad_name_offset == 0); j++) if (input[j].ad_flags & _DELETE_OK) wantdelete = 1; niopts = FOLLOW | AUDITVNPATH1; + /* need parent for vnode_authorize for deletion test */ if (wantdelete) niopts |= WANTPARENT; /* do the lookup */ - NDINIT(&nd, LOOKUP, niopts, UIO_SYSSPACE, CAST_USER_ADDR_T((const char *)input + input[i].ad_name_offset), &context); + NDINIT(&nd, LOOKUP, niopts, UIO_SYSSPACE, CAST_USER_ADDR_T(((const char *)input) + input[i].ad_name_offset), &context); error = namei(&nd); if (!error) { vp = nd.ni_vp; @@ -2465,11 +4163,13 @@ access_extended(__unused struct proc *p, struct access_extended_args *uap, __unu } } + AUDIT_ARG(data, result, sizeof(errno_t), desc_actual); + /* copy out results */ - error = copyout(result, uap->results, nent * sizeof(errno_t)); + error = copyout(result, uap->results, desc_actual * sizeof(errno_t)); out: - if (input) + if (input && input != stack_input) FREE(input, M_TEMP); if (result) FREE(result, M_TEMP); @@ -2477,18 +4177,33 @@ out: vnode_put(vp); if (dvp) vnode_put(dvp); - if (context.vc_ucred) - kauth_cred_rele(context.vc_ucred); + if (IS_VALID_CRED(context.vc_ucred)) + kauth_cred_unref(&context.vc_ucred); return(error); } + +/* + * Returns: 0 Success + * namei:EFAULT Bad address + * namei:ENAMETOOLONG Filename too long + * namei:ENOENT No such file or directory + * namei:ELOOP Too many levels of symbolic links + * namei:EBADF Bad file descriptor + * namei:ENOTDIR Not a directory + * namei:??? + * access1: + */ int -access(__unused struct proc *p, register struct access_args *uap, __unused register_t *retval) +access(__unused proc_t p, struct access_args *uap, __unused int32_t *retval) { int error; struct nameidata nd; int niopts; struct vfs_context context; +#if NAMEDRSRCFORK + int is_namedstream = 0; +#endif /* * Access is defined as checking against the process' @@ -2497,62 +4212,158 @@ access(__unused struct proc *p, register struct access_args *uap, __unused regis * in the context. */ context.vc_ucred = kauth_cred_copy_real(kauth_cred_get()); - context.vc_proc = current_proc(); + context.vc_thread = current_thread(); niopts = FOLLOW | AUDITVNPATH1; /* need parent for vnode_authorize for deletion test */ if (uap->flags & _DELETE_OK) niopts |= WANTPARENT; NDINIT(&nd, LOOKUP, niopts, UIO_USERSPACE, uap->path, &context); + +#if NAMEDRSRCFORK + /* access(F_OK) calls are allowed for resource forks. */ + if (uap->flags == F_OK) + nd.ni_cnd.cn_flags |= CN_ALLOWRSRCFORK; +#endif error = namei(&nd); if (error) goto out; +#if NAMEDRSRCFORK + /* Grab reference on the shadow stream file vnode to + * force an inactive on release which will mark it + * for recycle. + */ + if (vnode_isnamedstream(nd.ni_vp) && + (nd.ni_vp->v_parent != NULLVP) && + vnode_isshadow(nd.ni_vp)) { + is_namedstream = 1; + vnode_ref(nd.ni_vp); + } +#endif + error = access1(nd.ni_vp, nd.ni_dvp, uap->flags, &context); - + +#if NAMEDRSRCFORK + if (is_namedstream) { + vnode_rele(nd.ni_vp); + } +#endif + vnode_put(nd.ni_vp); if (uap->flags & _DELETE_OK) vnode_put(nd.ni_dvp); nameidone(&nd); out: - kauth_cred_rele(context.vc_ucred); + kauth_cred_unref(&context.vc_ucred); return(error); } +/* + * Returns: 0 Success + * EFAULT + * copyout:EFAULT + * namei:??? + * vn_stat:??? + */ static int -stat2(vfs_context_t ctx, struct nameidata *ndp, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) +stat2(vfs_context_t ctx, struct nameidata *ndp, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, int isstat64) { - struct stat sb; - struct user_stat user_sb; + union { + struct stat sb; + struct stat64 sb64; + } source; + union { + struct user64_stat user64_sb; + struct user32_stat user32_sb; + struct user64_stat64 user64_sb64; + struct user32_stat64 user32_sb64; + } dest; caddr_t sbp; int error, my_size; kauth_filesec_t fsec; size_t xsecurity_bufsize; + void * statptr; +#if NAMEDRSRCFORK + int is_namedstream = 0; + /* stat calls are allowed for resource forks. */ + ndp->ni_cnd.cn_flags |= CN_ALLOWRSRCFORK; +#endif error = namei(ndp); if (error) return (error); fsec = KAUTH_FILESEC_NONE; - error = vn_stat(ndp->ni_vp, &sb, (xsecurity != USER_ADDR_NULL ? &fsec : NULL), ctx); + + statptr = (void *)&source; + +#if NAMEDRSRCFORK + /* Grab reference on the shadow stream file vnode to + * force an inactive on release which will mark it + * for recycle. + */ + if (vnode_isnamedstream(ndp->ni_vp) && + (ndp->ni_vp->v_parent != NULLVP) && + vnode_isshadow(ndp->ni_vp)) { + is_namedstream = 1; + vnode_ref(ndp->ni_vp); + } +#endif + + error = vn_stat(ndp->ni_vp, statptr, (xsecurity != USER_ADDR_NULL ? &fsec : NULL), isstat64, ctx); + +#if NAMEDRSRCFORK + if (is_namedstream) { + vnode_rele(ndp->ni_vp); + } +#endif vnode_put(ndp->ni_vp); nameidone(ndp); if (error) return (error); /* Zap spare fields */ - sb.st_lspare = 0; - sb.st_qspare[0] = 0LL; - sb.st_qspare[1] = 0LL; - if (IS_64BIT_PROCESS(vfs_context_proc(ctx))) { - munge_stat(&sb, &user_sb); - my_size = sizeof(user_sb); - sbp = (caddr_t)&user_sb; - } - else { - my_size = sizeof(sb); - sbp = (caddr_t)&sb; + if (isstat64 != 0) { + source.sb64.st_lspare = 0; + source.sb64.st_qspare[0] = 0LL; + source.sb64.st_qspare[1] = 0LL; + if (IS_64BIT_PROCESS(vfs_context_proc(ctx))) { + munge_user64_stat64(&source.sb64, &dest.user64_sb64); + my_size = sizeof(dest.user64_sb64); + sbp = (caddr_t)&dest.user64_sb64; + } else { + munge_user32_stat64(&source.sb64, &dest.user32_sb64); + my_size = sizeof(dest.user32_sb64); + sbp = (caddr_t)&dest.user32_sb64; + } + /* + * Check if we raced (post lookup) against the last unlink of a file. + */ + if ((source.sb64.st_nlink == 0) && S_ISREG(source.sb64.st_mode)) { + source.sb64.st_nlink = 1; + } + } else { + source.sb.st_lspare = 0; + source.sb.st_qspare[0] = 0LL; + source.sb.st_qspare[1] = 0LL; + if (IS_64BIT_PROCESS(vfs_context_proc(ctx))) { + munge_user64_stat(&source.sb, &dest.user64_sb); + my_size = sizeof(dest.user64_sb); + sbp = (caddr_t)&dest.user64_sb; + } else { + munge_user32_stat(&source.sb, &dest.user32_sb); + my_size = sizeof(dest.user32_sb); + sbp = (caddr_t)&dest.user32_sb; + } + + /* + * Check if we raced (post lookup) against the last unlink of a file. + */ + if ((source.sb.st_nlink == 0) && S_ISREG(source.sb.st_mode)) { + source.sb.st_nlink = 1; + } } if ((error = copyout(sbp, ub, my_size)) != 0) goto out; @@ -2589,87 +4400,182 @@ out: /* * Get file status; this version follows links. + * + * Returns: 0 Success + * stat2:??? [see stat2() in this file] */ static int -stat1(struct proc *p, user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) +stat1(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, int isstat64) { struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + NDINIT(&nd, LOOKUP, NOTRIGGER | FOLLOW | AUDITVNPATH1, + UIO_USERSPACE, path, ctx); + return(stat2(ctx, &nd, ub, xsecurity, xsecurity_size, isstat64)); +} - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, path, &context); - return(stat2(&context, &nd, ub, xsecurity, xsecurity_size)); +/* + * stat_extended: Get file status; with extended security (ACL). + * + * Parameters: p (ignored) + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->path Path of file to get status from + * uap->ub User buffer (holds file status info) + * uap->xsecurity ACL to get (extended security) + * uap->xsecurity_size Size of ACL + * + * Returns: 0 Success + * !0 errno value + * + */ +int +stat_extended(__unused proc_t p, struct stat_extended_args *uap, __unused int32_t *retval) +{ + return (stat1(uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size, 0)); } +/* + * Returns: 0 Success + * stat1:??? [see stat1() in this file] + */ int -stat_extended(struct proc *p, struct stat_extended_args *uap, __unused register_t *retval) +stat(__unused proc_t p, struct stat_args *uap, __unused int32_t *retval) { - return (stat1(p, uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size)); + return(stat1(uap->path, uap->ub, 0, 0, 0)); } int -stat(struct proc *p, struct stat_args *uap, __unused register_t *retval) +stat64(__unused proc_t p, struct stat64_args *uap, __unused int32_t *retval) { - return(stat1(p, uap->path, uap->ub, 0, 0)); + return(stat1(uap->path, uap->ub, 0, 0, 1)); } +/* + * stat64_extended: Get file status; can handle large inode numbers; with extended security (ACL). + * + * Parameters: p (ignored) + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->path Path of file to get status from + * uap->ub User buffer (holds file status info) + * uap->xsecurity ACL to get (extended security) + * uap->xsecurity_size Size of ACL + * + * Returns: 0 Success + * !0 errno value + * + */ +int +stat64_extended(__unused proc_t p, struct stat64_extended_args *uap, __unused int32_t *retval) +{ + return (stat1(uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size, 1)); +} /* * Get file status; this version does not follow links. */ static int -lstat1(struct proc *p, user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) +lstat1(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size, int isstat64) { struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + NDINIT(&nd, LOOKUP, NOTRIGGER | NOFOLLOW | AUDITVNPATH1, + UIO_USERSPACE, path, ctx); - NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNPATH1, - UIO_USERSPACE, path, &context); + return(stat2(ctx, &nd, ub, xsecurity, xsecurity_size, isstat64)); +} + +/* + * lstat_extended: Get file status; does not follow links; with extended security (ACL). + * + * Parameters: p (ignored) + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->path Path of file to get status from + * uap->ub User buffer (holds file status info) + * uap->xsecurity ACL to get (extended security) + * uap->xsecurity_size Size of ACL + * + * Returns: 0 Success + * !0 errno value + * + */ +int +lstat_extended(__unused proc_t p, struct lstat_extended_args *uap, __unused int32_t *retval) +{ + return (lstat1(uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size, 0)); +} - return(stat2(&context, &nd, ub, xsecurity, xsecurity_size)); +int +lstat(__unused proc_t p, struct lstat_args *uap, __unused int32_t *retval) +{ + return(lstat1(uap->path, uap->ub, 0, 0, 0)); } int -lstat_extended(struct proc *p, struct lstat_extended_args *uap, __unused register_t *retval) +lstat64(__unused proc_t p, struct lstat64_args *uap, __unused int32_t *retval) { - return (lstat1(p, uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size)); + return(lstat1(uap->path, uap->ub, 0, 0, 1)); } +/* + * lstat64_extended: Get file status; can handle large inode numbers; does not + * follow links; with extended security (ACL). + * + * Parameters: p (ignored) + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->path Path of file to get status from + * uap->ub User buffer (holds file status info) + * uap->xsecurity ACL to get (extended security) + * uap->xsecurity_size Size of ACL + * + * Returns: 0 Success + * !0 errno value + * + */ int -lstat(struct proc *p, struct lstat_args *uap, __unused register_t *retval) +lstat64_extended(__unused proc_t p, struct lstat64_extended_args *uap, __unused int32_t *retval) { - return(lstat1(p, uap->path, uap->ub, 0, 0)); + return (lstat1(uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size, 1)); } /* * Get configurable pathname variables. + * + * Returns: 0 Success + * namei:??? + * vn_pathconf:??? + * + * Notes: Global implementation constants are intended to be + * implemented in this function directly; all other constants + * are per-FS implementation, and therefore must be handled in + * each respective FS, instead. + * + * XXX We implement some things globally right now that should actually be + * XXX per-FS; we will need to deal with this at some point. */ /* ARGSUSED */ int -pathconf(p, uap, retval) - struct proc *p; - register struct pathconf_args *uap; - register_t *retval; +pathconf(__unused proc_t p, struct pathconf_args *uap, int32_t *retval) { int error; struct nameidata nd; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vfs_context_t ctx = vfs_context_current(); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); - error = vn_pathconf(nd.ni_vp, uap->name, retval, &context); + error = vn_pathconf(nd.ni_vp, uap->name, retval, ctx); vnode_put(nd.ni_vp); nameidone(&nd); @@ -2681,24 +4587,18 @@ pathconf(p, uap, retval) */ /* ARGSUSED */ int -readlink(p, uap, retval) - struct proc *p; - register struct readlink_args *uap; - register_t *retval; +readlink(proc_t p, struct readlink_args *uap, int32_t *retval) { - register struct vnode *vp; + vnode_t vp; uio_t auio; int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; int error; struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); char uio_buf[ UIO_SIZEOF(1) ]; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); @@ -2712,12 +4612,18 @@ readlink(p, uap, retval) if (vp->v_type != VLNK) error = EINVAL; else { - error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, &context); +#if CONFIG_MACF + error = mac_vnode_check_readlink(ctx, + vp); +#endif + if (error == 0) + error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx); if (error == 0) - error = VNOP_READLINK(vp, auio, &context); + error = VNOP_READLINK(vp, auio, ctx); } vnode_put(vp); - // LP64todo - fix this + + /* Safe: uio_resid() is bounded above by "count", and "count" is an int */ *retval = uap->count - (int)uio_resid(auio); return (error); } @@ -2735,6 +4641,12 @@ chflags1(vnode_t vp, int flags, vfs_context_t ctx) VATTR_INIT(&va); VATTR_SET(&va, va_flags, flags); +#if CONFIG_MACF + error = mac_vnode_check_setflags(ctx, vp, flags); + if (error) + goto out; +#endif + /* request authorisation, disregard immutability */ if ((error = vnode_authattr(vp, &va, &action, ctx)) != 0) goto out; @@ -2747,6 +4659,9 @@ chflags1(vnode_t vp, int flags, vfs_context_t ctx) goto out; error = vnode_setattr(vp, &va, ctx); + if ((error == 0) && !VATTR_IS_SUPPORTED(&va, va_flags)) { + error = ENOTSUP; + } out: vnode_put(vp); return(error); @@ -2757,26 +4672,23 @@ out: */ /* ARGSUSED */ int -chflags(struct proc *p, register struct chflags_args *uap, __unused register_t *retval) +chflags(__unused proc_t p, struct chflags_args *uap, __unused int32_t *retval) { - register struct vnode *vp; - struct vfs_context context; + vnode_t vp; + vfs_context_t ctx = vfs_context_current(); int error; struct nameidata nd; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - AUDIT_ARG(fflags, uap->flags); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); vp = nd.ni_vp; nameidone(&nd); - error = chflags1(vp, uap->flags, &context); + error = chflags1(vp, uap->flags, ctx); return(error); } @@ -2786,10 +4698,9 @@ chflags(struct proc *p, register struct chflags_args *uap, __unused register_t * */ /* ARGSUSED */ int -fchflags(struct proc *p, register struct fchflags_args *uap, __unused register_t *retval) +fchflags(__unused proc_t p, struct fchflags_args *uap, __unused int32_t *retval) { - struct vfs_context context; - struct vnode *vp; + vnode_t vp; int error; AUDIT_ARG(fd, uap->fd); @@ -2804,10 +4715,7 @@ fchflags(struct proc *p, register struct fchflags_args *uap, __unused register_t AUDIT_ARG(vnpath, vp, ARG_VNODE1); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - error = chflags1(vp, uap->flags, &context); + error = chflags1(vp, uap->flags, vfs_context_current()); file_drop(uap->fd); return (error); @@ -2815,15 +4723,37 @@ fchflags(struct proc *p, register struct fchflags_args *uap, __unused register_t /* * Change security information on a filesystem object. + * + * Returns: 0 Success + * EPERM Operation not permitted + * vnode_authattr:??? [anything vnode_authattr can return] + * vnode_authorize:??? [anything vnode_authorize can return] + * vnode_setattr:??? [anything vnode_setattr can return] + * + * Notes: If vnode_authattr or vnode_authorize return EACCES, it will be + * translated to EPERM before being returned. */ static int -chmod2(vfs_context_t ctx, struct vnode *vp, struct vnode_attr *vap) +chmod2(vfs_context_t ctx, vnode_t vp, struct vnode_attr *vap) { kauth_action_t action; int error; - AUDIT_ARG(mode, (mode_t)vap->va_mode); -#warning XXX audit new args + AUDIT_ARG(mode, vap->va_mode); + /* XXX audit new args */ + +#if NAMEDSTREAMS + /* chmod calls are not allowed for resource forks. */ + if (vp->v_flag & VISNAMEDSTREAM) { + return (EPERM); + } +#endif + +#if CONFIG_MACF + error = mac_vnode_check_setmode(ctx, vp, (mode_t)vap->va_mode); + if (error) + return (error); +#endif /* make sure that the caller is allowed to set this security information */ if (((error = vnode_authattr(vp, vap, &action, ctx)) != 0) || @@ -2840,7 +4770,11 @@ chmod2(vfs_context_t ctx, struct vnode *vp, struct vnode_attr *vap) /* - * Change mode of a file given path name. + * Change mode of a file given a path name. + * + * Returns: 0 Success + * namei:??? [anything namei can return] + * chmod2:??? [anything chmod2 can return] */ static int chmod1(vfs_context_t ctx, user_addr_t path, struct vnode_attr *vap) @@ -2858,14 +4792,37 @@ chmod1(vfs_context_t ctx, user_addr_t path, struct vnode_attr *vap) return(error); } +/* + * chmod_extended: Change the mode of a file given a path name; with extended + * argument list (including extended security (ACL)). + * + * Parameters: p Process requesting the open + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->path Path to object (same as 'chmod') + * uap->uid UID to set + * uap->gid GID to set + * uap->mode File mode to set (same as 'chmod') + * uap->xsecurity ACL to set (or delete) + * + * Returns: 0 Success + * !0 errno value + * + * Notes: The kauth_filesec_t in 'va', if any, is in host byte order. + * + * XXX: We should enummerate the possible errno values here, and where + * in the code they originated. + */ int -chmod_extended(struct proc *p, struct chmod_extended_args *uap, __unused register_t *retval) +chmod_extended(__unused proc_t p, struct chmod_extended_args *uap, __unused int32_t *retval) { - struct vfs_context context; int error; struct vnode_attr va; kauth_filesec_t xsecdst; + AUDIT_ARG(owner, uap->uid, uap->gid); + VATTR_INIT(&va); if (uap->mode != -1) VATTR_SET(&va, va_mode, uap->mode & ALLPERMS); @@ -2889,43 +4846,37 @@ chmod_extended(struct proc *p, struct chmod_extended_args *uap, __unused registe VATTR_SET(&va, va_acl, &xsecdst->fsec_acl); KAUTH_DEBUG("CHMOD - setting ACL with %d entries", va.va_acl->acl_entrycount); } - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - error = chmod1(&context, uap->path, &va); + error = chmod1(vfs_context_current(), uap->path, &va); if (xsecdst != NULL) kauth_filesec_free(xsecdst); return(error); } +/* + * Returns: 0 Success + * chmod1:??? [anything chmod1 can return] + */ int -chmod(struct proc *p, register struct chmod_args *uap, __unused register_t *retval) +chmod(__unused proc_t p, struct chmod_args *uap, __unused int32_t *retval) { - struct vfs_context context; struct vnode_attr va; VATTR_INIT(&va); VATTR_SET(&va, va_mode, uap->mode & ALLPERMS); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - return(chmod1(&context, uap->path, &va)); + return(chmod1(vfs_context_current(), uap->path, &va)); } /* * Change mode of a file given a file descriptor. */ static int -fchmod1(struct proc *p, int fd, struct vnode_attr *vap) +fchmod1(__unused proc_t p, int fd, struct vnode_attr *vap) { - struct vnode *vp; + vnode_t vp; int error; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); AUDIT_ARG(fd, fd); @@ -2937,20 +4888,40 @@ fchmod1(struct proc *p, int fd, struct vnode_attr *vap) } AUDIT_ARG(vnpath, vp, ARG_VNODE1); - error = chmod2(&context, vp, vap); + error = chmod2(vfs_context_current(), vp, vap); (void)vnode_put(vp); file_drop(fd); return (error); } +/* + * fchmod_extended: Change mode of a file given a file descriptor; with + * extended argument list (including extended security (ACL)). + * + * Parameters: p Process requesting to change file mode + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->mode File mode to set (same as 'chmod') + * uap->uid UID to set + * uap->gid GID to set + * uap->xsecurity ACL to set (or delete) + * uap->fd File descriptor of file to change mode + * + * Returns: 0 Success + * !0 errno value + * + */ int -fchmod_extended(struct proc *p, struct fchmod_extended_args *uap, __unused register_t *retval) +fchmod_extended(proc_t p, struct fchmod_extended_args *uap, __unused int32_t *retval) { int error; struct vnode_attr va; kauth_filesec_t xsecdst; + AUDIT_ARG(owner, uap->uid, uap->gid); + VATTR_INIT(&va); if (uap->mode != -1) VATTR_SET(&va, va_mode, uap->mode & ALLPERMS); @@ -2987,7 +4958,7 @@ fchmod_extended(struct proc *p, struct fchmod_extended_args *uap, __unused regis } int -fchmod(struct proc *p, register struct fchmod_args *uap, __unused register_t *retval) +fchmod(proc_t p, struct fchmod_args *uap, __unused int32_t *retval) { struct vnode_attr va; @@ -3003,9 +4974,9 @@ fchmod(struct proc *p, register struct fchmod_args *uap, __unused register_t *re */ /* ARGSUSED */ static int -chown1(vfs_context_t ctx, register struct chown_args *uap, __unused register_t *retval, int follow) +chown1(vfs_context_t ctx, struct chown_args *uap, __unused int32_t *retval, int follow) { - register struct vnode *vp; + vnode_t vp; struct vnode_attr va; int error; struct nameidata nd; @@ -3013,7 +4984,7 @@ chown1(vfs_context_t ctx, register struct chown_args *uap, __unused register_t * AUDIT_ARG(owner, uap->uid, uap->gid); - NDINIT(&nd, LOOKUP, (follow ? FOLLOW : 0) | AUDITVNPATH1, + NDINIT(&nd, LOOKUP, (follow ? FOLLOW : 0) | NOTRIGGER | AUDITVNPATH1, UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) @@ -3022,21 +4993,18 @@ chown1(vfs_context_t ctx, register struct chown_args *uap, __unused register_t * nameidone(&nd); - /* - * XXX A TEMPORARY HACK FOR NOW: Try to track console_user - * by looking for chown() calls on /dev/console from a console process. - */ - if ((vp) && (vp->v_type == VBLK || vp->v_type == VCHR) && (vp->v_specinfo) && - (major(vp->v_specinfo->si_rdev) == CONSMAJOR) && - (minor(vp->v_specinfo->si_rdev) == 0)) { - console_user = uap->uid; - }; VATTR_INIT(&va); if (uap->uid != VNOVAL) VATTR_SET(&va, va_uid, uap->uid); if (uap->gid != VNOVAL) VATTR_SET(&va, va_gid, uap->gid); +#if CONFIG_MACF + error = mac_vnode_check_setowner(ctx, vp, uap->uid, uap->gid); + if (error) + goto out; +#endif + /* preflight and authorize attribute changes */ if ((error = vnode_authattr(vp, &va, &action, ctx)) != 0) goto out; @@ -3057,26 +5025,16 @@ out: } int -chown(struct proc *p, register struct chown_args *uap, register_t *retval) +chown(__unused proc_t p, struct chown_args *uap, int32_t *retval) { - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - return chown1(&context, uap, retval, 1); + return chown1(vfs_context_current(), uap, retval, 1); } int -lchown(struct proc *p, register struct lchown_args *uap, register_t *retval) +lchown(__unused proc_t p, struct lchown_args *uap, int32_t *retval) { - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - /* Argument list identical, but machine generated; cast for chown1() */ - return chown1(&context, (struct chown_args *)uap, retval, 0); + return chown1(vfs_context_current(), (struct chown_args *)uap, retval, 0); } /* @@ -3084,11 +5042,11 @@ lchown(struct proc *p, register struct lchown_args *uap, register_t *retval) */ /* ARGSUSED */ int -fchown(struct proc *p, register struct fchown_args *uap, __unused register_t *retval) +fchown(__unused proc_t p, struct fchown_args *uap, __unused int32_t *retval) { struct vnode_attr va; - struct vfs_context context; - struct vnode *vp; + vfs_context_t ctx = vfs_context_current(); + vnode_t vp; int error; kauth_action_t action; @@ -3110,18 +5068,29 @@ fchown(struct proc *p, register struct fchown_args *uap, __unused register_t *re if (uap->gid != VNOVAL) VATTR_SET(&va, va_gid, uap->gid); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); +#if NAMEDSTREAMS + /* chown calls are not allowed for resource forks. */ + if (vp->v_flag & VISNAMEDSTREAM) { + error = EPERM; + goto out; + } +#endif + +#if CONFIG_MACF + error = mac_vnode_check_setowner(ctx, vp, uap->uid, uap->gid); + if (error) + goto out; +#endif /* preflight and authorize attribute changes */ - if ((error = vnode_authattr(vp, &va, &action, &context)) != 0) + if ((error = vnode_authattr(vp, &va, &action, ctx)) != 0) goto out; - if (action && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) { + if (action && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) { if (error == EACCES) error = EPERM; goto out; } - error = vnode_setattr(vp, &va, &context); + error = vnode_setattr(vp, &va, ctx); out: (void)vnode_put(vp); @@ -3130,11 +5099,8 @@ out: } static int -getutimes(usrtvp, tsp) - user_addr_t usrtvp; - struct timespec *tsp; +getutimes(user_addr_t usrtvp, struct timespec *tsp) { - struct user_timeval tv[2]; int error; if (usrtvp == USER_ADDR_NULL) { @@ -3145,25 +5111,26 @@ getutimes(usrtvp, tsp) tsp[1] = tsp[0]; } else { if (IS_64BIT_PROCESS(current_proc())) { + struct user64_timeval tv[2]; error = copyin(usrtvp, (void *)tv, sizeof(tv)); + if (error) + return (error); + TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); + TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]); } else { - struct timeval old_tv[2]; - error = copyin(usrtvp, (void *)old_tv, sizeof(old_tv)); - tv[0].tv_sec = old_tv[0].tv_sec; - tv[0].tv_usec = old_tv[0].tv_usec; - tv[1].tv_sec = old_tv[1].tv_sec; - tv[1].tv_usec = old_tv[1].tv_usec; + struct user32_timeval tv[2]; + error = copyin(usrtvp, (void *)tv, sizeof(tv)); + if (error) + return (error); + TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); + TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]); } - if (error) - return (error); - TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); - TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]); } return 0; } static int -setutimes(vfs_context_t ctx, struct vnode *vp, const struct timespec *ts, +setutimes(vfs_context_t ctx, vnode_t vp, const struct timespec *ts, int nullflag) { int error; @@ -3178,11 +5145,31 @@ setutimes(vfs_context_t ctx, struct vnode *vp, const struct timespec *ts, if (nullflag) va.va_vaflags |= VA_UTIMES_NULL; - if ((error = vnode_authattr(vp, &va, &action, ctx)) != 0) +#if NAMEDSTREAMS + /* utimes calls are not allowed for resource forks. */ + if (vp->v_flag & VISNAMEDSTREAM) { + error = EPERM; + goto out; + } +#endif + +#if CONFIG_MACF + error = mac_vnode_check_setutimes(ctx, vp, ts[0], ts[1]); + if (error) + goto out; +#endif + if ((error = vnode_authattr(vp, &va, &action, ctx)) != 0) { + if (!nullflag && error == EACCES) + error = EPERM; goto out; + } + /* since we may not need to auth anything, check here */ - if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) + if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) { + if (!nullflag && error == EACCES) + error = EPERM; goto out; + } error = vnode_setattr(vp, &va, ctx); out: @@ -3194,22 +5181,20 @@ out: */ /* ARGSUSED */ int -utimes(struct proc *p, register struct utimes_args *uap, __unused register_t *retval) +utimes(__unused proc_t p, struct utimes_args *uap, __unused int32_t *retval) { struct timespec ts[2]; user_addr_t usrtvp; int error; struct nameidata nd; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vfs_context_t ctx = vfs_context_current(); - /* AUDIT: Needed to change the order of operations to do the + /* + * AUDIT: Needed to change the order of operations to do the * name lookup first because auditing wants the path. */ NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); @@ -3223,7 +5208,7 @@ utimes(struct proc *p, register struct utimes_args *uap, __unused register_t *re if ((error = getutimes(usrtvp, ts)) != 0) goto out; - error = setutimes(&context, nd.ni_vp, ts, usrtvp == USER_ADDR_NULL); + error = setutimes(ctx, nd.ni_vp, ts, usrtvp == USER_ADDR_NULL); out: vnode_put(nd.ni_vp); @@ -3235,16 +5220,12 @@ out: */ /* ARGSUSED */ int -futimes(struct proc *p, register struct futimes_args *uap, __unused register_t *retval) +futimes(__unused proc_t p, struct futimes_args *uap, __unused int32_t *retval) { struct timespec ts[2]; - struct vnode *vp; + vnode_t vp; user_addr_t usrtvp; int error; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); AUDIT_ARG(fd, uap->fd); usrtvp = uap->tptr; @@ -3257,7 +5238,7 @@ futimes(struct proc *p, register struct futimes_args *uap, __unused register_t * return(error); } - error = setutimes(&context, vp, ts, usrtvp == 0); + error = setutimes(vfs_context_current(), vp, ts, usrtvp == 0); vnode_put(vp); file_drop(uap->fd); return(error); @@ -3268,22 +5249,19 @@ futimes(struct proc *p, register struct futimes_args *uap, __unused register_t * */ /* ARGSUSED */ int -truncate(struct proc *p, register struct truncate_args *uap, __unused register_t *retval) +truncate(__unused proc_t p, struct truncate_args *uap, __unused int32_t *retval) { - register struct vnode *vp; + vnode_t vp; struct vnode_attr va; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error; struct nameidata nd; kauth_action_t action; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - if (uap->length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); if ((error = namei(&nd))) return (error); vp = nd.ni_vp; @@ -3292,11 +5270,18 @@ truncate(struct proc *p, register struct truncate_args *uap, __unused register_t VATTR_INIT(&va); VATTR_SET(&va, va_data_size, uap->length); - if ((error = vnode_authattr(vp, &va, &action, &context)) != 0) + +#if CONFIG_MACF + error = mac_vnode_check_truncate(ctx, NOCRED, vp); + if (error) goto out; - if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) +#endif + + if ((error = vnode_authattr(vp, &va, &action, ctx)) != 0) + goto out; + if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) goto out; - error = vnode_setattr(vp, &va, &context); + error = vnode_setattr(vp, &va, ctx); out: vnode_put(vp); return (error); @@ -3307,21 +5292,15 @@ out: */ /* ARGSUSED */ int -ftruncate(p, uap, retval) - struct proc *p; - register struct ftruncate_args *uap; - register_t *retval; +ftruncate(proc_t p, struct ftruncate_args *uap, int32_t *retval) { - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); struct vnode_attr va; - struct vnode *vp; + vnode_t vp; struct fileproc *fp; int error ; int fd = uap->fd; - context.vc_proc = current_proc(); - context.vc_ucred = kauth_cred_get(); - AUDIT_ARG(fd, uap->fd); if (uap->length < 0) return(EINVAL); @@ -3339,7 +5318,7 @@ ftruncate(p, uap, retval) goto out; } - vp = (struct vnode *)fp->f_fglob->fg_data; + vp = (vnode_t)fp->f_fglob->fg_data; if ((fp->f_fglob->fg_flag & FWRITE) == 0) { AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1); @@ -3353,9 +5332,17 @@ ftruncate(p, uap, retval) AUDIT_ARG(vnpath, vp, ARG_VNODE1); +#if CONFIG_MACF + error = mac_vnode_check_truncate(ctx, + fp->f_fglob->fg_cred, vp); + if (error) { + (void)vnode_put(vp); + goto out; + } +#endif VATTR_INIT(&va); VATTR_SET(&va, va_data_size, uap->length); - error = vnode_setattr(vp, &va, &context); + error = vnode_setattr(vp, &va, ctx); (void)vnode_put(vp); out: file_drop(fd); @@ -3364,27 +5351,99 @@ out: /* - * Sync an open file. + * Sync an open file with synchronized I/O _file_ integrity completion */ /* ARGSUSED */ int -fsync(struct proc *p, struct fsync_args *uap, __unused register_t *retval) +fsync(proc_t p, struct fsync_args *uap, __unused int32_t *retval) { - struct vnode *vp; + __pthread_testcancel(1); + return(fsync_common(p, uap, MNT_WAIT)); +} + + +/* + * Sync an open file with synchronized I/O _file_ integrity completion + * + * Notes: This is a legacy support function that does not test for + * thread cancellation points. + */ +/* ARGSUSED */ +int +fsync_nocancel(proc_t p, struct fsync_nocancel_args *uap, __unused int32_t *retval) +{ + return(fsync_common(p, (struct fsync_args *)uap, MNT_WAIT)); +} + + +/* + * Sync an open file with synchronized I/O _data_ integrity completion + */ +/* ARGSUSED */ +int +fdatasync(proc_t p, struct fdatasync_args *uap, __unused int32_t *retval) +{ + __pthread_testcancel(1); + return(fsync_common(p, (struct fsync_args *)uap, MNT_DWAIT)); +} + + +/* + * fsync_common + * + * Common fsync code to support both synchronized I/O file integrity completion + * (normal fsync) and synchronized I/O data integrity completion (fdatasync). + * + * If 'flags' is MNT_DWAIT, the caller is requesting data integrity, which + * will only guarantee that the file data contents are retrievable. If + * 'flags' is MNT_WAIT, the caller is rewuesting file integrity, which also + * includes additional metadata unnecessary for retrieving the file data + * contents, such as atime, mtime, ctime, etc., also be committed to stable + * storage. + * + * Parameters: p The process + * uap->fd The descriptor to synchronize + * flags The data integrity flags + * + * Returns: int Success + * fp_getfvp:EBADF Bad file descriptor + * fp_getfvp:ENOTSUP fd does not refer to a vnode + * VNOP_FSYNC:??? unspecified + * + * Notes: We use struct fsync_args because it is a short name, and all + * caller argument structures are otherwise identical. + */ +static int +fsync_common(proc_t p, struct fsync_args *uap, int flags) +{ + vnode_t vp; struct fileproc *fp; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error; + AUDIT_ARG(fd, uap->fd); + if ( (error = fp_getfvp(p, uap->fd, &fp, &vp)) ) return (error); if ( (error = vnode_getwithref(vp)) ) { file_drop(uap->fd); return(error); } - context.vc_proc = p; - context.vc_ucred = fp->f_fglob->fg_cred; - error = VNOP_FSYNC(vp, MNT_WAIT, &context); + AUDIT_ARG(vnpath, vp, ARG_VNODE1); + + error = VNOP_FSYNC(vp, flags, ctx); + +#if NAMEDRSRCFORK + /* Sync resource fork shadow file if necessary. */ + if ((error == 0) && + (vp->v_flag & VISNAMEDSTREAM) && + (vp->v_parent != NULLVP) && + vnode_isshadow(vp) && + (fp->f_flags & FP_WRITTEN)) { + (void) vnode_flushnamedstream(vp->v_parent, vp, ctx); + } +#endif (void)vnode_put(vp); file_drop(uap->fd); @@ -3400,15 +5459,12 @@ fsync(struct proc *p, struct fsync_args *uap, __unused register_t *retval) */ /* ARGSUSED */ int -copyfile(struct proc *p, register struct copyfile_args *uap, __unused register_t *retval) +copyfile(__unused proc_t p, struct copyfile_args *uap, __unused int32_t *retval) { vnode_t tvp, fvp, tdvp, sdvp; struct nameidata fromnd, tond; int error; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vfs_context_t ctx = vfs_context_current(); /* Check that the flags are valid. */ @@ -3417,13 +5473,13 @@ copyfile(struct proc *p, register struct copyfile_args *uap, __unused register_t } NDINIT(&fromnd, LOOKUP, SAVESTART | AUDITVNPATH1, - UIO_USERSPACE, uap->from, &context); + UIO_USERSPACE, uap->from, ctx); if ((error = namei(&fromnd))) return (error); fvp = fromnd.ni_vp; - NDINIT(&tond, CREATE, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | AUDITVNPATH2, - UIO_USERSPACE, uap->to, &context); + NDINIT(&tond, CREATE, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | AUDITVNPATH2 | CN_NBMOUNTLOOK, + UIO_USERSPACE, uap->to, ctx); if ((error = namei(&tond))) { goto out1; } @@ -3441,7 +5497,7 @@ copyfile(struct proc *p, register struct copyfile_args *uap, __unused register_t goto out; } - if ((error = vnode_authorize(tdvp, NULL, KAUTH_VNODE_ADD_FILE, &context)) != 0) + if ((error = vnode_authorize(tdvp, NULL, KAUTH_VNODE_ADD_FILE, ctx)) != 0) goto out; if (fvp == tdvp) @@ -3454,7 +5510,7 @@ copyfile(struct proc *p, register struct copyfile_args *uap, __unused register_t if (fvp == tvp) error = -1; if (!error) - error = VNOP_COPYFILE(fvp,tdvp,tvp,&tond.ni_cnd,uap->mode,uap->flags,&context); + error = VNOP_COPYFILE(fvp, tdvp, tvp, &tond.ni_cnd, uap->mode, uap->flags, ctx); out: sdvp = tond.ni_startdir; /* @@ -3486,37 +5542,48 @@ out1: */ /* ARGSUSED */ int -rename(proc_t p, register struct rename_args *uap, __unused register_t *retval) +rename(__unused proc_t p, struct rename_args *uap, __unused int32_t *retval) { vnode_t tvp, tdvp; vnode_t fvp, fdvp; struct nameidata fromnd, tond; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error; + int do_retry; int mntrename; - char *oname, *from_name, *to_name; - int from_len, to_len; + int need_event; + const char *oname; + char *from_name = NULL, *to_name = NULL; + int from_len=0, to_len=0; int holding_mntlock; mount_t locked_mp = NULL; vnode_t oparent; +#if CONFIG_FSE fse_info from_finfo, to_finfo; +#endif + int from_truncated=0, to_truncated; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); holding_mntlock = 0; + do_retry = 0; retry: fvp = tvp = NULL; fdvp = tdvp = NULL; mntrename = FALSE; - NDINIT(&fromnd, DELETE, WANTPARENT | AUDITVNPATH1, UIO_USERSPACE, uap->from, &context); + NDINIT(&fromnd, DELETE, WANTPARENT | AUDITVNPATH1, UIO_USERSPACE, uap->from, ctx); if ( (error = namei(&fromnd)) ) goto out1; fdvp = fromnd.ni_dvp; fvp = fromnd.ni_vp; - NDINIT(&tond, RENAME, WANTPARENT | AUDITVNPATH2, UIO_USERSPACE, uap->to, &context); +#if CONFIG_MACF + error = mac_vnode_check_rename_from(ctx, fdvp, fvp, &fromnd.ni_cnd); + if (error) + goto out1; +#endif + + NDINIT(&tond, RENAME, WANTPARENT | AUDITVNPATH2 | CN_NBMOUNTLOOK , UIO_USERSPACE, uap->to, ctx); if (fvp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; @@ -3531,6 +5598,13 @@ retry: tdvp = tond.ni_dvp; tvp = tond.ni_vp; +#if CONFIG_MACF + error = mac_vnode_check_rename_to(ctx, + tdvp, tvp, fdvp == tdvp, &tond.ni_cnd); + if (error) + goto out1; +#endif + if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { error = ENOTDIR; @@ -3544,18 +5618,37 @@ retry: error = EINVAL; goto out1; } + /* + * If the source and destination are the same (i.e. they're + * links to the same vnode) and the target file system is + * case sensitive, then there is nothing to do. + */ + if (fvp == tvp) { + int pathconf_val; + + /* + * Note: if _PC_CASE_SENSITIVE selector isn't supported, + * then assume that this file system is case sensitive. + */ + if (VNOP_PATHCONF(fvp, _PC_CASE_SENSITIVE, &pathconf_val, ctx) != 0 || + pathconf_val != 0) { + goto out1; + } + } /* * Authorization. * - * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp, - * the node is moving between directories and we need rights to remove from the - * old and add to the new. + * If tvp is a directory and not the same as fdvp, or tdvp is not + * the same as fdvp, the node is moving between directories and we + * need rights to remove from the old and add to the new. * - * If tvp already exists and is not a directory, we need to be allowed to delete it. + * If tvp already exists and is not a directory, we need to be + * allowed to delete it. * - * Note that we do not inherit when renaming. XXX this needs to be revisited to - * implement the deferred-inherit bit. + * Note that we do not inherit when renaming. + * + * XXX This needs to be revisited to implement the deferred-inherit bit */ { int moving = 0; @@ -3568,28 +5661,54 @@ retry: moving = 1; } /* - * must have delete rights to remove the old name even in the simple case of - * fdvp == tdvp + * must have delete rights to remove the old name even in + * the simple case of fdvp == tdvp. + * + * If fvp is a directory, and we are changing it's parent, + * then we also need rights to rewrite its ".." entry as well. */ - if ((error = vnode_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, &context)) != 0) + if (vnode_isdir(fvp)) { + if ((error = vnode_authorize(fvp, fdvp, KAUTH_VNODE_DELETE | KAUTH_VNODE_ADD_SUBDIRECTORY, ctx)) != 0) + goto auth_exit; + } else { + if ((error = vnode_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, ctx)) != 0) goto auth_exit; + } if (moving) { /* moving into tdvp or tvp, must have rights to add */ if ((error = vnode_authorize(((tvp != NULL) && vnode_isdir(tvp)) ? tvp : tdvp, NULL, vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE, - &context)) != 0) + ctx)) != 0) { + /* + * We could encounter a race where after doing the namei, tvp stops + * being valid. If so, simply re-drive the rename call from the + * top. + */ + if (error == ENOENT) { + do_retry = 1; + } goto auth_exit; + } } else { /* node staying in same directory, must be allowed to add new name */ if ((error = vnode_authorize(fdvp, NULL, - vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE, &context)) != 0) + vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE, ctx)) != 0) goto auth_exit; } /* overwriting tvp */ if ((tvp != NULL) && !vnode_isdir(tvp) && - ((error = vnode_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, &context)) != 0)) + ((error = vnode_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, ctx)) != 0)) { + /* + * We could encounter a race where after doing the namei, tvp stops + * being valid. If so, simply re-drive the rename call from the + * top. + */ + if (error == ENOENT) { + do_retry = 1; + } goto auth_exit; + } /* XXX more checks? */ @@ -3612,7 +5731,7 @@ auth_exit: (fdvp == tdvp) && ((fvp->v_mount->mnt_flag & (MNT_UNION | MNT_ROOTFS)) == 0) && (fvp->v_mount->mnt_vnodecovered != NULLVP)) { - struct vnode *coveredvp; + vnode_t coveredvp; /* switch fvp to the covered vnode */ coveredvp = fvp->v_mount->mnt_vnodecovered; @@ -3677,8 +5796,9 @@ auth_exit: * source. NOTE: Then the target is unlocked going into vnop_rename, * so not to cause locking problems. There is a single reference on tvp. * - * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE - * that correct behaviour then is just to remove the source (link) + * NOTE - that fvp == tvp also occurs if they are hard linked and + * that correct behaviour then is just to return success without doing + * anything. */ if (fvp == tvp && fdvp == tdvp) { if (fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && @@ -3766,40 +5886,40 @@ auth_exit: oname = fvp->v_name; oparent = fvp->v_parent; - if (need_fsevent(FSE_RENAME, fvp)) { - get_fse_info(fvp, &from_finfo, &context); +#if CONFIG_FSE + need_event = need_fsevent(FSE_RENAME, fvp); + if (need_event) { + get_fse_info(fvp, &from_finfo, ctx); if (tvp) { - get_fse_info(tvp, &to_finfo, &context); + get_fse_info(tvp, &to_finfo, ctx); + } + } +#else + need_event = 0; +#endif /* CONFIG_FSE */ + + if (need_event || kauth_authorize_fileop_has_listeners()) { + GET_PATH(from_name); + if (from_name == NULL) { + error = ENOMEM; + goto out1; } - from_name = get_pathbuff(); - from_len = MAXPATHLEN; - vn_getpath(fvp, from_name, &from_len); - to_name = get_pathbuff(); - to_len = MAXPATHLEN; + from_len = safe_getpath(fdvp, fromnd.ni_cnd.cn_nameptr, from_name, MAXPATHLEN, &from_truncated); - if (tvp && tvp->v_type != VDIR) { - vn_getpath(tvp, to_name, &to_len); - } else { - vn_getpath(tdvp, to_name, &to_len); - // if the path is not just "/", then append a "/" - if (to_len > 2) { - to_name[to_len-1] = '/'; - } else { - to_len--; - } - strcpy(&to_name[to_len], tond.ni_cnd.cn_nameptr); - to_len += tond.ni_cnd.cn_namelen + 1; - to_name[to_len] = '\0'; + GET_PATH(to_name); + if (to_name == NULL) { + error = ENOMEM; + goto out1; } - } else { - from_name = NULL; - to_name = NULL; - } + + to_len = safe_getpath(tdvp, tond.ni_cnd.cn_nameptr, to_name, MAXPATHLEN, &to_truncated); + } + error = VNOP_RENAME(fdvp, fvp, &fromnd.ni_cnd, tdvp, tvp, &tond.ni_cnd, - &context); + ctx); if (holding_mntlock) { /* @@ -3811,11 +5931,16 @@ auth_exit: holding_mntlock = 0; } if (error) { - if (to_name != NULL) - release_pathbuff(to_name); - if (from_name != NULL) - release_pathbuff(from_name); - from_name = to_name = NULL; + /* + * We may encounter a race in the VNOP where the destination didn't + * exist when we did the namei, but it does by the time we go and + * try to create the entry. In this case, we should re-drive this rename + * call from the top again. Currently, only HFS bubbles out ERECYCLE, + * but other filesystems susceptible to this race could return it, too. + */ + if (error == ERECYCLE) { + do_retry = 1; + } goto out1; } @@ -3823,30 +5948,32 @@ auth_exit: /* call out to allow 3rd party notification of rename. * Ignore result of kauth_authorize_fileop call. */ - kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_RENAME, - (uintptr_t)from_name, (uintptr_t)to_name); + kauth_authorize_fileop(vfs_context_ucred(ctx), + KAUTH_FILEOP_RENAME, + (uintptr_t)from_name, (uintptr_t)to_name); +#if CONFIG_FSE if (from_name != NULL && to_name != NULL) { + if (from_truncated || to_truncated) { + // set it here since only the from_finfo gets reported up to user space + from_finfo.mode |= FSE_TRUNCATED_PATH; + } if (tvp) { - add_fsevent(FSE_RENAME, &context, + add_fsevent(FSE_RENAME, ctx, FSE_ARG_STRING, from_len, from_name, FSE_ARG_FINFO, &from_finfo, FSE_ARG_STRING, to_len, to_name, FSE_ARG_FINFO, &to_finfo, FSE_ARG_DONE); } else { - add_fsevent(FSE_RENAME, &context, + add_fsevent(FSE_RENAME, ctx, FSE_ARG_STRING, from_len, from_name, FSE_ARG_FINFO, &from_finfo, FSE_ARG_STRING, to_len, to_name, FSE_ARG_DONE); } } - if (to_name != NULL) - release_pathbuff(to_name); - if (from_name != NULL) - release_pathbuff(from_name); - from_name = to_name = NULL; +#endif /* CONFIG_FSE */ /* * update filesystem's mount point data @@ -3882,7 +6009,7 @@ auth_exit: /* append name to prefix */ maxlen = MAXPATHLEN - (pathend - mp->mnt_vfsstat.f_mntonname); bzero(pathend, maxlen); - strncpy(pathend, mpname, maxlen - 1); + strlcpy(pathend, mpname, maxlen); } FREE_ZONE(tobuf, MAXPATHLEN, M_NAMEI); @@ -3905,9 +6032,18 @@ auth_exit: vnode_update_identity(fvp, tdvp, tond.ni_cnd.cn_nameptr, tond.ni_cnd.cn_namelen, tond.ni_cnd.cn_hash, update_flags); } out1: + if (to_name != NULL) { + RELEASE_PATH(to_name); + to_name = NULL; + } + if (from_name != NULL) { + RELEASE_PATH(from_name); + from_name = NULL; + } if (holding_mntlock) { mount_unlock_renames(locked_mp); mount_drop(locked_mp, 0); + holding_mntlock = 0; } if (tdvp) { /* @@ -3931,11 +6067,27 @@ out1: vnode_put(fvp); vnode_put(fdvp); } + + /* + * If things changed after we did the namei, then we will re-drive + * this rename call from the top. + */ + if(do_retry) { + do_retry = 0; + goto retry; + } + return (error); } /* * Make a directory file. + * + * Returns: 0 Success + * EEXIST + * namei:??? + * vnode_authorize:??? + * vn_create:??? */ /* ARGSUSED */ static int @@ -3960,12 +6112,20 @@ mkdir1(vfs_context_t ctx, user_addr_t path, struct vnode_attr *vap) error = EEXIST; goto out; } + + VATTR_SET(vap, va_type, VDIR); +#if CONFIG_MACF + error = mac_vnode_check_create(ctx, + nd.ni_dvp, &nd.ni_cnd, vap); + if (error) + goto out; +#endif + /* authorize addition of a directory to the parent */ if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx)) != 0) goto out; - VATTR_SET(vap, va_type, VDIR); /* make the directory */ if ((error = vn_create(dvp, &vp, &nd.ni_cnd, vap, 0, ctx)) != 0) @@ -3980,7 +6140,9 @@ mkdir1(vfs_context_t ctx, user_addr_t path, struct vnode_attr *vap) if (update_flags) vnode_update_identity(vp, dvp, nd.ni_cnd.cn_nameptr, nd.ni_cnd.cn_namelen, nd.ni_cnd.cn_hash, update_flags); +#if CONFIG_FSE add_fsevent(FSE_CREATE_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE); +#endif out: /* @@ -3996,47 +6158,55 @@ out: return (error); } - +/* + * mkdir_extended: Create a directory; with extended security (ACL). + * + * Parameters: p Process requesting to create the directory + * uap User argument descriptor (see below) + * retval (ignored) + * + * Indirect: uap->path Path of directory to create + * uap->mode Access permissions to set + * uap->xsecurity ACL to set + * + * Returns: 0 Success + * !0 Not success + * + */ int -mkdir_extended(struct proc *p, register struct mkdir_extended_args *uap, __unused register_t *retval) +mkdir_extended(proc_t p, struct mkdir_extended_args *uap, __unused int32_t *retval) { - struct vfs_context context; int ciferror; kauth_filesec_t xsecdst; struct vnode_attr va; + AUDIT_ARG(owner, uap->uid, uap->gid); + xsecdst = NULL; if ((uap->xsecurity != USER_ADDR_NULL) && ((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0)) return ciferror; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - VATTR_INIT(&va); VATTR_SET(&va, va_mode, (uap->mode & ACCESSPERMS) & ~p->p_fd->fd_cmask); if (xsecdst != NULL) VATTR_SET(&va, va_acl, &xsecdst->fsec_acl); - ciferror = mkdir1(&context, uap->path, &va); + ciferror = mkdir1(vfs_context_current(), uap->path, &va); if (xsecdst != NULL) kauth_filesec_free(xsecdst); return ciferror; } int -mkdir(struct proc *p, register struct mkdir_args *uap, __unused register_t *retval) +mkdir(proc_t p, struct mkdir_args *uap, __unused int32_t *retval) { - struct vfs_context context; struct vnode_attr va; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - VATTR_INIT(&va); VATTR_SET(&va, va_mode, (uap->mode & ACCESSPERMS) & ~p->p_fd->fd_cmask); - return(mkdir1(&context, uap->path, &va)); + return(mkdir1(vfs_context_current(), uap->path, &va)); } /* @@ -4044,110 +6214,283 @@ mkdir(struct proc *p, register struct mkdir_args *uap, __unused register_t *retv */ /* ARGSUSED */ int -rmdir(struct proc *p, struct rmdir_args *uap, __unused register_t *retval) +rmdir(__unused proc_t p, struct rmdir_args *uap, __unused int32_t *retval) { - vnode_t vp, dvp; + vnode_t vp, dvp; int error; struct nameidata nd; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vfs_context_t ctx = vfs_context_current(); - NDINIT(&nd, DELETE, LOCKPARENT | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); - error = namei(&nd); - if (error) - return (error); - dvp = nd.ni_dvp; - vp = nd.ni_vp; + int restart_flag; + uint32_t oldvp_id = UINT32_MAX; - if (vp->v_type != VDIR) { - /* - * rmdir only deals with directories - */ - error = ENOTDIR; - } else if (dvp == vp) { - /* - * No rmdir "." please. - */ - error = EINVAL; - } else if (vp->v_flag & VROOT) { - /* - * The root of a mounted filesystem cannot be deleted. + /* + * This loop exists to restart rmdir in the unlikely case that two + * processes are simultaneously trying to remove the same directory + * containing orphaned appleDouble files. + */ + do { + restart_flag = 0; + + NDINIT(&nd, DELETE, LOCKPARENT | AUDITVNPATH1, + UIO_USERSPACE, uap->path, ctx); + error = namei(&nd); + if (error) + return (error); + + dvp = nd.ni_dvp; + vp = nd.ni_vp; + + + /* + * If being restarted check if the new vp + * still has the same v_id. */ - error = EBUSY; - } else { - error = vnode_authorize(vp, nd.ni_dvp, KAUTH_VNODE_DELETE, &context); - } - if (!error) { - char *path = NULL; - int len; - fse_info finfo; - - if (need_fsevent(FSE_DELETE, dvp)) { - path = get_pathbuff(); - len = MAXPATHLEN; - vn_getpath(vp, path, &len); - get_fse_info(vp, &finfo, &context); + if (oldvp_id != UINT32_MAX && oldvp_id != vp->v_id) { + error = ENOENT; + goto out; } - error = VNOP_RMDIR(dvp, vp, &nd.ni_cnd, &context); - if (!error && path != NULL) { - add_fsevent(FSE_DELETE, &context, - FSE_ARG_STRING, len, path, - FSE_ARG_FINFO, &finfo, - FSE_ARG_DONE); + if (vp->v_type != VDIR) { + /* + * rmdir only deals with directories + */ + error = ENOTDIR; + } else if (dvp == vp) { + /* + * No rmdir "." please. + */ + error = EINVAL; + } else if (vp->v_flag & VROOT) { + /* + * The root of a mounted filesystem cannot be deleted. + */ + error = EBUSY; + } else { +#if CONFIG_MACF + error = mac_vnode_check_unlink(ctx, dvp, + vp, &nd.ni_cnd); + if (!error) +#endif + error = vnode_authorize(vp, nd.ni_dvp, KAUTH_VNODE_DELETE, ctx); } - if (path != NULL) - release_pathbuff(path); - } - /* - * nameidone has to happen before we vnode_put(dvp) - * since it may need to release the fs_nodelock on the dvp - */ - nameidone(&nd); + if (!error) { + char *path = NULL; + int len=0; + int has_listeners = 0; + int need_event = 0; + int truncated = 0; +#if CONFIG_FSE + fse_info finfo; + + need_event = need_fsevent(FSE_DELETE, dvp); + if (need_event) { + get_fse_info(vp, &finfo, ctx); + } +#endif + has_listeners = kauth_authorize_fileop_has_listeners(); + if (need_event || has_listeners) { + GET_PATH(path); + if (path == NULL) { + error = ENOMEM; + goto out; + } - vnode_put(dvp); - vnode_put(vp); + len = safe_getpath(vp, NULL, path, MAXPATHLEN, &truncated); +#if CONFIG_FSE + if (truncated) { + finfo.mode |= FSE_TRUNCATED_PATH; + } +#endif + } + + error = VNOP_RMDIR(dvp, vp, &nd.ni_cnd, ctx); + + /* + * Special case to remove orphaned AppleDouble + * files. I don't like putting this in the kernel, + * but carbon does not like putting this in carbon either, + * so here we are. + */ + if (error == ENOTEMPTY) { + error = rmdir_remove_orphaned_appleDouble(vp, ctx, &restart_flag); + if (error == EBUSY) { + oldvp_id = vp->v_id; + goto out; + } + + + /* + * Assuming everything went well, we will try the RMDIR again + */ + if (!error) + error = VNOP_RMDIR(dvp, vp, &nd.ni_cnd, ctx); + } + + /* + * Call out to allow 3rd party notification of delete. + * Ignore result of kauth_authorize_fileop call. + */ + if (!error) { + if (has_listeners) { + kauth_authorize_fileop(vfs_context_ucred(ctx), + KAUTH_FILEOP_DELETE, + (uintptr_t)vp, + (uintptr_t)path); + } + + if (vp->v_flag & VISHARDLINK) { + // see the comment in unlink1() about why we update + // the parent of a hard link when it is removed + vnode_update_identity(vp, NULL, NULL, 0, 0, VNODE_UPDATE_PARENT); + } + +#if CONFIG_FSE + if (need_event) { + add_fsevent(FSE_DELETE, ctx, + FSE_ARG_STRING, len, path, + FSE_ARG_FINFO, &finfo, + FSE_ARG_DONE); + } +#endif + } + if (path != NULL) + RELEASE_PATH(path); + } + +out: + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + vnode_put(dvp); + vnode_put(vp); + + if (restart_flag == 0) { + wakeup_one((caddr_t)vp); + return (error); + } + tsleep(vp, PVFS, "rm AD", 1); + + } while (restart_flag != 0); return (error); + } +/* Get direntry length padded to 8 byte alignment */ +#define DIRENT64_LEN(namlen) \ + ((sizeof(struct direntry) + (namlen) - (MAXPATHLEN-1) + 7) & ~7) + +static errno_t +vnode_readdir64(struct vnode *vp, struct uio *uio, int flags, int *eofflag, + int *numdirent, vfs_context_t ctxp) +{ + /* Check if fs natively supports VNODE_READDIR_EXTENDED */ + if (vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSREADDIR_EXTENDED) { + return VNOP_READDIR(vp, uio, flags, eofflag, numdirent, ctxp); + } else { + size_t bufsize; + void * bufptr; + uio_t auio; + struct direntry entry64; + struct dirent *dep; + int bytesread; + int error; + + /* + * Our kernel buffer needs to be smaller since re-packing + * will expand each dirent. The worse case (when the name + * length is 3) corresponds to a struct direntry size of 32 + * bytes (8-byte aligned) and a struct dirent size of 12 bytes + * (4-byte aligned). So having a buffer that is 3/8 the size + * will prevent us from reading more than we can pack. + * + * Since this buffer is wired memory, we will limit the + * buffer size to a maximum of 32K. We would really like to + * use 32K in the MIN(), but we use magic number 87371 to + * prevent uio_resid() * 3 / 8 from overflowing. + */ + bufsize = 3 * MIN(uio_resid(uio), 87371) / 8; + MALLOC(bufptr, void *, bufsize, M_TEMP, M_WAITOK); + if (bufptr == NULL) { + return ENOMEM; + } + + auio = uio_create(1, 0, UIO_SYSSPACE, UIO_READ); + uio_addiov(auio, (uintptr_t)bufptr, bufsize); + auio->uio_offset = uio->uio_offset; + + error = VNOP_READDIR(vp, auio, 0, eofflag, numdirent, ctxp); + + dep = (struct dirent *)bufptr; + bytesread = bufsize - uio_resid(auio); + + /* + * Convert all the entries and copy them out to user's buffer. + */ + while (error == 0 && (char *)dep < ((char *)bufptr + bytesread)) { + /* Convert a dirent to a dirent64. */ + entry64.d_ino = dep->d_ino; + entry64.d_seekoff = 0; + entry64.d_reclen = DIRENT64_LEN(dep->d_namlen); + entry64.d_namlen = dep->d_namlen; + entry64.d_type = dep->d_type; + bcopy(dep->d_name, entry64.d_name, dep->d_namlen + 1); + + /* Move to next entry. */ + dep = (struct dirent *)((char *)dep + dep->d_reclen); + + /* Copy entry64 to user's buffer. */ + error = uiomove((caddr_t)&entry64, entry64.d_reclen, uio); + } + + /* Update the real offset using the offset we got from VNOP_READDIR. */ + if (error == 0) { + uio->uio_offset = auio->uio_offset; + } + uio_free(auio); + FREE(bufptr, M_TEMP); + return (error); + } +} /* * Read a block of directory entries in a file system independent format. */ -int -getdirentries(p, uap, retval) - struct proc *p; - register struct getdirentries_args *uap; - register_t *retval; +static int +getdirentries_common(int fd, user_addr_t bufp, user_size_t bufsize, ssize_t *bytesread, + off_t *offset, int flags) { - struct vnode *vp; - struct vfs_context context; + vnode_t vp; + struct vfs_context context = *vfs_context_current(); /* local copy */ struct fileproc *fp; uio_t auio; - int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; - long loff; - int error, eofflag; - int fd = uap->fd; + int spacetype = proc_is64bit(vfs_context_proc(&context)) ? UIO_USERSPACE64 : UIO_USERSPACE32; + off_t loff; + int error, eofflag, numdirent; char uio_buf[ UIO_SIZEOF(1) ]; - AUDIT_ARG(fd, uap->fd); - error = fp_getfvp(p, fd, &fp, &vp); - if (error) + error = fp_getfvp(vfs_context_proc(&context), fd, &fp, &vp); + if (error) { return (error); - + } if ((fp->f_fglob->fg_flag & FREAD) == 0) { AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1); error = EBADF; goto out; } + +#if CONFIG_MACF + error = mac_file_check_change_offset(vfs_context_ucred(&context), fp->f_fglob); + if (error) + goto out; +#endif if ( (error = vnode_getwithref(vp)) ) { goto out; } - AUDIT_ARG(vnpath, vp, ARG_VNODE1); unionread: @@ -4156,107 +6499,141 @@ unionread: error = EINVAL; goto out; } - context.vc_proc = p; - context.vc_ucred = fp->f_fglob->fg_cred; + +#if CONFIG_MACF + error = mac_vnode_check_readdir(&context, vp); + if (error != 0) { + (void)vnode_put(vp); + goto out; + } +#endif /* MAC */ loff = fp->f_fglob->fg_offset; - auio = uio_createwithbuffer(1, loff, spacetype, UIO_READ, - &uio_buf[0], sizeof(uio_buf)); - uio_addiov(auio, uap->buf, uap->count); + auio = uio_createwithbuffer(1, loff, spacetype, UIO_READ, &uio_buf[0], sizeof(uio_buf)); + uio_addiov(auio, bufp, bufsize); - error = VNOP_READDIR(vp, auio, 0, &eofflag, (int *)NULL, &context); - fp->f_fglob->fg_offset = uio_offset(auio); + if (flags & VNODE_READDIR_EXTENDED) { + error = vnode_readdir64(vp, auio, flags, &eofflag, &numdirent, &context); + fp->f_fglob->fg_offset = uio_offset(auio); + } else { + error = VNOP_READDIR(vp, auio, 0, &eofflag, &numdirent, &context); + fp->f_fglob->fg_offset = uio_offset(auio); + } if (error) { (void)vnode_put(vp); goto out; } -#if UNION -{ - if ((uap->count == uio_resid(auio)) && - (vp->v_op == union_vnodeop_p)) { - struct vnode *lvp; - - lvp = union_dircache(vp, p); - if (lvp != NULLVP) { - struct vnode_attr va; - /* - * If the directory is opaque, - * then don't show lower entries - */ - VATTR_INIT(&va); - VATTR_WANTED(&va, va_flags); - error = vnode_getattr(vp, &va, &context); - if (va.va_flags & OPAQUE) { - vnode_put(lvp); - lvp = NULL; - } + if ((user_ssize_t)bufsize == uio_resid(auio)){ + if (union_dircheckp) { + error = union_dircheckp(&vp, fp, &context); + if (error == -1) + goto unionread; + if (error) + goto out; } - if (lvp != NULLVP) { - error = VNOP_OPEN(lvp, FREAD, &context); - if (error) { - vnode_put(lvp); - goto out; - } - vnode_ref(lvp); - fp->f_fglob->fg_data = (caddr_t) lvp; + if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { + struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; + vnode_getwithref(vp); + vnode_ref(vp); + fp->f_fglob->fg_data = (caddr_t) vp; fp->f_fglob->fg_offset = 0; - error = VNOP_CLOSE(vp, FREAD, &context); - vnode_rele(vp); - vnode_put(vp); - if (error) - goto out; - vp = lvp; + vnode_rele(tvp); + vnode_put(tvp); goto unionread; } } -} -#endif /* UNION */ - if (((user_ssize_t)uap->count == uio_resid(auio)) && - (vp->v_flag & VROOT) && - (vp->v_mount->mnt_flag & MNT_UNION)) { - struct vnode *tvp = vp; - vp = vp->v_mount->mnt_vnodecovered; - vnode_getwithref(vp); - vnode_ref(vp); - fp->f_fglob->fg_data = (caddr_t) vp; - fp->f_fglob->fg_offset = 0; - vnode_rele(tvp); - vnode_put(tvp); - goto unionread; - } vnode_put(vp); - error = copyout((caddr_t)&loff, uap->basep, sizeof(long)); - // LP64todo - fix this - *retval = uap->count - uio_resid(auio); + if (offset) { + *offset = loff; + } + + *bytesread = bufsize - uio_resid(auio); out: file_drop(fd); return (error); } + +int +getdirentries(__unused struct proc *p, struct getdirentries_args *uap, int32_t *retval) +{ + off_t offset; + ssize_t bytesread; + int error; + + AUDIT_ARG(fd, uap->fd); + error = getdirentries_common(uap->fd, uap->buf, uap->count, &bytesread, &offset, 0); + + if (error == 0) { + if (proc_is64bit(p)) { + user64_long_t base = (user64_long_t)offset; + error = copyout((caddr_t)&base, uap->basep, sizeof(user64_long_t)); + } else { + user32_long_t base = (user32_long_t)offset; + error = copyout((caddr_t)&base, uap->basep, sizeof(user32_long_t)); + } + *retval = bytesread; + } + return (error); +} + +int +getdirentries64(__unused struct proc *p, struct getdirentries64_args *uap, user_ssize_t *retval) +{ + off_t offset; + ssize_t bytesread; + int error; + + AUDIT_ARG(fd, uap->fd); + error = getdirentries_common(uap->fd, uap->buf, uap->bufsize, &bytesread, &offset, VNODE_READDIR_EXTENDED); + + if (error == 0) { + *retval = bytesread; + error = copyout((caddr_t)&offset, uap->position, sizeof(off_t)); + } + return (error); +} + + /* * Set the mode mask for creation of filesystem nodes. + * XXX implement xsecurity */ -#warning XXX implement xsecurity - #define UMASK_NOXSECURITY (void *)1 /* leave existing xsecurity alone */ static int -umask1(struct proc *p, int newmask, __unused kauth_filesec_t fsec, register_t *retval) +umask1(proc_t p, int newmask, __unused kauth_filesec_t fsec, int32_t *retval) { - register struct filedesc *fdp; + struct filedesc *fdp; AUDIT_ARG(mask, newmask); + proc_fdlock(p); fdp = p->p_fd; *retval = fdp->fd_cmask; fdp->fd_cmask = newmask & ALLPERMS; + proc_fdunlock(p); return (0); } - +/* + * umask_extended: Set the mode mask for creation of filesystem nodes; with extended security (ACL). + * + * Parameters: p Process requesting to set the umask + * uap User argument descriptor (see below) + * retval umask of the process (parameter p) + * + * Indirect: uap->newmask umask to set + * uap->xsecurity ACL to set + * + * Returns: 0 Success + * !0 Not success + * + */ int -umask_extended(struct proc *p, struct umask_extended_args *uap, register_t *retval) +umask_extended(proc_t p, struct umask_extended_args *uap, int32_t *retval) { int ciferror; kauth_filesec_t xsecdst; @@ -4277,7 +6654,7 @@ umask_extended(struct proc *p, struct umask_extended_args *uap, register_t *retv } int -umask(struct proc *p, struct umask_args *uap, register_t *retval) +umask(proc_t p, struct umask_args *uap, int32_t *retval) { return(umask1(p, uap->newmask, UMASK_NOXSECURITY, retval)); } @@ -4288,19 +6665,16 @@ umask(struct proc *p, struct umask_args *uap, register_t *retval) */ /* ARGSUSED */ int -revoke(struct proc *p, register struct revoke_args *uap, __unused register_t *retval) +revoke(proc_t p, struct revoke_args *uap, __unused int32_t *retval) { - register struct vnode *vp; + vnode_t vp; struct vnode_attr va; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error; struct nameidata nd; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) return (error); @@ -4308,15 +6682,31 @@ revoke(struct proc *p, register struct revoke_args *uap, __unused register_t *re nameidone(&nd); + if (!(vnode_ischr(vp) || vnode_isblk(vp))) { + error = ENOTSUP; + goto out; + } + + if (vnode_isblk(vp) && vnode_ismountedon(vp)) { + error = EBUSY; + goto out; + } + +#if CONFIG_MACF + error = mac_vnode_check_revoke(ctx, vp); + if (error) + goto out; +#endif + VATTR_INIT(&va); VATTR_WANTED(&va, va_uid); - if ((error = vnode_getattr(vp, &va, &context))) + if ((error = vnode_getattr(vp, &va, ctx))) goto out; - if (kauth_cred_getuid(context.vc_ucred) != va.va_uid && - (error = suser(context.vc_ucred, &p->p_acflag))) + if (kauth_cred_getuid(vfs_context_ucred(ctx)) != va.va_uid && + (error = suser(vfs_context_ucred(ctx), &p->p_acflag))) goto out; - if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) - VNOP_REVOKE(vp, REVOKEALL, &context); + if (vp->v_usecount > 0 || (vnode_isaliased(vp))) + VNOP_REVOKE(vp, REVOKEALL, ctx); out: vnode_put(vp); return (error); @@ -4340,7 +6730,7 @@ out: */ /* ARGSUSED */ int -mkcomplex(__unused struct proc *p, __unused struct mkcomplex_args *uap, __unused register_t *retval) +mkcomplex(__unused proc_t p, __unused struct mkcomplex_args *uap, __unused int32_t *retval) { return (ENOTSUP); } @@ -4350,9 +6740,9 @@ mkcomplex(__unused struct proc *p, __unused struct mkcomplex_args *uap, __unused */ /* ARGSUSED */ int -statv(__unused struct proc *p, +statv(__unused proc_t p, __unused struct statv_args *uap, - __unused register_t *retval) + __unused int32_t *retval) { return (ENOTSUP); /* We'll just return an error for now */ @@ -4363,9 +6753,9 @@ statv(__unused struct proc *p, */ /* ARGSUSED */ int -lstatv(__unused struct proc *p, +lstatv(__unused proc_t p, __unused struct lstatv_args *uap, - __unused register_t *retval) + __unused int32_t *retval) { return (ENOTSUP); /* We'll just return an error for now */ } /* end of lstatv system call */ @@ -4375,9 +6765,9 @@ lstatv(__unused struct proc *p, */ /* ARGSUSED */ int -fstatv(__unused struct proc *p, +fstatv(__unused proc_t p, __unused struct fstatv_args *uap, - __unused register_t *retval) + __unused int32_t *retval) { return (ENOTSUP); /* We'll just return an error for now */ } /* end of fstatv system call */ @@ -4398,19 +6788,18 @@ fstatv(__unused struct proc *p, /* ARGSUSED */ int -getdirentriesattr (struct proc *p, struct getdirentriesattr_args *uap, register_t *retval) +getdirentriesattr (proc_t p, struct getdirentriesattr_args *uap, int32_t *retval) { - struct vnode *vp; + vnode_t vp; struct fileproc *fp; uio_t auio = NULL; int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; - uint64_t actualcount; - u_long tmpcount; - u_long newstate; + uint32_t count; + uint32_t newstate; int error, eofflag; - u_long loff; + uint32_t loff; struct attrlist attributelist; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int fd = uap->fd; char uio_buf[ UIO_SIZEOF(1) ]; kauth_action_t action; @@ -4418,20 +6807,30 @@ getdirentriesattr (struct proc *p, struct getdirentriesattr_args *uap, register_ AUDIT_ARG(fd, fd); /* Get the attributes into kernel space */ - if ((error = copyin(uap->alist, (caddr_t) &attributelist, sizeof (attributelist)))) + if ((error = copyin(uap->alist, (caddr_t)&attributelist, sizeof(attributelist)))) { return(error); - actualcount = fuulong(uap->count); - if (actualcount == -1ULL) - return(-1); - - if ( (error = fp_getfvp(p, fd, &fp, &vp)) ) + } + if ((error = copyin(uap->count, (caddr_t)&count, sizeof(count)))) { + return(error); + } + if ( (error = fp_getfvp(p, fd, &fp, &vp)) ) { return (error); - + } if ((fp->f_fglob->fg_flag & FREAD) == 0) { AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1); error = EBADF; goto out; } + + +#if CONFIG_MACF + error = mac_file_check_change_offset(vfs_context_ucred(ctx), + fp->f_fglob); + if (error) + goto out; +#endif + + if ( (error = vnode_getwithref(vp)) ) goto out; @@ -4443,16 +6842,20 @@ getdirentriesattr (struct proc *p, struct getdirentriesattr_args *uap, register_ goto out; } +#if CONFIG_MACF + error = mac_vnode_check_readdir(ctx, vp); + if (error != 0) { + (void)vnode_put(vp); + goto out; + } +#endif /* MAC */ + /* set up the uio structure which will contain the users return buffer */ loff = fp->f_fglob->fg_offset; auio = uio_createwithbuffer(1, loff, spacetype, UIO_READ, &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, uap->buffer, uap->buffersize); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - tmpcount = (u_long) actualcount; - /* * If the only item requested is file names, we can let that past with * just LIST_DIRECTORY. If they want any other attributes, that means @@ -4463,27 +6866,31 @@ getdirentriesattr (struct proc *p, struct getdirentriesattr_args *uap, register_ attributelist.fileattr || attributelist.dirattr) action |= KAUTH_VNODE_SEARCH; - if ((error = vnode_authorize(vp, NULL, action, &context)) == 0) + if ((error = vnode_authorize(vp, NULL, action, ctx)) == 0) { + + /* Believe it or not, uap->options only has 32-bits of valid + * info, so truncate before extending again */ error = VNOP_READDIRATTR(vp, &attributelist, auio, - tmpcount, uap->options, &newstate, &eofflag, - &tmpcount, &context); + count, + (u_long)(uint32_t)uap->options, &newstate, &eofflag, + &count, ctx); + } (void)vnode_put(vp); - actualcount = tmpcount; if (error) goto out; fp->f_fglob->fg_offset = uio_offset(auio); /* should be multiple of dirent, not variable */ - if ((error = suulong(uap->count, actualcount)) != 0) + if ((error = copyout((caddr_t) &count, uap->count, sizeof(count)))) goto out; - if ((error = suulong(uap->newstate, (uint64_t)newstate)) != 0) + if ((error = copyout((caddr_t) &newstate, uap->newstate, sizeof(newstate)))) goto out; - if ((error = suulong(uap->basep, (uint64_t)loff)) != 0) + if ((error = copyout((caddr_t) &loff, uap->basep, sizeof(loff)))) goto out; *retval = eofflag; /* similar to getdirentries */ error = 0; - out: +out: file_drop(fd); return (error); /* return error earlier, an retval of 0 or 1 now */ @@ -4495,27 +6902,28 @@ getdirentriesattr (struct proc *p, struct getdirentriesattr_args *uap, register_ /* ARGSUSED */ int -exchangedata (struct proc *p, register struct exchangedata_args *uap, __unused register_t *retval) +exchangedata (__unused proc_t p, struct exchangedata_args *uap, __unused int32_t *retval) { struct nameidata fnd, snd; - struct vfs_context context; - struct vnode *fvp, *svp; - int error; - u_long nameiflags; + vfs_context_t ctx = vfs_context_current(); + vnode_t fvp; + vnode_t svp; + int error; + u_int32_t nameiflags; char *fpath = NULL; char *spath = NULL; - int flen, slen; + int flen=0, slen=0; + int from_truncated=0, to_truncated=0; +#if CONFIG_FSE fse_info f_finfo, s_finfo; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - +#endif + nameiflags = 0; if ((uap->options & FSOPT_NOFOLLOW) == 0) nameiflags |= FOLLOW; NDINIT(&fnd, LOOKUP, nameiflags | AUDITVNPATH1, - UIO_USERSPACE, uap->path1, &context); + UIO_USERSPACE, uap->path1, ctx); error = namei(&fnd); if (error) @@ -4524,8 +6932,8 @@ exchangedata (struct proc *p, register struct exchangedata_args *uap, __unused r nameidone(&fnd); fvp = fnd.ni_vp; - NDINIT(&snd, LOOKUP, nameiflags | AUDITVNPATH2, - UIO_USERSPACE, uap->path2, &context); + NDINIT(&snd, LOOKUP | CN_NBMOUNTLOOK, nameiflags | AUDITVNPATH2, + UIO_USERSPACE, uap->path2, ctx); error = namei(&snd); if (error) { @@ -4550,37 +6958,52 @@ exchangedata (struct proc *p, register struct exchangedata_args *uap, __unused r error = EXDEV; goto out; } - if (((error = vnode_authorize(fvp, NULL, KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, &context)) != 0) || - ((error = vnode_authorize(svp, NULL, KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, &context)) != 0)) + +#if CONFIG_MACF + error = mac_vnode_check_exchangedata(ctx, + fvp, svp); + if (error) + goto out; +#endif + if (((error = vnode_authorize(fvp, NULL, KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, ctx)) != 0) || + ((error = vnode_authorize(svp, NULL, KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, ctx)) != 0)) goto out; - if (need_fsevent(FSE_EXCHANGE, fvp) || kauth_authorize_fileop_has_listeners()) { - fpath = get_pathbuff(); - spath = get_pathbuff(); - flen = MAXPATHLEN; - slen = MAXPATHLEN; - if (vn_getpath(fvp, fpath, &flen) != 0 || fpath[0] == '\0') { - printf("exchange: vn_getpath(fvp=0x%x) failed <<%s>>\n", - fvp, fpath); + if ( +#if CONFIG_FSE + need_fsevent(FSE_EXCHANGE, fvp) || +#endif + kauth_authorize_fileop_has_listeners()) { + GET_PATH(fpath); + GET_PATH(spath); + if (fpath == NULL || spath == NULL) { + error = ENOMEM; + goto out; } - if (vn_getpath(svp, spath, &slen) != 0 || spath[0] == '\0') { - printf("exchange: vn_getpath(svp=0x%x) failed <<%s>>\n", - svp, spath); + + flen = safe_getpath(fvp, NULL, fpath, MAXPATHLEN, &from_truncated); + slen = safe_getpath(svp, NULL, spath, MAXPATHLEN, &to_truncated); + +#if CONFIG_FSE + get_fse_info(fvp, &f_finfo, ctx); + get_fse_info(svp, &s_finfo, ctx); + if (from_truncated || to_truncated) { + // set it here since only the f_finfo gets reported up to user space + f_finfo.mode |= FSE_TRUNCATED_PATH; } - get_fse_info(fvp, &f_finfo, &context); - get_fse_info(svp, &s_finfo, &context); +#endif } /* Ok, make the call */ - error = VNOP_EXCHANGE(fvp, svp, 0, &context); + error = VNOP_EXCHANGE(fvp, svp, 0, ctx); if (error == 0) { - char *tmpname; + const char *tmpname; if (fpath != NULL && spath != NULL) { /* call out to allow 3rd party notification of exchangedata. * Ignore result of kauth_authorize_fileop call. */ - kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_EXCHANGE, + kauth_authorize_fileop(vfs_context_ucred(ctx), KAUTH_FILEOP_EXCHANGE, (uintptr_t)fpath, (uintptr_t)spath); } name_cache_lock(); @@ -4590,7 +7013,7 @@ exchangedata (struct proc *p, register struct exchangedata_args *uap, __unused r svp->v_name = tmpname; if (fvp->v_parent != svp->v_parent) { - struct vnode *tmp; + vnode_t tmp; tmp = fvp->v_parent; fvp->v_parent = svp->v_parent; @@ -4598,21 +7021,23 @@ exchangedata (struct proc *p, register struct exchangedata_args *uap, __unused r } name_cache_unlock(); +#if CONFIG_FSE if (fpath != NULL && spath != NULL) { - add_fsevent(FSE_EXCHANGE, &context, + add_fsevent(FSE_EXCHANGE, ctx, FSE_ARG_STRING, flen, fpath, FSE_ARG_FINFO, &f_finfo, FSE_ARG_STRING, slen, spath, FSE_ARG_FINFO, &s_finfo, FSE_ARG_DONE); } +#endif } - if (spath != NULL) - release_pathbuff(spath); - if (fpath != NULL) - release_pathbuff(fpath); out: + if (fpath != NULL) + RELEASE_PATH(fpath); + if (spath != NULL) + RELEASE_PATH(spath); vnode_put(svp); vnode_put(fvp); out2: @@ -4620,131 +7045,49 @@ out2: } -#ifdef __APPLE_API_OBSOLETE - -/************************************************/ -/* *** Following calls will be deleted soon *** */ -/************************************************/ - -/* -* Check users access to a file -*/ - -/* ARGSUSED */ -#warning "checkuseraccess copies a cred in from user space but" -#warning "user space has no way of knowing what one looks like" -#warning "this code should use the access_extended spoof-as functionality" -int -checkuseraccess (struct proc *p, register struct checkuseraccess_args *uap, __unused register_t *retval) -{ - register struct vnode *vp; - int error; - struct nameidata nd; - struct ucred cred; /* XXX ILLEGAL */ - int flags; /*what will actually get passed to access*/ - u_long nameiflags; - struct vfs_context context; - - /* Make sure that the number of groups is correct before we do anything */ - - if ((uap->ngroups <= 0) || (uap->ngroups > NGROUPS)) - return (EINVAL); - - /* Verify that the caller is root */ - - if ((error = suser(kauth_cred_get(), &p->p_acflag))) - return(error); - - /* Fill in the credential structure */ - - cred.cr_ref = 0; - cred.cr_uid = uap->userid; - cred.cr_ngroups = uap->ngroups; - if ((error = copyin(CAST_USER_ADDR_T(uap->groups), (caddr_t) &(cred.cr_groups), (sizeof(gid_t))*uap->ngroups))) - return (error); - - context.vc_proc = p; - context.vc_ucred = &cred; - - /* Get our hands on the file */ - nameiflags = 0; - if ((uap->options & FSOPT_NOFOLLOW) == 0) nameiflags |= FOLLOW; - NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, - UIO_USERSPACE, CAST_USER_ADDR_T(uap->path), &context); - - if ((error = namei(&nd))) - return (error); - nameidone(&nd); - vp = nd.ni_vp; - - /* Flags == 0 means only check for existence. */ - - flags = 0; - - if (uap->accessrequired) { - if (uap->accessrequired & R_OK) - flags |= KAUTH_VNODE_READ_DATA; - if (uap->accessrequired & W_OK) - flags |= KAUTH_VNODE_WRITE_DATA; - if (uap->accessrequired & X_OK) - flags |= KAUTH_VNODE_EXECUTE; - } - error = vnode_authorize(vp, NULL, flags, &context); - - vnode_put(vp); - - if (error) - return (error); - - return (0); - -} /* end of checkuseraccess system call */ - -/************************************************/ -/* *** Preceding calls will be deleted soon *** */ -/************************************************/ - -#endif /* __APPLE_API_OBSOLETE */ - - - /* ARGSUSED */ int -searchfs (struct proc *p, register struct searchfs_args *uap, __unused register_t *retval) +searchfs(proc_t p, struct searchfs_args *uap, __unused int32_t *retval) { - register struct vnode *vp; + vnode_t vp; int error=0; int fserror = 0; struct nameidata nd; - struct user_fssearchblock searchblock; + struct user64_fssearchblock searchblock; struct searchstate *state; struct attrlist *returnattrs; + struct timeval timelimit; void *searchparams1,*searchparams2; uio_t auio = NULL; int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; - u_long nummatches; + uint32_t nummatches; int mallocsize; - u_long nameiflags; - struct vfs_context context; + uint32_t nameiflags; + vfs_context_t ctx = vfs_context_current(); char uio_buf[ UIO_SIZEOF(1) ]; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - /* Start by copying in fsearchblock paramater list */ if (IS_64BIT_PROCESS(p)) { - error = copyin(uap->searchblock, (caddr_t) &searchblock, sizeof(searchblock)); + error = copyin(uap->searchblock, (caddr_t) &searchblock, sizeof(searchblock)); + timelimit.tv_sec = searchblock.timelimit.tv_sec; + timelimit.tv_usec = searchblock.timelimit.tv_usec; } else { - struct fssearchblock tmp_searchblock; + struct user32_fssearchblock tmp_searchblock; + error = copyin(uap->searchblock, (caddr_t) &tmp_searchblock, sizeof(tmp_searchblock)); // munge into 64-bit version searchblock.returnattrs = CAST_USER_ADDR_T(tmp_searchblock.returnattrs); searchblock.returnbuffer = CAST_USER_ADDR_T(tmp_searchblock.returnbuffer); searchblock.returnbuffersize = tmp_searchblock.returnbuffersize; searchblock.maxmatches = tmp_searchblock.maxmatches; - searchblock.timelimit = tmp_searchblock.timelimit; + /* + * These casts are safe. We will promote the tv_sec into a 64 bit long if necessary + * from a 32 bit long, and tv_usec is already a signed 32 bit int. + */ + timelimit.tv_sec = (__darwin_time_t) tmp_searchblock.timelimit.tv_sec; + timelimit.tv_usec = (__darwin_useconds_t) tmp_searchblock.timelimit.tv_usec; searchblock.searchparams1 = CAST_USER_ADDR_T(tmp_searchblock.searchparams1); searchblock.sizeofsearchparams1 = tmp_searchblock.sizeofsearchparams1; searchblock.searchparams2 = CAST_USER_ADDR_T(tmp_searchblock.searchparams2); @@ -4789,9 +7132,55 @@ searchfs (struct proc *p, register struct searchfs_args *uap, __unused register_ if ((error = copyin(uap->state, (caddr_t) state, sizeof(struct searchstate)))) goto freeandexit; - - /* set up the uio structure which will contain the users return buffer */ + + /* + * Because searchparams1 and searchparams2 may contain an ATTR_CMN_NAME search parameter, + * which is passed in with an attrreference_t, we need to inspect the buffer manually here. + * The KPI does not provide us the ability to pass in the length of the buffers searchparams1 + * and searchparams2. To obviate the need for all searchfs-supporting filesystems to + * validate the user-supplied data offset of the attrreference_t, we'll do it here. + */ + + if (searchblock.searchattrs.commonattr & ATTR_CMN_NAME) { + attrreference_t* string_ref; + u_int32_t* start_length; + user64_size_t param_length; + + /* validate searchparams1 */ + param_length = searchblock.sizeofsearchparams1; + /* skip the word that specifies length of the buffer */ + start_length= (u_int32_t*) searchparams1; + start_length= start_length+1; + string_ref= (attrreference_t*) start_length; + + /* ensure no negative offsets or too big offsets */ + if (string_ref->attr_dataoffset < 0 ) { + error = EINVAL; + goto freeandexit; + } + if (string_ref->attr_length > MAXPATHLEN) { + error = EINVAL; + goto freeandexit; + } + + /* Check for pointer overflow in the string ref */ + if (((char*) string_ref + string_ref->attr_dataoffset) < (char*) string_ref) { + error = EINVAL; + goto freeandexit; + } + + if (((char*) string_ref + string_ref->attr_dataoffset) > ((char*)searchparams1 + param_length)) { + error = EINVAL; + goto freeandexit; + } + if (((char*)string_ref + string_ref->attr_dataoffset + string_ref->attr_length) > ((char*)searchparams1 + param_length)) { + error = EINVAL; + goto freeandexit; + } + } + + /* set up the uio structure which will contain the users return buffer */ auio = uio_createwithbuffer(1, 0, spacetype, UIO_READ, &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, searchblock.returnbuffer, searchblock.returnbuffersize); @@ -4799,7 +7188,7 @@ searchfs (struct proc *p, register struct searchfs_args *uap, __unused register_ nameiflags = 0; if ((uap->options & FSOPT_NOFOLLOW) == 0) nameiflags |= FOLLOW; NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, - UIO_USERSPACE, uap->path, &context); + UIO_USERSPACE, uap->path, ctx); error = namei(&nd); if (error) @@ -4830,15 +7219,15 @@ searchfs (struct proc *p, register struct searchfs_args *uap, __unused register_ searchparams1, searchparams2, &searchblock.searchattrs, - searchblock.maxmatches, - &searchblock.timelimit, + (u_long)searchblock.maxmatches, + &timelimit, returnattrs, &nummatches, - uap->scriptcode, - uap->options, + (u_long)uap->scriptcode, + (u_long)uap->options, auio, state, - &context); + ctx); saveandexit: @@ -4869,22 +7258,16 @@ freeandexit: * Make a filesystem-specific control call: */ /* ARGSUSED */ -int -fsctl (struct proc *p, struct fsctl_args *uap, __unused register_t *retval) +static int +fsctl_internal(proc_t p, vnode_t *arg_vp, u_long cmd, user_addr_t udata, u_long options, vfs_context_t ctx) { - int error; + int error=0; boolean_t is64bit; - struct nameidata nd; - u_long nameiflags; - u_long cmd = uap->cmd; - register u_int size; + u_int size; #define STK_PARAMS 128 char stkbuf[STK_PARAMS]; caddr_t data, memp; - struct vfs_context context; - - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); + vnode_t vp = *arg_vp; size = IOCPARM_LEN(cmd); if (size > IOCPARM_MAX) return (EINVAL); @@ -4901,14 +7284,14 @@ fsctl (struct proc *p, struct fsctl_args *uap, __unused register_t *retval) if (cmd & IOC_IN) { if (size) { - error = copyin(uap->data, data, size); + error = copyin(udata, data, size); if (error) goto FSCtl_Exit; } else { if (is64bit) { - *(user_addr_t *)data = uap->data; + *(user_addr_t *)data = udata; } else { - *(uint32_t *)data = (uint32_t)uap->data; + *(uint32_t *)data = (uint32_t)udata; } }; } else if ((cmd & IOC_OUT) && size) { @@ -4918,38 +7301,181 @@ fsctl (struct proc *p, struct fsctl_args *uap, __unused register_t *retval) */ bzero(data, size); } else if (cmd & IOC_VOID) { - if (is64bit) { - *(user_addr_t *)data = uap->data; - } - else { - *(uint32_t *)data = (uint32_t)uap->data; - } + if (is64bit) { + *(user_addr_t *)data = udata; + } + else { + *(uint32_t *)data = (uint32_t)udata; + } } - /* Get the vnode for the file we are getting info on: */ - nameiflags = 0; - if ((uap->options & FSOPT_NOFOLLOW) == 0) nameiflags |= FOLLOW; - NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, &context); - if ((error = namei(&nd))) goto FSCtl_Exit; + /* Check to see if it's a generic command */ + if (IOCBASECMD(cmd) == FSCTL_SYNC_VOLUME) { + mount_t mp = vp->v_mount; + int arg = *(uint32_t*)data; + + /* record vid of vp so we can drop it below. */ + uint32_t vvid = vp->v_id; + + /* + * Then grab mount_iterref so that we can release the vnode. + * Without this, a thread may call vnode_iterate_prepare then + * get into a deadlock because we've never released the root vp + */ + error = mount_iterref (mp, 0); + if (error) { + goto FSCtl_Exit; + } + vnode_put(vp); + + /* issue the sync for this volume */ + (void)sync_callback(mp, (arg & FSCTL_SYNC_WAIT) ? &arg : NULL); + + /* + * Then release the mount_iterref once we're done syncing; it's not + * needed for the VNOP_IOCTL below + */ + mount_iterdrop(mp); + + if (arg & FSCTL_SYNC_FULLSYNC) { + /* re-obtain vnode iocount on the root vp, if possible */ + error = vnode_getwithvid (vp, vvid); + if (error == 0) { + error = VNOP_IOCTL(vp, F_FULLFSYNC, (caddr_t)NULL, 0, ctx); + vnode_put (vp); + } + } + /* mark the argument VP as having been released */ + *arg_vp = NULL; + + } else if (IOCBASECMD(cmd) == FSCTL_SET_PACKAGE_EXTS) { + user_addr_t ext_strings; + uint32_t num_entries; + uint32_t max_width; + + if ( (is64bit && size != sizeof(user64_package_ext_info)) + || (is64bit == 0 && size != sizeof(user32_package_ext_info))) { + + // either you're 64-bit and passed a 64-bit struct or + // you're 32-bit and passed a 32-bit struct. otherwise + // it's not ok. + error = EINVAL; + goto FSCtl_Exit; + } + + if (is64bit) { + ext_strings = ((user64_package_ext_info *)data)->strings; + num_entries = ((user64_package_ext_info *)data)->num_entries; + max_width = ((user64_package_ext_info *)data)->max_width; + } else { + ext_strings = CAST_USER_ADDR_T(((user32_package_ext_info *)data)->strings); + num_entries = ((user32_package_ext_info *)data)->num_entries; + max_width = ((user32_package_ext_info *)data)->max_width; + } + + error = set_package_extensions_table(ext_strings, num_entries, max_width); - /* Invoke the filesystem-specific code */ - error = VNOP_IOCTL(nd.ni_vp, IOCBASECMD(cmd), data, uap->options, &context); + } else if (IOCBASECMD(cmd) == FSCTL_WAIT_FOR_SYNC) { + error = tsleep((caddr_t)&sync_wait_time, PVFS|PCATCH, "sync-wait", 0); + if (error == 0) { + *(uint32_t *)data = (uint32_t)sync_wait_time; + error = 0; + } else { + error *= -1; + } + + } else { + /* Invoke the filesystem-specific code */ + error = VNOP_IOCTL(vp, IOCBASECMD(cmd), data, options, ctx); + } - vnode_put(nd.ni_vp); - nameidone(&nd); /* * Copy any data to user, size was * already set and checked above. */ if (error == 0 && (cmd & IOC_OUT) && size) - error = copyout(data, uap->data, size); + error = copyout(data, udata, size); FSCtl_Exit: if (memp) kfree(memp, size); return error; } + +/* ARGSUSED */ +int +fsctl (proc_t p, struct fsctl_args *uap, __unused int32_t *retval) +{ + int error; + struct nameidata nd; + u_long nameiflags; + vnode_t vp = NULL; + vfs_context_t ctx = vfs_context_current(); + + AUDIT_ARG(cmd, uap->cmd); + AUDIT_ARG(value32, uap->options); + /* Get the vnode for the file we are getting info on: */ + nameiflags = 0; + if ((uap->options & FSOPT_NOFOLLOW) == 0) nameiflags |= FOLLOW; + NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, UIO_USERSPACE, + uap->path, ctx); + if ((error = namei(&nd))) goto done; + vp = nd.ni_vp; + nameidone(&nd); + +#if CONFIG_MACF + error = mac_mount_check_fsctl(ctx, vnode_mount(vp), uap->cmd); + if (error) { + goto done; + } +#endif + + error = fsctl_internal(p, &vp, uap->cmd, (user_addr_t)uap->data, uap->options, ctx); + +done: + if (vp) + vnode_put(vp); + return error; +} +/* ARGSUSED */ +int +ffsctl (proc_t p, struct ffsctl_args *uap, __unused int32_t *retval) +{ + int error; + vnode_t vp = NULL; + vfs_context_t ctx = vfs_context_current(); + int fd = -1; + + AUDIT_ARG(fd, uap->fd); + AUDIT_ARG(cmd, uap->cmd); + AUDIT_ARG(value32, uap->options); + + /* Get the vnode for the file we are getting info on: */ + if ((error = file_vnode(uap->fd, &vp))) + goto done; + fd = uap->fd; + if ((error = vnode_getwithref(vp))) { + goto done; + } + +#if CONFIG_MACF + error = mac_mount_check_fsctl(ctx, vnode_mount(vp), uap->cmd); + if (error) { + goto done; + } +#endif + + error = fsctl_internal(p, &vp, uap->cmd, (user_addr_t)uap->data, uap->options, ctx); + +done: + if (fd != -1) + file_drop(fd); + + if (vp) + vnode_put(vp); + return error; +} /* end of fsctl system call */ /* @@ -4976,28 +7502,25 @@ sync_internal(void) * Retrieve the data of an extended attribute. */ int -getxattr(struct proc *p, struct getxattr_args *uap, user_ssize_t *retval) +getxattr(proc_t p, struct getxattr_args *uap, user_ssize_t *retval) { - struct vnode *vp; + vnode_t vp; struct nameidata nd; char attrname[XATTR_MAXNAMELEN+1]; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); uio_t auio = NULL; int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; size_t attrsize = 0; size_t namelen; - u_long nameiflags; + u_int32_t nameiflags; int error; char uio_buf[ UIO_SIZEOF(1) ]; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - if (uap->options & XATTR_NOSECURITY) + if (uap->options & (XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW; - NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context); + NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, ctx); if ((error = namei(&nd))) { return (error); } @@ -5011,13 +7534,40 @@ getxattr(struct proc *p, struct getxattr_args *uap, user_ssize_t *retval) error = EPERM; goto out; } - if (uap->value && uap->size > 0) { + /* + * the specific check for 0xffffffff is a hack to preserve + * binaray compatibilty in K64 with applications that discovered + * that passing in a buf pointer and a size of -1 resulted in + * just the size of the indicated extended attribute being returned. + * this isn't part of the documented behavior, but because of the + * original implemtation's check for "uap->size > 0", this behavior + * was allowed. In K32 that check turned into a signed comparison + * even though uap->size is unsigned... in K64, we blow by that + * check because uap->size is unsigned and doesn't get sign smeared + * in the munger for a 32 bit user app. we also need to add a + * check to limit the maximum size of the buffer being passed in... + * unfortunately, the underlying fileystems seem to just malloc + * the requested size even if the actual extended attribute is tiny. + * because that malloc is for kernel wired memory, we have to put a + * sane limit on it. + * + * U32 running on K64 will yield 0x00000000ffffffff for uap->size + * U64 running on K64 will yield -1 (64 bits wide) + * U32/U64 running on K32 will yield -1 (32 bits wide) + */ + if (uap->size == 0xffffffff || uap->size == (size_t)-1) + goto no_uio; + + if (uap->size > (size_t)XATTR_MAXSIZE) + uap->size = XATTR_MAXSIZE; + + if (uap->value) { auio = uio_createwithbuffer(1, uap->position, spacetype, UIO_READ, &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, uap->value, uap->size); } - - error = vn_getxattr(vp, attrname, auio, &attrsize, uap->options, &context); +no_uio: + error = vn_getxattr(vp, attrname, auio, &attrsize, uap->options, ctx); out: vnode_put(vp); @@ -5034,11 +7584,10 @@ out: * Retrieve the data of an extended attribute. */ int -fgetxattr(struct proc *p, struct fgetxattr_args *uap, user_ssize_t *retval) +fgetxattr(proc_t p, struct fgetxattr_args *uap, user_ssize_t *retval) { - struct vnode *vp; + vnode_t vp; char attrname[XATTR_MAXNAMELEN+1]; - struct vfs_context context; uio_t auio = NULL; int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; size_t attrsize = 0; @@ -5046,7 +7595,7 @@ fgetxattr(struct proc *p, struct fgetxattr_args *uap, user_ssize_t *retval) int error; char uio_buf[ UIO_SIZEOF(1) ]; - if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY)) + if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); if ( (error = file_vnode(uap->fd, &vp)) ) { @@ -5068,10 +7617,8 @@ fgetxattr(struct proc *p, struct fgetxattr_args *uap, user_ssize_t *retval) &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, uap->value, uap->size); } - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - error = vn_getxattr(vp, attrname, auio, &attrsize, uap->options, &context); + error = vn_getxattr(vp, attrname, auio, &attrsize, uap->options, vfs_context_current()); out: (void)vnode_put(vp); file_drop(uap->fd); @@ -5088,23 +7635,20 @@ out: * Set the data of an extended attribute. */ int -setxattr(struct proc *p, struct setxattr_args *uap, int *retval) +setxattr(proc_t p, struct setxattr_args *uap, int *retval) { - struct vnode *vp; + vnode_t vp; struct nameidata nd; char attrname[XATTR_MAXNAMELEN+1]; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); uio_t auio = NULL; int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; size_t namelen; - u_long nameiflags; + u_int32_t nameiflags; int error; char uio_buf[ UIO_SIZEOF(1) ]; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - if (uap->options & XATTR_NOSECURITY) + if (uap->options & (XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) { @@ -5112,12 +7656,12 @@ setxattr(struct proc *p, struct setxattr_args *uap, int *retval) } if (xattr_protected(attrname)) return(EPERM); - if (uap->value == 0 || uap->size == 0) { + if (uap->size != 0 && uap->value == 0) { return (EINVAL); } nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW; - NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context); + NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, ctx); if ((error = namei(&nd))) { return (error); } @@ -5128,7 +7672,14 @@ setxattr(struct proc *p, struct setxattr_args *uap, int *retval) &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, uap->value, uap->size); - error = vn_setxattr(vp, attrname, auio, uap->options, &context); + error = vn_setxattr(vp, attrname, auio, uap->options, ctx); +#if CONFIG_FSE + if (error == 0) { + add_fsevent(FSE_XATTR_MODIFIED, ctx, + FSE_ARG_VNODE, vp, + FSE_ARG_DONE); + } +#endif vnode_put(vp); *retval = 0; return (error); @@ -5138,18 +7689,18 @@ setxattr(struct proc *p, struct setxattr_args *uap, int *retval) * Set the data of an extended attribute. */ int -fsetxattr(struct proc *p, struct fsetxattr_args *uap, int *retval) +fsetxattr(proc_t p, struct fsetxattr_args *uap, int *retval) { - struct vnode *vp; + vnode_t vp; char attrname[XATTR_MAXNAMELEN+1]; - struct vfs_context context; uio_t auio = NULL; int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; size_t namelen; int error; char uio_buf[ UIO_SIZEOF(1) ]; + vfs_context_t ctx = vfs_context_current(); - if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY)) + if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) { @@ -5157,7 +7708,7 @@ fsetxattr(struct proc *p, struct fsetxattr_args *uap, int *retval) } if (xattr_protected(attrname)) return(EPERM); - if (uap->value == 0 || uap->size == 0) { + if (uap->size != 0 && uap->value == 0) { return (EINVAL); } if ( (error = file_vnode(uap->fd, &vp)) ) { @@ -5170,10 +7721,15 @@ fsetxattr(struct proc *p, struct fsetxattr_args *uap, int *retval) auio = uio_createwithbuffer(1, uap->position, spacetype, UIO_WRITE, &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, uap->value, uap->size); - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - error = vn_setxattr(vp, attrname, auio, uap->options, &context); + error = vn_setxattr(vp, attrname, auio, uap->options, vfs_context_current()); +#if CONFIG_FSE + if (error == 0) { + add_fsevent(FSE_XATTR_MODIFIED, ctx, + FSE_ARG_VNODE, vp, + FSE_ARG_DONE); + } +#endif vnode_put(vp); file_drop(uap->fd); *retval = 0; @@ -5182,24 +7738,21 @@ fsetxattr(struct proc *p, struct fsetxattr_args *uap, int *retval) /* * Remove an extended attribute. + * XXX Code duplication here. */ -#warning "code duplication" int -removexattr(struct proc *p, struct removexattr_args *uap, int *retval) +removexattr(proc_t p, struct removexattr_args *uap, int *retval) { - struct vnode *vp; + vnode_t vp; struct nameidata nd; char attrname[XATTR_MAXNAMELEN+1]; int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); size_t namelen; - u_long nameiflags; + u_int32_t nameiflags; int error; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - if (uap->options & XATTR_NOSECURITY) + if (uap->options & (XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen); @@ -5209,14 +7762,21 @@ removexattr(struct proc *p, struct removexattr_args *uap, int *retval) if (xattr_protected(attrname)) return(EPERM); nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW; - NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context); + NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, ctx); if ((error = namei(&nd))) { return (error); } vp = nd.ni_vp; nameidone(&nd); - error = vn_removexattr(vp, attrname, uap->options, &context); + error = vn_removexattr(vp, attrname, uap->options, ctx); +#if CONFIG_FSE + if (error == 0) { + add_fsevent(FSE_XATTR_REMOVED, ctx, + FSE_ARG_VNODE, vp, + FSE_ARG_DONE); + } +#endif vnode_put(vp); *retval = 0; return (error); @@ -5224,18 +7784,18 @@ removexattr(struct proc *p, struct removexattr_args *uap, int *retval) /* * Remove an extended attribute. + * XXX Code duplication here. */ -#warning "code duplication" int -fremovexattr(struct proc *p, struct fremovexattr_args *uap, int *retval) +fremovexattr(__unused proc_t p, struct fremovexattr_args *uap, int *retval) { - struct vnode *vp; + vnode_t vp; char attrname[XATTR_MAXNAMELEN+1]; - struct vfs_context context; size_t namelen; int error; + vfs_context_t ctx = vfs_context_current(); - if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY)) + if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen); @@ -5251,10 +7811,15 @@ fremovexattr(struct proc *p, struct fremovexattr_args *uap, int *retval) file_drop(uap->fd); return(error); } - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - error = vn_removexattr(vp, attrname, uap->options, &context); + error = vn_removexattr(vp, attrname, uap->options, vfs_context_current()); +#if CONFIG_FSE + if (error == 0) { + add_fsevent(FSE_XATTR_REMOVED, ctx, + FSE_ARG_VNODE, vp, + FSE_ARG_DONE); + } +#endif vnode_put(vp); file_drop(uap->fd); *retval = 0; @@ -5263,42 +7828,38 @@ fremovexattr(struct proc *p, struct fremovexattr_args *uap, int *retval) /* * Retrieve the list of extended attribute names. + * XXX Code duplication here. */ -#warning "code duplication" int -listxattr(struct proc *p, struct listxattr_args *uap, user_ssize_t *retval) +listxattr(proc_t p, struct listxattr_args *uap, user_ssize_t *retval) { - struct vnode *vp; + vnode_t vp; struct nameidata nd; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); uio_t auio = NULL; int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; size_t attrsize = 0; - u_long nameiflags; + u_int32_t nameiflags; int error; char uio_buf[ UIO_SIZEOF(1) ]; - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - - if (uap->options & XATTR_NOSECURITY) + if (uap->options & (XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); - nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW; - NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context); + nameiflags = ((uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW) | NOTRIGGER; + NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, ctx); if ((error = namei(&nd))) { return (error); } vp = nd.ni_vp; nameidone(&nd); if (uap->namebuf != 0 && uap->bufsize > 0) { - // LP64todo - fix this! auio = uio_createwithbuffer(1, 0, spacetype, UIO_READ, &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, uap->namebuf, uap->bufsize); } - error = vn_listxattr(vp, auio, &attrsize, uap->options, &context); + error = vn_listxattr(vp, auio, &attrsize, uap->options, ctx); vnode_put(vp); if (auio) { @@ -5311,20 +7872,19 @@ listxattr(struct proc *p, struct listxattr_args *uap, user_ssize_t *retval) /* * Retrieve the list of extended attribute names. + * XXX Code duplication here. */ -#warning "code duplication" int -flistxattr(struct proc *p, struct flistxattr_args *uap, user_ssize_t *retval) +flistxattr(proc_t p, struct flistxattr_args *uap, user_ssize_t *retval) { - struct vnode *vp; - struct vfs_context context; + vnode_t vp; uio_t auio = NULL; int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32; size_t attrsize = 0; int error; char uio_buf[ UIO_SIZEOF(1) ]; - if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY)) + if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY | XATTR_NODEFAULT)) return (EINVAL); if ( (error = file_vnode(uap->fd, &vp)) ) { @@ -5335,15 +7895,12 @@ flistxattr(struct proc *p, struct flistxattr_args *uap, user_ssize_t *retval) return(error); } if (uap->namebuf != 0 && uap->bufsize > 0) { - // LP64todo - fix this! auio = uio_createwithbuffer(1, 0, spacetype, UIO_READ, &uio_buf[0], sizeof(uio_buf)); uio_addiov(auio, uap->namebuf, uap->bufsize); } - context.vc_proc = p; - context.vc_ucred = kauth_cred_get(); - error = vn_listxattr(vp, auio, &attrsize, uap->options, &context); + error = vn_listxattr(vp, auio, &attrsize, uap->options, vfs_context_current()); vnode_put(vp); file_drop(uap->fd); @@ -5355,9 +7912,76 @@ flistxattr(struct proc *p, struct flistxattr_args *uap, user_ssize_t *retval) return (error); } +/* + * Obtain the full pathname of a file system object by id. + * + * This is a private SPI used by the File Manager. + */ +__private_extern__ +int +fsgetpath(__unused proc_t p, struct fsgetpath_args *uap, user_ssize_t *retval) +{ + vnode_t vp; + struct mount *mp = NULL; + vfs_context_t ctx = vfs_context_current(); + fsid_t fsid; + char *realpath; + int bpflags; + int length; + int error; + + if ((error = copyin(uap->fsid, (caddr_t)&fsid, sizeof(fsid)))) { + return (error); + } + AUDIT_ARG(value32, fsid.val[0]); + AUDIT_ARG(value64, uap->objid); + /* Restrict output buffer size for now. */ + if (uap->bufsize > PAGE_SIZE) { + return (EINVAL); + } + MALLOC(realpath, char *, uap->bufsize, M_TEMP, M_WAITOK); + if (realpath == NULL) { + return (ENOMEM); + } + /* Find the target mountpoint. */ + if ((mp = mount_lookupby_volfsid(fsid.val[0], 1)) == NULL) { + error = ENOTSUP; /* unexpected failure */ + goto out; + } + /* Find the target vnode. */ + if (uap->objid == 2) { + error = VFS_ROOT(mp, &vp, ctx); + } else { + error = VFS_VGET(mp, (ino64_t)uap->objid, &vp, ctx); + } + vfs_unbusy(mp); + if (error) { + goto out; + } + /* Obtain the absolute path to this vnode. */ + bpflags = vfs_context_suser(ctx) ? BUILDPATH_CHECKACCESS : 0; + error = build_path(vp, realpath, uap->bufsize, &length, bpflags, ctx); + vnode_put(vp); + if (error) { + goto out; + } + AUDIT_ARG(text, realpath); + error = copyout((caddr_t)realpath, uap->buf, length); + + *retval = (user_ssize_t)length; /* may be superseded by error */ +out: + if (realpath) { + FREE(realpath, M_TEMP); + } + return (error); +} + /* * Common routine to handle various flavors of statfs data heading out * to user space. + * + * Returns: 0 Success + * EFAULT */ static int munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, @@ -5368,24 +7992,24 @@ munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, int my_size, copy_size; if (is_64_bit) { - struct user_statfs sfs; + struct user64_statfs sfs; my_size = copy_size = sizeof(sfs); bzero(&sfs, my_size); sfs.f_flags = mp->mnt_flag & MNT_VISFLAGMASK; sfs.f_type = mp->mnt_vtable->vfc_typenum; sfs.f_reserved1 = (short)sfsp->f_fssubtype; - sfs.f_bsize = (user_long_t)sfsp->f_bsize; - sfs.f_iosize = (user_long_t)sfsp->f_iosize; - sfs.f_blocks = (user_long_t)sfsp->f_blocks; - sfs.f_bfree = (user_long_t)sfsp->f_bfree; - sfs.f_bavail = (user_long_t)sfsp->f_bavail; - sfs.f_files = (user_long_t)sfsp->f_files; - sfs.f_ffree = (user_long_t)sfsp->f_ffree; + sfs.f_bsize = (user64_long_t)sfsp->f_bsize; + sfs.f_iosize = (user64_long_t)sfsp->f_iosize; + sfs.f_blocks = (user64_long_t)sfsp->f_blocks; + sfs.f_bfree = (user64_long_t)sfsp->f_bfree; + sfs.f_bavail = (user64_long_t)sfsp->f_bavail; + sfs.f_files = (user64_long_t)sfsp->f_files; + sfs.f_ffree = (user64_long_t)sfsp->f_ffree; sfs.f_fsid = sfsp->f_fsid; sfs.f_owner = sfsp->f_owner; - strncpy(&sfs.f_fstypename[0], &sfsp->f_fstypename[0], MFSNAMELEN-1); - strncpy(&sfs.f_mntonname[0], &sfsp->f_mntonname[0], MNAMELEN-1); - strncpy(&sfs.f_mntfromname[0], &sfsp->f_mntfromname[0], MNAMELEN-1); + strlcpy(&sfs.f_fstypename[0], &sfsp->f_fstypename[0], MFSNAMELEN); + strlcpy(&sfs.f_mntonname[0], &sfsp->f_mntonname[0], MNAMELEN); + strlcpy(&sfs.f_mntfromname[0], &sfsp->f_mntfromname[0], MNAMELEN); if (partial_copy) { copy_size -= (sizeof(sfs.f_reserved3) + sizeof(sfs.f_reserved4)); @@ -5393,7 +8017,8 @@ munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, error = copyout((caddr_t)&sfs, bufp, copy_size); } else { - struct statfs sfs; + struct user32_statfs sfs; + my_size = copy_size = sizeof(sfs); bzero(&sfs, my_size); @@ -5406,7 +8031,7 @@ munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, * have to fudge the numbers here in that case. We inflate the blocksize in order * to reflect the filesystem size as best we can. */ - if ((sfsp->f_blocks > LONG_MAX) + if ((sfsp->f_blocks > INT_MAX) /* Hack for 4061702 . I think the real fix is for Carbon to * look for some volume capability and not depend on hidden * semantics agreed between a FS and carbon. @@ -5416,9 +8041,9 @@ munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, * disk as they look huge. This change should not affect * XSAN as they should not setting these to -1.. */ - && (sfsp->f_blocks != 0xffffffffffffffff) - && (sfsp->f_bfree != 0xffffffffffffffff) - && (sfsp->f_bavail != 0xffffffffffffffff)) { + && (sfsp->f_blocks != 0xffffffffffffffffULL) + && (sfsp->f_bfree != 0xffffffffffffffffULL) + && (sfsp->f_bavail != 0xffffffffffffffffULL)) { int shift; /* @@ -5431,33 +8056,33 @@ munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, * being smaller than f_bsize. */ for (shift = 0; shift < 32; shift++) { - if ((sfsp->f_blocks >> shift) <= LONG_MAX) + if ((sfsp->f_blocks >> shift) <= INT_MAX) break; - if ((sfsp->f_bsize << (shift + 1)) > LONG_MAX) + if ((sfsp->f_bsize << (shift + 1)) > INT_MAX) break; } -#define __SHIFT_OR_CLIP(x, s) ((((x) >> (s)) > LONG_MAX) ? LONG_MAX : ((x) >> (s))) - sfs.f_blocks = (long)__SHIFT_OR_CLIP(sfsp->f_blocks, shift); - sfs.f_bfree = (long)__SHIFT_OR_CLIP(sfsp->f_bfree, shift); - sfs.f_bavail = (long)__SHIFT_OR_CLIP(sfsp->f_bavail, shift); +#define __SHIFT_OR_CLIP(x, s) ((((x) >> (s)) > INT_MAX) ? INT_MAX : ((x) >> (s))) + sfs.f_blocks = (user32_long_t)__SHIFT_OR_CLIP(sfsp->f_blocks, shift); + sfs.f_bfree = (user32_long_t)__SHIFT_OR_CLIP(sfsp->f_bfree, shift); + sfs.f_bavail = (user32_long_t)__SHIFT_OR_CLIP(sfsp->f_bavail, shift); #undef __SHIFT_OR_CLIP - sfs.f_bsize = (long)(sfsp->f_bsize << shift); + sfs.f_bsize = (user32_long_t)(sfsp->f_bsize << shift); sfs.f_iosize = lmax(sfsp->f_iosize, sfsp->f_bsize); } else { /* filesystem is small enough to be reported honestly */ - sfs.f_bsize = (long)sfsp->f_bsize; - sfs.f_iosize = (long)sfsp->f_iosize; - sfs.f_blocks = (long)sfsp->f_blocks; - sfs.f_bfree = (long)sfsp->f_bfree; - sfs.f_bavail = (long)sfsp->f_bavail; + sfs.f_bsize = (user32_long_t)sfsp->f_bsize; + sfs.f_iosize = (user32_long_t)sfsp->f_iosize; + sfs.f_blocks = (user32_long_t)sfsp->f_blocks; + sfs.f_bfree = (user32_long_t)sfsp->f_bfree; + sfs.f_bavail = (user32_long_t)sfsp->f_bavail; } - sfs.f_files = (long)sfsp->f_files; - sfs.f_ffree = (long)sfsp->f_ffree; + sfs.f_files = (user32_long_t)sfsp->f_files; + sfs.f_ffree = (user32_long_t)sfsp->f_ffree; sfs.f_fsid = sfsp->f_fsid; sfs.f_owner = sfsp->f_owner; - strncpy(&sfs.f_fstypename[0], &sfsp->f_fstypename[0], MFSNAMELEN-1); - strncpy(&sfs.f_mntonname[0], &sfsp->f_mntonname[0], MNAMELEN-1); - strncpy(&sfs.f_mntfromname[0], &sfsp->f_mntfromname[0], MNAMELEN-1); + strlcpy(&sfs.f_fstypename[0], &sfsp->f_fstypename[0], MFSNAMELEN); + strlcpy(&sfs.f_mntonname[0], &sfsp->f_mntonname[0], MNAMELEN); + strlcpy(&sfs.f_mntfromname[0], &sfsp->f_mntfromname[0], MNAMELEN); if (partial_copy) { copy_size -= (sizeof(sfs.f_reserved3) + sizeof(sfs.f_reserved4)); @@ -5474,8 +8099,125 @@ munge_statfs(struct mount *mp, struct vfsstatfs *sfsp, /* * copy stat structure into user_stat structure. */ -void munge_stat(struct stat *sbp, struct user_stat *usbp) +void munge_user64_stat(struct stat *sbp, struct user64_stat *usbp) +{ + bzero(usbp, sizeof(*usbp)); + + usbp->st_dev = sbp->st_dev; + usbp->st_ino = sbp->st_ino; + usbp->st_mode = sbp->st_mode; + usbp->st_nlink = sbp->st_nlink; + usbp->st_uid = sbp->st_uid; + usbp->st_gid = sbp->st_gid; + usbp->st_rdev = sbp->st_rdev; +#ifndef _POSIX_C_SOURCE + usbp->st_atimespec.tv_sec = sbp->st_atimespec.tv_sec; + usbp->st_atimespec.tv_nsec = sbp->st_atimespec.tv_nsec; + usbp->st_mtimespec.tv_sec = sbp->st_mtimespec.tv_sec; + usbp->st_mtimespec.tv_nsec = sbp->st_mtimespec.tv_nsec; + usbp->st_ctimespec.tv_sec = sbp->st_ctimespec.tv_sec; + usbp->st_ctimespec.tv_nsec = sbp->st_ctimespec.tv_nsec; +#else + usbp->st_atime = sbp->st_atime; + usbp->st_atimensec = sbp->st_atimensec; + usbp->st_mtime = sbp->st_mtime; + usbp->st_mtimensec = sbp->st_mtimensec; + usbp->st_ctime = sbp->st_ctime; + usbp->st_ctimensec = sbp->st_ctimensec; +#endif + usbp->st_size = sbp->st_size; + usbp->st_blocks = sbp->st_blocks; + usbp->st_blksize = sbp->st_blksize; + usbp->st_flags = sbp->st_flags; + usbp->st_gen = sbp->st_gen; + usbp->st_lspare = sbp->st_lspare; + usbp->st_qspare[0] = sbp->st_qspare[0]; + usbp->st_qspare[1] = sbp->st_qspare[1]; +} + +void munge_user32_stat(struct stat *sbp, struct user32_stat *usbp) +{ + bzero(usbp, sizeof(*usbp)); + + usbp->st_dev = sbp->st_dev; + usbp->st_ino = sbp->st_ino; + usbp->st_mode = sbp->st_mode; + usbp->st_nlink = sbp->st_nlink; + usbp->st_uid = sbp->st_uid; + usbp->st_gid = sbp->st_gid; + usbp->st_rdev = sbp->st_rdev; +#ifndef _POSIX_C_SOURCE + usbp->st_atimespec.tv_sec = sbp->st_atimespec.tv_sec; + usbp->st_atimespec.tv_nsec = sbp->st_atimespec.tv_nsec; + usbp->st_mtimespec.tv_sec = sbp->st_mtimespec.tv_sec; + usbp->st_mtimespec.tv_nsec = sbp->st_mtimespec.tv_nsec; + usbp->st_ctimespec.tv_sec = sbp->st_ctimespec.tv_sec; + usbp->st_ctimespec.tv_nsec = sbp->st_ctimespec.tv_nsec; +#else + usbp->st_atime = sbp->st_atime; + usbp->st_atimensec = sbp->st_atimensec; + usbp->st_mtime = sbp->st_mtime; + usbp->st_mtimensec = sbp->st_mtimensec; + usbp->st_ctime = sbp->st_ctime; + usbp->st_ctimensec = sbp->st_ctimensec; +#endif + usbp->st_size = sbp->st_size; + usbp->st_blocks = sbp->st_blocks; + usbp->st_blksize = sbp->st_blksize; + usbp->st_flags = sbp->st_flags; + usbp->st_gen = sbp->st_gen; + usbp->st_lspare = sbp->st_lspare; + usbp->st_qspare[0] = sbp->st_qspare[0]; + usbp->st_qspare[1] = sbp->st_qspare[1]; +} + +/* + * copy stat64 structure into user_stat64 structure. + */ +void munge_user64_stat64(struct stat64 *sbp, struct user64_stat64 *usbp) +{ + bzero(usbp, sizeof(*usbp)); + + usbp->st_dev = sbp->st_dev; + usbp->st_ino = sbp->st_ino; + usbp->st_mode = sbp->st_mode; + usbp->st_nlink = sbp->st_nlink; + usbp->st_uid = sbp->st_uid; + usbp->st_gid = sbp->st_gid; + usbp->st_rdev = sbp->st_rdev; +#ifndef _POSIX_C_SOURCE + usbp->st_atimespec.tv_sec = sbp->st_atimespec.tv_sec; + usbp->st_atimespec.tv_nsec = sbp->st_atimespec.tv_nsec; + usbp->st_mtimespec.tv_sec = sbp->st_mtimespec.tv_sec; + usbp->st_mtimespec.tv_nsec = sbp->st_mtimespec.tv_nsec; + usbp->st_ctimespec.tv_sec = sbp->st_ctimespec.tv_sec; + usbp->st_ctimespec.tv_nsec = sbp->st_ctimespec.tv_nsec; + usbp->st_birthtimespec.tv_sec = sbp->st_birthtimespec.tv_sec; + usbp->st_birthtimespec.tv_nsec = sbp->st_birthtimespec.tv_nsec; +#else + usbp->st_atime = sbp->st_atime; + usbp->st_atimensec = sbp->st_atimensec; + usbp->st_mtime = sbp->st_mtime; + usbp->st_mtimensec = sbp->st_mtimensec; + usbp->st_ctime = sbp->st_ctime; + usbp->st_ctimensec = sbp->st_ctimensec; + usbp->st_birthtime = sbp->st_birthtime; + usbp->st_birthtimensec = sbp->st_birthtimensec; +#endif + usbp->st_size = sbp->st_size; + usbp->st_blocks = sbp->st_blocks; + usbp->st_blksize = sbp->st_blksize; + usbp->st_flags = sbp->st_flags; + usbp->st_gen = sbp->st_gen; + usbp->st_lspare = sbp->st_lspare; + usbp->st_qspare[0] = sbp->st_qspare[0]; + usbp->st_qspare[1] = sbp->st_qspare[1]; +} + +void munge_user32_stat64(struct stat64 *sbp, struct user32_stat64 *usbp) { + bzero(usbp, sizeof(*usbp)); + usbp->st_dev = sbp->st_dev; usbp->st_ino = sbp->st_ino; usbp->st_mode = sbp->st_mode; @@ -5483,13 +8225,15 @@ void munge_stat(struct stat *sbp, struct user_stat *usbp) usbp->st_uid = sbp->st_uid; usbp->st_gid = sbp->st_gid; usbp->st_rdev = sbp->st_rdev; -#ifndef _POSIX_SOURCE +#ifndef _POSIX_C_SOURCE usbp->st_atimespec.tv_sec = sbp->st_atimespec.tv_sec; usbp->st_atimespec.tv_nsec = sbp->st_atimespec.tv_nsec; usbp->st_mtimespec.tv_sec = sbp->st_mtimespec.tv_sec; usbp->st_mtimespec.tv_nsec = sbp->st_mtimespec.tv_nsec; usbp->st_ctimespec.tv_sec = sbp->st_ctimespec.tv_sec; usbp->st_ctimespec.tv_nsec = sbp->st_ctimespec.tv_nsec; + usbp->st_birthtimespec.tv_sec = sbp->st_birthtimespec.tv_sec; + usbp->st_birthtimespec.tv_nsec = sbp->st_birthtimespec.tv_nsec; #else usbp->st_atime = sbp->st_atime; usbp->st_atimensec = sbp->st_atimensec; @@ -5497,6 +8241,8 @@ void munge_stat(struct stat *sbp, struct user_stat *usbp) usbp->st_mtimensec = sbp->st_mtimensec; usbp->st_ctime = sbp->st_ctime; usbp->st_ctimensec = sbp->st_ctimensec; + usbp->st_birthtime = sbp->st_birthtime; + usbp->st_birthtimensec = sbp->st_birthtimensec; #endif usbp->st_size = sbp->st_size; usbp->st_blocks = sbp->st_blocks;