#include <sys/param.h>
#include <sys/systm.h>
#include <sys/namei.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/file_internal.h>
#include <sys/stat.h>
#include <sys/vnode_internal.h>
#include <sys/mount_internal.h>
#include <sys/proc_internal.h>
#include <sys/kauth.h>
#include <sys/uio_internal.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/dirent.h>
#include <sys/attr.h>
#include <sys/sysctl.h>
#include <sys/ubc.h>
#include <sys/quota.h>
#include <sys/kdebug.h>
#include <sys/fsevents.h>
#include <sys/sysproto.h>
#include <sys/xattr.h>
#include <sys/ubc_internal.h>
#include <machine/cons.h>
#include <machine/limits.h>
#include <miscfs/specfs/specdev.h>
#include <bsm/audit_kernel.h>
#include <bsm/audit_kevents.h>
#include <mach/mach_types.h>
#include <kern/kern_types.h>
#include <kern/kalloc.h>
#include <vm/vm_pageout.h>
#include <architecture/byte_order.h>
#include <libkern/OSAtomic.h>
uid_t console_user;
static int change_dir(struct nameidata *ndp, vfs_context_t ctx);
static void checkdirs(struct vnode *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 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);
__private_extern__ int sync_internal(void);
#ifdef __APPLE_API_OBSOLETE
struct fstatv_args {
int fd;
struct vstat *vsb;
};
struct lstatv_args {
const char *path;
struct vstat *vsb;
};
struct mkcomplex_args {
const char *path;
mode_t mode;
u_long type;
};
struct statv_args {
const char *path;
struct vstat *vsb;
};
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);
#endif
#if UNION
extern int (**union_vnodeop_p)(void *);
extern struct vnode *union_dircache(struct vnode*, struct proc*);
#endif
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);
int
mount(struct proc *p, register struct mount_args *uap, __unused register_t *retval)
{
struct vnode *vp;
struct vnode *devvp = NULLVP;
struct vnode *device_vnode = NULLVP;
struct mount *mp;
struct vfstable *vfsp;
int error, flag = 0;
struct vnode_attr va;
struct vfs_context context;
struct nameidata nd;
struct nameidata nd1;
char fstypename[MFSNAMELEN];
size_t dummy=0;
user_addr_t devpath = USER_ADDR_NULL;
user_addr_t fsmountargs = uap->data;
int ronly = 0;
int mntalloc = 0;
mode_t accessmode;
boolean_t is_64bit;
boolean_t is_rwlock_locked = FALSE;
AUDIT_ARG(fflags, uap->flags);
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
is_64bit = proc_is64bit(p);
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
if ((vp->v_flag & VROOT) &&
(vp->v_mount->mnt_flag & MNT_ROOTFS))
uap->flags |= MNT_UPDATE;
if (uap->flags & MNT_UPDATE) {
if ((vp->v_flag & VROOT) == 0) {
error = EINVAL;
goto out1;
}
mp = vp->v_mount;
mount_lock(mp);
if (mp->mnt_lflag & MNT_LUNMOUNT) {
mount_unlock(mp);
error = EBUSY;
goto out1;
}
mount_unlock(mp);
lck_rw_lock_exclusive(&mp->mnt_rwlock);
is_rwlock_locked = TRUE;
if ((uap->flags & MNT_RELOAD) &&
((mp->mnt_flag & MNT_RDONLY) == 0)) {
error = ENOTSUP;
goto out1;
}
if (mp->mnt_vfsstat.f_owner != kauth_cred_getuid(context.vc_ucred) &&
(error = suser(context.vc_ucred, &p->p_acflag))) {
goto out1;
}
if (suser(context.vc_ucred, NULL)) {
uap->flags |= MNT_NOSUID | MNT_NODEV;
if (mp->mnt_flag & MNT_NOEXEC)
uap->flags |= MNT_NOEXEC;
}
flag = mp->mnt_flag;
mp->mnt_flag |=
uap->flags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE);
vfsp = mp->mnt_vtable;
goto update;
}
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)))) {
goto out1;
}
if (suser(context.vc_ucred, 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)) )
goto out1;
if ( (error = buf_invalidateblks(vp, BUF_WRITE_DATA, 0, 0)) )
goto out1;
if (vp->v_type != VDIR) {
error = ENOTDIR;
goto out1;
}
if ( (error = copyinstr(uap->type, fstypename, MFSNAMELEN, &dummy)) )
goto out1;
AUDIT_ARG(text, fstypename);
mount_list_lock();
for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
if (!strcmp(vfsp->vfc_name, fstypename))
break;
mount_list_unlock();
if (vfsp == NULL) {
error = ENODEV;
goto out1;
}
if (ISSET(vp->v_flag, VMOUNT) && (vp->v_mountedhere != NULL)) {
error = EBUSY;
goto out1;
}
SET(vp->v_flag, VMOUNT);
MALLOC_ZONE(mp, struct mount *, (u_long)sizeof(struct mount),
M_MOUNT, M_WAITOK);
bzero((char *)mp, (u_long)sizeof(struct mount));
mntalloc = 1;
mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
mp->mnt_maxsegreadsize = mp->mnt_maxreadcnt;
mp->mnt_maxsegwritesize = mp->mnt_maxwritecnt;
mp->mnt_devblocksize = DEV_BSIZE;
TAILQ_INIT(&mp->mnt_vnodelist);
TAILQ_INIT(&mp->mnt_workerqueue);
TAILQ_INIT(&mp->mnt_newvnodes);
mount_lock_init(mp);
lck_rw_lock_exclusive(&mp->mnt_rwlock);
is_rwlock_locked = TRUE;
mp->mnt_op = vfsp->vfc_vfsops;
mp->mnt_vtable = vfsp;
mount_list_lock();
vfsp->vfc_refcount++;
mount_list_unlock();
mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK;
strncpy(mp->mnt_vfsstat.f_fstypename, vfsp->vfc_name, MFSTYPENAMELEN);
strncpy(mp->mnt_vfsstat.f_mntonname, nd.ni_cnd.cn_pnbuf, MAXPATHLEN);
mp->mnt_vnodecovered = vp;
mp->mnt_vfsstat.f_owner = kauth_cred_getuid(context.vc_ucred);
vfs_setowner(mp, KAUTH_UID_NONE, KAUTH_GID_NONE);
update:
if (uap->flags & MNT_RDONLY)
mp->mnt_flag |= MNT_RDONLY;
else if (mp->mnt_flag & MNT_RDONLY)
mp->mnt_kern_flag |= MNTK_WANTRDWR;
mp->mnt_flag &= ~(MNT_NOSUID | MNT_NOEXEC | MNT_NODEV |
MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC |
MNT_UNKNOWNPERMISSIONS | MNT_DONTBROWSE | MNT_AUTOMOUNTED);
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);
if (vfsp->vfc_vfsflags & VFC_VFSLOCALARGS) {
if (is_64bit) {
if ( (error = copyin(fsmountargs, (caddr_t)&devpath, sizeof(devpath))) )
goto out1;
fsmountargs += sizeof(devpath);
} else {
char *tmp;
if ( (error = copyin(fsmountargs, (caddr_t)&tmp, sizeof(tmp))) )
goto out1;
devpath = CAST_USER_ADDR_T(tmp);
fsmountargs += sizeof(tmp);
}
if ((devpath)) {
NDINIT(&nd1, LOOKUP, FOLLOW, UIO_USERSPACE, devpath, &context);
if ( (error = namei(&nd1)) )
goto out1;
strncpy(mp->mnt_vfsstat.f_mntfromname, nd1.ni_cnd.cn_pnbuf, MAXPATHLEN);
devvp = nd1.ni_vp;
nameidone(&nd1);
if (devvp->v_type != VBLK) {
error = ENOTBLK;
goto out2;
}
if (major(devvp->v_rdev) >= nblkdev) {
error = ENXIO;
goto out2;
}
if (suser(context.vc_ucred, 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)
goto out2;
}
}
if (devpath && ((uap->flags & MNT_UPDATE) == 0)) {
if ( (error = vnode_ref(devvp)) )
goto out2;
if ( (error = vfs_mountedon(devvp)) )
goto out3;
if (vcount(devvp) > 1 && !(vfs_flags(mp) & MNT_ROOTFS)) {
error = EBUSY;
goto out3;
}
if ( (error = VNOP_FSYNC(devvp, MNT_WAIT, &context)) ) {
error = ENOTBLK;
goto out3;
}
if ( (error = buf_invalidateblks(devvp, BUF_WRITE_DATA, 0, 0)) )
goto out3;
ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
if ( (error = VNOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, &context)) )
goto out3;
mp->mnt_devvp = devvp;
device_vnode = devvp;
} else {
if ((mp->mnt_flag & MNT_RDONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
device_vnode = mp->mnt_devvp;
if (device_vnode && suser(context.vc_ucred, NULL)) {
if ((error = vnode_authorize(device_vnode, NULL,
KAUTH_VNODE_READ_DATA | KAUTH_VNODE_WRITE_DATA, &context)) != 0)
goto out2;
}
}
device_vnode = NULLVP;
}
}
error = VFS_MOUNT(mp, device_vnode, fsmountargs, &context);
if (uap->flags & MNT_UPDATE) {
if (mp->mnt_kern_flag & MNTK_WANTRDWR)
mp->mnt_flag &= ~MNT_RDONLY;
mp->mnt_flag &=~
(MNT_UPDATE | MNT_RELOAD | MNT_FORCE);
mp->mnt_kern_flag &=~ MNTK_WANTRDWR;
if (error)
mp->mnt_flag = flag;
vfs_event_signal(NULL, VQ_UPDATE, (intptr_t)NULL);
lck_rw_done(&mp->mnt_rwlock);
is_rwlock_locked = FALSE;
if (!error)
enablequotas(mp,&context);
goto out2;
}
if (!error) {
CLR(vp->v_flag, VMOUNT);
vnode_lock(vp);
vp->v_mountedhere = mp;
vnode_unlock(vp);
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);
(void)VFS_START(mp, 0, &context);
OSAddAtomic(1, (SInt32 *)&vfs_nummntops);
enablequotas(mp,&context);
if (device_vnode) {
device_vnode->v_specflags |= SI_MOUNTEDON;
vfs_init_io_attributes(device_vnode, mp);
}
} else {
CLR(vp->v_flag, VMOUNT);
mount_list_lock();
mp->mnt_vtable->vfc_refcount--;
mount_list_unlock();
if (device_vnode ) {
VNOP_CLOSE(device_vnode, ronly ? FREAD : FREAD|FWRITE, &context);
vnode_rele(device_vnode);
}
lck_rw_done(&mp->mnt_rwlock);
is_rwlock_locked = FALSE;
mount_lock_destroy(mp);
FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT);
}
nameidone(&nd);
if (devpath && devvp)
vnode_put(devvp);
vnode_put(vp);
return(error);
out3:
vnode_rele(devvp);
out2:
if (devpath && devvp)
vnode_put(devvp);
out1:
if (is_rwlock_locked == TRUE) {
lck_rw_done(&mp->mnt_rwlock);
}
if (mntalloc)
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)
{
struct nameidata qnd;
int type;
char qfpath[MAXPATHLEN];
const char *qfname = QUOTAFILENAME;
const char *qfopsname = QUOTAOPSNAME;
const char *qfextension[] = INITQFNAMES;
if ((strcmp(mp->mnt_vfsstat.f_fstypename, "hfs") != 0 )
&& (strcmp( mp->mnt_vfsstat.f_fstypename, "ufs") != 0))
return;
for (type=0; type < MAXQUOTAS; type++) {
sprintf(qfpath, "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfopsname, qfextension[type]);
NDINIT(&qnd, LOOKUP, FOLLOW, UIO_SYSSPACE32, CAST_USER_ADDR_T(qfpath), context);
if (namei(&qnd) != 0)
continue;
vnode_put(qnd.ni_vp);
nameidone(&qnd);
sprintf(qfpath, "%s/%s.%s", mp->mnt_vfsstat.f_mntonname, qfname, qfextension[type]);
(void) VFS_QUOTACTL(mp, QCMD(Q_QUOTAON, type), 0, qfpath, context);
}
return;
}
static void
checkdirs(olddp, context)
struct vnode *olddp;
vfs_context_t context;
{
struct filedesc *fdp;
struct vnode *newdp;
struct proc *p;
struct vnode *tvp;
struct vnode *fdp_cvp;
struct vnode *fdp_rvp;
int cdir_changed = 0;
int rdir_changed = 0;
boolean_t funnel_state;
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);
for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) {
proc_fdlock(p);
fdp = p->p_fd;
if (fdp == (struct filedesc *)0) {
proc_fdunlock(p);
continue;
}
fdp_cvp = fdp->fd_cdir;
fdp_rvp = fdp->fd_rdir;
proc_fdunlock(p);
if (fdp_cvp == olddp) {
vnode_ref(newdp);
tvp = fdp->fd_cdir;
fdp_cvp = newdp;
cdir_changed = 1;
vnode_rele(tvp);
}
if (fdp_rvp == olddp) {
vnode_ref(newdp);
tvp = fdp->fd_rdir;
fdp_rvp = newdp;
rdir_changed = 1;
vnode_rele(tvp);
}
if (cdir_changed || rdir_changed) {
proc_fdlock(p);
fdp->fd_cdir = fdp_cvp;
fdp->fd_rdir = fdp_rvp;
proc_fdunlock(p);
}
}
if (rootvnode == olddp) {
vnode_ref(newdp);
tvp = rootvnode;
rootvnode = newdp;
vnode_rele(tvp);
}
thread_funnel_set(kernel_flock, funnel_state);
vnode_put(newdp);
}
int
unmount(struct proc *p, register struct unmount_args *uap, __unused register_t *retval)
{
register struct vnode *vp;
struct mount *mp;
int error;
struct nameidata nd;
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
mp = vp->v_mount;
nameidone(&nd);
if ((vp->v_flag & VROOT) == 0) {
vnode_put(vp);
return (EINVAL);
}
vnode_put(vp);
return (safedounmount(mp, uap->flags, p));
}
int
safedounmount(mp, flags, p)
struct mount *mp;
int flags;
struct proc *p;
{
int error;
if ((mp->mnt_vfsstat.f_owner != kauth_cred_getuid(kauth_cred_get())) &&
(error = suser(kauth_cred_get(), &p->p_acflag)))
return (error);
if (mp->mnt_flag & MNT_ROOTFS)
return (EBUSY);
return (dounmount(mp, flags, p));
}
int
dounmount(mp, flags, p)
register struct mount *mp;
int flags;
struct proc *p;
{
struct vnode *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();
if (flags & MNT_FORCE)
forcedunmount = 1;
mount_lock(mp);
if ((flags & MNT_FORCE)) {
mp->mnt_kern_flag |= MNTK_FRCUNMOUNT;
mp->mnt_lflag |= MNT_LFORCE;
}
if (mp->mnt_lflag & MNT_LUNMOUNT) {
mp->mnt_lflag |= MNT_LWAIT;
msleep((caddr_t)mp, &mp->mnt_mlock, (PVFS | PDROP), "dounmount", 0 );
return (EBUSY);
}
mp->mnt_kern_flag |= MNTK_UNMOUNT;
mp->mnt_lflag |= MNT_LUNMOUNT;
mp->mnt_flag &=~ MNT_ASYNC;
mount_unlock(mp);
lck_rw_lock_exclusive(&mp->mnt_rwlock);
fsevent_unmount(mp);
error = 0;
if (forcedunmount == 0) {
ubc_umount(mp);
if ((mp->mnt_flag & MNT_RDONLY) == 0) {
error = VFS_SYNC(mp, MNT_WAIT, &context);
if (error) {
mount_lock(mp);
mp->mnt_kern_flag &= ~MNTK_UNMOUNT;
mp->mnt_lflag &= ~MNT_LUNMOUNT;
mp->mnt_lflag &= ~MNT_LFORCE;
goto out;
}
}
}
if (forcedunmount)
lflags |= FORCECLOSE;
error = vflush(mp, NULLVP, SKIPSWAP | SKIPSYSTEM | SKIPROOT | lflags);
if ((forcedunmount == 0) && error) {
mount_lock(mp);
mp->mnt_kern_flag &= ~MNTK_UNMOUNT;
mp->mnt_lflag &= ~MNT_LUNMOUNT;
mp->mnt_lflag &= ~MNT_LFORCE;
goto out;
}
mount_iterdrain(mp);
error = VFS_UNMOUNT(mp, flags, &context);
if (error) {
mount_iterreset(mp);
mount_lock(mp);
mp->mnt_kern_flag &= ~MNTK_UNMOUNT;
mp->mnt_lflag &= ~MNT_LUNMOUNT;
mp->mnt_lflag &= ~MNT_LFORCE;
goto out;
}
if (!error)
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);
}
lck_rw_done(&mp->mnt_rwlock);
mount_list_remove(mp);
lck_rw_lock_exclusive(&mp->mnt_rwlock);
if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) {
vnode_getwithref(coveredvp);
vnode_lock(coveredvp);
coveredvp->v_mountedhere = (struct mount *)0;
vnode_unlock(coveredvp);
vnode_put(coveredvp);
}
mount_list_lock();
mp->mnt_vtable->vfc_refcount--;
mount_list_unlock();
cache_purgevfs(mp);
vfs_event_signal(NULL, VQ_UNMOUNT, (intptr_t)NULL);
mount_lock(mp);
mp->mnt_lflag |= MNT_LDEAD;
if (mp->mnt_lflag & MNT_LWAIT) {
mp->mnt_lflag &= ~MNT_LWAIT;
wakeup((caddr_t)mp);
}
mount_refdrain(mp);
out:
if (mp->mnt_lflag & MNT_LWAIT) {
mp->mnt_lflag &= ~MNT_LWAIT;
needwakeup = 1;
}
mount_unlock(mp);
lck_rw_done(&mp->mnt_rwlock);
if (needwakeup)
wakeup((caddr_t)mp);
if (!error) {
if ((coveredvp != NULLVP)) {
vnode_getwithref(coveredvp);
vnode_rele(coveredvp);
vnode_lock(coveredvp);
if(mp->mnt_crossref == 0) {
vnode_unlock(coveredvp);
mount_lock_destroy(mp);
FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT);
} else {
coveredvp->v_lflag |= VL_MOUNTDEAD;
vnode_unlock(coveredvp);
}
vnode_put(coveredvp);
} else if (mp->mnt_flag & MNT_ROOTFS) {
mount_lock_destroy(mp);
FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT);
} else
panic("dounmount: no coveredvp");
}
return (error);
}
void
mount_dropcrossref(mount_t mp, vnode_t dp, int need_put)
{
vnode_lock(dp);
mp->mnt_crossref--;
if (mp->mnt_crossref < 0)
panic("mount cross refs -ve");
if (((dp->v_lflag & VL_MOUNTDEAD) == VL_MOUNTDEAD) && (mp->mnt_crossref == 0)) {
dp->v_lflag &= ~VL_MOUNTDEAD;
if (need_put)
vnode_put_locked(dp);
vnode_unlock(dp);
mount_lock_destroy(mp);
FREE_ZONE((caddr_t)mp, sizeof (struct mount), M_MOUNT);
return;
}
if (need_put)
vnode_put_locked(dp);
vnode_unlock(dp);
}
#if DIAGNOSTIC
int syncprt = 0;
struct ctldebug debug0 = { "syncprt", &syncprt };
#endif
int print_vmpage_stat=0;
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);
if (asyncflag)
mp->mnt_flag |= MNT_ASYNC;
}
return(VFS_RETURNED);
}
extern unsigned int vp_pagein, vp_pgodirty, vp_pgoclean;
extern unsigned int dp_pgins, dp_pgouts;
int
sync(__unused struct proc *p, __unused struct sync_args *uap, __unused register_t *retval)
{
vfs_iterate(LK_NOWAIT, sync_callback, (void *)0);
{
if(print_vmpage_stat) {
vm_countdirtypages();
printf("VP: %d: %d: %d: %d: %d\n", vp_pgodirty, vp_pgoclean, vp_pagein,
dp_pgins, dp_pgouts);
}
}
#if DIAGNOSTIC
if (syncprt)
vfs_bufstats();
#endif
return (0);
}
int
quotactl(struct proc *p, register struct quotactl_args *uap, __unused register_t *retval)
{
register struct mount *mp;
int error, quota_cmd, quota_status;
caddr_t datap;
size_t fnamelen;
struct nameidata nd;
struct vfs_context context;
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);
error = namei(&nd);
if (error)
return (error);
mp = nd.ni_vp->v_mount;
vnode_put(nd.ni_vp);
nameidone(&nd);
quota_cmd = uap->cmd >> SUBCMDSHIFT;
switch (quota_cmd) {
case Q_QUOTAON:
fnamelen = MAXPATHLEN;
datap = kalloc(MAXPATHLEN);
error = copyinstr(uap->arg, datap, MAXPATHLEN, &fnamelen);
break;
case Q_GETQUOTA:
datap = (caddr_t) &my_dqblk;
break;
case Q_SETQUOTA:
case Q_SETUSE:
datap = (caddr_t) &my_dqblk;
if (proc_is64bit(p)) {
struct user_dqblk my_dqblk64;
error = copyin(uap->arg, (caddr_t)&my_dqblk64, sizeof (my_dqblk64));
if (error == 0) {
munge_dqblk(&my_dqblk, &my_dqblk64, FALSE);
}
}
else {
error = copyin(uap->arg, (caddr_t)&my_dqblk, sizeof (my_dqblk));
}
break;
case Q_QUOTASTAT:
datap = (caddr_t) "a_status;
break;
default:
datap = NULL;
break;
}
if (error == 0) {
error = VFS_QUOTACTL(mp, uap->cmd, uap->uid, datap, &context);
}
switch (quota_cmd) {
case Q_QUOTAON:
if (datap != NULL)
kfree(datap, MAXPATHLEN);
break;
case Q_GETQUOTA:
if (error == 0) {
if (proc_is64bit(p)) {
struct user_dqblk my_dqblk64;
munge_dqblk(&my_dqblk, &my_dqblk64, TRUE);
error = copyout((caddr_t)&my_dqblk64, uap->arg, sizeof (my_dqblk64));
}
else {
error = copyout(datap, uap->arg, sizeof (struct dqblk));
}
}
break;
case Q_QUOTASTAT:
if (error == 0) {
error = copyout(datap, uap->arg, sizeof(quota_status));
}
break;
default:
break;
}
return (error);
}
int
statfs(struct proc *p, register struct statfs_args *uap, __unused register_t *retval)
{
struct mount *mp;
struct vfsstatfs *sp;
int error;
struct nameidata nd;
struct vfs_context context;
vnode_t vp;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
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, &context);
vnode_put(vp);
if (error != 0)
return (error);
error = munge_statfs(mp, sp, uap->buf, NULL, IS_64BIT_PROCESS(p), TRUE);
return (error);
}
int
fstatfs(struct proc *p, register struct fstatfs_args *uap, __unused register_t *retval)
{
struct vnode *vp;
struct mount *mp;
struct vfsstatfs *sp;
int error;
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
AUDIT_ARG(fd, uap->fd);
if ( (error = file_vnode(uap->fd, &vp)) )
return (error);
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, &context)) != 0) {
file_drop(uap->fd);
return (error);
}
file_drop(uap->fd);
error = munge_statfs(mp, sp, uap->buf, NULL, IS_64BIT_PROCESS(p), TRUE);
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)
{
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 (((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);
}
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);
}
int
getfsstat(__unused proc_t p, struct getfsstat_args *uap, int *retval)
{
user_addr_t sfsp;
int count, maxcount;
struct getfsstat_struct fst;
if (IS_64BIT_PROCESS(p)) {
maxcount = uap->bufsize / sizeof(struct user_statfs);
}
else {
maxcount = uap->bufsize / sizeof(struct statfs);
}
sfsp = uap->buf;
count = 0;
fst.sfsp = sfsp;
fst.flags = uap->flags;
fst.count = 0;
fst.error = 0;
fst.maxcount = maxcount;
vfs_iterate(0, getfsstat_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;
{
return (ENOTSUP);
}
#endif
int
fchdir(struct proc *p, struct fchdir_args *uap, __unused register_t *retval)
{
register struct filedesc *fdp = p->p_fd;
struct vnode *vp, *tdp, *tvp;
struct mount *mp;
int error;
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
if ( (error = file_vnode(uap->fd, &vp)) )
return(error);
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
if (vp->v_type != VDIR)
error = ENOTDIR;
else
error = vnode_authorize(vp, NULL, KAUTH_VNODE_SEARCH, &context);
while (!error && (mp = vp->v_mountedhere) != NULL) {
if (vfs_busy(mp, LK_NOWAIT)) {
error = EACCES;
goto out;
}
error = VFS_ROOT(mp, &tdp, &context);
vfs_unbusy(mp);
if (error)
break;
vnode_put(vp);
vp = tdp;
}
if (error)
goto out;
if ( (error = vnode_ref(vp)) )
goto out;
vnode_put(vp);
proc_fdlock(p);
tvp = fdp->fd_cdir;
fdp->fd_cdir = vp;
proc_fdunlock(p);
if (tvp)
vnode_rele(tvp);
file_drop(uap->fd);
return (0);
out:
vnode_put(vp);
file_drop(uap->fd);
return(error);
}
int
chdir(struct proc *p, struct chdir_args *uap, __unused register_t *retval)
{
register 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();
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = change_dir(&nd, &context);
if (error)
return (error);
if ( (error = vnode_ref(nd.ni_vp)) ) {
vnode_put(nd.ni_vp);
return (error);
}
vnode_put(nd.ni_vp);
proc_fdlock(p);
tvp = fdp->fd_cdir;
fdp->fd_cdir = nd.ni_vp;
proc_fdunlock(p);
if (tvp)
vnode_rele(tvp);
return (0);
}
int
chroot(struct proc *p, struct chroot_args *uap, __unused register_t *retval)
{
register 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();
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);
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,
(int)nd.ni_vp))) {
vnode_put(nd.ni_vp);
return (error);
}
if ( (error = vnode_ref(nd.ni_vp)) ) {
vnode_put(nd.ni_vp);
return (error);
}
vnode_put(nd.ni_vp);
proc_fdlock(p);
tvp = fdp->fd_rdir;
fdp->fd_rdir = nd.ni_vp;
fdp->fd_flags |= FD_CHROOT;
proc_fdunlock(p);
if (tvp != NULL)
vnode_rele(tvp);
return (0);
}
static int
change_dir(struct nameidata *ndp, vfs_context_t ctx)
{
struct vnode *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)
vnode_put(vp);
return (error);
}
#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)
{
struct proc *p = vfs_context_proc(ctx);
register struct filedesc *fdp = p->p_fd;
register struct fileproc *fp;
register struct vnode *vp;
int flags, oflags;
struct fileproc *nfp;
int type, indx, error;
struct flock lf;
struct nameidata nd;
oflags = uflags;
if ((oflags & O_ACCMODE) == O_ACCMODE)
return(EINVAL);
flags = FFLAGS(uflags);
AUDIT_ARG(fflags, oflags);
AUDIT_ARG(mode, vap->va_mode);
if ( (error = falloc(p, &nfp, &indx)) ) {
return (error);
}
fp = nfp;
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, upath, ctx);
p->p_dupfd = -indx - 1;
if ((error = vn_open_auth(&nd, &flags, vap))) {
if ((error == ENODEV || error == ENXIO) && (p->p_dupfd >= 0)) {
if ((error = dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) {
fp_drop(p, indx, 0, 0);
*retval = indx;
return (0);
}
}
if (error == ERESTART)
error = EINTR;
fp_free(p, indx, fp);
return (error);
}
p->p_dupfd = 0;
vp = nd.ni_vp;
fp->f_fglob->fg_flag = flags & (FMASK | O_EVTONLY);
fp->f_fglob->fg_type = DTYPE_VNODE;
fp->f_fglob->fg_ops = &vnops;
fp->f_fglob->fg_data = (caddr_t)vp;
if (flags & (O_EXLOCK | O_SHLOCK)) {
lf.l_whence = SEEK_SET;
lf.l_start = 0;
lf.l_len = 0;
if (flags & O_EXLOCK)
lf.l_type = F_WRLCK;
else
lf.l_type = F_RDLCK;
type = F_FLOCK;
if ((flags & FNONBLOCK) == 0)
type |= F_WAIT;
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;
vnode_put(vp);
proc_fdlock(p);
*fdflags(p, indx) &= ~UF_RESERVED;
fp_drop(p, indx, fp, 1);
proc_fdunlock(p);
*retval = indx;
return (0);
bad:
vn_close(vp, fp->f_fglob->fg_flag, fp->f_fglob->fg_cred, p);
vnode_put(vp);
fp_free(p, indx, fp);
return (error);
}
int
open_extended(struct proc *p, struct open_extended_args *uap, register_t *retval)
{
struct vfs_context context;
register struct filedesc *fdp = p->p_fd;
int ciferror;
kauth_filesec_t xsecdst;
struct vnode_attr va;
int cmode;
xsecdst = NULL;
if ((uap->xsecurity != USER_ADDR_NULL) &&
((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0))
return ciferror;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
VATTR_INIT(&va);
cmode = ((uap->mode &~ fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT;
VATTR_SET(&va, va_mode, cmode);
if (uap->uid != KAUTH_UID_NONE)
VATTR_SET(&va, va_uid, uap->uid);
if (uap->gid != KAUTH_GID_NONE)
VATTR_SET(&va, va_gid, uap->gid);
if (xsecdst != NULL)
VATTR_SET(&va, va_acl, &xsecdst->fsec_acl);
ciferror = open1(&context, uap->path, uap->flags, &va, retval);
if (xsecdst != NULL)
kauth_filesec_free(xsecdst);
return ciferror;
}
int
open(struct proc *p, struct open_args *uap, register_t *retval)
{
struct vfs_context context;
register struct filedesc *fdp = p->p_fd;
struct vnode_attr va;
int cmode;
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 & ACCESSPERMS);
return(open1(&context, uap->path, 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)
{
struct vnode_attr va;
struct vfs_context context;
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 ((uap->mode & S_IFMT) == S_IFIFO)
return(mkfifo1(&context, uap->path, &va));
AUDIT_ARG(mode, uap->mode);
AUDIT_ARG(dev, uap->dev);
if ((error = suser(context.vc_ucred, &p->p_acflag)))
return (error);
NDINIT(&nd, CREATE, LOCKPARENT | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
return (error);
dvp = nd.ni_dvp;
vp = nd.ni_vp;
if (vp != NULL) {
error = EEXIST;
goto out;
}
if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context)) != 0)
goto out;
switch (uap->mode & S_IFMT) {
case S_IFMT:
VATTR_SET(&va, va_type, VBAD);
break;
case S_IFCHR:
VATTR_SET(&va, va_type, VCHR);
break;
case S_IFBLK:
VATTR_SET(&va, va_type, VBLK);
break;
case S_IFWHT:
whiteout = 1;
break;
default:
error = EINVAL;
goto out;
}
if (whiteout) {
error = VNOP_WHITEOUT(dvp, &nd.ni_cnd, CREATE, &context);
} else {
error = vn_create(dvp, &vp, &nd.ni_cnd, &va, 0, &context);
}
if (error)
goto out;
if (vp) {
int update_flags = 0;
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);
}
out:
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
return (error);
}
static int
mkfifo1(vfs_context_t ctx, user_addr_t upath, struct vnode_attr *vap)
{
vnode_t vp, dvp;
int error;
struct nameidata nd;
NDINIT(&nd, CREATE, LOCKPARENT | AUDITVNPATH1,
UIO_USERSPACE, upath, ctx);
error = namei(&nd);
if (error)
return (error);
dvp = nd.ni_dvp;
vp = nd.ni_vp;
if (vp != NULL) {
error = EEXIST;
goto out;
}
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:
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
return error;
}
int
mkfifo_extended(struct proc *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;
if (uap->xsecurity != USER_ADDR_NULL) {
if ((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0)
return ciferror;
}
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
VATTR_INIT(&va);
VATTR_SET(&va, va_mode, (uap->mode & ALLPERMS) & ~p->p_fd->fd_cmask);
if (uap->uid != KAUTH_UID_NONE)
VATTR_SET(&va, va_uid, uap->uid);
if (uap->gid != KAUTH_GID_NONE)
VATTR_SET(&va, va_gid, uap->gid);
if (xsecdst != KAUTH_FILESEC_NONE)
VATTR_SET(&va, va_acl, &xsecdst->fsec_acl);
ciferror = mkfifo1(&context, uap->path, &va);
if (xsecdst != KAUTH_FILESEC_NONE)
kauth_filesec_free(xsecdst);
return ciferror;
}
int
mkfifo(struct proc *p, register 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));
}
int
link(struct proc *p, register struct link_args *uap, __unused register_t *retval)
{
vnode_t vp, dvp, lvp;
struct nameidata nd;
struct vfs_context context;
int error;
fse_info finfo;
int need_event, has_listeners;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
vp = dvp = lvp = NULLVP;
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
nameidone(&nd);
if (vp->v_type == VDIR) {
error = EPERM;
goto out;
}
if ((error = vnode_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, &context)) != 0)
goto out;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | AUDITVNPATH2;
nd.ni_dirp = uap->link;
error = namei(&nd);
if (error != 0)
goto out;
dvp = nd.ni_dvp;
lvp = nd.ni_vp;
if (lvp != NULLVP) {
error = EEXIST;
goto out2;
}
if (vnode_mount(vp) != vnode_mount(dvp)) {
error = EXDEV;
goto out2;
}
if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context)) != 0)
goto out2;
error = VNOP_LINK(vp, dvp, &nd.ni_cnd, &context);
if (error)
goto out2;
need_event = need_fsevent(FSE_CREATE_FILE, dvp);
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;
target_path = get_pathbuff();
len = MAXPATHLEN;
vn_getpath(dvp, target_path, &len);
target_path[len-1] = '/';
strcpy(&target_path[len], nd.ni_cnd.cn_nameptr);
len += nd.ni_cnd.cn_namelen;
if (has_listeners) {
link_to_path = get_pathbuff();
link_name_len = MAXPATHLEN;
vn_getpath(vp, link_to_path, &link_name_len);
kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_LINK,
(uintptr_t)link_to_path, (uintptr_t)target_path);
if (link_to_path != NULL)
release_pathbuff(link_to_path);
}
if (need_event) {
if (get_fse_info(vp, &finfo, &context) == 0) {
add_fsevent(FSE_CREATE_FILE, &context,
FSE_ARG_STRING, len, target_path,
FSE_ARG_FINFO, &finfo,
FSE_ARG_DONE);
}
}
release_pathbuff(target_path);
}
out2:
nameidone(&nd);
out:
if (lvp)
vnode_put(lvp);
if (dvp)
vnode_put(dvp);
vnode_put(vp);
return (error);
}
int
symlink(struct proc *p, register struct symlink_args *uap, __unused register_t *retval)
{
struct vnode_attr va;
char *path;
int error;
struct nameidata nd;
struct vfs_context context;
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)
goto out;
AUDIT_ARG(text, path);
NDINIT(&nd, CREATE, LOCKPARENT | AUDITVNPATH1,
UIO_USERSPACE, uap->link, &context);
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);
error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context);
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);
if (error == 0)
error = vnode_setattr_fallback(vp, &va, &context);
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)
goto skipit;
}
#if 0
if (kauth_authorize_fileop_has_listeners() &&
namei(&nd) == 0) {
char *new_link_path = NULL;
int len;
new_link_path = get_pathbuff();
len = MAXPATHLEN;
vn_getpath(dvp, new_link_path, &len);
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);
}
#endif
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;
skipit:
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
out:
FREE_ZONE(path, MAXPATHLEN, M_NAMEI);
return (error);
}
#warning XXX authorization not implmented for whiteouts
int
undelete(struct proc *p, register struct undelete_args *uap, __unused register_t *retval)
{
int error;
struct nameidata nd;
struct vfs_context context;
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);
error = namei(&nd);
if (error)
return (error);
dvp = nd.ni_dvp;
vp = nd.ni_vp;
if (vp == NULLVP && (nd.ni_cnd.cn_flags & ISWHITEOUT)) {
error = VNOP_WHITEOUT(dvp, &nd.ni_cnd, DELETE, &context);
} else
error = EEXIST;
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
return (error);
}
static int
_unlink(struct proc *p, struct unlink_args *uap, __unused register_t *retval, int nodelbusy)
{
vnode_t vp, dvp;
int error;
struct nameidata nd;
struct vfs_context context;
struct componentname *cnp;
int flags = 0;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
NDINIT(&nd, DELETE, LOCKPARENT | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
cnp = &nd.ni_cnd;
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) {
error = EPERM;
} else {
if (vp->v_flag & VROOT) {
error = EBUSY;
}
}
if (!error)
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);
}
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);
}
nameidone(&nd);
vnode_put(dvp);
vnode_put(vp);
return (error);
}
int
unlink(p, uap, retval)
struct proc *p;
struct unlink_args *uap;
register_t *retval;
{
return _unlink(p, uap, retval, 0);
}
int
delete(p, uap, retval)
struct proc *p;
struct delete_args *uap;
register_t *retval;
{
return _unlink(p, (struct unlink_args *)uap, retval, 1);
}
int
lseek(p, uap, retval)
struct proc *p;
register struct lseek_args *uap;
off_t *retval;
{
struct fileproc *fp;
struct vnode *vp;
struct vfs_context context;
off_t offset = uap->offset, file_size;
int error;
if ( (error = fp_getfvp(p,uap->fd, &fp, &vp)) ) {
if (error == ENOTSUP)
return (ESPIPE);
return (error);
}
if (vnode_isfifo(vp)) {
file_drop(uap->fd);
return(ESPIPE);
}
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
switch (uap->whence) {
case L_INCR:
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)
break;
offset += file_size;
break;
case L_SET:
break;
default:
error = EINVAL;
}
if (error == 0) {
if (uap->offset > 0 && offset < 0) {
error = EOVERFLOW;
} else {
if (offset < 0 && vp->v_type != VCHR) {
error = EINVAL;
} else {
fp->f_fglob->fg_offset = offset;
*retval = fp->f_fglob->fg_offset;
}
}
}
(void)vnode_put(vp);
file_drop(uap->fd);
return (error);
}
static int
access1(vnode_t vp, vnode_t dvp, int uflags, vfs_context_t ctx)
{
kauth_action_t action;
int error;
if (!(uflags & _ACCESS_EXTENDED_MASK)) {
action = 0;
if (uflags & R_OK)
action |= KAUTH_VNODE_READ_DATA;
if (uflags & W_OK) {
if (vnode_isdir(vp)) {
action |= KAUTH_VNODE_ADD_FILE |
KAUTH_VNODE_ADD_SUBDIRECTORY;
} else {
action |= KAUTH_VNODE_WRITE_DATA;
}
}
if (uflags & X_OK) {
if (vnode_isdir(vp)) {
action |= KAUTH_VNODE_SEARCH;
} else {
action |= KAUTH_VNODE_EXECUTE;
}
}
} else {
action = uflags >> 8;
}
if (action != 0) {
error = vnode_authorize(vp, dvp, action | KAUTH_VNODE_ACCESS, ctx);
} else {
error = 0;
}
return(error);
}
int
access_extended(__unused struct proc *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 vfs_context context;
struct nameidata nd;
int niopts;
vnode_t vp, dvp;
input = NULL;
result = NULL;
error = 0;
vp = NULL;
dvp = NULL;
context.vc_ucred = NULL;
if (uap->size > ACCESSX_MAX_TABLESIZE)
return(ENOMEM);
if (uap->size < sizeof(struct accessx_descriptor))
return(EINVAL);
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;
context.vc_ucred = kauth_cred_copy_real(kauth_cred_get());
context.vc_proc = current_proc();
limit = uap->size / sizeof(struct accessx_descriptor);
nent = limit;
wantdelete = 0;
for (i = 0; i < nent; i++) {
j = input[i].ad_name_offset / sizeof(struct accessx_descriptor);
if (j > limit) {
error = EINVAL;
goto out;
}
if (j == 0) {
if (i == 0) {
error = EINVAL;
goto out;
}
continue;
}
if (j < nent)
nent = j;
}
if (nent > ACCESSX_MAX_DESCRIPTORS) {
error = ENOMEM;
goto out;
}
MALLOC(result, errno_t *, nent * sizeof(errno_t), M_TEMP, M_WAITOK);
if (result == NULL) {
error = ENOMEM;
goto out;
}
error = 0;
for (i = 0; i < nent; i++) {
if (input[i].ad_name_offset != 0) {
if (vp) {
vnode_put(vp);
vp = NULL;
}
if (dvp) {
vnode_put(dvp);
dvp = NULL;
}
wantdelete = input[i].ad_flags & _DELETE_OK;
for (j = i + 1; (j < nent) && (input[j].ad_name_offset == 0); j++)
if (input[j].ad_flags & _DELETE_OK)
wantdelete = 1;
niopts = FOLLOW | AUDITVNPATH1;
if (wantdelete)
niopts |= WANTPARENT;
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;
if (wantdelete)
dvp = nd.ni_dvp;
}
nameidone(&nd);
}
switch(error) {
case ENOENT:
case EACCES:
case EPERM:
case ENOTDIR:
result[i] = error;
break;
case 0:
result[i] = access1(vp, dvp, input[i].ad_flags, &context);
break;
default:
goto out;
}
}
error = copyout(result, uap->results, nent * sizeof(errno_t));
out:
if (input)
FREE(input, M_TEMP);
if (result)
FREE(result, M_TEMP);
if (vp)
vnode_put(vp);
if (dvp)
vnode_put(dvp);
if (context.vc_ucred)
kauth_cred_rele(context.vc_ucred);
return(error);
}
int
access(__unused struct proc *p, register struct access_args *uap, __unused register_t *retval)
{
int error;
struct nameidata nd;
int niopts;
struct vfs_context context;
context.vc_ucred = kauth_cred_copy_real(kauth_cred_get());
context.vc_proc = current_proc();
niopts = FOLLOW | AUDITVNPATH1;
if (uap->flags & _DELETE_OK)
niopts |= WANTPARENT;
NDINIT(&nd, LOOKUP, niopts, UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
goto out;
error = access1(nd.ni_vp, nd.ni_dvp, uap->flags, &context);
vnode_put(nd.ni_vp);
if (uap->flags & _DELETE_OK)
vnode_put(nd.ni_dvp);
nameidone(&nd);
out:
kauth_cred_rele(context.vc_ucred);
return(error);
}
static int
stat2(vfs_context_t ctx, struct nameidata *ndp, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size)
{
struct stat sb;
struct user_stat user_sb;
caddr_t sbp;
int error, my_size;
kauth_filesec_t fsec;
size_t xsecurity_bufsize;
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);
vnode_put(ndp->ni_vp);
nameidone(ndp);
if (error)
return (error);
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 ((error = copyout(sbp, ub, my_size)) != 0)
goto out;
if (xsecurity != USER_ADDR_NULL) {
if (fsec == KAUTH_FILESEC_NONE) {
if (susize(xsecurity_size, 0) != 0) {
error = EFAULT;
goto out;
}
} else {
xsecurity_bufsize = fusize(xsecurity_size);
if (susize(xsecurity_size, KAUTH_FILESEC_COPYSIZE(fsec)) != 0) {
error = EFAULT;
goto out;
}
if (xsecurity_bufsize >= KAUTH_FILESEC_COPYSIZE(fsec))
error = copyout(fsec, xsecurity, KAUTH_FILESEC_COPYSIZE(fsec));
}
}
out:
if (fsec != KAUTH_FILESEC_NONE)
kauth_filesec_free(fsec);
return (error);
}
static int
stat1(struct proc *p, user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size)
{
struct nameidata nd;
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, path, &context);
return(stat2(&context, &nd, ub, xsecurity, xsecurity_size));
}
int
stat_extended(struct proc *p, struct stat_extended_args *uap, __unused register_t *retval)
{
return (stat1(p, uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size));
}
int
stat(struct proc *p, struct stat_args *uap, __unused register_t *retval)
{
return(stat1(p, uap->path, uap->ub, 0, 0));
}
static int
lstat1(struct proc *p, user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size)
{
struct nameidata nd;
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNPATH1,
UIO_USERSPACE, path, &context);
return(stat2(&context, &nd, ub, xsecurity, xsecurity_size));
}
int
lstat_extended(struct proc *p, struct lstat_extended_args *uap, __unused register_t *retval)
{
return (lstat1(p, uap->path, uap->ub, uap->xsecurity, uap->xsecurity_size));
}
int
lstat(struct proc *p, struct lstat_args *uap, __unused register_t *retval)
{
return(lstat1(p, uap->path, uap->ub, 0, 0));
}
int
pathconf(p, uap, retval)
struct proc *p;
register 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();
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
return (error);
error = vn_pathconf(nd.ni_vp, uap->name, retval, &context);
vnode_put(nd.ni_vp);
nameidone(&nd);
return (error);
}
int
readlink(p, uap, retval)
struct proc *p;
register struct readlink_args *uap;
register_t *retval;
{
register struct vnode *vp;
uio_t auio;
int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
int error;
struct nameidata nd;
struct vfs_context context;
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);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
nameidone(&nd);
auio = uio_createwithbuffer(1, 0, spacetype, UIO_READ,
&uio_buf[0], sizeof(uio_buf));
uio_addiov(auio, uap->buf, uap->count);
if (vp->v_type != VLNK)
error = EINVAL;
else {
error = vnode_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, &context);
if (error == 0)
error = VNOP_READLINK(vp, auio, &context);
}
vnode_put(vp);
*retval = uap->count - (int)uio_resid(auio);
return (error);
}
static int
chflags1(vnode_t vp, int flags, vfs_context_t ctx)
{
struct vnode_attr va;
kauth_action_t action;
int error;
VATTR_INIT(&va);
VATTR_SET(&va, va_flags, flags);
if ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)
goto out;
if (action && ((error = vnode_authorize(vp, NULL, action | KAUTH_VNODE_NOIMMUTABLE, ctx)) != 0))
goto out;
error = vnode_setattr(vp, &va, ctx);
out:
vnode_put(vp);
return(error);
}
int
chflags(struct proc *p, register struct chflags_args *uap, __unused register_t *retval)
{
register struct vnode *vp;
struct vfs_context context;
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);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
nameidone(&nd);
error = chflags1(vp, uap->flags, &context);
return(error);
}
int
fchflags(struct proc *p, register struct fchflags_args *uap, __unused register_t *retval)
{
struct vfs_context context;
struct vnode *vp;
int error;
AUDIT_ARG(fd, uap->fd);
AUDIT_ARG(fflags, uap->flags);
if ( (error = file_vnode(uap->fd, &vp)) )
return (error);
if ((error = vnode_getwithref(vp))) {
file_drop(uap->fd);
return(error);
}
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
error = chflags1(vp, uap->flags, &context);
file_drop(uap->fd);
return (error);
}
static int
chmod2(vfs_context_t ctx, struct vnode *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 (((error = vnode_authattr(vp, vap, &action, ctx)) != 0) ||
((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
if (error == EACCES)
error = EPERM;
return(error);
}
error = vnode_setattr(vp, vap, ctx);
return (error);
}
static int
chmod1(vfs_context_t ctx, user_addr_t path, struct vnode_attr *vap)
{
struct nameidata nd;
int error;
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, path, ctx);
if ((error = namei(&nd)))
return (error);
error = chmod2(ctx, nd.ni_vp, vap);
vnode_put(nd.ni_vp);
nameidone(&nd);
return(error);
}
int
chmod_extended(struct proc *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_INIT(&va);
if (uap->mode != -1)
VATTR_SET(&va, va_mode, uap->mode & ALLPERMS);
if (uap->uid != KAUTH_UID_NONE)
VATTR_SET(&va, va_uid, uap->uid);
if (uap->gid != KAUTH_GID_NONE)
VATTR_SET(&va, va_gid, uap->gid);
xsecdst = NULL;
switch(uap->xsecurity) {
case CAST_USER_ADDR_T((void *)1):
VATTR_SET(&va, va_acl, NULL);
break;
case USER_ADDR_NULL:
break;
default:
if ((error = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0)
return(error);
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);
if (xsecdst != NULL)
kauth_filesec_free(xsecdst);
return(error);
}
int
chmod(struct proc *p, register 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));
}
static int
fchmod1(struct proc *p, int fd, struct vnode_attr *vap)
{
struct vnode *vp;
int error;
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
AUDIT_ARG(fd, fd);
if ((error = file_vnode(fd, &vp)) != 0)
return (error);
if ((error = vnode_getwithref(vp)) != 0) {
file_drop(fd);
return(error);
}
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
error = chmod2(&context, vp, vap);
(void)vnode_put(vp);
file_drop(fd);
return (error);
}
int
fchmod_extended(struct proc *p, struct fchmod_extended_args *uap, __unused register_t *retval)
{
int error;
struct vnode_attr va;
kauth_filesec_t xsecdst;
VATTR_INIT(&va);
if (uap->mode != -1)
VATTR_SET(&va, va_mode, uap->mode & ALLPERMS);
if (uap->uid != KAUTH_UID_NONE)
VATTR_SET(&va, va_uid, uap->uid);
if (uap->gid != KAUTH_GID_NONE)
VATTR_SET(&va, va_gid, uap->gid);
xsecdst = NULL;
switch(uap->xsecurity) {
case USER_ADDR_NULL:
VATTR_SET(&va, va_acl, NULL);
break;
case CAST_USER_ADDR_T(-1):
break;
default:
if ((error = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0)
return(error);
VATTR_SET(&va, va_acl, &xsecdst->fsec_acl);
}
error = fchmod1(p, uap->fd, &va);
switch(uap->xsecurity) {
case USER_ADDR_NULL:
case CAST_USER_ADDR_T(-1):
break;
default:
if (xsecdst != NULL)
kauth_filesec_free(xsecdst);
}
return(error);
}
int
fchmod(struct proc *p, register struct fchmod_args *uap, __unused register_t *retval)
{
struct vnode_attr va;
VATTR_INIT(&va);
VATTR_SET(&va, va_mode, uap->mode & ALLPERMS);
return(fchmod1(p, uap->fd, &va));
}
static int
chown1(vfs_context_t ctx, register struct chown_args *uap, __unused register_t *retval, int follow)
{
register struct vnode *vp;
struct vnode_attr va;
int error;
struct nameidata nd;
kauth_action_t action;
AUDIT_ARG(owner, uap->uid, uap->gid);
NDINIT(&nd, LOOKUP, (follow ? FOLLOW : 0) | AUDITVNPATH1,
UIO_USERSPACE, uap->path, ctx);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
nameidone(&nd);
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 ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)
goto out;
if (action && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0))
goto out;
error = vnode_setattr(vp, &va, ctx);
out:
if (error == EACCES)
error = EPERM;
vnode_put(vp);
return (error);
}
int
chown(struct proc *p, register 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);
}
int
lchown(struct proc *p, register struct lchown_args *uap, register_t *retval)
{
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
return chown1(&context, (struct chown_args *)uap, retval, 0);
}
int
fchown(struct proc *p, register struct fchown_args *uap, __unused register_t *retval)
{
struct vnode_attr va;
struct vfs_context context;
struct vnode *vp;
int error;
kauth_action_t action;
AUDIT_ARG(owner, uap->uid, uap->gid);
AUDIT_ARG(fd, uap->fd);
if ( (error = file_vnode(uap->fd, &vp)) )
return (error);
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
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);
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
if ((error = vnode_authattr(vp, &va, &action, &context)) != 0)
goto out;
if (action && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) {
if (error == EACCES)
error = EPERM;
goto out;
}
error = vnode_setattr(vp, &va, &context);
out:
(void)vnode_put(vp);
file_drop(uap->fd);
return (error);
}
static int
getutimes(usrtvp, tsp)
user_addr_t usrtvp;
struct timespec *tsp;
{
struct user_timeval tv[2];
int error;
if (usrtvp == USER_ADDR_NULL) {
struct timeval old_tv;
microtime(&old_tv);
TIMEVAL_TO_TIMESPEC(&old_tv, &tsp[0]);
tsp[1] = tsp[0];
} else {
if (IS_64BIT_PROCESS(current_proc())) {
error = copyin(usrtvp, (void *)tv, sizeof(tv));
} else {
struct timeval old_tv[2];
error = copyin(usrtvp, (void *)old_tv, sizeof(old_tv));
tv[0].tv_sec = old_tv[0].tv_sec;
tv[0].tv_usec = old_tv[0].tv_usec;
tv[1].tv_sec = old_tv[1].tv_sec;
tv[1].tv_usec = old_tv[1].tv_usec;
}
if (error)
return (error);
TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]);
TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]);
}
return 0;
}
static int
setutimes(vfs_context_t ctx, struct vnode *vp, const struct timespec *ts,
int nullflag)
{
int error;
struct vnode_attr va;
kauth_action_t action;
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
VATTR_INIT(&va);
VATTR_SET(&va, va_access_time, ts[0]);
VATTR_SET(&va, va_modify_time, ts[1]);
if (nullflag)
va.va_vaflags |= VA_UTIMES_NULL;
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, ctx);
out:
return error;
}
int
utimes(struct proc *p, register 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();
NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
return (error);
nameidone(&nd);
usrtvp = uap->tptr;
if ((error = getutimes(usrtvp, ts)) != 0)
goto out;
error = setutimes(&context, nd.ni_vp, ts, usrtvp == USER_ADDR_NULL);
out:
vnode_put(nd.ni_vp);
return (error);
}
int
futimes(struct proc *p, register struct futimes_args *uap, __unused register_t *retval)
{
struct timespec ts[2];
struct vnode *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;
if ((error = getutimes(usrtvp, ts)) != 0)
return (error);
if ((error = file_vnode(uap->fd, &vp)) != 0)
return (error);
if((error = vnode_getwithref(vp))) {
file_drop(uap->fd);
return(error);
}
error = setutimes(&context, vp, ts, usrtvp == 0);
vnode_put(vp);
file_drop(uap->fd);
return(error);
}
int
truncate(struct proc *p, register struct truncate_args *uap, __unused register_t *retval)
{
register struct vnode *vp;
struct vnode_attr va;
struct vfs_context context;
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);
if ((error = namei(&nd)))
return (error);
vp = nd.ni_vp;
nameidone(&nd);
VATTR_INIT(&va);
VATTR_SET(&va, va_data_size, uap->length);
if ((error = vnode_authattr(vp, &va, &action, &context)) != 0)
goto out;
if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0))
goto out;
error = vnode_setattr(vp, &va, &context);
out:
vnode_put(vp);
return (error);
}
int
ftruncate(p, uap, retval)
struct proc *p;
register struct ftruncate_args *uap;
register_t *retval;
{
struct vfs_context context;
struct vnode_attr va;
struct vnode *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);
if ( (error = fp_lookup(p,fd,&fp,0)) ) {
return(error);
}
if (fp->f_fglob->fg_type == DTYPE_PSXSHM) {
error = pshm_truncate(p, fp, uap->fd, uap->length, retval);
goto out;
}
if (fp->f_fglob->fg_type != DTYPE_VNODE) {
error = EINVAL;
goto out;
}
vp = (struct vnode *)fp->f_fglob->fg_data;
if ((fp->f_fglob->fg_flag & FWRITE) == 0) {
AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1);
error = EINVAL;
goto out;
}
if ((error = vnode_getwithref(vp)) != 0) {
goto out;
}
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
VATTR_INIT(&va);
VATTR_SET(&va, va_data_size, uap->length);
error = vnode_setattr(vp, &va, &context);
(void)vnode_put(vp);
out:
file_drop(fd);
return (error);
}
int
fsync(struct proc *p, struct fsync_args *uap, __unused register_t *retval)
{
struct vnode *vp;
struct fileproc *fp;
struct vfs_context context;
int error;
if ( (error = fp_getfvp(p, uap->fd, &fp, &vp)) )
return (error);
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
context.vc_proc = p;
context.vc_ucred = fp->f_fglob->fg_cred;
error = VNOP_FSYNC(vp, MNT_WAIT, &context);
(void)vnode_put(vp);
file_drop(uap->fd);
return (error);
}
int
copyfile(struct proc *p, register 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();
if (uap->flags & ~CPF_MASK) {
return(EINVAL);
}
NDINIT(&fromnd, LOOKUP, SAVESTART | AUDITVNPATH1,
UIO_USERSPACE, uap->from, &context);
if ((error = namei(&fromnd)))
return (error);
fvp = fromnd.ni_vp;
NDINIT(&tond, CREATE, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | AUDITVNPATH2,
UIO_USERSPACE, uap->to, &context);
if ((error = namei(&tond))) {
goto out1;
}
tdvp = tond.ni_dvp;
tvp = tond.ni_vp;
if (tvp != NULL) {
if (!(uap->flags & CPF_OVERWRITE)) {
error = EEXIST;
goto out;
}
}
if (fvp->v_type == VDIR || (tvp && tvp->v_type == VDIR)) {
error = EISDIR;
goto out;
}
if ((error = vnode_authorize(tdvp, NULL, KAUTH_VNODE_ADD_FILE, &context)) != 0)
goto out;
if (fvp == tdvp)
error = EINVAL;
if (fvp == tvp)
error = -1;
if (!error)
error = VNOP_COPYFILE(fvp,tdvp,tvp,&tond.ni_cnd,uap->mode,uap->flags,&context);
out:
sdvp = tond.ni_startdir;
nameidone(&tond);
if (tvp)
vnode_put(tvp);
vnode_put(tdvp);
vnode_put(sdvp);
out1:
vnode_put(fvp);
if (fromnd.ni_startdir)
vnode_put(fromnd.ni_startdir);
nameidone(&fromnd);
if (error == -1)
return (0);
return (error);
}
int
rename(proc_t p, register struct rename_args *uap, __unused register_t *retval)
{
vnode_t tvp, tdvp;
vnode_t fvp, fdvp;
struct nameidata fromnd, tond;
struct vfs_context context;
int error;
int mntrename;
char *oname, *from_name, *to_name;
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;
retry:
fvp = tvp = NULL;
fdvp = tdvp = NULL;
mntrename = FALSE;
NDINIT(&fromnd, DELETE, WANTPARENT | AUDITVNPATH1, UIO_USERSPACE, uap->from, &context);
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 (fvp->v_type == VDIR)
tond.ni_cnd.cn_flags |= WILLBEDIR;
if ( (error = namei(&tond)) ) {
if (error == EISDIR && fvp->v_type == VDIR)
error = EINVAL;
goto out1;
}
tdvp = tond.ni_dvp;
tvp = tond.ni_vp;
if (tvp != NULL) {
if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
error = ENOTDIR;
goto out1;
} else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
error = EISDIR;
goto out1;
}
}
if (fvp == tdvp) {
error = EINVAL;
goto out1;
}
{
int moving = 0;
error = 0;
if ((tvp != NULL) && vnode_isdir(tvp)) {
if (tvp != fdvp)
moving = 1;
} else if (tdvp != fdvp) {
moving = 1;
}
if ((error = vnode_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, &context)) != 0)
goto auth_exit;
if (moving) {
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)
goto auth_exit;
} else {
if ((error = vnode_authorize(fdvp, NULL,
vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE, &context)) != 0)
goto auth_exit;
}
if ((tvp != NULL) && !vnode_isdir(tvp) &&
((error = vnode_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, &context)) != 0))
goto auth_exit;
auth_exit:
if (error != 0)
goto out1;
}
if ((fvp->v_flag & VROOT) &&
(fvp->v_type == VDIR) &&
(tvp == NULL) &&
(fvp->v_mountedhere == NULL) &&
(fdvp == tdvp) &&
((fvp->v_mount->mnt_flag & (MNT_UNION | MNT_ROOTFS)) == 0) &&
(fvp->v_mount->mnt_vnodecovered != NULLVP)) {
struct vnode *coveredvp;
coveredvp = fvp->v_mount->mnt_vnodecovered;
if ( (vnode_getwithref(coveredvp)) ) {
error = ENOENT;
goto out1;
}
vnode_put(fvp);
fvp = coveredvp;
mntrename = TRUE;
}
if ((fvp->v_mount != tdvp->v_mount) ||
(tvp && (fvp->v_mount != tvp->v_mount))) {
error = EXDEV;
goto out1;
}
if (fvp->v_type == VDIR &&
((fdvp == fvp) ||
(fromnd.ni_cnd.cn_namelen == 1 && fromnd.ni_cnd.cn_nameptr[0] == '.') ||
((fromnd.ni_cnd.cn_flags | tond.ni_cnd.cn_flags) & ISDOTDOT)) ) {
error = EINVAL;
goto out1;
}
if (tdvp->v_parent == fvp) {
error = EINVAL;
goto out1;
}
if (fvp == tvp && fdvp == tdvp) {
if (fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen &&
!bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr,
fromnd.ni_cnd.cn_namelen)) {
goto out1;
}
}
if (holding_mntlock && fvp->v_mount != locked_mp) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
holding_mntlock = 0;
}
if (tdvp != fdvp && fvp->v_type == VDIR) {
if (!holding_mntlock) {
locked_mp = fvp->v_mount;
mount_ref(locked_mp, 0);
nameidone(&tond);
if (tvp)
vnode_put(tvp);
vnode_put(tdvp);
nameidone(&fromnd);
vnode_put(fvp);
vnode_put(fdvp);
mount_lock_renames(locked_mp);
holding_mntlock = 1;
goto retry;
}
} else {
if (holding_mntlock) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
holding_mntlock = 0;
}
}
oname = fvp->v_name;
oparent = fvp->v_parent;
if (need_fsevent(FSE_RENAME, fvp)) {
get_fse_info(fvp, &from_finfo, &context);
if (tvp) {
get_fse_info(tvp, &to_finfo, &context);
}
from_name = get_pathbuff();
from_len = MAXPATHLEN;
vn_getpath(fvp, from_name, &from_len);
to_name = get_pathbuff();
to_len = MAXPATHLEN;
if (tvp && tvp->v_type != VDIR) {
vn_getpath(tvp, to_name, &to_len);
} else {
vn_getpath(tdvp, to_name, &to_len);
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';
}
} else {
from_name = NULL;
to_name = NULL;
}
error = VNOP_RENAME(fdvp, fvp, &fromnd.ni_cnd,
tdvp, tvp, &tond.ni_cnd,
&context);
if (holding_mntlock) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
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;
goto out1;
}
kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_RENAME,
(uintptr_t)from_name, (uintptr_t)to_name);
if (from_name != NULL && to_name != NULL) {
if (tvp) {
add_fsevent(FSE_RENAME, &context,
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,
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;
if (mntrename) {
char *cp, *pathend, *mpname;
char * tobuf;
struct mount *mp;
int maxlen;
size_t len = 0;
mp = fvp->v_mountedhere;
if (vfs_busy(mp, LK_NOWAIT)) {
error = EBUSY;
goto out1;
}
MALLOC_ZONE(tobuf, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
error = copyinstr(uap->to, tobuf, MAXPATHLEN, &len);
if (!error) {
pathend = &mp->mnt_vfsstat.f_mntonname[0];
for (cp = pathend; *cp != '\0'; ++cp) {
if (*cp == '/')
pathend = cp + 1;
}
for (mpname = cp = tobuf; *cp != '\0'; ++cp) {
if (*cp == '/')
mpname = cp + 1;
}
maxlen = MAXPATHLEN - (pathend - mp->mnt_vfsstat.f_mntonname);
bzero(pathend, maxlen);
strncpy(pathend, mpname, maxlen - 1);
}
FREE_ZONE(tobuf, MAXPATHLEN, M_NAMEI);
vfs_unbusy(mp);
}
if (oname == fvp->v_name && oparent == fvp->v_parent) {
int update_flags;
update_flags = VNODE_UPDATE_NAME;
if (fdvp != tdvp)
update_flags |= VNODE_UPDATE_PARENT;
vnode_update_identity(fvp, tdvp, tond.ni_cnd.cn_nameptr, tond.ni_cnd.cn_namelen, tond.ni_cnd.cn_hash, update_flags);
}
out1:
if (holding_mntlock) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
}
if (tdvp) {
nameidone(&tond);
if (tvp)
vnode_put(tvp);
vnode_put(tdvp);
}
if (fdvp) {
nameidone(&fromnd);
if (fvp)
vnode_put(fvp);
vnode_put(fdvp);
}
return (error);
}
static int
mkdir1(vfs_context_t ctx, user_addr_t path, struct vnode_attr *vap)
{
vnode_t vp, dvp;
int error;
int update_flags = 0;
struct nameidata nd;
AUDIT_ARG(mode, vap->va_mode);
NDINIT(&nd, CREATE, LOCKPARENT | AUDITVNPATH1,
UIO_USERSPACE, path, ctx);
nd.ni_cnd.cn_flags |= WILLBEDIR;
error = namei(&nd);
if (error)
return (error);
dvp = nd.ni_dvp;
vp = nd.ni_vp;
if (vp != NULL) {
error = EEXIST;
goto out;
}
if ((error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx)) != 0)
goto out;
VATTR_SET(vap, va_type, VDIR);
if ((error = vn_create(dvp, &vp, &nd.ni_cnd, vap, 0, ctx)) != 0)
goto out;
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_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
out:
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
return (error);
}
int
mkdir_extended(struct proc *p, register struct mkdir_extended_args *uap, __unused register_t *retval)
{
struct vfs_context context;
int ciferror;
kauth_filesec_t xsecdst;
struct vnode_attr va;
xsecdst = NULL;
if ((uap->xsecurity != USER_ADDR_NULL) &&
((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0))
return ciferror;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
VATTR_INIT(&va);
VATTR_SET(&va, va_mode, (uap->mode & ACCESSPERMS) & ~p->p_fd->fd_cmask);
if (xsecdst != NULL)
VATTR_SET(&va, va_acl, &xsecdst->fsec_acl);
ciferror = mkdir1(&context, uap->path, &va);
if (xsecdst != NULL)
kauth_filesec_free(xsecdst);
return ciferror;
}
int
mkdir(struct proc *p, register 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));
}
int
rmdir(struct proc *p, struct rmdir_args *uap, __unused register_t *retval)
{
vnode_t vp, dvp;
int error;
struct nameidata nd;
struct vfs_context context;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
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;
if (vp->v_type != VDIR) {
error = ENOTDIR;
} else if (dvp == vp) {
error = EINVAL;
} else if (vp->v_flag & VROOT) {
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);
}
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 (path != NULL)
release_pathbuff(path);
}
nameidone(&nd);
vnode_put(dvp);
vnode_put(vp);
return (error);
}
int
getdirentries(p, uap, retval)
struct proc *p;
register struct getdirentries_args *uap;
register_t *retval;
{
struct vnode *vp;
struct vfs_context context;
struct fileproc *fp;
uio_t auio;
int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
long loff;
int error, eofflag;
int fd = uap->fd;
char uio_buf[ UIO_SIZEOF(1) ];
AUDIT_ARG(fd, uap->fd);
error = fp_getfvp(p, 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 ( (error = vnode_getwithref(vp)) ) {
goto out;
}
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
unionread:
if (vp->v_type != VDIR) {
(void)vnode_put(vp);
error = EINVAL;
goto out;
}
context.vc_proc = p;
context.vc_ucred = fp->f_fglob->fg_cred;
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);
error = VNOP_READDIR(vp, auio, 0, &eofflag, (int *)NULL, &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;
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 (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;
fp->f_fglob->fg_offset = 0;
error = VNOP_CLOSE(vp, FREAD, &context);
vnode_rele(vp);
vnode_put(vp);
if (error)
goto out;
vp = lvp;
goto unionread;
}
}
}
#endif
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));
*retval = uap->count - uio_resid(auio);
out:
file_drop(fd);
return (error);
}
#warning XXX implement xsecurity
#define UMASK_NOXSECURITY (void *)1
static int
umask1(struct proc *p, int newmask, __unused kauth_filesec_t fsec, register_t *retval)
{
register struct filedesc *fdp;
AUDIT_ARG(mask, newmask);
fdp = p->p_fd;
*retval = fdp->fd_cmask;
fdp->fd_cmask = newmask & ALLPERMS;
return (0);
}
int
umask_extended(struct proc *p, struct umask_extended_args *uap, register_t *retval)
{
int ciferror;
kauth_filesec_t xsecdst;
xsecdst = KAUTH_FILESEC_NONE;
if (uap->xsecurity != USER_ADDR_NULL) {
if ((ciferror = kauth_copyinfilesec(uap->xsecurity, &xsecdst)) != 0)
return ciferror;
} else {
xsecdst = KAUTH_FILESEC_NONE;
}
ciferror = umask1(p, uap->newmask, xsecdst, retval);
if (xsecdst != KAUTH_FILESEC_NONE)
kauth_filesec_free(xsecdst);
return ciferror;
}
int
umask(struct proc *p, struct umask_args *uap, register_t *retval)
{
return(umask1(p, uap->newmask, UMASK_NOXSECURITY, retval));
}
int
revoke(struct proc *p, register struct revoke_args *uap, __unused register_t *retval)
{
register struct vnode *vp;
struct vnode_attr va;
struct vfs_context context;
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);
error = namei(&nd);
if (error)
return (error);
vp = nd.ni_vp;
nameidone(&nd);
VATTR_INIT(&va);
VATTR_WANTED(&va, va_uid);
if ((error = vnode_getattr(vp, &va, &context)))
goto out;
if (kauth_cred_getuid(context.vc_ucred) != va.va_uid &&
(error = suser(context.vc_ucred, &p->p_acflag)))
goto out;
if (vp->v_usecount > 1 || (vp->v_flag & VALIASED))
VNOP_REVOKE(vp, REVOKEALL, &context);
out:
vnode_put(vp);
return (error);
}
#ifdef __APPLE_API_OBSOLETE
int
mkcomplex(__unused struct proc *p, __unused struct mkcomplex_args *uap, __unused register_t *retval)
{
return (ENOTSUP);
}
int
statv(__unused struct proc *p,
__unused struct statv_args *uap,
__unused register_t *retval)
{
return (ENOTSUP);
}
int
lstatv(__unused struct proc *p,
__unused struct lstatv_args *uap,
__unused register_t *retval)
{
return (ENOTSUP);
}
int
fstatv(__unused struct proc *p,
__unused struct fstatv_args *uap,
__unused register_t *retval)
{
return (ENOTSUP);
}
#endif
int
getdirentriesattr (struct proc *p, struct getdirentriesattr_args *uap, register_t *retval)
{
struct vnode *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;
int error, eofflag;
u_long loff;
struct attrlist attributelist;
struct vfs_context context;
int fd = uap->fd;
char uio_buf[ UIO_SIZEOF(1) ];
kauth_action_t action;
AUDIT_ARG(fd, fd);
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)) )
return (error);
if ((fp->f_fglob->fg_flag & FREAD) == 0) {
AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1);
error = EBADF;
goto out;
}
if ( (error = vnode_getwithref(vp)) )
goto out;
AUDIT_ARG(vnpath, vp, ARG_VNODE1);
if (vp->v_type != VDIR) {
(void)vnode_put(vp);
error = EINVAL;
goto out;
}
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;
action = KAUTH_VNODE_LIST_DIRECTORY;
if ((attributelist.commonattr & ~ATTR_CMN_NAME) ||
attributelist.fileattr || attributelist.dirattr)
action |= KAUTH_VNODE_SEARCH;
if ((error = vnode_authorize(vp, NULL, action, &context)) == 0)
error = VNOP_READDIRATTR(vp, &attributelist, auio,
tmpcount, uap->options, &newstate, &eofflag,
&tmpcount, &context);
(void)vnode_put(vp);
actualcount = tmpcount;
if (error)
goto out;
fp->f_fglob->fg_offset = uio_offset(auio);
if ((error = suulong(uap->count, actualcount)) != 0)
goto out;
if ((error = suulong(uap->newstate, (uint64_t)newstate)) != 0)
goto out;
if ((error = suulong(uap->basep, (uint64_t)loff)) != 0)
goto out;
*retval = eofflag;
error = 0;
out:
file_drop(fd);
return (error);
}
int
exchangedata (struct proc *p, register struct exchangedata_args *uap, __unused register_t *retval)
{
struct nameidata fnd, snd;
struct vfs_context context;
struct vnode *fvp, *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);
error = namei(&fnd);
if (error)
goto out2;
nameidone(&fnd);
fvp = fnd.ni_vp;
NDINIT(&snd, LOOKUP, nameiflags | AUDITVNPATH2,
UIO_USERSPACE, uap->path2, &context);
error = namei(&snd);
if (error) {
vnode_put(fvp);
goto out2;
}
nameidone(&snd);
svp = snd.ni_vp;
if (svp == fvp) {
error = EINVAL;
goto out;
}
if (svp->v_mount != fvp->v_mount) {
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))
goto out;
if (need_fsevent(FSE_EXCHANGE, fvp) || kauth_authorize_fileop_has_listeners()) {
fpath = get_pathbuff();
spath = get_pathbuff();
flen = MAXPATHLEN;
slen = MAXPATHLEN;
if (vn_getpath(fvp, fpath, &flen) != 0 || fpath[0] == '\0') {
printf("exchange: vn_getpath(fvp=0x%x) failed <<%s>>\n",
fvp, fpath);
}
if (vn_getpath(svp, spath, &slen) != 0 || spath[0] == '\0') {
printf("exchange: vn_getpath(svp=0x%x) failed <<%s>>\n",
svp, spath);
}
get_fse_info(fvp, &f_finfo, &context);
get_fse_info(svp, &s_finfo, &context);
}
error = VNOP_EXCHANGE(fvp, svp, 0, &context);
if (error == 0) {
char *tmpname;
if (fpath != NULL && spath != NULL) {
kauth_authorize_fileop(vfs_context_ucred(&context), KAUTH_FILEOP_EXCHANGE,
(uintptr_t)fpath, (uintptr_t)spath);
}
name_cache_lock();
tmpname = fvp->v_name;
fvp->v_name = svp->v_name;
svp->v_name = tmpname;
if (fvp->v_parent != svp->v_parent) {
struct vnode *tmp;
tmp = fvp->v_parent;
fvp->v_parent = svp->v_parent;
svp->v_parent = tmp;
}
name_cache_unlock();
if (fpath != NULL && spath != NULL) {
add_fsevent(FSE_EXCHANGE, &context,
FSE_ARG_STRING, flen, fpath,
FSE_ARG_FINFO, &f_finfo,
FSE_ARG_STRING, slen, spath,
FSE_ARG_FINFO, &s_finfo,
FSE_ARG_DONE);
}
}
if (spath != NULL)
release_pathbuff(spath);
if (fpath != NULL)
release_pathbuff(fpath);
out:
vnode_put(svp);
vnode_put(fvp);
out2:
return (error);
}
#ifdef __APPLE_API_OBSOLETE
#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;
int flags;
u_long nameiflags;
struct vfs_context context;
if ((uap->ngroups <= 0) || (uap->ngroups > NGROUPS))
return (EINVAL);
if ((error = suser(kauth_cred_get(), &p->p_acflag)))
return(error);
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;
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;
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);
}
#endif
int
searchfs (struct proc *p, register struct searchfs_args *uap, __unused register_t *retval)
{
register struct vnode *vp;
int error=0;
int fserror = 0;
struct nameidata nd;
struct user_fssearchblock searchblock;
struct searchstate *state;
struct attrlist *returnattrs;
void *searchparams1,*searchparams2;
uio_t auio = NULL;
int spacetype = proc_is64bit(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
u_long nummatches;
int mallocsize;
u_long nameiflags;
struct vfs_context context;
char uio_buf[ UIO_SIZEOF(1) ];
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
if (IS_64BIT_PROCESS(p)) {
error = copyin(uap->searchblock, (caddr_t) &searchblock, sizeof(searchblock));
}
else {
struct fssearchblock tmp_searchblock;
error = copyin(uap->searchblock, (caddr_t) &tmp_searchblock, sizeof(tmp_searchblock));
searchblock.returnattrs = CAST_USER_ADDR_T(tmp_searchblock.returnattrs);
searchblock.returnbuffer = CAST_USER_ADDR_T(tmp_searchblock.returnbuffer);
searchblock.returnbuffersize = tmp_searchblock.returnbuffersize;
searchblock.maxmatches = tmp_searchblock.maxmatches;
searchblock.timelimit = tmp_searchblock.timelimit;
searchblock.searchparams1 = CAST_USER_ADDR_T(tmp_searchblock.searchparams1);
searchblock.sizeofsearchparams1 = tmp_searchblock.sizeofsearchparams1;
searchblock.searchparams2 = CAST_USER_ADDR_T(tmp_searchblock.searchparams2);
searchblock.sizeofsearchparams2 = tmp_searchblock.sizeofsearchparams2;
searchblock.searchattrs = tmp_searchblock.searchattrs;
}
if (error)
return(error);
if (searchblock.sizeofsearchparams1 > SEARCHFS_MAX_SEARCHPARMS ||
searchblock.sizeofsearchparams2 > SEARCHFS_MAX_SEARCHPARMS)
return(EINVAL);
mallocsize = searchblock.sizeofsearchparams1 + searchblock.sizeofsearchparams2 +
sizeof(struct attrlist) + sizeof(struct searchstate);
MALLOC(searchparams1, void *, mallocsize, M_TEMP, M_WAITOK);
searchparams2 = (void *) (((caddr_t) searchparams1) + searchblock.sizeofsearchparams1);
returnattrs = (struct attrlist *) (((caddr_t) searchparams2) + searchblock.sizeofsearchparams2);
state = (struct searchstate *) (((caddr_t) returnattrs) + sizeof (struct attrlist));
if ((error = copyin(searchblock.searchparams1, searchparams1, searchblock.sizeofsearchparams1)))
goto freeandexit;
if ((error = copyin(searchblock.searchparams2, searchparams2, searchblock.sizeofsearchparams2)))
goto freeandexit;
if ((error = copyin(searchblock.returnattrs, (caddr_t) returnattrs, sizeof(struct attrlist))))
goto freeandexit;
if ((error = copyin(uap->state, (caddr_t) state, sizeof(struct searchstate))))
goto freeandexit;
auio = uio_createwithbuffer(1, 0, spacetype, UIO_READ,
&uio_buf[0], sizeof(uio_buf));
uio_addiov(auio, searchblock.returnbuffer, searchblock.returnbuffersize);
nameiflags = 0;
if ((uap->options & FSOPT_NOFOLLOW) == 0) nameiflags |= FOLLOW;
NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1,
UIO_USERSPACE, uap->path, &context);
error = namei(&nd);
if (error)
goto freeandexit;
nameidone(&nd);
vp = nd.ni_vp;
if (searchblock.maxmatches == 0) {
nummatches = 0;
goto saveandexit;
}
fserror = VNOP_SEARCHFS(vp,
searchparams1,
searchparams2,
&searchblock.searchattrs,
searchblock.maxmatches,
&searchblock.timelimit,
returnattrs,
&nummatches,
uap->scriptcode,
uap->options,
auio,
state,
&context);
saveandexit:
vnode_put(vp);
if ((error = copyout((caddr_t) state, uap->state, sizeof(struct searchstate))) != 0)
goto freeandexit;
if ((error = suulong(uap->nummatches, (uint64_t)nummatches)) != 0)
goto freeandexit;
error = fserror;
freeandexit:
FREE(searchparams1,M_TEMP);
return(error);
}
int
fsctl (struct proc *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;
#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();
size = IOCPARM_LEN(cmd);
if (size > IOCPARM_MAX) return (EINVAL);
is64bit = proc_is64bit(p);
memp = NULL;
if (size > sizeof (stkbuf)) {
if ((memp = (caddr_t)kalloc(size)) == 0) return ENOMEM;
data = memp;
} else {
data = &stkbuf[0];
};
if (cmd & IOC_IN) {
if (size) {
error = copyin(uap->data, data, size);
if (error) goto FSCtl_Exit;
} else {
if (is64bit) {
*(user_addr_t *)data = uap->data;
}
else {
*(uint32_t *)data = (uint32_t)uap->data;
}
};
} else if ((cmd & IOC_OUT) && size) {
bzero(data, size);
} else if (cmd & IOC_VOID) {
if (is64bit) {
*(user_addr_t *)data = uap->data;
}
else {
*(uint32_t *)data = (uint32_t)uap->data;
}
}
nameiflags = 0;
if ((uap->options & FSOPT_NOFOLLOW) == 0) nameiflags |= FOLLOW;
NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, &context);
if ((error = namei(&nd))) goto FSCtl_Exit;
error = VNOP_IOCTL(nd.ni_vp, IOCBASECMD(cmd), data, uap->options, &context);
vnode_put(nd.ni_vp);
nameidone(&nd);
if (error == 0 && (cmd & IOC_OUT) && size)
error = copyout(data, uap->data, size);
FSCtl_Exit:
if (memp) kfree(memp, size);
return error;
}
__private_extern__ int
sync_internal(void)
{
int error;
struct sync_args data;
int retval[2];
error = sync(current_proc(), &data, &retval[0]);
return (error);
}
int
getxattr(struct proc *p, struct getxattr_args *uap, user_ssize_t *retval)
{
struct vnode *vp;
struct nameidata nd;
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;
size_t namelen;
u_long nameiflags;
int error;
char uio_buf[ UIO_SIZEOF(1) ];
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
if (uap->options & XATTR_NOSECURITY)
return (EINVAL);
nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW;
NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context);
if ((error = namei(&nd))) {
return (error);
}
vp = nd.ni_vp;
nameidone(&nd);
if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) {
goto out;
}
if (xattr_protected(attrname)) {
error = EPERM;
goto out;
}
if (uap->value && uap->size > 0) {
auio = uio_createwithbuffer(1, uap->position, spacetype, UIO_READ,
&uio_buf[0], sizeof(uio_buf));
uio_addiov(auio, uap->value, uap->size);
}
error = vn_getxattr(vp, attrname, auio, &attrsize, uap->options, &context);
out:
vnode_put(vp);
if (auio) {
*retval = uap->size - uio_resid(auio);
} else {
*retval = (user_ssize_t)attrsize;
}
return (error);
}
int
fgetxattr(struct proc *p, struct fgetxattr_args *uap, user_ssize_t *retval)
{
struct vnode *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;
size_t namelen;
int error;
char uio_buf[ UIO_SIZEOF(1) ];
if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY))
return (EINVAL);
if ( (error = file_vnode(uap->fd, &vp)) ) {
return (error);
}
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) {
goto out;
}
if (xattr_protected(attrname)) {
error = EPERM;
goto out;
}
if (uap->value && uap->size > 0) {
auio = uio_createwithbuffer(1, uap->position, spacetype, UIO_READ,
&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);
out:
(void)vnode_put(vp);
file_drop(uap->fd);
if (auio) {
*retval = uap->size - uio_resid(auio);
} else {
*retval = (user_ssize_t)attrsize;
}
return (error);
}
int
setxattr(struct proc *p, struct setxattr_args *uap, int *retval)
{
struct vnode *vp;
struct nameidata nd;
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;
u_long nameiflags;
int error;
char uio_buf[ UIO_SIZEOF(1) ];
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
if (uap->options & XATTR_NOSECURITY)
return (EINVAL);
if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) {
return (error);
}
if (xattr_protected(attrname))
return(EPERM);
if (uap->value == 0 || uap->size == 0) {
return (EINVAL);
}
nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW;
NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context);
if ((error = namei(&nd))) {
return (error);
}
vp = nd.ni_vp;
nameidone(&nd);
auio = uio_createwithbuffer(1, uap->position, spacetype, UIO_WRITE,
&uio_buf[0], sizeof(uio_buf));
uio_addiov(auio, uap->value, uap->size);
error = vn_setxattr(vp, attrname, auio, uap->options, &context);
vnode_put(vp);
*retval = 0;
return (error);
}
int
fsetxattr(struct proc *p, struct fsetxattr_args *uap, int *retval)
{
struct vnode *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) ];
if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY))
return (EINVAL);
if ((error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen) != 0)) {
return (error);
}
if (xattr_protected(attrname))
return(EPERM);
if (uap->value == 0 || uap->size == 0) {
return (EINVAL);
}
if ( (error = file_vnode(uap->fd, &vp)) ) {
return (error);
}
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
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);
vnode_put(vp);
file_drop(uap->fd);
*retval = 0;
return (error);
}
#warning "code duplication"
int
removexattr(struct proc *p, struct removexattr_args *uap, int *retval)
{
struct vnode *vp;
struct nameidata nd;
char attrname[XATTR_MAXNAMELEN+1];
int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
struct vfs_context context;
size_t namelen;
u_long nameiflags;
int error;
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
if (uap->options & XATTR_NOSECURITY)
return (EINVAL);
error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen);
if (error != 0) {
return (error);
}
if (xattr_protected(attrname))
return(EPERM);
nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW;
NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context);
if ((error = namei(&nd))) {
return (error);
}
vp = nd.ni_vp;
nameidone(&nd);
error = vn_removexattr(vp, attrname, uap->options, &context);
vnode_put(vp);
*retval = 0;
return (error);
}
#warning "code duplication"
int
fremovexattr(struct proc *p, struct fremovexattr_args *uap, int *retval)
{
struct vnode *vp;
char attrname[XATTR_MAXNAMELEN+1];
struct vfs_context context;
size_t namelen;
int error;
if (uap->options & (XATTR_NOFOLLOW | XATTR_NOSECURITY))
return (EINVAL);
error = copyinstr(uap->attrname, attrname, sizeof(attrname), &namelen);
if (error != 0) {
return (error);
}
if (xattr_protected(attrname))
return(EPERM);
if ( (error = file_vnode(uap->fd, &vp)) ) {
return (error);
}
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
error = vn_removexattr(vp, attrname, uap->options, &context);
vnode_put(vp);
file_drop(uap->fd);
*retval = 0;
return (error);
}
#warning "code duplication"
int
listxattr(struct proc *p, struct listxattr_args *uap, user_ssize_t *retval)
{
struct vnode *vp;
struct nameidata nd;
struct vfs_context context;
uio_t auio = NULL;
int spacetype = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
size_t attrsize = 0;
u_long nameiflags;
int error;
char uio_buf[ UIO_SIZEOF(1) ];
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
if (uap->options & XATTR_NOSECURITY)
return (EINVAL);
nameiflags = (uap->options & XATTR_NOFOLLOW) ? 0 : FOLLOW;
NDINIT(&nd, LOOKUP, nameiflags, spacetype, uap->path, &context);
if ((error = namei(&nd))) {
return (error);
}
vp = nd.ni_vp;
nameidone(&nd);
if (uap->namebuf != 0 && uap->bufsize > 0) {
auio = uio_createwithbuffer(1, 0, spacetype,
UIO_READ, &uio_buf[0], sizeof(uio_buf));
uio_addiov(auio, uap->namebuf, uap->bufsize);
}
error = vn_listxattr(vp, auio, &attrsize, uap->options, &context);
vnode_put(vp);
if (auio) {
*retval = (user_ssize_t)uap->bufsize - uio_resid(auio);
} else {
*retval = (user_ssize_t)attrsize;
}
return (error);
}
#warning "code duplication"
int
flistxattr(struct proc *p, struct flistxattr_args *uap, user_ssize_t *retval)
{
struct vnode *vp;
struct vfs_context context;
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))
return (EINVAL);
if ( (error = file_vnode(uap->fd, &vp)) ) {
return (error);
}
if ( (error = vnode_getwithref(vp)) ) {
file_drop(uap->fd);
return(error);
}
if (uap->namebuf != 0 && uap->bufsize > 0) {
auio = uio_createwithbuffer(1, 0, spacetype,
UIO_READ, &uio_buf[0], sizeof(uio_buf));
uio_addiov(auio, uap->namebuf, uap->bufsize);
}
context.vc_proc = p;
context.vc_ucred = kauth_cred_get();
error = vn_listxattr(vp, auio, &attrsize, uap->options, &context);
vnode_put(vp);
file_drop(uap->fd);
if (auio) {
*retval = (user_ssize_t)uap->bufsize - uio_resid(auio);
} else {
*retval = (user_ssize_t)attrsize;
}
return (error);
}
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)
{
int error;
int my_size, copy_size;
if (is_64_bit) {
struct user_statfs sfs;
my_size = copy_size = sizeof(sfs);
bzero(&sfs, my_size);
sfs.f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
sfs.f_type = mp->mnt_vtable->vfc_typenum;
sfs.f_reserved1 = (short)sfsp->f_fssubtype;
sfs.f_bsize = (user_long_t)sfsp->f_bsize;
sfs.f_iosize = (user_long_t)sfsp->f_iosize;
sfs.f_blocks = (user_long_t)sfsp->f_blocks;
sfs.f_bfree = (user_long_t)sfsp->f_bfree;
sfs.f_bavail = (user_long_t)sfsp->f_bavail;
sfs.f_files = (user_long_t)sfsp->f_files;
sfs.f_ffree = (user_long_t)sfsp->f_ffree;
sfs.f_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);
if (partial_copy) {
copy_size -= (sizeof(sfs.f_reserved3) + sizeof(sfs.f_reserved4));
}
error = copyout((caddr_t)&sfs, bufp, copy_size);
}
else {
struct statfs sfs;
my_size = copy_size = sizeof(sfs);
bzero(&sfs, my_size);
sfs.f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
sfs.f_type = mp->mnt_vtable->vfc_typenum;
sfs.f_reserved1 = (short)sfsp->f_fssubtype;
if ((sfsp->f_blocks > LONG_MAX)
&& (sfsp->f_blocks != 0xffffffffffffffff)
&& (sfsp->f_bfree != 0xffffffffffffffff)
&& (sfsp->f_bavail != 0xffffffffffffffff)) {
int shift;
for (shift = 0; shift < 32; shift++) {
if ((sfsp->f_blocks >> shift) <= LONG_MAX)
break;
if ((sfsp->f_bsize << (shift + 1)) > LONG_MAX)
break;
}
#define __SHIFT_OR_CLIP(x, s) ((((x) >> (s)) > LONG_MAX) ? LONG_MAX : ((x) >> (s)))
sfs.f_blocks = (long)__SHIFT_OR_CLIP(sfsp->f_blocks, shift);
sfs.f_bfree = (long)__SHIFT_OR_CLIP(sfsp->f_bfree, shift);
sfs.f_bavail = (long)__SHIFT_OR_CLIP(sfsp->f_bavail, shift);
#undef __SHIFT_OR_CLIP
sfs.f_bsize = (long)(sfsp->f_bsize << shift);
sfs.f_iosize = lmax(sfsp->f_iosize, sfsp->f_bsize);
} else {
sfs.f_bsize = (long)sfsp->f_bsize;
sfs.f_iosize = (long)sfsp->f_iosize;
sfs.f_blocks = (long)sfsp->f_blocks;
sfs.f_bfree = (long)sfsp->f_bfree;
sfs.f_bavail = (long)sfsp->f_bavail;
}
sfs.f_files = (long)sfsp->f_files;
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);
if (partial_copy) {
copy_size -= (sizeof(sfs.f_reserved3) + sizeof(sfs.f_reserved4));
}
error = copyout((caddr_t)&sfs, bufp, copy_size);
}
if (sizep != NULL) {
*sizep = my_size;
}
return(error);
}
void munge_stat(struct stat *sbp, struct user_stat *usbp)
{
usbp->st_dev = sbp->st_dev;
usbp->st_ino = sbp->st_ino;
usbp->st_mode = sbp->st_mode;
usbp->st_nlink = sbp->st_nlink;
usbp->st_uid = sbp->st_uid;
usbp->st_gid = sbp->st_gid;
usbp->st_rdev = sbp->st_rdev;
#ifndef _POSIX_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];
}