/*
- * Copyright (c) 1995-2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1995-2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
*
* @(#)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 <sys/param.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/xattr.h>
#include <sys/ubc_internal.h>
+#include <sys/disk.h>
#include <machine/cons.h>
#include <machine/limits.h>
#include <miscfs/specfs/specdev.h>
+#include <miscfs/union/union.h>
#include <bsm/audit_kernel.h>
#include <bsm/audit_kevents.h>
#include <vm/vm_pageout.h>
-#include <architecture/byte_order.h>
#include <libkern/OSAtomic.h>
+#if CONFIG_MACF
+#include <security/mac.h>
+#include <security/mac_framework.h>
+#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 void checkdirs(struct vnode *olddp, 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);
+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 *, register_t *);
+
+__private_extern__
+int unlink1(vfs_context_t, struct nameidata *, int);
-__private_extern__ int sync_internal(void);
#ifdef __APPLE_API_OBSOLETE
struct fstatv_args {
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, register_t *retval);
+int lstatv(proc_t p, struct lstatv_args *uap, register_t *retval);
+int mkcomplex(proc_t p, struct mkcomplex_args *uap, register_t *retval);
+int statv(proc_t p, struct statv_args *uap, register_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
+ */
+int 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 *);
/*
*/
/* ARGSUSED */
int
-mount(struct proc *p, register struct mount_args *uap, __unused register_t *retval)
+mount(proc_t p, struct mount_args *uap, __unused register_t *retval)
+{
+ 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));
+}
+
+int
+__mac_mount(struct proc *p, register struct __mac_mount_args *uap, __unused register_t *retval)
{
struct vnode *vp;
struct vnode *devvp = NULLVP;
struct vnode *device_vnode = NULLVP;
+#if CONFIG_MACF
+ struct vnode *rvp;
+#endif
struct mount *mp;
- struct vfstable *vfsp;
+ 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];
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,
+ UIO_USERSPACE, uap->path, ctx);
error = namei(&nd);
if (error)
return (error);
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;
if (uap->flags & MNT_UPDATE) {
if ((vp->v_flag & VROOT) == 0) {
* 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;
*/
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)) )
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))
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_spin(vp);
SET(vp->v_flag, VMOUNT);
+ vnode_unlock(vp);
/*
* Allocate and initialize the filesystem.
mp->mnt_maxsegreadsize = mp->mnt_maxreadcnt;
mp->mnt_maxsegwritesize = mp->mnt_maxwritecnt;
mp->mnt_devblocksize = DEV_BSIZE;
+ mp->mnt_alignmentmask = PAGE_MASK;
+ 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);
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);
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);
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);
+
+#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) {
/* 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;
* 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;
}
}
error = EBUSY;
goto out3;
}
- if ( (error = VNOP_FSYNC(devvp, MNT_WAIT, &context)) ) {
+ if ( (error = VNOP_FSYNC(devvp, MNT_WAIT, ctx)) ) {
error = ENOTBLK;
goto out3;
}
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;
* that user has necessary permissions on the device.
*/
device_vnode = mp->mnt_devvp;
- if (device_vnode && suser(context.vc_ucred, NULL)) {
+ if (device_vnode && suser(vfs_context_ucred(ctx), NULL)) {
if ((error = vnode_authorize(device_vnode, NULL,
- KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, &context)) != 0)
+ KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, ctx)) != 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)
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) {
- CLR(vp->v_flag, VMOUNT);
+ if (error == 0) {
+ 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;
+ }
+
+ /* VFS_ROOT provides reference so needref = 0 */
+ error = vnode_label(mp, NULL, rvp, NULL, 0, ctx);
+ if (error)
+ goto out3;
+ }
+#endif /* MAC */
- vnode_lock(vp);
+ vnode_lock_spin(vp);
+ CLR(vp->v_flag, VMOUNT);
vp->v_mountedhere = mp;
vnode_unlock(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();
+
vnode_ref(vp);
- vfs_event_signal(NULL, VQ_MOUNT, (intptr_t)NULL);
- checkdirs(vp, &context);
- lck_rw_done(&mp->mnt_rwlock);
- is_rwlock_locked = FALSE;
- mount_list_add(mp);
+ error = checkdirs(vp, ctx);
+ if (error != 0) {
+ /* Unmount the filesystem as cdir/rdirs cannot be updated */
+ goto out4;
+ }
/*
* 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);
+
+ 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);
+ enablequotas(mp, ctx);
if (device_vnode) {
device_vnode->v_specflags |= SI_MOUNTEDON;
*/
vfs_init_io_attributes(device_vnode, mp);
}
+
+ /* Now that mount is setup, notify the listeners */
+ vfs_event_signal(NULL, VQ_MOUNT, (intptr_t)NULL);
} else {
+ vnode_lock_spin(vp);
CLR(vp->v_flag, VMOUNT);
+ vnode_unlock(vp);
mount_list_lock();
mp->mnt_vtable->vfc_refcount--;
mount_list_unlock();
if (device_vnode ) {
- VNOP_CLOSE(device_vnode, ronly ? FREAD : FREAD|FWRITE, &context);
+ VNOP_CLOSE(device_vnode, ronly ? FREAD : FREAD|FWRITE, ctx);
vnode_rele(device_vnode);
}
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);
vnode_put(vp);
return(error);
+out4:
+ (void)VFS_UNMOUNT(mp, MNT_FORCE, ctx);
+ if (device_vnode != NULLVP) {
+ VNOP_CLOSE(device_vnode, mp->mnt_flag & MNT_RDONLY ? FREAD : FREAD|FWRITE,
+ ctx);
+ }
+ vnode_lock_spin(vp);
+ vp->v_mountedhere = (mount_t) 0;
+ vnode_unlock(vp);
+ vnode_rele(vp);
out3:
- vnode_rele(devvp);
+ if (devpath && ((uap->flags & MNT_UPDATE) == 0))
+ vnode_rele(devvp);
out2:
if (devpath && devvp)
vnode_put(devvp);
if (is_rwlock_locked == TRUE) {
lck_rw_done(&mp->mnt_rwlock);
}
- if (mntalloc)
+ if (mntalloc) {
+#if CONFIG_MACF
+ mac_mount_label_destroy(mp);
+#endif
+ mount_list_lock();
+ vfsp->vfc_refcount--;
+ mount_list_unlock();
FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT);
+ }
vnode_put(vp);
nameidone(&nd);
return(error);
-
}
void
-enablequotas(struct mount *mp, vfs_context_t context)
+enablequotas(struct mount *mp, vfs_context_t ctx)
{
struct nameidata qnd;
int type;
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))
+ /* XXX Shoulkd be an MNTK_ flag, instead of strncmp()'s */
+ if ((strncmp(mp->mnt_vfsstat.f_fstypename, "hfs", sizeof("hfs")) != 0 )
+ && (strncmp( mp->mnt_vfsstat.f_fstypename, "ufs", sizeof("ufs")) != 0))
return;
/*
* 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);
+ snprintf(qfpath, sizeof(qfpath), "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfopsname, qfextension[type]);
+ NDINIT(&qnd, LOOKUP, FOLLOW, UIO_SYSSPACE32, 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);
- sprintf(qfpath, "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfname, qfextension[type]);
+ 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, context);
+ (void) VFS_QUOTACTL(mp, QCMD(Q_QUOTAON, type), 0, qfpath, ctx);
}
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.
- */
-static void
-checkdirs(olddp, context)
- struct vnode *olddp;
- vfs_context_t context;
+
+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;
- struct vnode *newdp;
- struct proc *p;
- struct vnode *tvp;
- struct vnode *fdp_cvp;
- struct vnode *fdp_rvp;
+ vnode_t tvp;
+ vnode_t fdp_cvp;
+ vnode_t fdp_rvp;
int cdir_changed = 0;
int rdir_changed = 0;
- boolean_t funnel_state;
- if (olddp->v_usecount == 1)
- return;
- if (VFS_ROOT(olddp->v_mountedhere, &newdp, context))
- panic("mount: lost mount");
- funnel_state = thread_funnel_set(kernel_flock, TRUE);
+ /*
+ * 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);
- for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) {
+ 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 = p->p_fd;
- if (fdp == (struct filedesc *)0) {
- proc_fdunlock(p);
- continue;
- }
- fdp_cvp = fdp->fd_cdir;
- fdp_rvp = fdp->fd_rdir;
+ fdp->fd_cdir = fdp_cvp;
+ fdp->fd_rdir = fdp_rvp;
proc_fdunlock(p);
+ }
+ return(PROC_RETURNED);
+}
- 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);
- }
+
+
+/*
+ * 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);
}
- thread_funnel_set(kernel_flock, funnel_state);
vnode_put(newdp);
+ return(0);
}
/*
*/
/* ARGSUSED */
int
-unmount(struct proc *p, register struct unmount_args *uap, __unused register_t *retval)
+unmount(__unused proc_t p, struct unmount_args *uap, __unused register_t *retval)
{
- register struct vnode *vp;
+ vnode_t vp;
struct mount *mp;
int error;
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, FOLLOW | AUDITVNPATH1,
- UIO_USERSPACE, uap->path, &context);
+ NDINIT(&nd, LOOKUP, NOTRIGGER | FOLLOW | AUDITVNPATH1,
+ UIO_USERSPACE, uap->path, ctx);
error = namei(&nd);
if (error)
return (error);
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
*/
vnode_put(vp);
return (EINVAL);
}
+ mount_ref(mp, 0);
vnode_put(vp);
- return (safedounmount(mp, uap->flags, p));
+ /* 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.
- *
- * XXX Should take a "vfs_context_t" instead of a "struct proc *"
*/
int
-safedounmount(mp, flags, p)
- struct mount *mp;
- int flags;
- struct proc *p;
+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
*/
if ((mp->mnt_vfsstat.f_owner != kauth_cred_getuid(kauth_cred_get())) &&
(error = suser(kauth_cred_get(), &p->p_acflag)))
- return (error);
+ goto out;
/*
* Don't allow unmounting the root file system.
*/
- if (mp->mnt_flag & MNT_ROOTFS)
- return (EBUSY); /* the root is always busy */
+ if (mp->mnt_flag & MNT_ROOTFS) {
+ error = EBUSY; /* the root is always busy */
+ goto out;
+ }
+
+ return (dounmount(mp, flags, 1, ctx));
- return (dounmount(mp, flags, p));
+out:
+ mount_drop(mp, 0);
+ return(error);
}
/*
* Do the actual file system unmount.
*/
int
-dounmount(mp, flags, p)
- register struct mount *mp;
- int flags;
- 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;
}
if (mp->mnt_lflag & MNT_LUNMOUNT) {
mp->mnt_lflag |= MNT_LWAIT;
- msleep((caddr_t)mp, &mp->mnt_mlock, (PVFS | PDROP), "dounmount", 0 );
+ if(withref != 0)
+ mount_drop(mp, 1);
+ 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.
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;
/* 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);
OSAddAtomic(1, (SInt32 *)&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_clearmountedon(devvp);
+ vnode_getalways(devvp);
+ vnode_rele(devvp);
+ VNOP_CLOSE(devvp, mp->mnt_flag & MNT_RDONLY ? FREAD : FREAD|FWRITE,
+ ctx);
+ vnode_put(devvp);
}
lck_rw_done(&mp->mnt_rwlock);
mount_list_remove(mp);
/* 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);
if ((coveredvp != NULLVP)) {
vnode_getwithref(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_put(coveredvp);
} 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");
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;
}
static int
sync_callback(mount_t mp, __unused 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, MNT_NOWAIT, vfs_context_current());
if (asyncflag)
mp->mnt_flag |= MNT_ASYNC;
}
/* 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 register_t *retval)
{
vfs_iterate(LK_NOWAIT, sync_callback, (void *)0);
/*
* Change filesystem quotas.
*/
-/* ARGSUSED */
+#if QUOTA
+static int quotactl_funneled(proc_t p, struct quotactl_args *uap, register_t *retval);
+
int
-quotactl(struct proc *p, register struct quotactl_args *uap, __unused register_t *retval)
+quotactl(proc_t p, struct quotactl_args *uap, register_t *retval)
+{
+ 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 register_t *retval)
{
- register struct mount *mp;
+ 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(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);
} /* 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) {
return (error);
}
+#else
+int
+quotactl(__unused proc_t p, __unused struct quotactl_args *uap, __unused register_t *retval)
+{
+ return (EOPNOTSUPP);
+}
+#endif /* QUOTA */
/*
* Get filesystem statistics.
+ *
+ * Returns: 0 Success
+ * namei:???
+ * vfs_update_vfsstat:???
+ * munge_statfs:EFAULT
*/
/* ARGSUSED */
int
-statfs(struct proc *p, register struct statfs_args *uap, __unused register_t *retval)
+statfs(__unused proc_t p, struct statfs_args *uap, __unused register_t *retval)
{
struct mount *mp;
struct vfsstatfs *sp;
int error;
struct nameidata nd;
- struct vfs_context context;
+ vfs_context_t ctx = 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, ctx);
error = namei(&nd);
if (error)
return (error);
sp = &mp->mnt_vfsstat;
nameidone(&nd);
- error = vfs_update_vfsstat(mp, &context);
+ error = vfs_update_vfsstat(mp, ctx, VFS_USER_EVENT);
vnode_put(vp);
if (error != 0)
return (error);
*/
/* ARGSUSED */
int
-fstatfs(struct proc *p, register struct fstatfs_args *uap, __unused register_t *retval)
+fstatfs(__unused proc_t p, struct fstatfs_args *uap, __unused register_t *retval)
{
- struct vnode *vp;
+ vnode_t 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);
return (EBADF);
}
sp = &mp->mnt_vfsstat;
- if ((error = vfs_update_vfsstat(mp, &context)) != 0) {
+ if ((error = vfs_update_vfsstat(mp,vfs_context_current(),VFS_USER_EVENT)) != 0) {
file_drop(uap->fd);
return (error);
}
return (error);
}
-
-struct getfsstat_struct {
- user_addr_t sfsp;
- int count;
- int maxcount;
- int flags;
- int error;
-};
-
-
-static int
-getfsstat_callback(mount_t mp, void * arg)
+/*
+ * 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;
- 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();
-
- 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.
- */
- if (((fstp->flags & MNT_NOWAIT) == 0 || (fstp->flags & MNT_WAIT)) &&
- (error = vfs_update_vfsstat(mp, &context))) {
- KAUTH_DEBUG("vfs_update_vfsstat returned %d", error);
- return(VFS_RETURNED);
- }
+ 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));
- /*
- * Need to handle LP64 version of struct statfs
- */
- error = munge_statfs(mp, sp, fstp->sfsp, &my_size, IS_64BIT_PROCESS(p), FALSE);
- if (error) {
- fstp->error = error;
- return(VFS_RETURNED_DONE);
- }
- fstp->sfsp += my_size;
- }
- fstp->count++;
- return(VFS_RETURNED);
+ return(error);
}
-/*
- * Get statistics on all filesystems.
+/*
+ * Get file system statistics in 64-bit mode
+ */
+int
+statfs64(__unused struct proc *p, struct statfs64_args *uap, __unused register_t *retval)
+{
+ struct mount *mp;
+ struct vfsstatfs *sp;
+ int error;
+ struct nameidata nd;
+ vfs_context_t ctxp = vfs_context_current();
+ vnode_t vp;
+
+ NDINIT(&nd, LOOKUP, NOTRIGGER | FOLLOW | AUDITVNPATH1,
+ UIO_USERSPACE, uap->path, ctxp);
+ 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, ctxp, VFS_USER_EVENT);
+ vnode_put(vp);
+ if (error != 0)
+ return (error);
+
+ error = statfs64_common(mp, sp, uap->buf);
+
+ return (error);
+}
+
+/*
+ * Get file system statistics in 64-bit mode
+ */
+int
+fstatfs64(__unused struct proc *p, struct fstatfs64_args *uap, __unused register_t *retval)
+{
+ struct vnode *vp;
+ struct mount *mp;
+ struct vfsstatfs *sp;
+ int error;
+
+ AUDIT_ARG(fd, uap->fd);
+
+ if ( (error = file_vnode(uap->fd, &vp)) )
+ return (error);
+
+ AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1);
+
+ mp = vp->v_mount;
+ if (!mp) {
+ file_drop(uap->fd);
+ return (EBADF);
+ }
+ sp = &mp->mnt_vfsstat;
+ if ((error = vfs_update_vfsstat(mp, vfs_context_current(), VFS_USER_EVENT)) != 0) {
+ file_drop(uap->fd);
+ return (error);
+ }
+ file_drop(uap->fd);
+
+ error = statfs64_common(mp, sp, uap->buf);
+
+ return (error);
+}
+
+struct getfsstat_struct {
+ user_addr_t sfsp;
+ user_addr_t *mp;
+ int count;
+ int maxcount;
+ int flags;
+ int error;
+};
+
+
+static int
+getfsstat_callback(mount_t mp, void * arg)
+{
+
+ struct getfsstat_struct *fstp = (struct getfsstat_struct *)arg;
+ struct vfsstatfs *sp;
+ int error, my_size;
+ 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.
+ */
+ if (((fstp->flags & MNT_NOWAIT) == 0 || (fstp->flags & MNT_WAIT)) &&
+ (error = vfs_update_vfsstat(mp, ctx,
+ VFS_USER_EVENT))) {
+ KAUTH_DEBUG("vfs_update_vfsstat returned %d", error);
+ return(VFS_RETURNED);
+ }
+
+ /*
+ * Need to handle LP64 version of struct statfs
+ */
+ 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);
+}
+
+/*
+ * Get statistics on all filesystems.
*/
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));
+}
+
+int
+__mac_getfsstat(__unused proc_t p, struct __mac_getfsstat_args *uap, int *retval)
{
user_addr_t sfsp;
+ user_addr_t *mp;
int count, maxcount;
struct getfsstat_struct fst;
sfsp = uap->buf;
count = 0;
+ mp = NULL;
+
+#if CONFIG_MACF
+ if (uap->mac != USER_ADDR_NULL) {
+ u_int32_t *mp0;
+ int error;
+ int i;
+
+ count = (int)(uap->macsize / (IS_64BIT_PROCESS(p) ? 8 : 4));
+ if (count != maxcount)
+ return (EINVAL);
+
+ /* Copy in the array */
+ MALLOC(mp0, u_int32_t *, uap->macsize, M_MACTEMP, M_WAITOK);
+ error = copyin(uap->mac, mp0, uap->macsize);
+ if (error)
+ return (error);
+
+ /* Normalize to an array of user_addr_t */
+ MALLOC(mp, user_addr_t *, count * sizeof(user_addr_t), M_MACTEMP, M_WAITOK);
+ 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;
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);
return (0);
}
+static int
+getfsstat64_callback(mount_t mp, void * arg)
+{
+ 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.
+ */
+ if (((fstp->flags & MNT_NOWAIT) == 0 || (fstp->flags & MNT_WAIT)) &&
+ (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);
+}
+
#if COMPAT_GETFSSTAT
-ogetfsstat(p, uap, retval)
- struct proc *p;
- register struct getfsstat_args *uap;
- register_t *retval;
+ogetfsstat(proc_t p, struct getfsstat_args *uap, register_t *retval)
{
return (ENOTSUP);
}
* 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();
+ 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);
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;
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, (UInt32 *)&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);
return(error);
}
+int
+fchdir(proc_t p, struct fchdir_args *uap, __unused register_t *retval)
+{
+ return common_fchdir(p, uap, 0);
+}
+
+int
+__pthread_fchdir(proc_t p, struct __pthread_fchdir_args *uap, __unused register_t *retval)
+{
+ return common_fchdir(p, (void *)uap, 1);
+}
+
/*
* 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)) ) {
*/
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, (UInt32 *)&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);
return (0);
}
+int
+chdir(proc_t p, struct chdir_args *uap, __unused register_t *retval)
+{
+ return common_chdir(p, (void *)uap, 0);
+}
+
+int
+__pthread_chdir(proc_t p, struct __pthread_chdir_args *uap, __unused register_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 register_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);
/*
* 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);
}
/*
* 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:???
*/
-
#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, register_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;
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);
}
}
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;
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;
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);
- *fdflags(p, indx) &= ~UF_RESERVED;
+ procfdtbl_releasefd(p, indx, NULL);
fp_drop(p, indx, fp, 1);
proc_fdunlock(p);
*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);
}
+/*
+ * An open system call using an extended argument list compared to the regular
+ * system call 'open'.
+ *
+ * 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, register_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;
xsecdst = 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);
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);
}
int
-open(struct proc *p, struct open_args *uap, register_t *retval)
+open(proc_t p, struct open_args *uap, register_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, register_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));
}
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 register_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);
- 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);
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);
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;
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:
/*
* 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)
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:
return error;
}
+
+/*
+ * A mkfifo system call using an extended argument list compared to the regular
+ * system call 'mkfifo'.
+ *
+ * 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 register_t *retval)
{
int ciferror;
kauth_filesec_t xsecdst;
- struct vfs_context context;
struct vnode_attr va;
xsecdst = KAUTH_FILESEC_NONE;
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)
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);
/* ARGSUSED */
int
-mkfifo(struct proc *p, register struct mkfifo_args *uap, __unused register_t *retval)
+mkfifo(proc_t p, struct mkfifo_args *uap, __unused register_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));
}
/*
* 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 register_t *retval)
{
vnode_t vp, dvp, lvp;
struct nameidata nd;
- struct vfs_context context;
+ vfs_context_t ctx = vfs_context_current();
int error;
fse_info finfo;
int need_event, has_listeners;
+ char *target_path = NULL;
- 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);
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;
}
/* 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();
+ GET_PATH(target_path);
+ if (target_path == NULL) {
+ error = ENOMEM;
+ goto out2;
+ }
+
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;
+ if ((len + 1 + nd.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
+ target_path[len-1] = '/';
+ strlcpy(&target_path[len], nd.ni_cnd.cn_nameptr, MAXPATHLEN-len);
+ len += nd.ni_cnd.cn_namelen;
+ }
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) {
// 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);
}
}
- release_pathbuff(target_path);
+#endif
}
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);
*/
/* ARGSUSED */
int
-symlink(struct proc *p, register struct symlink_args *uap, __unused register_t *retval)
+symlink(proc_t p, struct symlink_args *uap, __unused register_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)
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:
/*
/* 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 register_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);
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;
* 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;
+ fse_info finfo;
int flags = 0;
+ int need_event = 0;
+ int has_listeners = 0;
- context.vc_proc = p;
- context.vc_ucred = kauth_cred_get();
+ ndp->ni_cnd.cn_flags |= LOCKPARENT;
+ cnp = &ndp->ni_cnd;
- NDINIT(&nd, DELETE, LOCKPARENT | AUDITVNPATH1,
- UIO_USERSPACE, uap->path, &context);
- cnp = &nd.ni_cnd;
+ error = namei(ndp);
+ if (error)
+ return (error);
+ 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 = vnode_authorize(vp, nd.ni_dvp, KAUTH_VNODE_DELETE, &context);
+ error = mac_vnode_check_unlink(ctx,
+ dvp, vp, cnp);
+#endif /* MAC */
+ if (!error)
+ 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 = MAXPATHLEN;
+ vn_getpath(vp, path, &len);
+ }
+
+#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);
+ }
+ 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:
+ nameidone(ndp);
vnode_put(dvp);
vnode_put(vp);
return (error);
* 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 register_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 register_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;
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);
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;
/*
* Check access permissions.
+ *
+ * Returns: 0 Success
+ * vnode_authorize:???
*/
static int
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);
-/* XXX need to support the check-as uid argument */
+/*
+ * access_extended
+ *
+ * Description: uap->entries Pointer to argument descriptor
+ * uap->size Size of the area pointed to by
+ * the descriptor
+ * 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 terniated
+ * 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 descrptor 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 NUL 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 register_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;
/*
- * 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 */
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;
}
/* 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);
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 register_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'
* real identity, even if operations are checking the
* 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) &&
+ ((nd.ni_vp->v_parent->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0)) {
+ 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 stat64 sb64;
struct user_stat user_sb;
+ struct user_stat64 user_sb64;
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);
+ if (isstat64 != 0)
+ statptr = (void *)&sb64;
+ else
+ statptr = (void *)&sb;
+
+#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) &&
+ ((ndp->ni_vp->v_parent->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) == 0)) {
+ 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) {
+ sb64.st_lspare = 0;
+ sb64.st_qspare[0] = 0LL;
+ sb64.st_qspare[1] = 0LL;
+ if (IS_64BIT_PROCESS(vfs_context_proc(ctx))) {
+ munge_stat64(&sb64, &user_sb64);
+ my_size = sizeof(user_sb64);
+ sbp = (caddr_t)&user_sb64;
+ } else {
+ my_size = sizeof(sb64);
+ sbp = (caddr_t)&sb64;
+ }
+ /*
+ * Check if we raced (post lookup) against the last unlink of a file.
+ */
+ if ((sb64.st_nlink == 0) && S_ISREG(sb64.st_mode)) {
+ sb64.st_nlink = 1;
+ }
+ } else {
+ 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;
+ }
+
+ /*
+ * Check if we raced (post lookup) against the last unlink of a file.
+ */
+ if ((sb.st_nlink == 0) && S_ISREG(sb.st_mode)) {
+ sb.st_nlink = 1;
+ }
}
if ((error = copyout(sbp, ub, my_size)) != 0)
goto 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));
+int
+stat_extended(__unused proc_t p, struct stat_extended_args *uap, __unused register_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 register_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 register_t *retval)
{
- return(stat1(p, uap->path, uap->ub, 0, 0));
+ return(stat1(uap->path, uap->ub, 0, 0, 1));
}
+int
+stat64_extended(__unused proc_t p, struct stat64_extended_args *uap, __unused register_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));
+}
- return(stat2(&context, &nd, ub, xsecurity, xsecurity_size));
+int
+lstat_extended(__unused proc_t p, struct lstat_extended_args *uap, __unused register_t *retval)
+{
+ return (lstat1(uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size, 0));
}
int
-lstat_extended(struct proc *p, struct lstat_extended_args *uap, __unused register_t *retval)
+lstat(__unused proc_t p, struct lstat_args *uap, __unused register_t *retval)
+{
+ return(lstat1(uap->path, uap->ub, 0, 0, 0));
+}
+int
+lstat64(__unused proc_t p, struct lstat64_args *uap, __unused register_t *retval)
{
- return (lstat1(p, uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size));
+ return(lstat1(uap->path, uap->ub, 0, 0, 1));
}
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 register_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, register_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);
*/
/* 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, register_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);
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
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;
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);
*/
/* 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 register_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);
}
*/
/* 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 register_t *retval)
{
- struct vfs_context context;
- struct vnode *vp;
+ vnode_t vp;
int error;
AUDIT_ARG(fd, uap->fd);
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);
/*
* 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
+#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) ||
((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
/*
* Change mode of a file given 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)
return(error);
}
+/*
+ * A chmod system call using an extended argument list compared to the regular
+ * system call 'mkfifo'.
+ *
+ * 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 register_t *retval)
{
- struct vfs_context context;
int error;
struct vnode_attr va;
kauth_filesec_t xsecdst;
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 register_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);
}
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);
}
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 register_t *retval)
{
int error;
struct vnode_attr va;
}
int
-fchmod(struct proc *p, register struct fchmod_args *uap, __unused register_t *retval)
+fchmod(proc_t p, struct fchmod_args *uap, __unused register_t *retval)
{
struct vnode_attr va;
*/
/* 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 register_t *retval, int follow)
{
- register struct vnode *vp;
+ vnode_t vp;
struct vnode_attr va;
int error;
struct nameidata nd;
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)
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;
}
int
-chown(struct proc *p, register struct chown_args *uap, register_t *retval)
+chown(__unused proc_t p, struct chown_args *uap, register_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, register_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);
}
/*
*/
/* 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 register_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;
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);
}
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;
}
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;
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:
*/
/* 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 register_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);
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);
*/
/* 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 register_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;
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);
*/
/* 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 register_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;
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);
*/
/* 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, register_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);
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);
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);
*/
/* ARGSUSED */
int
-fsync(struct proc *p, struct fsync_args *uap, __unused register_t *retval)
+fsync(proc_t p, struct fsync_args *uap, register_t *retval)
{
- struct vnode *vp;
+ __pthread_testcancel(1);
+ return(fsync_nocancel(p, (struct fsync_nocancel_args *)uap, retval));
+}
+
+int
+fsync_nocancel(proc_t p, struct fsync_nocancel_args *uap, __unused register_t *retval)
+{
+ vnode_t vp;
struct fileproc *fp;
- struct vfs_context context;
+ vfs_context_t ctx = vfs_context_current();
int error;
if ( (error = fp_getfvp(p, uap->fd, &fp, &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);
+ error = VNOP_FSYNC(vp, MNT_WAIT, ctx);
+
+#if NAMEDRSRCFORK
+ /* Sync resource fork shadow file if necessary. */
+ if ((error == 0) &&
+ (vp->v_flag & VISNAMEDSTREAM) &&
+ (vp->v_parent != NULLVP) &&
+ !(vp->v_parent->v_mount->mnt_kern_flag & MNTK_NAMED_STREAMS) &&
+ (fp->f_flags & FP_WRITTEN)) {
+ (void) vnode_flushnamedstream(vp->v_parent, vp, ctx);
+ }
+#endif
(void)vnode_put(vp);
file_drop(uap->fd);
*/
/* 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 register_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. */
}
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;
}
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)
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;
/*
*/
/* 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 register_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 need_event;
+ const char *oname;
+ char *from_name = NULL, *to_name = NULL;
int from_len, to_len;
int holding_mntlock;
mount_t locked_mp = NULL;
vnode_t oparent;
fse_info from_finfo, to_finfo;
- 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;
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;
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.
*
- * Note that we do not inherit when renaming. XXX this needs to be revisited to
- * implement the deferred-inherit bit.
+ * XXX This needs to be revisited to implement the deferred-inherit bit
*/
{
int moving = 0;
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? */
(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;
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);
+ vn_getpath(fdvp, from_name, &from_len);
+ if ((from_len + 1 + fromnd.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
+ if (from_len > 2) {
+ from_name[from_len-1] = '/';
+ } else {
+ from_len--;
+ }
+ strlcpy(&from_name[from_len], fromnd.ni_cnd.cn_nameptr, MAXPATHLEN-from_len);
+ from_len += fromnd.ni_cnd.cn_namelen + 1;
+ from_name[from_len] = '\0';
+ }
- to_name = get_pathbuff();
- to_len = MAXPATHLEN;
+ GET_PATH(to_name);
+ if (to_name == NULL) {
+ error = ENOMEM;
+ goto out1;
+ }
- 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';
+ to_len = MAXPATHLEN;
+ vn_getpath(tdvp, to_name, &to_len);
+ // if the path is not just "/", then append a "/"
+ if ((to_len + 1 + tond.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
+ if (to_len > 2) {
+ to_name[to_len-1] = '/';
+ } else {
+ to_len--;
+ }
+ strlcpy(&to_name[to_len], tond.ni_cnd.cn_nameptr, MAXPATHLEN-to_len);
+ to_len += tond.ni_cnd.cn_namelen + 1;
+ to_name[to_len] = '\0';
}
- } else {
- from_name = NULL;
- to_name = NULL;
- }
+ }
+
error = VNOP_RENAME(fdvp, fvp, &fromnd.ni_cnd,
tdvp, tvp, &tond.ni_cnd,
- &context);
+ ctx);
if (holding_mntlock) {
/*
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.
+ */
+ if (error == EEXIST) {
+ do_retry = 1;
+ }
goto out1;
}
/* 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 (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
/* 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);
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) {
/*
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
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)
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:
/*
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 register_t *retval)
{
- struct vfs_context context;
int ciferror;
kauth_filesec_t xsecdst;
struct vnode_attr va;
((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 register_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));
}
/*
*/
/* ARGSUSED */
int
-rmdir(struct proc *p, struct rmdir_args *uap, __unused register_t *retval)
+rmdir(__unused proc_t p, struct rmdir_args *uap, __unused register_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, oldvp_id = -1;
- 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 != -1 && 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;
+ fse_info finfo;
+ int has_listeners = 0;
+ int need_event = 0;
+
+#if CONFIG_FSE
+ 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;
+ }
+ len = MAXPATHLEN;
+ vn_getpath(vp, path, &len);
+ }
- vnode_put(dvp);
- vnode_put(vp);
+ 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);
+
+ auio = uio_create(1, 0, UIO_SYSSPACE32, 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:
if (vp->v_type != VDIR) {
(void)vnode_put(vp);
- error = EINVAL;
+ error = EINVAL;
+ goto out;
+ }
+
+#if CONFIG_MACF
+ error = mac_vnode_check_readdir(&context, vp);
+ if (error != 0) {
+ (void)vnode_put(vp);
goto out;
}
- context.vc_proc = p;
- context.vc_ucred = fp->f_fglob->fg_cred;
+#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));
+ if (offset) {
+ *offset = loff;
+ }
// LP64todo - fix this
- *retval = uap->count - uio_resid(auio);
+ *bytesread = bufsize - uio_resid(auio);
out:
file_drop(fd);
return (error);
}
+
+int
+getdirentries(__unused struct proc *p, struct getdirentries_args *uap, register_t *retval)
+{
+ off_t offset;
+ long loff;
+ 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) {
+ loff = (long)offset;
+ error = copyout((caddr_t)&loff, uap->basep, sizeof(long));
+ *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.
*/
#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, register_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);
}
int
-umask_extended(struct proc *p, struct umask_extended_args *uap, register_t *retval)
+umask_extended(proc_t p, struct umask_extended_args *uap, register_t *retval)
{
int ciferror;
kauth_filesec_t xsecdst;
}
int
-umask(struct proc *p, struct umask_args *uap, register_t *retval)
+umask(proc_t p, struct umask_args *uap, register_t *retval)
{
return(umask1(p, uap->newmask, UMASK_NOXSECURITY, 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 register_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);
nameidone(&nd);
+#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);
+ VNOP_REVOKE(vp, REVOKEALL, ctx);
out:
vnode_put(vp);
return (error);
*/
/* 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 register_t *retval)
{
return (ENOTSUP);
}
*/
/* ARGSUSED */
int
-statv(__unused struct proc *p,
+statv(__unused proc_t p,
__unused struct statv_args *uap,
__unused register_t *retval)
{
*/
/* ARGSUSED */
int
-lstatv(__unused struct proc *p,
+lstatv(__unused proc_t p,
__unused struct lstatv_args *uap,
__unused register_t *retval)
{
*/
/* ARGSUSED */
int
-fstatv(__unused struct proc *p,
+fstatv(__unused proc_t p,
__unused struct fstatv_args *uap,
__unused register_t *retval)
{
/* ARGSUSED */
int
-getdirentriesattr (struct proc *p, struct getdirentriesattr_args *uap, register_t *retval)
+getdirentriesattr (proc_t p, struct getdirentriesattr_args *uap, register_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;
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;
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
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) {
+ u_long ulcount = count;
+
error = VNOP_READDIRATTR(vp, &attributelist, auio,
- tmpcount, uap->options, &newstate, &eofflag,
- &tmpcount, &context);
+ count,
+ uap->options, (unsigned long *)&newstate, &eofflag,
+ &ulcount, ctx);
+ if (!error)
+ count = ulcount;
+ }
(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 */
/* 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 register_t *retval)
{
struct nameidata fnd, snd;
- struct vfs_context context;
- struct vnode *fvp, *svp;
- int error;
+ vfs_context_t ctx = vfs_context_current();
+ vnode_t fvp;
+ vnode_t svp;
+ int error;
u_long nameiflags;
char *fpath = NULL;
char *spath = NULL;
int flen, slen;
fse_info f_finfo, s_finfo;
- context.vc_proc = p;
- context.vc_ucred = kauth_cred_get();
-
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)
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) {
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();
+ 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;
+ }
flen = MAXPATHLEN;
slen = MAXPATHLEN;
if (vn_getpath(fvp, fpath, &flen) != 0 || fpath[0] == '\0') {
- printf("exchange: vn_getpath(fvp=0x%x) failed <<%s>>\n",
+ printf("exchange: vn_getpath(fvp=%p) failed <<%s>>\n",
fvp, fpath);
}
if (vn_getpath(svp, spath, &slen) != 0 || spath[0] == '\0') {
- printf("exchange: vn_getpath(svp=0x%x) failed <<%s>>\n",
+ printf("exchange: vn_getpath(svp=%p) failed <<%s>>\n",
svp, spath);
}
- get_fse_info(fvp, &f_finfo, &context);
- get_fse_info(svp, &s_finfo, &context);
+#if CONFIG_FSE
+ get_fse_info(fvp, &f_finfo, ctx);
+ get_fse_info(svp, &s_finfo, ctx);
+#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();
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;
}
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:
}
-#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 register_t *retval)
{
- register struct vnode *vp;
+ vnode_t vp;
int error=0;
int fserror = 0;
struct nameidata nd;
u_long nummatches;
int mallocsize;
u_long nameiflags;
- 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();
-
/* Start by copying in fsearchblock paramater list */
if (IS_64BIT_PROCESS(p)) {
error = copyin(uap->searchblock, (caddr_t) &searchblock, sizeof(searchblock));
searchblock.returnbuffer = CAST_USER_ADDR_T(tmp_searchblock.returnbuffer);
searchblock.returnbuffersize = tmp_searchblock.returnbuffersize;
searchblock.maxmatches = tmp_searchblock.maxmatches;
- searchblock.timelimit = tmp_searchblock.timelimit;
+ searchblock.timelimit.tv_sec = tmp_searchblock.timelimit.tv_sec;
+ searchblock.timelimit.tv_usec = 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);
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)
uap->options,
auio,
state,
- &context);
+ ctx);
saveandexit:
*/
/* ARGSUSED */
int
-fsctl (struct proc *p, struct fsctl_args *uap, __unused register_t *retval)
+fsctl (proc_t p, struct fsctl_args *uap, __unused register_t *retval)
{
int error;
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();
+ vfs_context_t ctx = vfs_context_current();
size = IOCPARM_LEN(cmd);
if (size > IOCPARM_MAX) return (EINVAL);
/* 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);
+ NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, ctx);
if ((error = namei(&nd))) goto FSCtl_Exit;
+#if CONFIG_MACF
+ error = mac_mount_check_fsctl(ctx, vnode_mount(nd.ni_vp), cmd);
+ if (error) {
+ vnode_put(nd.ni_vp);
+ nameidone(&nd);
+ goto FSCtl_Exit;
+ }
+#endif
+
/* Invoke the filesystem-specific code */
- error = VNOP_IOCTL(nd.ni_vp, IOCBASECMD(cmd), data, uap->options, &context);
+ error = VNOP_IOCTL(nd.ni_vp, IOCBASECMD(cmd), data, uap->options, ctx);
vnode_put(nd.ni_vp);
nameidone(&nd);
* 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;
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);
}
uio_addiov(auio, uap->value, uap->size);
}
- error = vn_getxattr(vp, attrname, auio, &attrsize, uap->options, &context);
+ error = vn_getxattr(vp, attrname, auio, &attrsize, uap->options, ctx);
out:
vnode_put(vp);
* 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;
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)) ) {
&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);
* 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;
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)) {
}
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);
}
&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);
* 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)) {
}
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)) ) {
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;
*/
#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;
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);
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);
*/
#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);
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;
*/
#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;
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);
}
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) {
*/
#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)) ) {
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);
/*
* 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,
sfs.f_ffree = (user_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));
* 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;
/*
sfs.f_ffree = (long)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));
*/
void munge_stat(struct stat *sbp, struct user_stat *usbp)
{
+ bzero(usbp, sizeof(struct user_stat));
+
+ 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_stat64(struct stat64 *sbp, struct user_stat64 *usbp)
+{
+ bzero(usbp, sizeof(struct user_stat));
+
usbp->st_dev = sbp->st_dev;
usbp->st_ino = sbp->st_ino;
usbp->st_mode = sbp->st_mode;
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;
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;