Index: head/sys/fs/nullfs/null_vfsops.c =================================================================== --- head/sys/fs/nullfs/null_vfsops.c (revision 74272) +++ head/sys/fs/nullfs/null_vfsops.c (revision 74273) @@ -1,440 +1,443 @@ /* * Copyright (c) 1992, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 * * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 * $FreeBSD$ */ /* * Null Layer * (See null_vnops.c for a description of what this does.) */ #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_NULLFSMNT, "NULLFS mount", "NULLFS mount structure"); static int nullfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp); static int nullfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp); static int nullfs_mount(struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p); static int nullfs_quotactl(struct mount *mp, int cmd, uid_t uid, caddr_t arg, struct proc *p); static int nullfs_root(struct mount *mp, struct vnode **vpp); static int nullfs_start(struct mount *mp, int flags, struct proc *p); static int nullfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p); static int nullfs_sync(struct mount *mp, int waitfor, struct ucred *cred, struct proc *p); static int nullfs_unmount(struct mount *mp, int mntflags, struct proc *p); static int nullfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp); static int nullfs_vptofh(struct vnode *vp, struct fid *fhp); static int nullfs_extattrctl(struct mount *mp, int cmd, - const char *attrname, caddr_t arg, struct proc *p); + struct vnode *filename_vp, + int namespace, const char *attrname, + struct proc *p); /* * Mount null layer */ static int nullfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; struct null_args args; struct vnode *lowerrootvp, *vp; struct vnode *nullm_rootvp; struct null_mount *xmp; u_int size; int isvnunlocked = 0; NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/ } /* * Get argument */ error = copyin(data, (caddr_t)&args, sizeof(struct null_args)); if (error) return (error); /* * Unlock lower node to avoid deadlock. * (XXX) VOP_ISLOCKED is needed? */ if ((mp->mnt_vnodecovered->v_op == null_vnodeop_p) && VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) { VOP_UNLOCK(mp->mnt_vnodecovered, 0, p); isvnunlocked = 1; } /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); error = namei(ndp); /* * Re-lock vnode. */ if (isvnunlocked && !VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY, p); if (error) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; vrele(ndp->ni_dvp); ndp->ni_dvp = NULLVP; /* * Check multi null mount to avoid `lock against myself' panic. */ if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) { NULLFSDEBUG("nullfs_mount: multi null mount?\n"); vput(lowerrootvp); return (EDEADLK); } xmp = (struct null_mount *) malloc(sizeof(struct null_mount), M_NULLFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ xmp->nullm_vfs = lowerrootvp->v_mount; /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = null_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp, 0, p); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(xmp, M_NULLFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in nullfs_unmount. */ nullm_rootvp = vp; nullm_rootvp->v_flag |= VROOT; xmp->nullm_rootvp = nullm_rootvp; if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) xmp; vfs_getnewfsid(mp); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)nullfs_statfs(mp, &mp->mnt_stat, p); NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ static int nullfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */ } /* * Free reference to null layer */ static int nullfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; void *mntdata; int error; int flags = 0; NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); if (mntflags & MNT_FORCE) flags |= FORCECLOSE; error = VFS_ROOT(mp, &vp); if (error) return (error); if (vp->v_usecount > 2) { NULLFSDEBUG("nullfs_unmount: rootvp is busy(%d)\n", vp->v_usecount); vput(vp); return (EBUSY); } error = vflush(mp, vp, flags); if (error) return (error); #ifdef NULLFS_DEBUG vprint("alias root of lower", vp); #endif vput(vp); /* * Release reference on underlying root vnode */ vrele(vp); /* * And blow it away for future re-use */ vgone(vp); /* * Finally, throw away the null_mount structure */ mntdata = mp->mnt_data; mp->mnt_data = 0; free(mntdata, M_NULLFSMNT); return 0; } static int nullfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct proc *p = curproc; /* XXX */ struct vnode *vp; NULLFSDEBUG("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); /* * Return locked reference to root. */ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; VREF(vp); #ifdef NULLFS_DEBUG if (VOP_ISLOCKED(vp, NULL)) { Debugger("root vnode is locked.\n"); vrele(vp); return (EDEADLK); } #endif vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); *vpp = vp; return 0; } static int nullfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p); } static int nullfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } static int nullfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at null layer. */ return (0); } static int nullfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { int error; error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp); if (error) return (error); return (null_node_create(mp, *vpp, vpp)); } static int nullfs_fhtovp(mp, fidp, vpp) struct mount *mp; struct fid *fidp; struct vnode **vpp; { int error; error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, vpp); if (error) return (error); return (null_node_create(mp, *vpp, vpp)); } static int nullfs_checkexp(mp, nam, extflagsp, credanonp) struct mount *mp; struct sockaddr *nam; int *extflagsp; struct ucred **credanonp; { return VFS_CHECKEXP(MOUNTTONULLMOUNT(mp)->nullm_vfs, nam, extflagsp, credanonp); } static int nullfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp); } static int -nullfs_extattrctl(mp, cmd, attrname, arg, p) +nullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname, p) struct mount *mp; int cmd; + struct vnode *filename_vp; + int namespace; const char *attrname; - caddr_t arg; struct proc *p; { - return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, attrname, - arg, p); + return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, filename_vp, + namespace, attrname, p); } static struct vfsops null_vfsops = { nullfs_mount, nullfs_start, nullfs_unmount, nullfs_root, nullfs_quotactl, nullfs_statfs, nullfs_sync, nullfs_vget, nullfs_fhtovp, nullfs_checkexp, nullfs_vptofh, nullfs_init, nullfs_uninit, nullfs_extattrctl, }; VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK); Index: head/sys/fs/umapfs/umap_vfsops.c =================================================================== --- head/sys/fs/umapfs/umap_vfsops.c (revision 74272) +++ head/sys/fs/umapfs/umap_vfsops.c (revision 74273) @@ -1,462 +1,464 @@ /* * Copyright (c) 1992, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * the UCLA Ficus project. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)umap_vfsops.c 8.8 (Berkeley) 5/14/95 * * $FreeBSD$ */ /* * Umap Layer * (See mount_umap(8) for a description of this layer.) */ #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_UMAPFSMNT, "UMAP mount", "UMAP mount structure"); static int umapfs_fhtovp __P((struct mount *mp, struct fid *fidp, struct vnode **vpp)); static int umapfs_checkexp __P((struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp)); static int umapfs_mount __P((struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p)); static int umapfs_quotactl __P((struct mount *mp, int cmd, uid_t uid, caddr_t arg, struct proc *p)); static int umapfs_root __P((struct mount *mp, struct vnode **vpp)); static int umapfs_start __P((struct mount *mp, int flags, struct proc *p)); static int umapfs_statfs __P((struct mount *mp, struct statfs *sbp, struct proc *p)); static int umapfs_sync __P((struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)); static int umapfs_unmount __P((struct mount *mp, int mntflags, struct proc *p)); static int umapfs_vget __P((struct mount *mp, ino_t ino, struct vnode **vpp)); static int umapfs_vptofh __P((struct vnode *vp, struct fid *fhp)); static int umapfs_extattrctl __P((struct mount *mp, int cmd, - const char *attrname, caddr_t arg, + struct vnode *filename_vp, + int namespace, const char *attrname, struct proc *p)); /* * Mount umap layer */ static int umapfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct umap_args args; struct vnode *lowerrootvp, *vp; struct vnode *umapm_rootvp; struct umap_mount *amp; u_int size; int error; #ifdef DEBUG int i; #endif /* * Only for root */ if ((error = suser(p)) != 0) return (error); #ifdef DEBUG printf("umapfs_mount(mp = %p)\n", (void *)mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/ } /* * Get argument */ error = copyin(data, (caddr_t)&args, sizeof(struct umap_args)); if (error) return (error); /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); error = namei(ndp); if (error) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; #ifdef DEBUG printf("vp = %p, check for VDIR...\n", (void *)lowerrootvp); #endif vrele(ndp->ni_dvp); ndp->ni_dvp = 0; if (lowerrootvp->v_type != VDIR) { vput(lowerrootvp); return (EINVAL); } #ifdef DEBUG printf("mp = %p\n", (void *)mp); #endif amp = (struct umap_mount *) malloc(sizeof(struct umap_mount), M_UMAPFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ amp->umapm_vfs = lowerrootvp->v_mount; /* * Now copy in the number of entries and maps for umap mapping. */ amp->info_nentries = args.nentries; amp->info_gnentries = args.gnentries; error = copyin(args.mapdata, (caddr_t)amp->info_mapdata, 2*sizeof(u_long)*args.nentries); if (error) return (error); #ifdef DEBUG printf("umap_mount:nentries %d\n",args.nentries); for (i = 0; i < args.nentries; i++) printf(" %lu maps to %lu\n", amp->info_mapdata[i][0], amp->info_mapdata[i][1]); #endif error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata, 2*sizeof(u_long)*args.gnentries); if (error) return (error); #ifdef DEBUG printf("umap_mount:gnentries %d\n",args.gnentries); for (i = 0; i < args.gnentries; i++) printf(" group %lu maps to %lu\n", amp->info_gmapdata[i][0], amp->info_gmapdata[i][1]); #endif /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = umap_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp, 0, p); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(amp, M_UMAPFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in umapfs_unmount. */ umapm_rootvp = vp; umapm_rootvp->v_flag |= VROOT; amp->umapm_rootvp = umapm_rootvp; if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) amp; vfs_getnewfsid(mp); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)umapfs_statfs(mp, &mp->mnt_stat, p); #ifdef DEBUG printf("umapfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ static int umapfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */ } /* * Free reference to umap layer */ static int umapfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; int error; int flags = 0; #ifdef DEBUG printf("umapfs_unmount(mp = %p)\n", (void *)mp); #endif if (mntflags & MNT_FORCE) flags |= FORCECLOSE; /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #ifdef notyet mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (umapm_rootvp->v_usecount > 1) return (EBUSY); error = vflush(mp, umapm_rootvp, flags); if (error) return (error); #ifdef DEBUG vprint("alias root of lower", umapm_rootvp); #endif /* * Release reference on underlying root vnode */ vrele(umapm_rootvp); /* * And blow it away for future re-use */ vgone(umapm_rootvp); /* * Finally, throw away the umap_mount structure */ free(mp->mnt_data, M_UMAPFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } static int umapfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct proc *p = curproc; /* XXX */ struct vnode *vp; #ifdef DEBUG printf("umapfs_root(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, (void *)UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)); #endif /* * Return locked reference to root. */ vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; VREF(vp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); *vpp = vp; return (0); } static int umapfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p)); } static int umapfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; #ifdef DEBUG printf("umapfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, (void *)UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)); #endif bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } static int umapfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at umap layer. */ return (0); } static int umapfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp)); } static int umapfs_fhtovp(mp, fidp, vpp) struct mount *mp; struct fid *fidp; struct vnode **vpp; { return (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, vpp)); } static int umapfs_checkexp(mp, nam, exflagsp, credanonp) struct mount *mp; struct sockaddr *nam; int *exflagsp; struct ucred **credanonp; { return (VFS_CHECKEXP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, nam, exflagsp, credanonp)); } static int umapfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp)); } static int -umapfs_extattrctl(mp, cmd, attrname, arg, p) +umapfs_extattrctl(mp, cmd, filename_vp, namespace, attrname, p) struct mount *mp; int cmd; + struct vnode *filename_vp; + int namespace; const char *attrname; - caddr_t arg; struct proc *p; { - return (VFS_EXTATTRCTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, attrname, - arg, p)); + return (VFS_EXTATTRCTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, + filename_vp, namespace, attrname, p)); } static struct vfsops umap_vfsops = { umapfs_mount, umapfs_start, umapfs_unmount, umapfs_root, umapfs_quotactl, umapfs_statfs, umapfs_sync, umapfs_vget, umapfs_fhtovp, umapfs_checkexp, umapfs_vptofh, umapfs_init, vfs_stduninit, umapfs_extattrctl, }; VFS_SET(umap_vfsops, umap, VFCF_LOOPBACK); Index: head/sys/kern/syscalls.master =================================================================== --- head/sys/kern/syscalls.master (revision 74272) +++ head/sys/kern/syscalls.master (revision 74273) @@ -1,534 +1,535 @@ $FreeBSD$ ; from: @(#)syscalls.master 8.2 (Berkeley) 1/13/94 ; ; System call name/number master file. ; Processed to created init_sysent.c, syscalls.c and syscall.h. ; Columns: number [MPSAFE] type nargs namespc name alt{name,tag,rtyp}/comments ; number system call number, must be in order ; MPSAFE optional field, specifies that syscall does not want the ; BGL grabbed automatically (it is SMP safe). ; type one of STD, OBSOL, UNIMPL, COMPAT, CPT_NOA, LIBCOMPAT, ; NODEF, NOARGS, NOPROTO, NOIMPL, NOSTD ; namespc one of POSIX, BSD, NOHIDE ; name psuedo-prototype of syscall routine ; If one of the following alts is different, then all appear: ; altname name of system call if different ; alttag name of args struct tag if different from [o]`name'"_args" ; altrtyp return type if not int (bogus - syscalls always return int) ; for UNIMPL/OBSOL, name continues with comments ; types: ; STD always included ; COMPAT included on COMPAT #ifdef ; LIBCOMPAT included on COMPAT #ifdef, and placed in syscall.h ; OBSOL obsolete, not included in system, only specifies name ; UNIMPL not implemented, placeholder only ; NOSTD implemented but as a lkm that can be statically ; compiled in sysent entry will be filled with lkmsys ; so the SYSCALL_MODULE macro works ; #ifdef's, etc. may be included, and are copied to the output files. #include #include #include ; Reserved/unimplemented system calls in the range 0-150 inclusive ; are reserved for use in future Berkeley releases. ; Additional system calls implemented in vendor and other ; redistributions should be placed in the reserved range at the end ; of the current calls. 0 STD NOHIDE { int nosys(void); } syscall nosys_args int 1 STD NOHIDE { void sys_exit(int rval); } exit sys_exit_args void 2 STD POSIX { int fork(void); } 3 STD POSIX { ssize_t read(int fd, void *buf, size_t nbyte); } 4 STD POSIX { ssize_t write(int fd, const void *buf, size_t nbyte); } 5 STD POSIX { int open(char *path, int flags, int mode); } ; XXX should be { int open(const char *path, int flags, ...); } ; but we're not ready for `const' or varargs. ; XXX man page says `mode_t mode'. 6 STD POSIX { int close(int fd); } 7 STD BSD { int wait4(int pid, int *status, int options, \ struct rusage *rusage); } wait4 wait_args int 8 COMPAT BSD { int creat(char *path, int mode); } 9 STD POSIX { int link(char *path, char *link); } 10 STD POSIX { int unlink(char *path); } 11 OBSOL NOHIDE execv 12 STD POSIX { int chdir(char *path); } 13 STD BSD { int fchdir(int fd); } 14 STD POSIX { int mknod(char *path, int mode, int dev); } 15 STD POSIX { int chmod(char *path, int mode); } 16 STD POSIX { int chown(char *path, int uid, int gid); } 17 STD BSD { int obreak(char *nsize); } break obreak_args int 18 STD BSD { int getfsstat(struct statfs *buf, long bufsize, \ int flags); } 19 COMPAT POSIX { long lseek(int fd, long offset, int whence); } 20 STD POSIX { pid_t getpid(void); } 21 STD BSD { int mount(char *type, char *path, int flags, \ caddr_t data); } ; XXX 4.4lite2 uses `char *type' but we're not ready for that. ; XXX `path' should have type `const char *' but we're not ready for that. 22 STD BSD { int unmount(char *path, int flags); } 23 STD POSIX { int setuid(uid_t uid); } 24 MPSAFE STD POSIX { uid_t getuid(void); } 25 MPSAFE STD POSIX { uid_t geteuid(void); } 26 STD BSD { int ptrace(int req, pid_t pid, caddr_t addr, \ int data); } 27 STD BSD { int recvmsg(int s, struct msghdr *msg, int flags); } 28 STD BSD { int sendmsg(int s, caddr_t msg, int flags); } 29 STD BSD { int recvfrom(int s, caddr_t buf, size_t len, \ int flags, caddr_t from, int *fromlenaddr); } 30 STD BSD { int accept(int s, caddr_t name, int *anamelen); } 31 STD BSD { int getpeername(int fdes, caddr_t asa, int *alen); } 32 STD BSD { int getsockname(int fdes, caddr_t asa, int *alen); } 33 STD POSIX { int access(char *path, int flags); } 34 STD BSD { int chflags(char *path, int flags); } 35 STD BSD { int fchflags(int fd, int flags); } 36 STD BSD { int sync(void); } 37 STD POSIX { int kill(int pid, int signum); } 38 COMPAT POSIX { int stat(char *path, struct ostat *ub); } 39 STD POSIX { pid_t getppid(void); } 40 COMPAT POSIX { int lstat(char *path, struct ostat *ub); } 41 STD POSIX { int dup(u_int fd); } 42 STD POSIX { int pipe(void); } 43 STD POSIX { gid_t getegid(void); } 44 STD BSD { int profil(caddr_t samples, size_t size, \ size_t offset, u_int scale); } 45 STD BSD { int ktrace(const char *fname, int ops, int facs, \ int pid); } 46 COMPAT POSIX { int sigaction(int signum, struct osigaction *nsa, \ struct osigaction *osa); } 47 MPSAFE STD POSIX { gid_t getgid(void); } 48 MPSAFE COMPAT POSIX { int sigprocmask(int how, osigset_t mask); } ; XXX note nonstandard (bogus) calling convention - the libc stub passes ; us the mask, not a pointer to it, and we return the old mask as the ; (int) return value. 49 STD BSD { int getlogin(char *namebuf, u_int namelen); } 50 STD BSD { int setlogin(char *namebuf); } 51 STD BSD { int acct(char *path); } 52 COMPAT POSIX { int sigpending(void); } 53 STD BSD { int sigaltstack(stack_t *ss, stack_t *oss); } 54 STD POSIX { int ioctl(int fd, u_long com, caddr_t data); } 55 STD BSD { int reboot(int opt); } 56 STD POSIX { int revoke(char *path); } 57 STD POSIX { int symlink(char *path, char *link); } 58 STD POSIX { int readlink(char *path, char *buf, int count); } 59 STD POSIX { int execve(char *fname, char **argv, char **envv); } 60 MPSAFE STD POSIX { int umask(int newmask); } umask umask_args int 61 STD BSD { int chroot(char *path); } 62 COMPAT POSIX { int fstat(int fd, struct ostat *sb); } 63 COMPAT BSD { int getkerninfo(int op, char *where, size_t *size, \ int arg); } getkerninfo getkerninfo_args int 64 COMPAT BSD { int getpagesize(void); } \ getpagesize getpagesize_args int 65 STD BSD { int msync(void *addr, size_t len, int flags); } 66 STD BSD { int vfork(void); } 67 OBSOL NOHIDE vread 68 OBSOL NOHIDE vwrite 69 STD BSD { int sbrk(int incr); } 70 STD BSD { int sstk(int incr); } 71 COMPAT BSD { int mmap(void *addr, int len, int prot, \ int flags, int fd, long pos); } 72 STD BSD { int ovadvise(int anom); } vadvise ovadvise_args int 73 STD BSD { int munmap(void *addr, size_t len); } 74 STD BSD { int mprotect(const void *addr, size_t len, int prot); } 75 STD BSD { int madvise(void *addr, size_t len, int behav); } 76 OBSOL NOHIDE vhangup 77 OBSOL NOHIDE vlimit 78 STD BSD { int mincore(const void *addr, size_t len, \ char *vec); } 79 STD POSIX { int getgroups(u_int gidsetsize, gid_t *gidset); } 80 STD POSIX { int setgroups(u_int gidsetsize, gid_t *gidset); } 81 MPSAFE STD POSIX { int getpgrp(void); } 82 STD POSIX { int setpgid(int pid, int pgid); } 83 STD BSD { int setitimer(u_int which, struct itimerval *itv, \ struct itimerval *oitv); } 84 COMPAT BSD { int wait(void); } 85 STD BSD { int swapon(char *name); } 86 STD BSD { int getitimer(u_int which, struct itimerval *itv); } 87 COMPAT BSD { int gethostname(char *hostname, u_int len); } \ gethostname gethostname_args int 88 COMPAT BSD { int sethostname(char *hostname, u_int len); } \ sethostname sethostname_args int 89 STD BSD { int getdtablesize(void); } 90 STD POSIX { int dup2(u_int from, u_int to); } 91 UNIMPL BSD getdopt 92 STD POSIX { int fcntl(int fd, int cmd, long arg); } ; XXX should be { int fcntl(int fd, int cmd, ...); } ; but we're not ready for varargs. ; XXX man page says `int arg' too. 93 STD BSD { int select(int nd, fd_set *in, fd_set *ou, \ fd_set *ex, struct timeval *tv); } 94 UNIMPL BSD setdopt 95 STD POSIX { int fsync(int fd); } 96 STD BSD { int setpriority(int which, int who, int prio); } 97 STD BSD { int socket(int domain, int type, int protocol); } 98 STD BSD { int connect(int s, caddr_t name, int namelen); } 99 CPT_NOA BSD { int accept(int s, caddr_t name, int *anamelen); } \ accept accept_args int 100 STD BSD { int getpriority(int which, int who); } 101 COMPAT BSD { int send(int s, caddr_t buf, int len, int flags); } 102 COMPAT BSD { int recv(int s, caddr_t buf, int len, int flags); } 103 COMPAT BSD { int sigreturn(struct osigcontext *sigcntxp); } 104 STD BSD { int bind(int s, caddr_t name, int namelen); } 105 STD BSD { int setsockopt(int s, int level, int name, \ caddr_t val, int valsize); } 106 STD BSD { int listen(int s, int backlog); } 107 OBSOL NOHIDE vtimes 108 COMPAT BSD { int sigvec(int signum, struct sigvec *nsv, \ struct sigvec *osv); } 109 COMPAT BSD { int sigblock(int mask); } 110 COMPAT BSD { int sigsetmask(int mask); } 111 COMPAT POSIX { int sigsuspend(osigset_t mask); } ; XXX note nonstandard (bogus) calling convention - the libc stub passes ; us the mask, not a pointer to it. 112 COMPAT BSD { int sigstack(struct sigstack *nss, \ struct sigstack *oss); } 113 COMPAT BSD { int recvmsg(int s, struct omsghdr *msg, int flags); } 114 COMPAT BSD { int sendmsg(int s, caddr_t msg, int flags); } 115 OBSOL NOHIDE vtrace 116 STD BSD { int gettimeofday(struct timeval *tp, \ struct timezone *tzp); } 117 STD BSD { int getrusage(int who, struct rusage *rusage); } 118 STD BSD { int getsockopt(int s, int level, int name, \ caddr_t val, int *avalsize); } 119 UNIMPL NOHIDE resuba (BSD/OS 2.x) 120 STD BSD { int readv(int fd, struct iovec *iovp, u_int iovcnt); } 121 STD BSD { int writev(int fd, struct iovec *iovp, \ u_int iovcnt); } 122 STD BSD { int settimeofday(struct timeval *tv, \ struct timezone *tzp); } 123 STD BSD { int fchown(int fd, int uid, int gid); } 124 STD BSD { int fchmod(int fd, int mode); } 125 CPT_NOA BSD { int recvfrom(int s, caddr_t buf, size_t len, \ int flags, caddr_t from, int *fromlenaddr); } \ recvfrom recvfrom_args int 126 STD BSD { int setreuid(int ruid, int euid); } 127 STD BSD { int setregid(int rgid, int egid); } 128 STD POSIX { int rename(char *from, char *to); } 129 COMPAT BSD { int truncate(char *path, long length); } 130 COMPAT BSD { int ftruncate(int fd, long length); } 131 STD BSD { int flock(int fd, int how); } 132 STD POSIX { int mkfifo(char *path, int mode); } 133 STD BSD { int sendto(int s, caddr_t buf, size_t len, \ int flags, caddr_t to, int tolen); } 134 STD BSD { int shutdown(int s, int how); } 135 STD BSD { int socketpair(int domain, int type, int protocol, \ int *rsv); } 136 STD POSIX { int mkdir(char *path, int mode); } 137 STD POSIX { int rmdir(char *path); } 138 STD BSD { int utimes(char *path, struct timeval *tptr); } 139 OBSOL NOHIDE 4.2 sigreturn 140 STD BSD { int adjtime(struct timeval *delta, \ struct timeval *olddelta); } 141 COMPAT BSD { int getpeername(int fdes, caddr_t asa, int *alen); } 142 COMPAT BSD { long gethostid(void); } 143 COMPAT BSD { int sethostid(long hostid); } 144 COMPAT BSD { int getrlimit(u_int which, struct orlimit *rlp); } 145 COMPAT BSD { int setrlimit(u_int which, struct orlimit *rlp); } 146 COMPAT BSD { int killpg(int pgid, int signum); } 147 STD POSIX { int setsid(void); } 148 STD BSD { int quotactl(char *path, int cmd, int uid, \ caddr_t arg); } 149 COMPAT BSD { int quota(void); } 150 CPT_NOA BSD { int getsockname(int fdec, caddr_t asa, int *alen); }\ getsockname getsockname_args int ; Syscalls 151-180 inclusive are reserved for vendor-specific ; system calls. (This includes various calls added for compatibity ; with other Unix variants.) ; Some of these calls are now supported by BSD... 151 UNIMPL NOHIDE sem_lock (BSD/OS 2.x) 152 UNIMPL NOHIDE sem_wakeup (BSD/OS 2.x) 153 UNIMPL NOHIDE asyncdaemon (BSD/OS 2.x) 154 UNIMPL NOHIDE nosys ; 155 is initialized by the NFS code, if present. 155 NOIMPL BSD { int nfssvc(int flag, caddr_t argp); } 156 COMPAT BSD { int getdirentries(int fd, char *buf, u_int count, \ long *basep); } 157 STD BSD { int statfs(char *path, struct statfs *buf); } 158 STD BSD { int fstatfs(int fd, struct statfs *buf); } 159 UNIMPL NOHIDE nosys 160 UNIMPL NOHIDE nosys 161 STD BSD { int getfh(char *fname, struct fhandle *fhp); } 162 STD BSD { int getdomainname(char *domainname, int len); } 163 STD BSD { int setdomainname(char *domainname, int len); } 164 STD BSD { int uname(struct utsname *name); } 165 STD BSD { int sysarch(int op, char *parms); } 166 STD BSD { int rtprio(int function, pid_t pid, \ struct rtprio *rtp); } 167 UNIMPL NOHIDE nosys 168 UNIMPL NOHIDE nosys ; 169 is initialized by the SYSVSEM code if present or loaded 169 NOSTD BSD { int semsys(int which, int a2, int a3, int a4, \ int a5); } ; 169 is initialized by the SYSVMSG code if present or loaded ; XXX should be { int semsys(int which, ...); } 170 NOSTD BSD { int msgsys(int which, int a2, int a3, int a4, \ int a5, int a6); } ; 169 is initialized by the SYSVSHM code if present or loaded ; XXX should be { int msgsys(int which, ...); } 171 NOSTD BSD { int shmsys(int which, int a2, int a3, int a4); } ; XXX should be { int shmsys(int which, ...); } 172 UNIMPL NOHIDE nosys 173 STD POSIX { ssize_t pread(int fd, void *buf, size_t nbyte, \ int pad, off_t offset); } 174 STD POSIX { ssize_t pwrite(int fd, const void *buf, \ size_t nbyte, int pad, off_t offset); } 175 UNIMPL NOHIDE nosys 176 STD BSD { int ntp_adjtime(struct timex *tp); } 177 UNIMPL NOHIDE sfork (BSD/OS 2.x) 178 UNIMPL NOHIDE getdescriptor (BSD/OS 2.x) 179 UNIMPL NOHIDE setdescriptor (BSD/OS 2.x) 180 UNIMPL NOHIDE nosys ; Syscalls 181-199 are used by/reserved for BSD 181 STD POSIX { int setgid(gid_t gid); } 182 STD BSD { int setegid(gid_t egid); } 183 STD BSD { int seteuid(uid_t euid); } 184 UNIMPL BSD lfs_bmapv 185 UNIMPL BSD lfs_markv 186 UNIMPL BSD lfs_segclean 187 UNIMPL BSD lfs_segwait 188 STD POSIX { int stat(char *path, struct stat *ub); } 189 STD POSIX { int fstat(int fd, struct stat *sb); } 190 STD POSIX { int lstat(char *path, struct stat *ub); } 191 STD POSIX { int pathconf(char *path, int name); } 192 STD POSIX { int fpathconf(int fd, int name); } 193 UNIMPL NOHIDE nosys 194 STD BSD { int getrlimit(u_int which, \ struct rlimit *rlp); } \ getrlimit __getrlimit_args int 195 STD BSD { int setrlimit(u_int which, \ struct rlimit *rlp); } \ setrlimit __setrlimit_args int 196 STD BSD { int getdirentries(int fd, char *buf, u_int count, \ long *basep); } 197 STD BSD { caddr_t mmap(caddr_t addr, size_t len, int prot, \ int flags, int fd, int pad, off_t pos); } 198 STD NOHIDE { int nosys(void); } __syscall __syscall_args int 199 STD POSIX { off_t lseek(int fd, int pad, off_t offset, \ int whence); } 200 STD BSD { int truncate(char *path, int pad, off_t length); } 201 STD BSD { int ftruncate(int fd, int pad, off_t length); } 202 STD BSD { int __sysctl(int *name, u_int namelen, void *old, \ size_t *oldlenp, void *new, size_t newlen); } \ __sysctl sysctl_args int ; properly, __sysctl should be a NOHIDE, but making an exception ; here allows to avoid one in libc/sys/Makefile.inc. 203 STD BSD { int mlock(const void *addr, size_t len); } 204 STD BSD { int munlock(const void *addr, size_t len); } 205 STD BSD { int undelete(char *path); } 206 STD BSD { int futimes(int fd, struct timeval *tptr); } 207 STD BSD { int getpgid(pid_t pid); } 208 UNIMPL NOHIDE newreboot (NetBSD) 209 STD BSD { int poll(struct pollfd *fds, u_int nfds, \ int timeout); } ; ; The following are reserved for loadable syscalls ; 210 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 211 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 212 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 213 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 214 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 215 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 216 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 217 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 218 NODEF NOHIDE lkmnosys lkmnosys nosys_args int 219 NODEF NOHIDE lkmnosys lkmnosys nosys_args int ; ; The following were introduced with NetBSD/4.4Lite-2 ; They are initialized by thier respective modules/sysinits 220 NOSTD BSD { int __semctl(int semid, int semnum, int cmd, \ union semun *arg); } 221 NOSTD BSD { int semget(key_t key, int nsems, int semflg); } 222 NOSTD BSD { int semop(int semid, struct sembuf *sops, \ u_int nsops); } 223 UNIMPL NOHIDE semconfig 224 NOSTD BSD { int msgctl(int msqid, int cmd, \ struct msqid_ds *buf); } 225 NOSTD BSD { int msgget(key_t key, int msgflg); } 226 NOSTD BSD { int msgsnd(int msqid, void *msgp, size_t msgsz, \ int msgflg); } 227 NOSTD BSD { int msgrcv(int msqid, void *msgp, size_t msgsz, \ long msgtyp, int msgflg); } 228 NOSTD BSD { int shmat(int shmid, void *shmaddr, int shmflg); } 229 NOSTD BSD { int shmctl(int shmid, int cmd, \ struct shmid_ds *buf); } 230 NOSTD BSD { int shmdt(void *shmaddr); } 231 NOSTD BSD { int shmget(key_t key, int size, int shmflg); } ; 232 STD POSIX { int clock_gettime(clockid_t clock_id, \ struct timespec *tp); } 233 STD POSIX { int clock_settime(clockid_t clock_id, \ const struct timespec *tp); } 234 STD POSIX { int clock_getres(clockid_t clock_id, \ struct timespec *tp); } 235 UNIMPL NOHIDE timer_create 236 UNIMPL NOHIDE timer_delete 237 UNIMPL NOHIDE timer_settime 238 UNIMPL NOHIDE timer_gettime 239 UNIMPL NOHIDE timer_getoverrun 240 STD POSIX { int nanosleep(const struct timespec *rqtp, \ struct timespec *rmtp); } 241 UNIMPL NOHIDE nosys 242 UNIMPL NOHIDE nosys 243 UNIMPL NOHIDE nosys 244 UNIMPL NOHIDE nosys 245 UNIMPL NOHIDE nosys 246 UNIMPL NOHIDE nosys 247 UNIMPL NOHIDE nosys 248 UNIMPL NOHIDE nosys 249 UNIMPL NOHIDE nosys ; syscall numbers initially used in OpenBSD 250 STD BSD { int minherit(void *addr, size_t len, int inherit); } 251 STD BSD { int rfork(int flags); } 252 STD BSD { int openbsd_poll(struct pollfd *fds, u_int nfds, \ int timeout); } 253 STD BSD { int issetugid(void); } 254 STD BSD { int lchown(char *path, int uid, int gid); } 255 UNIMPL NOHIDE nosys 256 UNIMPL NOHIDE nosys 257 UNIMPL NOHIDE nosys 258 UNIMPL NOHIDE nosys 259 UNIMPL NOHIDE nosys 260 UNIMPL NOHIDE nosys 261 UNIMPL NOHIDE nosys 262 UNIMPL NOHIDE nosys 263 UNIMPL NOHIDE nosys 264 UNIMPL NOHIDE nosys 265 UNIMPL NOHIDE nosys 266 UNIMPL NOHIDE nosys 267 UNIMPL NOHIDE nosys 268 UNIMPL NOHIDE nosys 269 UNIMPL NOHIDE nosys 270 UNIMPL NOHIDE nosys 271 UNIMPL NOHIDE nosys 272 STD BSD { int getdents(int fd, char *buf, size_t count); } 273 UNIMPL NOHIDE nosys 274 STD BSD { int lchmod(char *path, mode_t mode); } 275 NOPROTO BSD { int lchown(char *path, uid_t uid, gid_t gid); } netbsd_lchown lchown_args int 276 STD BSD { int lutimes(char *path, struct timeval *tptr); } 277 NOPROTO BSD { int msync(void *addr, size_t len, int flags); } netbsd_msync msync_args int 278 STD BSD { int nstat(char *path, struct nstat *ub); } 279 STD BSD { int nfstat(int fd, struct nstat *sb); } 280 STD BSD { int nlstat(char *path, struct nstat *ub); } 281 UNIMPL NOHIDE nosys 282 UNIMPL NOHIDE nosys 283 UNIMPL NOHIDE nosys 284 UNIMPL NOHIDE nosys 285 UNIMPL NOHIDE nosys 286 UNIMPL NOHIDE nosys 287 UNIMPL NOHIDE nosys 288 UNIMPL NOHIDE nosys 289 UNIMPL NOHIDE nosys 290 UNIMPL NOHIDE nosys 291 UNIMPL NOHIDE nosys 292 UNIMPL NOHIDE nosys 293 UNIMPL NOHIDE nosys 294 UNIMPL NOHIDE nosys 295 UNIMPL NOHIDE nosys 296 UNIMPL NOHIDE nosys ; XXX 297 is 300 in NetBSD 297 STD BSD { int fhstatfs(const struct fhandle *u_fhp, struct statfs *buf); } 298 STD BSD { int fhopen(const struct fhandle *u_fhp, int flags); } 299 STD BSD { int fhstat(const struct fhandle *u_fhp, struct stat *sb); } ; syscall numbers for FreeBSD 300 STD BSD { int modnext(int modid); } 301 STD BSD { int modstat(int modid, struct module_stat* stat); } 302 STD BSD { int modfnext(int modid); } 303 STD BSD { int modfind(const char *name); } 304 STD BSD { int kldload(const char *file); } 305 STD BSD { int kldunload(int fileid); } 306 STD BSD { int kldfind(const char *file); } 307 STD BSD { int kldnext(int fileid); } 308 STD BSD { int kldstat(int fileid, struct kld_file_stat* stat); } 309 STD BSD { int kldfirstmod(int fileid); } 310 STD BSD { int getsid(pid_t pid); } 311 STD BSD { int setresuid(uid_t ruid, uid_t euid, uid_t suid); } 312 STD BSD { int setresgid(gid_t rgid, gid_t egid, gid_t sgid); } 313 OBSOL NOHIDE signanosleep 314 STD BSD { int aio_return(struct aiocb *aiocbp); } 315 STD BSD { int aio_suspend(struct aiocb * const * aiocbp, int nent, const struct timespec *timeout); } 316 STD BSD { int aio_cancel(int fd, struct aiocb *aiocbp); } 317 STD BSD { int aio_error(struct aiocb *aiocbp); } 318 STD BSD { int aio_read(struct aiocb *aiocbp); } 319 STD BSD { int aio_write(struct aiocb *aiocbp); } 320 STD BSD { int lio_listio(int mode, struct aiocb * const *acb_list, int nent, struct sigevent *sig); } 321 STD BSD { int yield(void); } 322 OBSOL NOHIDE thr_sleep 323 OBSOL NOHIDE thr_wakeup 324 STD BSD { int mlockall(int how); } 325 STD BSD { int munlockall(void); } 326 STD BSD { int __getcwd(u_char *buf, u_int buflen); } 327 STD POSIX { int sched_setparam (pid_t pid, const struct sched_param *param); } 328 STD POSIX { int sched_getparam (pid_t pid, struct sched_param *param); } 329 STD POSIX { int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param); } 330 STD POSIX { int sched_getscheduler (pid_t pid); } 331 STD POSIX { int sched_yield (void); } 332 STD POSIX { int sched_get_priority_max (int policy); } 333 STD POSIX { int sched_get_priority_min (int policy); } 334 STD POSIX { int sched_rr_get_interval (pid_t pid, struct timespec *interval); } 335 STD BSD { int utrace(const void *addr, size_t len); } 336 STD BSD { int sendfile(int fd, int s, off_t offset, size_t nbytes, \ struct sf_hdtr *hdtr, off_t *sbytes, int flags); } 337 STD BSD { int kldsym(int fileid, int cmd, void *data); } 338 STD BSD { int jail(struct jail *jail); } 339 UNIMPL BSD pioctl 340 MPSAFE STD POSIX { int sigprocmask(int how, const sigset_t *set, \ sigset_t *oset); } 341 STD POSIX { int sigsuspend(const sigset_t *sigmask); } 342 STD POSIX { int sigaction(int sig, const struct sigaction *act, \ struct sigaction *oact); } 343 STD POSIX { int sigpending(sigset_t *set); } 344 STD BSD { int sigreturn(ucontext_t *sigcntxp); } 345 UNIMPL NOHIDE sigtimedwait 346 UNIMPL NOHIDE sigwaitinfo 347 STD BSD { int __acl_get_file(const char *path, \ acl_type_t type, struct acl *aclp); } 348 STD BSD { int __acl_set_file(const char *path, \ acl_type_t type, struct acl *aclp); } 349 STD BSD { int __acl_get_fd(int filedes, acl_type_t type, \ struct acl *aclp); } 350 STD BSD { int __acl_set_fd(int filedes, acl_type_t type, \ struct acl *aclp); } 351 STD BSD { int __acl_delete_file(const char *path, \ acl_type_t type); } 352 STD BSD { int __acl_delete_fd(int filedes, acl_type_t type); } 353 STD BSD { int __acl_aclcheck_file(const char *path, \ acl_type_t type, struct acl *aclp); } 354 STD BSD { int __acl_aclcheck_fd(int filedes, acl_type_t type, \ struct acl *aclp); } 355 STD BSD { int extattrctl(const char *path, int cmd, \ - const char *attrname, char *arg); } + const char *filename, int namespace, \ + const char *attrname); } 356 STD BSD { int extattr_set_file(const char *path, \ - const char *attrname, struct iovec *iovp, \ - unsigned iovcnt); } + int namespace, const char *attrname, \ + struct iovec *iovp, unsigned iovcnt); } 357 STD BSD { int extattr_get_file(const char *path, \ - const char *attrname, struct iovec *iovp, \ - unsigned iovcnt); } + int namespace, const char *attrname, \ + struct iovec *iovp, unsigned iovcnt); } 358 STD BSD { int extattr_delete_file(const char *path, \ - const char *attrname); } + int namespace, const char *attrname); } 359 STD BSD { int aio_waitcomplete(struct aiocb **aiocbp, struct timespec *timeout); } 360 STD BSD { int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); } 361 STD BSD { int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); } 362 STD BSD { int kqueue(void); } 363 STD BSD { int kevent(int fd, \ const struct kevent *changelist, int nchanges, \ struct kevent *eventlist, int nevents, \ const struct timespec *timeout); } 364 STD BSD { int __cap_get_proc(struct cap *cap_p); } 365 STD BSD { int __cap_set_proc(struct cap *cap_p); } 366 STD BSD { int __cap_get_fd(int fd, struct cap *cap_p); } 367 STD BSD { int __cap_get_file(const char *path_p, struct cap *cap_p); } 368 STD BSD { int __cap_set_fd(int fd, struct cap *cap_p); } 369 STD BSD { int __cap_set_file(const char *path_p, struct cap *cap_p); } 370 NODEF NOHIDE lkmressys lkmressys nosys_args int Index: head/sys/kern/vfs_default.c =================================================================== --- head/sys/kern/vfs_default.c (revision 74272) +++ head/sys/kern/vfs_default.c (revision 74273) @@ -1,752 +1,753 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed * to Berkeley by John Heidemann of the UCLA Ficus project. * * Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int vop_nolookup __P((struct vop_lookup_args *)); static int vop_nostrategy __P((struct vop_strategy_args *)); /* * This vnode table stores what we want to do if the filesystem doesn't * implement a particular VOP. * * If there is no specific entry here, we will return EOPNOTSUPP. * */ vop_t **default_vnodeop_p; static struct vnodeopv_entry_desc default_vnodeop_entries[] = { { &vop_default_desc, (vop_t *) vop_eopnotsupp }, { &vop_advlock_desc, (vop_t *) vop_einval }, { &vop_bwrite_desc, (vop_t *) vop_stdbwrite }, { &vop_close_desc, (vop_t *) vop_null }, { &vop_createvobject_desc, (vop_t *) vop_stdcreatevobject }, { &vop_destroyvobject_desc, (vop_t *) vop_stddestroyvobject }, { &vop_fsync_desc, (vop_t *) vop_null }, { &vop_getvobject_desc, (vop_t *) vop_stdgetvobject }, { &vop_inactive_desc, (vop_t *) vop_stdinactive }, { &vop_ioctl_desc, (vop_t *) vop_enotty }, { &vop_islocked_desc, (vop_t *) vop_noislocked }, { &vop_lease_desc, (vop_t *) vop_null }, { &vop_lock_desc, (vop_t *) vop_nolock }, { &vop_lookup_desc, (vop_t *) vop_nolookup }, { &vop_open_desc, (vop_t *) vop_null }, { &vop_pathconf_desc, (vop_t *) vop_einval }, { &vop_poll_desc, (vop_t *) vop_nopoll }, { &vop_readlink_desc, (vop_t *) vop_einval }, { &vop_revoke_desc, (vop_t *) vop_revoke }, { &vop_strategy_desc, (vop_t *) vop_nostrategy }, { &vop_unlock_desc, (vop_t *) vop_nounlock }, { NULL, NULL } }; static struct vnodeopv_desc default_vnodeop_opv_desc = { &default_vnodeop_p, default_vnodeop_entries }; VNODEOP_SET(default_vnodeop_opv_desc); int vop_eopnotsupp(struct vop_generic_args *ap) { /* printf("vop_notsupp[%s]\n", ap->a_desc->vdesc_name); */ return (EOPNOTSUPP); } int vop_ebadf(struct vop_generic_args *ap) { return (EBADF); } int vop_enotty(struct vop_generic_args *ap) { return (ENOTTY); } int vop_einval(struct vop_generic_args *ap) { return (EINVAL); } int vop_null(struct vop_generic_args *ap) { return (0); } int vop_defaultop(struct vop_generic_args *ap) { return (VOCALL(default_vnodeop_p, ap->a_desc->vdesc_offset, ap)); } int vop_panic(struct vop_generic_args *ap) { panic("filesystem goof: vop_panic[%s]", ap->a_desc->vdesc_name); } static int vop_nolookup(ap) struct vop_lookup_args /* { struct vnode *a_dvp; struct vnode **a_vpp; struct componentname *a_cnp; } */ *ap; { *ap->a_vpp = NULL; return (ENOTDIR); } /* * vop_nostrategy: * * Strategy routine for VFS devices that have none. * * BIO_ERROR and B_INVAL must be cleared prior to calling any strategy * routine. Typically this is done for a BIO_READ strategy call. * Typically B_INVAL is assumed to already be clear prior to a write * and should not be cleared manually unless you just made the buffer * invalid. BIO_ERROR should be cleared either way. */ static int vop_nostrategy (struct vop_strategy_args *ap) { printf("No strategy for buffer at %p\n", ap->a_bp); vprint("", ap->a_vp); vprint("", ap->a_bp->b_vp); ap->a_bp->b_ioflags |= BIO_ERROR; ap->a_bp->b_error = EOPNOTSUPP; bufdone(ap->a_bp); return (EOPNOTSUPP); } int vop_stdpathconf(ap) struct vop_pathconf_args /* { struct vnode *a_vp; int a_name; int *a_retval; } */ *ap; { switch (ap->a_name) { case _PC_LINK_MAX: *ap->a_retval = LINK_MAX; return (0); case _PC_MAX_CANON: *ap->a_retval = MAX_CANON; return (0); case _PC_MAX_INPUT: *ap->a_retval = MAX_INPUT; return (0); case _PC_PIPE_BUF: *ap->a_retval = PIPE_BUF; return (0); case _PC_CHOWN_RESTRICTED: *ap->a_retval = 1; return (0); case _PC_VDISABLE: *ap->a_retval = _POSIX_VDISABLE; return (0); default: return (EINVAL); } /* NOTREACHED */ } /* * Standard lock, unlock and islocked functions. * * These depend on the lock structure being the first element in the * inode, ie: vp->v_data points to the the lock! */ int vop_stdlock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; #ifndef DEBUG_LOCKS return (lockmgr(&vp->v_lock, ap->a_flags, &vp->v_interlock, ap->a_p)); #else return (debuglockmgr(&vp->v_lock, ap->a_flags, &vp->v_interlock, ap->a_p, "vop_stdlock", vp->filename, vp->line)); #endif } int vop_stdunlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { struct vnode *vp = ap->a_vp; return (lockmgr(&vp->v_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p)); } int vop_stdislocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { return (lockstatus(&ap->a_vp->v_lock, ap->a_p)); } int vop_stdinactive(ap) struct vop_inactive_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { VOP_UNLOCK(ap->a_vp, 0, ap->a_p); return (0); } /* * Return true for select/poll. */ int vop_nopoll(ap) struct vop_poll_args /* { struct vnode *a_vp; int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { /* * Return true for read/write. If the user asked for something * special, return POLLNVAL, so that clients have a way of * determining reliably whether or not the extended * functionality is present without hard-coding knowledge * of specific filesystem implementations. */ if (ap->a_events & ~POLLSTANDARD) return (POLLNVAL); return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); } /* * Implement poll for local filesystems that support it. */ int vop_stdpoll(ap) struct vop_poll_args /* { struct vnode *a_vp; int a_events; struct ucred *a_cred; struct proc *a_p; } */ *ap; { if ((ap->a_events & ~POLLSTANDARD) == 0) return (ap->a_events & (POLLRDNORM|POLLWRNORM)); return (vn_pollrecord(ap->a_vp, ap->a_p, ap->a_events)); } int vop_stdbwrite(ap) struct vop_bwrite_args *ap; { return (bwrite(ap->a_bp)); } /* * Stubs to use when there is no locking to be done on the underlying object. * A minimal shared lock is necessary to ensure that the underlying object * is not revoked while an operation is in progress. So, an active shared * count is maintained in an auxillary vnode lock structure. */ int vop_sharedlock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { /* * This code cannot be used until all the non-locking filesystems * (notably NFS) are converted to properly lock and release nodes. * Also, certain vnode operations change the locking state within * the operation (create, mknod, remove, link, rename, mkdir, rmdir, * and symlink). Ideally these operations should not change the * lock state, but should be changed to let the caller of the * function unlock them. Otherwise all intermediate vnode layers * (such as union, umapfs, etc) must catch these functions to do * the necessary locking at their layer. Note that the inactive * and lookup operations also change their lock state, but this * cannot be avoided, so these two operations will always need * to be handled in intermediate layers. */ struct vnode *vp = ap->a_vp; int vnflags, flags = ap->a_flags; switch (flags & LK_TYPE_MASK) { case LK_DRAIN: vnflags = LK_DRAIN; break; case LK_EXCLUSIVE: #ifdef DEBUG_VFS_LOCKS /* * Normally, we use shared locks here, but that confuses * the locking assertions. */ vnflags = LK_EXCLUSIVE; break; #endif case LK_SHARED: vnflags = LK_SHARED; break; case LK_UPGRADE: case LK_EXCLUPGRADE: case LK_DOWNGRADE: return (0); case LK_RELEASE: default: panic("vop_sharedlock: bad operation %d", flags & LK_TYPE_MASK); } if (flags & LK_INTERLOCK) vnflags |= LK_INTERLOCK; #ifndef DEBUG_LOCKS return (lockmgr(&vp->v_lock, vnflags, &vp->v_interlock, ap->a_p)); #else return (debuglockmgr(&vp->v_lock, vnflags, &vp->v_interlock, ap->a_p, "vop_sharedlock", vp->filename, vp->line)); #endif } /* * Stubs to use when there is no locking to be done on the underlying object. * A minimal shared lock is necessary to ensure that the underlying object * is not revoked while an operation is in progress. So, an active shared * count is maintained in an auxillary vnode lock structure. */ int vop_nolock(ap) struct vop_lock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { #ifdef notyet /* * This code cannot be used until all the non-locking filesystems * (notably NFS) are converted to properly lock and release nodes. * Also, certain vnode operations change the locking state within * the operation (create, mknod, remove, link, rename, mkdir, rmdir, * and symlink). Ideally these operations should not change the * lock state, but should be changed to let the caller of the * function unlock them. Otherwise all intermediate vnode layers * (such as union, umapfs, etc) must catch these functions to do * the necessary locking at their layer. Note that the inactive * and lookup operations also change their lock state, but this * cannot be avoided, so these two operations will always need * to be handled in intermediate layers. */ struct vnode *vp = ap->a_vp; int vnflags, flags = ap->a_flags; switch (flags & LK_TYPE_MASK) { case LK_DRAIN: vnflags = LK_DRAIN; break; case LK_EXCLUSIVE: case LK_SHARED: vnflags = LK_SHARED; break; case LK_UPGRADE: case LK_EXCLUPGRADE: case LK_DOWNGRADE: return (0); case LK_RELEASE: default: panic("vop_nolock: bad operation %d", flags & LK_TYPE_MASK); } if (flags & LK_INTERLOCK) vnflags |= LK_INTERLOCK; return(lockmgr(&vp->v_lock, vnflags, &vp->v_interlock, ap->a_p)); #else /* for now */ /* * Since we are not using the lock manager, we must clear * the interlock here. */ if (ap->a_flags & LK_INTERLOCK) mtx_unlock(&ap->a_vp->v_interlock); return (0); #endif } /* * Do the inverse of vop_nolock, handling the interlock in a compatible way. */ int vop_nounlock(ap) struct vop_unlock_args /* { struct vnode *a_vp; int a_flags; struct proc *a_p; } */ *ap; { /* * Since we are not using the lock manager, we must clear * the interlock here. */ if (ap->a_flags & LK_INTERLOCK) mtx_unlock(&ap->a_vp->v_interlock); return (0); } /* * Return whether or not the node is in use. */ int vop_noislocked(ap) struct vop_islocked_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap; { return (0); } /* * Return our mount point, as we will take charge of the writes. */ int vop_stdgetwritemount(ap) struct vop_getwritemount_args /* { struct vnode *a_vp; struct mount **a_mpp; } */ *ap; { *(ap->a_mpp) = ap->a_vp->v_mount; return (0); } int vop_stdcreatevobject(ap) struct vop_createvobject_args /* { struct vnode *vp; struct ucred *cred; struct proc *p; } */ *ap; { struct vnode *vp = ap->a_vp; struct ucred *cred = ap->a_cred; struct proc *p = ap->a_p; struct vattr vat; vm_object_t object; int error = 0; if (!vn_isdisk(vp, NULL) && vn_canvmio(vp) == FALSE) return (0); retry: if ((object = vp->v_object) == NULL) { if (vp->v_type == VREG || vp->v_type == VDIR) { if ((error = VOP_GETATTR(vp, &vat, cred, p)) != 0) goto retn; object = vnode_pager_alloc(vp, vat.va_size, 0, 0); } else if (devsw(vp->v_rdev) != NULL) { /* * This simply allocates the biggest object possible * for a disk vnode. This should be fixed, but doesn't * cause any problems (yet). */ object = vnode_pager_alloc(vp, IDX_TO_OFF(INT_MAX), 0, 0); } else { goto retn; } /* * Dereference the reference we just created. This assumes * that the object is associated with the vp. */ object->ref_count--; vp->v_usecount--; } else { if (object->flags & OBJ_DEAD) { VOP_UNLOCK(vp, 0, p); tsleep(object, PVM, "vodead", 0); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); goto retry; } } KASSERT(vp->v_object != NULL, ("vfs_object_create: NULL object")); vp->v_flag |= VOBJBUF; retn: return (error); } int vop_stddestroyvobject(ap) struct vop_destroyvobject_args /* { struct vnode *vp; } */ *ap; { struct vnode *vp = ap->a_vp; vm_object_t obj = vp->v_object; if (vp->v_object == NULL) return (0); if (obj->ref_count == 0) { /* * vclean() may be called twice. The first time * removes the primary reference to the object, * the second time goes one further and is a * special-case to terminate the object. */ vm_object_terminate(obj); } else { /* * Woe to the process that tries to page now :-). */ vm_pager_deallocate(obj); } return (0); } int vop_stdgetvobject(ap) struct vop_getvobject_args /* { struct vnode *vp; struct vm_object **objpp; } */ *ap; { struct vnode *vp = ap->a_vp; struct vm_object **objpp = ap->a_objpp; if (objpp) *objpp = vp->v_object; return (vp->v_object ? 0 : EINVAL); } /* * vfs default ops * used to fill the vfs fucntion table to get reasonable default return values. */ int vfs_stdmount (mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { return (0); } int vfs_stdunmount (mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { return (0); } int vfs_stdroot (mp, vpp) struct mount *mp; struct vnode **vpp; { return (EOPNOTSUPP); } int vfs_stdstatfs (mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { return (EOPNOTSUPP); } int vfs_stdvptofh (vp, fhp) struct vnode *vp; struct fid *fhp; { return (EOPNOTSUPP); } int vfs_stdstart (mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); } int vfs_stdquotactl (mp, cmds, uid, arg, p) struct mount *mp; int cmds; uid_t uid; caddr_t arg; struct proc *p; { return (EOPNOTSUPP); } int vfs_stdsync (mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { return (0); } int vfs_stdvget (mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (EOPNOTSUPP); } int vfs_stdfhtovp (mp, fhp, vpp) struct mount *mp; struct fid *fhp; struct vnode **vpp; { return (EOPNOTSUPP); } int vfs_stdcheckexp (mp, nam, extflagsp, credanonp) struct mount *mp; struct sockaddr *nam; int *extflagsp; struct ucred **credanonp; { return (EOPNOTSUPP); } int vfs_stdinit (vfsp) struct vfsconf *vfsp; { return (0); } int vfs_stduninit (vfsp) struct vfsconf *vfsp; { return(0); } int -vfs_stdextattrctl(mp, cmd, attrname, arg, p) +vfs_stdextattrctl(mp, cmd, filename_vp, namespace, attrname, p) struct mount *mp; int cmd; + struct vnode *filename_vp; + int namespace; const char *attrname; - caddr_t arg; struct proc *p; { return(EOPNOTSUPP); } /* end of vfs default ops */ Index: head/sys/kern/vfs_extattr.c =================================================================== --- head/sys/kern/vfs_extattr.c (revision 74272) +++ head/sys/kern/vfs_extattr.c (revision 74273) @@ -1,3867 +1,3912 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 * $FreeBSD$ */ /* For 4.3 integer FS ID compatibility */ #include "opt_compat.h" #include "opt_ffs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int change_dir __P((struct nameidata *ndp, struct proc *p)); static void checkdirs __P((struct vnode *olddp, struct vnode *newdp)); static int chroot_refuse_vdir_fds __P((struct filedesc *fdp)); static int getutimes __P((const struct timeval *, struct timespec *)); static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t)); static int setfmode __P((struct proc *, struct vnode *, int)); static int setfflags __P((struct proc *, struct vnode *, int)); static int setutimes __P((struct proc *, struct vnode *, const struct timespec *, int)); static int usermount = 0; /* if 1, non-root can mount fs. */ int (*union_dircheckp) __P((struct proc *, struct vnode **, struct file *)); SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, ""); /* * Virtual File System System Calls */ /* * Mount a file system. */ #ifndef _SYS_SYSPROTO_H_ struct mount_args { char *type; char *path; int flags; caddr_t data; }; #endif /* ARGSUSED */ int mount(p, uap) struct proc *p; struct mount_args /* { syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; syscallarg(caddr_t) data; } */ *uap; { char *fstype; char *fspath; int error; fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK | M_ZERO); fspath = malloc(MNAMELEN, M_TEMP, M_WAITOK | M_ZERO); /* * vfs_mount() actually takes a kernel string for `type' and * `path' now, so extract them. */ error = copyinstr(SCARG(uap, type), fstype, MFSNAMELEN, NULL); if (error) goto finish; error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); if (error) goto finish; error = vfs_mount(p, fstype, fspath, SCARG(uap, flags), SCARG(uap, data)); finish: free(fstype, M_TEMP); free(fspath, M_TEMP); return (error); } /* * vfs_mount(): actually attempt a filesystem mount. * * This routine is designed to be a "generic" entry point for routines * that wish to mount a filesystem. All parameters except `fsdata' are * pointers into kernel space. `fsdata' is currently still a pointer * into userspace. */ int vfs_mount(p, fstype, fspath, fsflags, fsdata) struct proc *p; char *fstype; char *fspath; int fsflags; void *fsdata; { struct vnode *vp; struct mount *mp; struct vfsconf *vfsp; int error, flag = 0, flag2 = 0; struct vattr va; struct nameidata nd; /* * Be ultra-paranoid about making sure the type and fspath * variables will fit in our mp buffers, including the * terminating NUL. */ if ((strlen(fstype) >= MFSNAMELEN - 1) || (strlen(fspath) >= MNAMELEN - 1)) return (ENAMETOOLONG); if (usermount == 0 && (error = suser(p))) return (error); /* * Do not allow NFS export by non-root users. */ if (fsflags & MNT_EXPORTED) { error = suser(p); if (error) return (error); } /* * Silently enforce MNT_NOSUID and MNT_NODEV for non-root users */ if (suser_xxx(p->p_ucred, 0, 0)) fsflags |= MNT_NOSUID | MNT_NODEV; /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (fsflags & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; flag2 = mp->mnt_kern_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((fsflags & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } /* * Only root, or the user that did the original mount is * permitted to update it. */ if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && (error = suser(p))) { vput(vp); return (error); } if (vfs_busy(mp, LK_NOWAIT, 0, p)) { vput(vp); return (EBUSY); } mtx_lock(&vp->v_interlock); if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) { mtx_unlock(&vp->v_interlock); vfs_unbusy(mp, p); vput(vp); return (EBUSY); } vp->v_flag |= VMOUNT; mtx_unlock(&vp->v_interlock); mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT); VOP_UNLOCK(vp, 0, p); goto update; } /* * If the user is not root, ensure that they own the directory * onto which we are attempting to mount. */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && (error = suser(p)))) { vput(vp); return (error); } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) { vput(vp); return (error); } if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstype)) break; if (vfsp == NULL) { linker_file_t lf; /* Only load modules for root (very important!) */ if ((error = suser(p)) != 0) { vput(vp); return error; } error = linker_load_file(fstype, &lf); if (error || lf == NULL) { vput(vp); if (lf == NULL) error = ENODEV; return error; } lf->userrefs++; /* lookup again, see if the VFS was loaded */ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstype)) break; if (vfsp == NULL) { lf->userrefs--; linker_file_unload(lf); vput(vp); return (ENODEV); } } mtx_lock(&vp->v_interlock); if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) { mtx_unlock(&vp->v_interlock); vput(vp); return (EBUSY); } vp->v_flag |= VMOUNT; mtx_unlock(&vp->v_interlock); /* * Allocate and initialize the filesystem. */ mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK | M_ZERO); lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, LK_NOPAUSE); (void)vfs_busy(mp, LK_NOWAIT, 0, p); mp->mnt_op = vfsp->vfc_vfsops; mp->mnt_vfc = vfsp; vfsp->vfc_refcount++; mp->mnt_stat.f_type = vfsp->vfc_typenum; mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK; strncpy(mp->mnt_stat.f_fstypename, fstype, MFSNAMELEN); mp->mnt_stat.f_fstypename[MFSNAMELEN - 1] = '\0'; mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid; strncpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN); mp->mnt_stat.f_mntonname[MNAMELEN - 1] = '\0'; mp->mnt_iosize_max = DFLTPHYS; VOP_UNLOCK(vp, 0, p); update: /* * Set the mount level flags. */ if (fsflags & 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_NOATIME | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); mp->mnt_flag |= fsflags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_FORCE | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); /* * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they * get. No freeing of cn_pnbuf. */ error = VFS_MOUNT(mp, fspath, fsdata, &nd, p); if (mp->mnt_flag & MNT_UPDATE) { if (mp->mnt_kern_flag & MNTK_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_SNAPSHOT); mp->mnt_kern_flag &=~ MNTK_WANTRDWR; if (error) { mp->mnt_flag = flag; mp->mnt_kern_flag = flag2; } if ((mp->mnt_flag & MNT_RDONLY) == 0) { if (mp->mnt_syncer == NULL) error = vfs_allocate_syncvnode(mp); } else { if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); mp->mnt_syncer = NULL; } vfs_unbusy(mp, p); mtx_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; mtx_unlock(&vp->v_interlock); vrele(vp); return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { struct vnode *newdp; mtx_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; vp->v_mountedhere = mp; mtx_unlock(&vp->v_interlock); mtx_lock(&mountlist_mtx); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mtx_unlock(&mountlist_mtx); if (VFS_ROOT(mp, &newdp)) panic("mount: lost mount"); checkdirs(vp, newdp); vput(newdp); VOP_UNLOCK(vp, 0, p); if ((mp->mnt_flag & MNT_RDONLY) == 0) error = vfs_allocate_syncvnode(mp); vfs_unbusy(mp, p); if ((error = VFS_START(mp, 0, p)) != 0) vrele(vp); } else { mtx_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; mtx_unlock(&vp->v_interlock); mp->mnt_vfc->vfc_refcount--; vfs_unbusy(mp, p); free((caddr_t)mp, M_MOUNT); vput(vp); } return (error); } /* * Scan all active processes to see if any of them have a current * or root directory of `olddp'. If so, replace them with the new * mount point. */ static void checkdirs(olddp, newdp) struct vnode *olddp, *newdp; { struct filedesc *fdp; struct proc *p; if (olddp->v_usecount == 1) return; ALLPROC_LOCK(AP_SHARED); LIST_FOREACH(p, &allproc, p_list) { fdp = p->p_fd; if (fdp == NULL) continue; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); VREF(newdp); fdp->fd_cdir = newdp; } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); VREF(newdp); fdp->fd_rdir = newdp; } } ALLPROC_LOCK(AP_RELEASE); if (rootvnode == olddp) { vrele(rootvnode); VREF(newdp); rootvnode = newdp; } } /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ #ifndef _SYS_SYSPROTO_H_ struct unmount_args { char *path; int flags; }; #endif /* ARGSUSED */ int unmount(p, uap) struct proc *p; register struct unmount_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); mp = vp->v_mount; /* * Only root, or the user that did the original mount is * permitted to unmount this filesystem. */ if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && (error = suser(p))) { vput(vp); return (error); } /* * Don't allow unmounting the root file system. */ if (mp->mnt_flag & MNT_ROOTFS) { vput(vp); return (EINVAL); } /* * Must be the root of the filesystem */ if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } vput(vp); return (dounmount(mp, SCARG(uap, flags), p)); } /* * Do the actual file system unmount. */ int dounmount(mp, flags, p) struct mount *mp; int flags; struct proc *p; { struct vnode *coveredvp, *fsrootvp; int error; int async_flag; mtx_lock(&mountlist_mtx); mp->mnt_kern_flag |= MNTK_UNMOUNT; lockmgr(&mp->mnt_lock, LK_DRAIN | LK_INTERLOCK, &mountlist_mtx, p); vn_start_write(NULL, &mp, V_WAIT); if (mp->mnt_flag & MNT_EXPUBLIC) vfs_setpublicfs(NULL, NULL, NULL); vfs_msync(mp, MNT_WAIT); async_flag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &=~ MNT_ASYNC; cache_purgevfs(mp); /* remove cache entries for this file sys */ if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); /* Move process cdir/rdir refs on fs root to underlying vnode. */ if (VFS_ROOT(mp, &fsrootvp) == 0) { if (mp->mnt_vnodecovered != NULL) checkdirs(fsrootvp, mp->mnt_vnodecovered); if (fsrootvp == rootvnode) { vrele(rootvnode); rootvnode = NULL; } vput(fsrootvp); } if (((mp->mnt_flag & MNT_RDONLY) || (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || (flags & MNT_FORCE)) { error = VFS_UNMOUNT(mp, flags, p); } vn_finished_write(mp); mtx_lock(&mountlist_mtx); if (error) { /* Undo cdir/rdir and rootvnode changes made above. */ if (VFS_ROOT(mp, &fsrootvp) == 0) { if (mp->mnt_vnodecovered != NULL) checkdirs(mp->mnt_vnodecovered, fsrootvp); if (rootvnode == NULL) { rootvnode = fsrootvp; vref(rootvnode); } vput(fsrootvp); } if ((mp->mnt_flag & MNT_RDONLY) == 0 && mp->mnt_syncer == NULL) (void) vfs_allocate_syncvnode(mp); mp->mnt_kern_flag &= ~MNTK_UNMOUNT; mp->mnt_flag |= async_flag; lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK | LK_REENABLE, &mountlist_mtx, p); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); return (error); } TAILQ_REMOVE(&mountlist, mp, mnt_list); if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { coveredvp->v_mountedhere = (struct mount *)0; vrele(coveredvp); } mp->mnt_vfc->vfc_refcount--; if (!LIST_EMPTY(&mp->mnt_vnodelist)) panic("unmount: dangling vnode"); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_mtx, p); lockdestroy(&mp->mnt_lock); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); free((caddr_t)mp, M_MOUNT); return (0); } /* * Sync each mounted filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct sync_args { int dummy; }; #endif #ifdef DEBUG static int syncprt = 0; SYSCTL_INT(_debug, OID_AUTO, syncprt, CTLFLAG_RW, &syncprt, 0, ""); #endif /* ARGSUSED */ int sync(p, uap) struct proc *p; struct sync_args *uap; { struct mount *mp, *nmp; int asyncflag; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if ((mp->mnt_flag & MNT_RDONLY) == 0 && vn_start_write(NULL, &mp, V_NOWAIT) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; vfs_msync(mp, MNT_NOWAIT); VFS_SYNC(mp, MNT_NOWAIT, ((p != NULL) ? p->p_ucred : NOCRED), p); mp->mnt_flag |= asyncflag; vn_finished_write(mp); } mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } mtx_unlock(&mountlist_mtx); #if 0 /* * XXX don't call vfs_bufstats() yet because that routine * was not imported in the Lite2 merge. */ #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ #endif return (0); } /* XXX PRISON: could be per prison flag */ static int prison_quotas; #if 0 SYSCTL_INT(_kern_prison, OID_AUTO, quotas, CTLFLAG_RW, &prison_quotas, 0, ""); #endif /* * Change filesystem quotas. */ #ifndef _SYS_SYSPROTO_H_ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; #endif /* ARGSUSED */ int quotactl(p, uap) struct proc *p; register struct quotactl_args /* { syscallarg(char *) path; syscallarg(int) cmd; syscallarg(int) uid; syscallarg(caddr_t) arg; } */ *uap; { struct mount *mp; int error; struct nameidata nd; if (jailed(p->p_ucred) && !prison_quotas) return (EPERM); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH); vrele(nd.ni_vp); if (error) return (error); error = VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid), SCARG(uap, arg), p); vn_finished_write(mp); return (error); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct statfs_args { char *path; struct statfs *buf; }; #endif /* ARGSUSED */ int statfs(p, uap) struct proc *p; register struct statfs_args /* { syscallarg(char *) path; syscallarg(struct statfs *) buf; } */ *uap; { register struct mount *mp; register struct statfs *sp; int error; struct nameidata nd; struct statfs sb; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct fstatfs_args { int fd; struct statfs *buf; }; #endif /* ARGSUSED */ int fstatfs(p, uap) struct proc *p; register struct fstatfs_args /* { syscallarg(int) fd; syscallarg(struct statfs *) buf; } */ *uap; { struct file *fp; struct mount *mp; register struct statfs *sp; int error; struct statfs sb; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); mp = ((struct vnode *)fp->f_data)->v_mount; sp = &mp->mnt_stat; error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; #endif int getfsstat(p, uap) struct proc *p; register struct getfsstat_args /* { syscallarg(struct statfs *) buf; syscallarg(long) bufsize; syscallarg(int) flags; } */ *uap; { register struct mount *mp, *nmp; register struct statfs *sp; caddr_t sfsp; long count, maxcount, error; maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); sfsp = (caddr_t)SCARG(uap, buf); count = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((SCARG(uap, flags) & (MNT_LAZY|MNT_NOWAIT)) == 0 || (SCARG(uap, flags) & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, p))) { mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); continue; } sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); if (error) { vfs_unbusy(mp, p); return (error); } sfsp += sizeof(*sp); } count++; mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } mtx_unlock(&mountlist_mtx); if (sfsp && count > maxcount) p->p_retval[0] = maxcount; else p->p_retval[0] = count; return (0); } /* * Change current working directory to a given file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchdir_args { int fd; }; #endif /* ARGSUSED */ int fchdir(p, uap) struct proc *p; struct fchdir_args /* { syscallarg(int) fd; } */ *uap; { register struct filedesc *fdp = p->p_fd; struct vnode *vp, *tdp; struct mount *mp; struct file *fp; int error; if ((error = getvnode(fdp, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VREF(vp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0, 0, p)) continue; error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp, p); if (error) break; vput(vp); vp = tdp; } if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, p); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); } /* * Change current working directory (``.''). */ #ifndef _SYS_SYSPROTO_H_ struct chdir_args { char *path; }; #endif /* ARGSUSED */ int chdir(p, uap) struct proc *p; struct chdir_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_cdir); fdp->fd_cdir = nd.ni_vp; return (0); } /* * Helper function for raised chroot(2) security function: Refuse if * any filedescriptors are open directories. */ static int chroot_refuse_vdir_fds(fdp) struct filedesc *fdp; { struct vnode *vp; struct file *fp; int error; int fd; for (fd = 0; fd < fdp->fd_nfiles ; fd++) { error = getvnode(fdp, fd, &fp); if (error) continue; vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) continue; return(EPERM); } return (0); } /* * This sysctl determines if we will allow a process to chroot(2) if it * has a directory open: * 0: disallowed for all processes. * 1: allowed for processes that were not already chroot(2)'ed. * 2: allowed for all processes. */ static int chroot_allow_open_directories = 1; SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, &chroot_allow_open_directories, 0, ""); /* * Change notion of root (``/'') directory. */ #ifndef _SYS_SYSPROTO_H_ struct chroot_args { char *path; }; #endif /* ARGSUSED */ int chroot(p, uap) struct proc *p; struct chroot_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; error = suser_xxx(0, p, PRISON_ROOT); if (error) return (error); if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) error = chroot_refuse_vdir_fds(fdp); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_rdir); fdp->fd_rdir = nd.ni_vp; if (!fdp->fd_jdir) { fdp->fd_jdir = nd.ni_vp; VREF(fdp->fd_jdir); } return (0); } /* * Common routine for chroot and chdir. */ static int change_dir(ndp, p) register struct nameidata *ndp; struct proc *p; { struct vnode *vp; int error; error = namei(ndp); if (error) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); if (error) vput(vp); else VOP_UNLOCK(vp, 0, p); return (error); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ #ifndef _SYS_SYSPROTO_H_ struct open_args { char *path; int flags; int mode; }; #endif int open(p, uap) struct proc *p; register struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ *uap; { struct filedesc *fdp = p->p_fd; struct file *fp; struct vnode *vp; struct vattr vat; struct mount *mp; int cmode, flags, oflags; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; oflags = SCARG(uap, flags); if ((oflags & O_ACCMODE) == O_ACCMODE) return (EINVAL); flags = FFLAGS(oflags); error = falloc(p, &nfp, &indx); if (error) return (error); fp = nfp; cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ /* * Bump the ref count to prevent another process from closing * the descriptor while we are blocked in vn_open() */ fhold(fp); error = vn_open(&nd, &flags, cmode); if (error) { /* * release our own reference */ fdrop(fp, p); /* * handle special fdopen() case. bleh. dupfdopen() is * responsible for dropping the old contents of ofiles[indx] * if it succeeds. */ if ((error == ENODEV || error == ENXIO) && p->p_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(p, fdp, indx, p->p_dupfd, flags, error)) == 0) { p->p_retval[0] = indx; return (0); } /* * Clean up the descriptor, but only if another thread hadn't * replaced or closed it. */ if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdrop(fp, p); } if (error == ERESTART) error = EINTR; return (error); } p->p_dupfd = 0; NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* * There should be 2 references on the file, one from the descriptor * table, and one for us. * * Handle the case where someone closed the file (via its file * descriptor) while we were blocked. The end result should look * like opening the file succeeded but it was immediately closed. */ if (fp->f_count == 1) { KASSERT(fdp->fd_ofiles[indx] != fp, ("Open file descriptor lost all refs")); VOP_UNLOCK(vp, 0, p); vn_close(vp, flags & FMASK, fp->f_cred, p); fdrop(fp, p); p->p_retval[0] = indx; return 0; } fp->f_data = (caddr_t)vp; fp->f_flag = flags & FMASK; fp->f_ops = &vnops; fp->f_type = (vp->v_type == VFIFO ? DTYPE_FIFO : DTYPE_VNODE); VOP_UNLOCK(vp, 0, p); 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 = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) goto bad; fp->f_flag |= FHASLOCK; } if (flags & O_TRUNC) { if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto bad; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); VATTR_NULL(&vat); vat.va_size = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_SETATTR(vp, &vat, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); if (error) goto bad; } /* assert that vn_open created a backing object if one is needed */ KASSERT(!vn_canvmio(vp) || VOP_GETVOBJECT(vp, NULL) == 0, ("open: vmio vnode has no backing object after vn_open")); /* * Release our private reference, leaving the one associated with * the descriptor table intact. */ fdrop(fp, p); p->p_retval[0] = indx; return (0); bad: if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdrop(fp, p); } fdrop(fp, p); return (error); } #ifdef COMPAT_43 /* * Create a file. */ #ifndef _SYS_SYSPROTO_H_ struct ocreat_args { char *path; int mode; }; #endif int ocreat(p, uap) struct proc *p; register struct ocreat_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, mode) = SCARG(uap, mode); SCARG(&nuap, flags) = O_WRONLY | O_CREAT | O_TRUNC; return (open(p, &nuap)); } #endif /* COMPAT_43 */ /* * Create a special file. */ #ifndef _SYS_SYSPROTO_H_ struct mknod_args { char *path; int mode; int dev; }; #endif /* ARGSUSED */ int mknod(p, uap) struct proc *p; register struct mknod_args /* { syscallarg(char *) path; syscallarg(int) mode; syscallarg(int) dev; } */ *uap; { struct vnode *vp; struct mount *mp; struct vattr vattr; int error; int whiteout = 0; struct nameidata nd; switch (SCARG(uap, mode) & S_IFMT) { case S_IFCHR: case S_IFBLK: error = suser(p); break; default: error = suser_xxx(0, p, PRISON_ROOT); break; } if (error) return (error); restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { vrele(vp); error = EEXIST; } else { VATTR_NULL(&vattr); vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; vattr.va_rdev = SCARG(uap, dev); whiteout = 0; switch (SCARG(uap, mode) & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; case S_IFWHT: whiteout = 1; break; default: error = EINVAL; break; } } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); } } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mknod"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mknod"); return (error); } /* * Create a named pipe. */ #ifndef _SYS_SYSPROTO_H_ struct mkfifo_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkfifo(p, uap) struct proc *p; register struct mkfifo_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct mount *mp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VFIFO; vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); return (error); } /* * Make a hard file link. */ #ifndef _SYS_SYSPROTO_H_ struct link_args { char *path; char *link; }; #endif /* ARGSUSED */ int link(p, uap) struct proc *p; register struct link_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { struct vnode *vp; struct mount *mp; struct nameidata nd; int error; bwillwrite(); NDINIT(&nd, LOOKUP, FOLLOW|NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type == VDIR) { vrele(vp); return (EPERM); /* POSIX */ } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) { vrele(nd.ni_vp); error = EEXIST; } else { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); } vrele(vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "link"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "link"); return (error); } /* * Make a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct symlink_args { char *path; char *link; }; #endif /* ARGSUSED */ int symlink(p, uap) struct proc *p; register struct symlink_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { struct mount *mp; struct vattr vattr; char *path; int error; struct nameidata nd; path = zalloc(namei_zone); if ((error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, NULL)) != 0) goto out; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); vput(nd.ni_dvp); error = EEXIST; goto out; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); NDFREE(&nd, NDF_ONLY_PNBUF); if (error == 0) vput(nd.ni_vp); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "symlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "symlink"); out: zfree(namei_zone, path); return (error); } /* * Delete a whiteout from the filesystem. */ /* ARGSUSED */ int undelete(p, uap) struct proc *p; register struct undelete_args /* { syscallarg(char *) path; } */ *uap; { int error; struct mount *mp; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, SCARG(uap, path), p); error = namei(&nd); if (error) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp) vrele(nd.ni_vp); vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "undelete"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "undelete"); return (error); } /* * Delete a name from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct unlink_args { char *path; }; #endif /* ARGSUSED */ int unlink(p, uap) struct proc *p; struct unlink_args /* { syscallarg(char *) path; } */ *uap; { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { /* * The root of a mounted filesystem cannot be deleted. * * XXX: can this only be a VDIR case? */ if (vp->v_flag & VROOT) error = EBUSY; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vput(vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "unlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "unlink"); return (error); } /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct lseek_args { int fd; int pad; off_t offset; int whence; }; #endif int lseek(p, uap) struct proc *p; register struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ *uap; { struct ucred *cred = p->p_ucred; register struct filedesc *fdp = p->p_fd; register struct file *fp; struct vattr vattr; int error; if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); switch (SCARG(uap, whence)) { case L_INCR: fp->f_offset += SCARG(uap, offset); break; case L_XTND: error=VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p); if (error) return (error); fp->f_offset = SCARG(uap, offset) + vattr.va_size; break; case L_SET: fp->f_offset = SCARG(uap, offset); break; default: return (EINVAL); } *(off_t *)(p->p_retval) = fp->f_offset; return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct olseek_args { int fd; long offset; int whence; }; #endif int olseek(p, uap) struct proc *p; register struct olseek_args /* { syscallarg(int) fd; syscallarg(long) offset; syscallarg(int) whence; } */ *uap; { struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ nuap; int error; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, offset) = SCARG(uap, offset); SCARG(&nuap, whence) = SCARG(uap, whence); error = lseek(p, &nuap); return (error); } #endif /* COMPAT_43 */ /* * Check access permissions. */ #ifndef _SYS_SYSPROTO_H_ struct access_args { char *path; int flags; }; #endif int access(p, uap) struct proc *p; register struct access_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { struct ucred *cred, *tmpcred; register struct vnode *vp; int error, flags; struct nameidata nd; cred = p->p_ucred; /* * Create and modify a temporary credential instead of one that * is potentially shared. This could also mess up socket * buffer accounting which can run in an interrupt context. * * XXX - Depending on how "threads" are finally implemented, it * may be better to explicitly pass the credential to namei() * rather than to modify the potentially shared process structure. */ tmpcred = crdup(cred); tmpcred->cr_uid = p->p_cred->p_ruid; tmpcred->cr_groups[0] = p->p_cred->p_rgid; p->p_ucred = tmpcred; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; /* Flags == 0 means only check for existence. */ if (SCARG(uap, flags)) { flags = 0; if (SCARG(uap, flags) & R_OK) flags |= VREAD; if (SCARG(uap, flags) & W_OK) flags |= VWRITE; if (SCARG(uap, flags) & X_OK) flags |= VEXEC; if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, p); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); out1: p->p_ucred = cred; crfree(tmpcred); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct ostat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int ostat(p, uap) struct proc *p; register struct ostat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct olstat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int olstat(p, uap) struct proc *p; register struct olstat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct vnode *vp; struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct stat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int stat(p, uap) struct proc *p; register struct stat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { struct stat sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int lstat(p, uap) struct proc *p; register struct lstat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } /* * Implementation of the NetBSD stat() function. * XXX This should probably be collapsed with the FreeBSD version, * as the differences are only due to vn_stat() clearing spares at * the end of the structures. vn_stat could be split to avoid this, * and thus collapse the following to close to zero code. */ void cvtnstat(sb, nsb) struct stat *sb; struct nstat *nsb; { nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; nsb->st_mode = sb->st_mode; nsb->st_nlink = sb->st_nlink; nsb->st_uid = sb->st_uid; nsb->st_gid = sb->st_gid; nsb->st_rdev = sb->st_rdev; nsb->st_atimespec = sb->st_atimespec; nsb->st_mtimespec = sb->st_mtimespec; nsb->st_ctimespec = sb->st_ctimespec; nsb->st_size = sb->st_size; nsb->st_blocks = sb->st_blocks; nsb->st_blksize = sb->st_blksize; nsb->st_flags = sb->st_flags; nsb->st_gen = sb->st_gen; nsb->st_qspare[0] = sb->st_qspare[0]; nsb->st_qspare[1] = sb->st_qspare[1]; } #ifndef _SYS_SYSPROTO_H_ struct nstat_args { char *path; struct nstat *ub; }; #endif /* ARGSUSED */ int nstat(p, uap) struct proc *p; register struct nstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { struct stat sb; struct nstat nsb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * NetBSD lstat. Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int nlstat(p, uap) struct proc *p; register struct nlstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nstat nsb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * Get configurable pathname variables. */ #ifndef _SYS_SYSPROTO_H_ struct pathconf_args { char *path; int name; }; #endif /* ARGSUSED */ int pathconf(p, uap) struct proc *p; register struct pathconf_args /* { syscallarg(char *) path; syscallarg(int) name; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), p->p_retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct readlink_args { char *path; char *buf; int count; }; #endif /* ARGSUSED */ int readlink(p, uap) struct proc *p; register struct readlink_args /* { syscallarg(char *) path; syscallarg(char *) buf; syscallarg(int) count; } */ *uap; { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); error = VOP_READLINK(vp, &auio, p->p_ucred); } vput(vp); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } /* * Common implementation code for chflags() and fchflags(). */ static int setfflags(p, vp, flags) struct proc *p; struct vnode *vp; int flags; { int error; struct mount *mp; struct vattr vattr; /* * Prevent non-root users from setting flags on devices. When * a device is reused, users can retain ownership of the device * if they are allowed to set flags and programs assume that * chown can't fail when done as root. */ if ((vp->v_type == VCHR || vp->v_type == VBLK) && ((error = suser_xxx(p->p_ucred, p, PRISON_ROOT)) != 0)) return (error); if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_flags = flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } /* * Change flags of a file given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chflags_args { char *path; int flags; }; #endif /* ARGSUSED */ int chflags(p, uap) struct proc *p; register struct chflags_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(p, nd.ni_vp, SCARG(uap, flags)); vrele(nd.ni_vp); return error; } /* * Change flags of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchflags_args { int fd; int flags; }; #endif /* ARGSUSED */ int fchflags(p, uap) struct proc *p; register struct fchflags_args /* { syscallarg(int) fd; syscallarg(int) flags; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfflags(p, (struct vnode *) fp->f_data, SCARG(uap, flags)); } /* * Common implementation code for chmod(), lchmod() and fchmod(). */ static int setfmode(p, vp, mode) struct proc *p; struct vnode *vp; int mode; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_mode = mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return error; } /* * Change mode of a file given path name. */ #ifndef _SYS_SYSPROTO_H_ struct chmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int chmod(p, uap) struct proc *p; register struct chmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given path name (don't follow links.) */ #ifndef _SYS_SYSPROTO_H_ struct lchmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int lchmod(p, uap) struct proc *p; register struct lchmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchmod_args { int fd; int mode; }; #endif /* ARGSUSED */ int fchmod(p, uap) struct proc *p; register struct fchmod_args /* { syscallarg(int) fd; syscallarg(int) mode; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfmode(p, (struct vnode *)fp->f_data, SCARG(uap, mode)); } /* * Common implementation for chown(), lchown(), and fchown() */ static int setfown(p, vp, uid, gid) struct proc *p; struct vnode *vp; uid_t uid; gid_t gid; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_uid = uid; vattr.va_gid = gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return error; } /* * Set ownership given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int chown(p, uap) struct proc *p; register struct chown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a path name, do not cross symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int lchown(p, uap) struct proc *p; register struct lchown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchown_args { int fd; int uid; int gid; }; #endif /* ARGSUSED */ int fchown(p, uap) struct proc *p; register struct fchown_args /* { syscallarg(int) fd; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfown(p, (struct vnode *)fp->f_data, SCARG(uap, uid), SCARG(uap, gid)); } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int getutimes(usrtvp, tsp) const struct timeval *usrtvp; struct timespec *tsp; { struct timeval tv[2]; int error; if (usrtvp == NULL) { microtime(&tv[0]); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); tsp[1] = tsp[0]; } else { if ((error = copyin(usrtvp, tv, sizeof (tv))) != 0) return (error); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]); } return 0; } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int setutimes(p, vp, ts, nullflag) struct proc *p; struct vnode *vp; const struct timespec *ts; int nullflag; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_atime = ts[0]; vattr.va_mtime = ts[1]; if (nullflag) vattr.va_vaflags |= VA_UTIMES_NULL; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return error; } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct utimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int utimes(p, uap) struct proc *p; register struct utimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct lutimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int lutimes(p, uap) struct proc *p; register struct lutimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct futimes_args { int fd; struct timeval *tptr; }; #endif /* ARGSUSED */ int futimes(p, uap) struct proc *p; register struct futimes_args /* { syscallarg(int ) fd; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct file *fp; struct timeval *usrtvp; int error; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setutimes(p, (struct vnode *)fp->f_data, ts, usrtvp == NULL); } /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct truncate_args { char *path; int pad; off_t length; }; #endif /* ARGSUSED */ int truncate(p, uap) struct proc *p; register struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (uap->length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); vn_finished_write(mp); return (error); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ftruncate_args { int fd; int pad; off_t length; }; #endif /* ARGSUSED */ int ftruncate(p, uap) struct proc *p; register struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { struct mount *mp; struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FWRITE) == 0) return (EINVAL); vp = (struct vnode *)fp->f_data; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct otruncate_args { char *path; long length; }; #endif /* ARGSUSED */ int otruncate(p, uap) struct proc *p; register struct otruncate_args /* { syscallarg(char *) path; syscallarg(long) length; } */ *uap; { struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, length) = SCARG(uap, length); return (truncate(p, &nuap)); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct oftruncate_args { int fd; long length; }; #endif /* ARGSUSED */ int oftruncate(p, uap) struct proc *p; register struct oftruncate_args /* { syscallarg(int) fd; syscallarg(long) length; } */ *uap; { struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, length) = SCARG(uap, length); return (ftruncate(p, &nuap)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Sync an open file. */ #ifndef _SYS_SYSPROTO_H_ struct fsync_args { int fd; }; #endif /* ARGSUSED */ int fsync(p, uap) struct proc *p; struct fsync_args /* { syscallarg(int) fd; } */ *uap; { struct vnode *vp; struct mount *mp; struct file *fp; vm_object_t obj; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (VOP_GETVOBJECT(vp, &obj) == 0) vm_object_page_clean(obj, 0, 0, 0); error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); #ifdef SOFTUPDATES if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP)) error = softdep_fsync(vp); #endif VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ #ifndef _SYS_SYSPROTO_H_ struct rename_args { char *from; char *to; }; #endif /* ARGSUSED */ int rename(p, uap) struct proc *p; register struct rename_args /* { syscallarg(char *) from; syscallarg(char *) to; } */ *uap; { struct mount *mp; struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; bwillwrite(); NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, SCARG(uap, from), p); if ((error = namei(&fromnd)) != 0) return (error); fvp = fromnd.ni_vp; if ((error = vn_start_write(fvp, &mp, V_WAIT | PCATCH)) != 0) { NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | NOOBJ, UIO_USERSPACE, SCARG(uap, to), p); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { /* Translate error code for rename("dir1", "dir2/."). */ if (error == EISDIR && fvp->v_type == VDIR) error = EINVAL; NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); 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 out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If source is the same as the destination (that is the * same inode number with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && 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)) error = -1; out: if (!error) { VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) { VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); } if (tvp) { VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(fromnd.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(fromnd.ni_vp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_vp, "rename"); out1: if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); if (error == -1) return (0); return (error); } /* * Make a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct mkdir_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkdir(p, uap) struct proc *p; register struct mkdir_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VDIR; vattr.va_mode = (SCARG(uap, mode) & ACCESSPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (!error) vput(nd.ni_vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mkdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mkdir"); return (error); } /* * Remove a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct rmdir_args { char *path; }; #endif /* ARGSUSED */ int rmdir(p, uap) struct proc *p; struct rmdir_args /* { syscallarg(char *) path; } */ *uap; { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) { error = EBUSY; goto out; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); vn_finished_write(mp); out: NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "rmdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "rmdir"); return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int ogetdirentries(p, uap) struct proc *p; register struct ogetdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, eofflag, readcnt; long loff; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = SCARG(uap, count); MALLOC(dirbuf, caddr_t, SCARG(uap, count), M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = SCARG(uap, count) - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #endif /* COMPAT_43 */ /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int getdirentries(p, uap) struct proc *p; register struct getdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error, eofflag; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); /* vn_lock(vp, LK_SHARED | LK_RETRY, p); */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } if (SCARG(uap, basep) != NULL) { error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); } p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #ifndef _SYS_SYSPROTO_H_ struct getdents_args { int fd; char *buf; size_t count; }; #endif int getdents(p, uap) struct proc *p; register struct getdents_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; } */ *uap; { struct getdirentries_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; return getdirentries(p, &ap); } /* * Set the mode mask for creation of filesystem nodes. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct umask_args { int newmask; }; #endif int umask(p, uap) struct proc *p; struct umask_args /* { syscallarg(int) newmask; } */ *uap; { register struct filedesc *fdp; fdp = p->p_fd; p->p_retval[0] = fdp->fd_cmask; fdp->fd_cmask = SCARG(uap, newmask) & ALLPERMS; return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ #ifndef _SYS_SYSPROTO_H_ struct revoke_args { char *path; }; #endif /* ARGSUSED */ int revoke(p, uap) struct proc *p; register struct revoke_args /* { syscallarg(char *) path; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VCHR) { error = EINVAL; goto out; } if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) goto out; if (p->p_ucred->cr_uid != vattr.va_uid && (error = suser_xxx(0, p, PRISON_ROOT))) goto out; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto out; if (vcount(vp) > 1) VOP_REVOKE(vp, REVOKEALL); vn_finished_write(mp); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; int fd; struct file **fpp; { struct file *fp; if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE && fp->f_type != DTYPE_FIFO) return (EINVAL); *fpp = fp; return (0); } /* * Get (NFS) file handle */ #ifndef _SYS_SYSPROTO_H_ struct getfh_args { char *fname; fhandle_t *fhp; }; #endif int getfh(p, uap) struct proc *p; register struct getfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } /* * syscall for the rpc.lockd to use to translate a NFS file handle into * an open descriptor. * * warning: do not remove the suser() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct fhopen_args { const struct fhandle *u_fhp; int flags; }; #endif int fhopen(p, uap) struct proc *p; struct fhopen_args /* { syscallarg(const struct fhandle *) u_fhp; syscallarg(int) flags; } */ *uap; { struct mount *mp; struct vnode *vp; struct fhandle fhp; struct vattr vat; struct vattr *vap = &vat; struct flock lf; struct file *fp; register struct filedesc *fdp = p->p_fd; int fmode, mode, error, type; struct file *nfp; int indx; /* * Must be super user */ error = suser(p); if (error) return (error); fmode = FFLAGS(SCARG(uap, flags)); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(SCARG(uap,u_fhp), &fhp, sizeof(fhp)); if (error) return(error); /* find the mount point */ mp = vfs_getvfs(&fhp.fh_fsid); if (mp == NULL) return (ESTALE); /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); if (error) return (error); /* * from now on we have to make sure not * to forget about the vnode * any error that causes an abort must vput(vp) * just set error = err and 'goto bad;'. */ /* * from vn_open */ if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (mode) { error = VOP_ACCESS(vp, mode, p->p_ucred, p); if (error) goto bad; } if (fmode & O_TRUNC) { VOP_UNLOCK(vp, 0, p); /* XXX */ if ((error = vn_start_write(NULL, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ VATTR_NULL(vap); vap->va_size = 0; error = VOP_SETATTR(vp, vap, p->p_ucred, p); vn_finished_write(mp); if (error) goto bad; } error = VOP_OPEN(vp, fmode, p->p_ucred, p); if (error) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, p, p->p_ucred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; /* * end of vn_open code */ if ((error = falloc(p, &nfp, &indx)) != 0) goto bad; fp = nfp; /* * Hold an extra reference to avoid having fp ripped out * from under us while we block in the lock op */ fhold(fp); nfp->f_data = (caddr_t)vp; nfp->f_flag = fmode & FMASK; nfp->f_ops = &vnops; nfp->f_type = DTYPE_VNODE; if (fmode & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (fmode & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((fmode & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp, 0, p); if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { /* * The lock request failed. Normally close the * descriptor but handle the case where someone might * have dup()d or close()d it when we weren't looking. */ if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdrop(fp, p); } /* * release our private reference */ fdrop(fp, p); return(error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); fp->f_flag |= FHASLOCK; } if ((vp->v_type == VREG) && (VOP_GETVOBJECT(vp, NULL) != 0)) vfs_object_create(vp, p, p->p_ucred); VOP_UNLOCK(vp, 0, p); fdrop(fp, p); p->p_retval[0] = indx; return (0); bad: vput(vp); return (error); } /* * Stat an (NFS) file handle. */ #ifndef _SYS_SYSPROTO_H_ struct fhstat_args { struct fhandle *u_fhp; struct stat *sb; }; #endif int fhstat(p, uap) struct proc *p; register struct fhstat_args /* { syscallarg(struct fhandle *) u_fhp; syscallarg(struct stat *) sb; } */ *uap; { struct stat sb; fhandle_t fh; struct mount *mp; struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t)); if (error) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); error = copyout(&sb, SCARG(uap, sb), sizeof(sb)); return (error); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct fhstatfs_args { struct fhandle *u_fhp; struct statfs *buf; }; #endif int fhstatfs(p, uap) struct proc *p; struct fhstatfs_args /* { syscallarg(struct fhandle) *u_fhp; syscallarg(struct statfs) *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; struct statfs sb; fhandle_t fh; int error; /* * Must be super user */ if ((error = suser(p))) return (error); if ((error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); if ((error = VFS_STATFS(mp, sp, p)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, SCARG(uap, buf), sizeof(*sp))); } /* * Syscall to push extended attribute configuration information into the * VFS. Accepts a path, which it converts to a mountpoint, as well as * a command (int cmd), and attribute name and misc data. For now, the * attribute name is left in userspace for consumption by the VFS_op. * It will probably be changed to be copied into sysspace by the * syscall in the future, once issues with various consumers of the * attribute code have raised their hands. * * Currently this is used only by UFS Extended Attributes. */ int extattrctl(p, uap) struct proc *p; struct extattrctl_args *uap; { + struct vnode *filename_vp; struct nameidata nd; struct mount *mp; + char attrname[EXTATTR_MAXNAMELEN]; int error; + /* + * SCARG(uap, attrname) not always defined. We check again later + * when we invoke the VFS call so as to pass in NULL there if needed. + */ + if (SCARG(uap, attrname) != NULL) { + error = copyinstr(SCARG(uap, attrname), attrname, + EXTATTR_MAXNAMELEN, NULL); + if (error) + return (error); + } + + /* + * SCARG(uap, filename) not always defined. If it is, grab + * a vnode lock, which VFS_EXTATTRCTL() will later release. + */ + filename_vp = NULL; + if (SCARG(uap, filename) != NULL) { + NDINIT(&nd, LOOKUP | LOCKLEAF, FOLLOW, UIO_USERSPACE, + SCARG(uap, filename), p); + if ((error = namei(&nd)) != 0) + return (error); + filename_vp = nd.ni_vp; + NDFREE(&nd, NDF_NO_VP_RELE | NDF_NO_VP_UNLOCK); + } + + /* SCARG(uap, path) always defined. */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH); NDFREE(&nd, 0); - if (error) + if (error) { + if (filename_vp) + vrele(filename_vp); return (error); - error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), SCARG(uap, attrname), - SCARG(uap, arg), p); + } + + if (SCARG(uap, attrname) != NULL) { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, namespace), attrname, p); + } else { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, namespace), NULL, p); + } + vn_finished_write(mp); + /* + * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, + * filename_vp, so vrele it if it is defined. + */ + if (filename_vp != NULL) + vrele(filename_vp); + return (error); } /* * Syscall to set a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to the data to set. * The uio is consumed in the style of writev(). The real work happens * in VOP_SETEXTATTR(). */ int extattr_set_file(p, uap) struct proc *p; struct extattr_set_file_args *uap; { struct nameidata nd; struct mount *mp; struct uio auio; struct iovec *iov, *needfree = NULL, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); if ((error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH)) != 0) { NDFREE(&nd, 0); return (error); } iovlen = uap->iovcnt * sizeof(struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { error = EINVAL; goto done; } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else iov = aiov; auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; - error = VOP_SETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, - p); + error = VOP_SETEXTATTR(nd.ni_vp, SCARG(uap, namespace), attrname, + &auio, p->p_cred->pc_ucred, p); cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); vn_finished_write(mp); return (error); } /* * Syscall to get a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to a buffer for the * data. The uio is consumed in the style of readv(). The real work * happens in VOP_GETEXTATTR(); */ int extattr_get_file(p, uap) struct proc *p; struct extattr_get_file_args *uap; { struct nameidata nd; struct uio auio; struct iovec *iov, *needfree, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); iovlen = uap->iovcnt * sizeof (struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { NDFREE(&nd, 0); return (EINVAL); } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else { iov = aiov; needfree = NULL; } auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; - error = VOP_GETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, - p); + error = VOP_GETEXTATTR(nd.ni_vp, SCARG(uap, namespace), attrname, + &auio, p->p_cred->pc_ucred, p); cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); return(error); } /* * Syscall to delete a named extended attribute from a file or directory. * Accepts attribute name. The real work happens in VOP_SETEXTATTR(). */ int extattr_delete_file(p, uap) struct proc *p; struct extattr_delete_file_args *uap; { struct mount *mp; struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return(error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); if ((error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH)) != 0) { NDFREE(&nd, 0); return (error); } - error = VOP_SETEXTATTR(nd.ni_vp, attrname, NULL, p->p_cred->pc_ucred, - p); + error = VOP_SETEXTATTR(nd.ni_vp, SCARG(uap, namespace), attrname, + NULL, p->p_cred->pc_ucred, p); NDFREE(&nd, 0); vn_finished_write(mp); return(error); } Index: head/sys/kern/vfs_syscalls.c =================================================================== --- head/sys/kern/vfs_syscalls.c (revision 74272) +++ head/sys/kern/vfs_syscalls.c (revision 74273) @@ -1,3867 +1,3912 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 * $FreeBSD$ */ /* For 4.3 integer FS ID compatibility */ #include "opt_compat.h" #include "opt_ffs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int change_dir __P((struct nameidata *ndp, struct proc *p)); static void checkdirs __P((struct vnode *olddp, struct vnode *newdp)); static int chroot_refuse_vdir_fds __P((struct filedesc *fdp)); static int getutimes __P((const struct timeval *, struct timespec *)); static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t)); static int setfmode __P((struct proc *, struct vnode *, int)); static int setfflags __P((struct proc *, struct vnode *, int)); static int setutimes __P((struct proc *, struct vnode *, const struct timespec *, int)); static int usermount = 0; /* if 1, non-root can mount fs. */ int (*union_dircheckp) __P((struct proc *, struct vnode **, struct file *)); SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, ""); /* * Virtual File System System Calls */ /* * Mount a file system. */ #ifndef _SYS_SYSPROTO_H_ struct mount_args { char *type; char *path; int flags; caddr_t data; }; #endif /* ARGSUSED */ int mount(p, uap) struct proc *p; struct mount_args /* { syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; syscallarg(caddr_t) data; } */ *uap; { char *fstype; char *fspath; int error; fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK | M_ZERO); fspath = malloc(MNAMELEN, M_TEMP, M_WAITOK | M_ZERO); /* * vfs_mount() actually takes a kernel string for `type' and * `path' now, so extract them. */ error = copyinstr(SCARG(uap, type), fstype, MFSNAMELEN, NULL); if (error) goto finish; error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); if (error) goto finish; error = vfs_mount(p, fstype, fspath, SCARG(uap, flags), SCARG(uap, data)); finish: free(fstype, M_TEMP); free(fspath, M_TEMP); return (error); } /* * vfs_mount(): actually attempt a filesystem mount. * * This routine is designed to be a "generic" entry point for routines * that wish to mount a filesystem. All parameters except `fsdata' are * pointers into kernel space. `fsdata' is currently still a pointer * into userspace. */ int vfs_mount(p, fstype, fspath, fsflags, fsdata) struct proc *p; char *fstype; char *fspath; int fsflags; void *fsdata; { struct vnode *vp; struct mount *mp; struct vfsconf *vfsp; int error, flag = 0, flag2 = 0; struct vattr va; struct nameidata nd; /* * Be ultra-paranoid about making sure the type and fspath * variables will fit in our mp buffers, including the * terminating NUL. */ if ((strlen(fstype) >= MFSNAMELEN - 1) || (strlen(fspath) >= MNAMELEN - 1)) return (ENAMETOOLONG); if (usermount == 0 && (error = suser(p))) return (error); /* * Do not allow NFS export by non-root users. */ if (fsflags & MNT_EXPORTED) { error = suser(p); if (error) return (error); } /* * Silently enforce MNT_NOSUID and MNT_NODEV for non-root users */ if (suser_xxx(p->p_ucred, 0, 0)) fsflags |= MNT_NOSUID | MNT_NODEV; /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (fsflags & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; flag2 = mp->mnt_kern_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((fsflags & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } /* * Only root, or the user that did the original mount is * permitted to update it. */ if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && (error = suser(p))) { vput(vp); return (error); } if (vfs_busy(mp, LK_NOWAIT, 0, p)) { vput(vp); return (EBUSY); } mtx_lock(&vp->v_interlock); if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) { mtx_unlock(&vp->v_interlock); vfs_unbusy(mp, p); vput(vp); return (EBUSY); } vp->v_flag |= VMOUNT; mtx_unlock(&vp->v_interlock); mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT); VOP_UNLOCK(vp, 0, p); goto update; } /* * If the user is not root, ensure that they own the directory * onto which we are attempting to mount. */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && (error = suser(p)))) { vput(vp); return (error); } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) { vput(vp); return (error); } if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstype)) break; if (vfsp == NULL) { linker_file_t lf; /* Only load modules for root (very important!) */ if ((error = suser(p)) != 0) { vput(vp); return error; } error = linker_load_file(fstype, &lf); if (error || lf == NULL) { vput(vp); if (lf == NULL) error = ENODEV; return error; } lf->userrefs++; /* lookup again, see if the VFS was loaded */ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (!strcmp(vfsp->vfc_name, fstype)) break; if (vfsp == NULL) { lf->userrefs--; linker_file_unload(lf); vput(vp); return (ENODEV); } } mtx_lock(&vp->v_interlock); if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) { mtx_unlock(&vp->v_interlock); vput(vp); return (EBUSY); } vp->v_flag |= VMOUNT; mtx_unlock(&vp->v_interlock); /* * Allocate and initialize the filesystem. */ mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK | M_ZERO); lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, LK_NOPAUSE); (void)vfs_busy(mp, LK_NOWAIT, 0, p); mp->mnt_op = vfsp->vfc_vfsops; mp->mnt_vfc = vfsp; vfsp->vfc_refcount++; mp->mnt_stat.f_type = vfsp->vfc_typenum; mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK; strncpy(mp->mnt_stat.f_fstypename, fstype, MFSNAMELEN); mp->mnt_stat.f_fstypename[MFSNAMELEN - 1] = '\0'; mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid; strncpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN); mp->mnt_stat.f_mntonname[MNAMELEN - 1] = '\0'; mp->mnt_iosize_max = DFLTPHYS; VOP_UNLOCK(vp, 0, p); update: /* * Set the mount level flags. */ if (fsflags & 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_NOATIME | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); mp->mnt_flag |= fsflags & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_FORCE | MNT_NOSYMFOLLOW | MNT_IGNORE | MNT_NOATIME | MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR); /* * Mount the filesystem. * XXX The final recipients of VFS_MOUNT just overwrite the ndp they * get. No freeing of cn_pnbuf. */ error = VFS_MOUNT(mp, fspath, fsdata, &nd, p); if (mp->mnt_flag & MNT_UPDATE) { if (mp->mnt_kern_flag & MNTK_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_SNAPSHOT); mp->mnt_kern_flag &=~ MNTK_WANTRDWR; if (error) { mp->mnt_flag = flag; mp->mnt_kern_flag = flag2; } if ((mp->mnt_flag & MNT_RDONLY) == 0) { if (mp->mnt_syncer == NULL) error = vfs_allocate_syncvnode(mp); } else { if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); mp->mnt_syncer = NULL; } vfs_unbusy(mp, p); mtx_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; mtx_unlock(&vp->v_interlock); vrele(vp); return (error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { struct vnode *newdp; mtx_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; vp->v_mountedhere = mp; mtx_unlock(&vp->v_interlock); mtx_lock(&mountlist_mtx); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); mtx_unlock(&mountlist_mtx); if (VFS_ROOT(mp, &newdp)) panic("mount: lost mount"); checkdirs(vp, newdp); vput(newdp); VOP_UNLOCK(vp, 0, p); if ((mp->mnt_flag & MNT_RDONLY) == 0) error = vfs_allocate_syncvnode(mp); vfs_unbusy(mp, p); if ((error = VFS_START(mp, 0, p)) != 0) vrele(vp); } else { mtx_lock(&vp->v_interlock); vp->v_flag &= ~VMOUNT; mtx_unlock(&vp->v_interlock); mp->mnt_vfc->vfc_refcount--; vfs_unbusy(mp, p); free((caddr_t)mp, M_MOUNT); vput(vp); } return (error); } /* * Scan all active processes to see if any of them have a current * or root directory of `olddp'. If so, replace them with the new * mount point. */ static void checkdirs(olddp, newdp) struct vnode *olddp, *newdp; { struct filedesc *fdp; struct proc *p; if (olddp->v_usecount == 1) return; ALLPROC_LOCK(AP_SHARED); LIST_FOREACH(p, &allproc, p_list) { fdp = p->p_fd; if (fdp == NULL) continue; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); VREF(newdp); fdp->fd_cdir = newdp; } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); VREF(newdp); fdp->fd_rdir = newdp; } } ALLPROC_LOCK(AP_RELEASE); if (rootvnode == olddp) { vrele(rootvnode); VREF(newdp); rootvnode = newdp; } } /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ #ifndef _SYS_SYSPROTO_H_ struct unmount_args { char *path; int flags; }; #endif /* ARGSUSED */ int unmount(p, uap) struct proc *p; register struct unmount_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); mp = vp->v_mount; /* * Only root, or the user that did the original mount is * permitted to unmount this filesystem. */ if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && (error = suser(p))) { vput(vp); return (error); } /* * Don't allow unmounting the root file system. */ if (mp->mnt_flag & MNT_ROOTFS) { vput(vp); return (EINVAL); } /* * Must be the root of the filesystem */ if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } vput(vp); return (dounmount(mp, SCARG(uap, flags), p)); } /* * Do the actual file system unmount. */ int dounmount(mp, flags, p) struct mount *mp; int flags; struct proc *p; { struct vnode *coveredvp, *fsrootvp; int error; int async_flag; mtx_lock(&mountlist_mtx); mp->mnt_kern_flag |= MNTK_UNMOUNT; lockmgr(&mp->mnt_lock, LK_DRAIN | LK_INTERLOCK, &mountlist_mtx, p); vn_start_write(NULL, &mp, V_WAIT); if (mp->mnt_flag & MNT_EXPUBLIC) vfs_setpublicfs(NULL, NULL, NULL); vfs_msync(mp, MNT_WAIT); async_flag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &=~ MNT_ASYNC; cache_purgevfs(mp); /* remove cache entries for this file sys */ if (mp->mnt_syncer != NULL) vrele(mp->mnt_syncer); /* Move process cdir/rdir refs on fs root to underlying vnode. */ if (VFS_ROOT(mp, &fsrootvp) == 0) { if (mp->mnt_vnodecovered != NULL) checkdirs(fsrootvp, mp->mnt_vnodecovered); if (fsrootvp == rootvnode) { vrele(rootvnode); rootvnode = NULL; } vput(fsrootvp); } if (((mp->mnt_flag & MNT_RDONLY) || (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || (flags & MNT_FORCE)) { error = VFS_UNMOUNT(mp, flags, p); } vn_finished_write(mp); mtx_lock(&mountlist_mtx); if (error) { /* Undo cdir/rdir and rootvnode changes made above. */ if (VFS_ROOT(mp, &fsrootvp) == 0) { if (mp->mnt_vnodecovered != NULL) checkdirs(mp->mnt_vnodecovered, fsrootvp); if (rootvnode == NULL) { rootvnode = fsrootvp; vref(rootvnode); } vput(fsrootvp); } if ((mp->mnt_flag & MNT_RDONLY) == 0 && mp->mnt_syncer == NULL) (void) vfs_allocate_syncvnode(mp); mp->mnt_kern_flag &= ~MNTK_UNMOUNT; mp->mnt_flag |= async_flag; lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK | LK_REENABLE, &mountlist_mtx, p); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); return (error); } TAILQ_REMOVE(&mountlist, mp, mnt_list); if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { coveredvp->v_mountedhere = (struct mount *)0; vrele(coveredvp); } mp->mnt_vfc->vfc_refcount--; if (!LIST_EMPTY(&mp->mnt_vnodelist)) panic("unmount: dangling vnode"); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_mtx, p); lockdestroy(&mp->mnt_lock); if (mp->mnt_kern_flag & MNTK_MWAIT) wakeup((caddr_t)mp); free((caddr_t)mp, M_MOUNT); return (0); } /* * Sync each mounted filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct sync_args { int dummy; }; #endif #ifdef DEBUG static int syncprt = 0; SYSCTL_INT(_debug, OID_AUTO, syncprt, CTLFLAG_RW, &syncprt, 0, ""); #endif /* ARGSUSED */ int sync(p, uap) struct proc *p; struct sync_args *uap; { struct mount *mp, *nmp; int asyncflag; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if ((mp->mnt_flag & MNT_RDONLY) == 0 && vn_start_write(NULL, &mp, V_NOWAIT) == 0) { asyncflag = mp->mnt_flag & MNT_ASYNC; mp->mnt_flag &= ~MNT_ASYNC; vfs_msync(mp, MNT_NOWAIT); VFS_SYNC(mp, MNT_NOWAIT, ((p != NULL) ? p->p_ucred : NOCRED), p); mp->mnt_flag |= asyncflag; vn_finished_write(mp); } mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } mtx_unlock(&mountlist_mtx); #if 0 /* * XXX don't call vfs_bufstats() yet because that routine * was not imported in the Lite2 merge. */ #ifdef DIAGNOSTIC if (syncprt) vfs_bufstats(); #endif /* DIAGNOSTIC */ #endif return (0); } /* XXX PRISON: could be per prison flag */ static int prison_quotas; #if 0 SYSCTL_INT(_kern_prison, OID_AUTO, quotas, CTLFLAG_RW, &prison_quotas, 0, ""); #endif /* * Change filesystem quotas. */ #ifndef _SYS_SYSPROTO_H_ struct quotactl_args { char *path; int cmd; int uid; caddr_t arg; }; #endif /* ARGSUSED */ int quotactl(p, uap) struct proc *p; register struct quotactl_args /* { syscallarg(char *) path; syscallarg(int) cmd; syscallarg(int) uid; syscallarg(caddr_t) arg; } */ *uap; { struct mount *mp; int error; struct nameidata nd; if (jailed(p->p_ucred) && !prison_quotas) return (EPERM); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH); vrele(nd.ni_vp); if (error) return (error); error = VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid), SCARG(uap, arg), p); vn_finished_write(mp); return (error); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct statfs_args { char *path; struct statfs *buf; }; #endif /* ARGSUSED */ int statfs(p, uap) struct proc *p; register struct statfs_args /* { syscallarg(char *) path; syscallarg(struct statfs *) buf; } */ *uap; { register struct mount *mp; register struct statfs *sp; int error; struct nameidata nd; struct statfs sb; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); mp = nd.ni_vp->v_mount; sp = &mp->mnt_stat; NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get filesystem statistics. */ #ifndef _SYS_SYSPROTO_H_ struct fstatfs_args { int fd; struct statfs *buf; }; #endif /* ARGSUSED */ int fstatfs(p, uap) struct proc *p; register struct fstatfs_args /* { syscallarg(int) fd; syscallarg(struct statfs *) buf; } */ *uap; { struct file *fp; struct mount *mp; register struct statfs *sp; int error; struct statfs sb; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); mp = ((struct vnode *)fp->f_data)->v_mount; sp = &mp->mnt_stat; error = VFS_STATFS(mp, sp, p); if (error) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); } /* * Get statistics on all filesystems. */ #ifndef _SYS_SYSPROTO_H_ struct getfsstat_args { struct statfs *buf; long bufsize; int flags; }; #endif int getfsstat(p, uap) struct proc *p; register struct getfsstat_args /* { syscallarg(struct statfs *) buf; syscallarg(long) bufsize; syscallarg(int) flags; } */ *uap; { register struct mount *mp, *nmp; register struct statfs *sp; caddr_t sfsp; long count, maxcount, error; maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); sfsp = (caddr_t)SCARG(uap, buf); count = 0; mtx_lock(&mountlist_mtx); for (mp = TAILQ_FIRST(&mountlist); mp != NULL; mp = nmp) { if (vfs_busy(mp, LK_NOWAIT, &mountlist_mtx, p)) { nmp = TAILQ_NEXT(mp, mnt_list); continue; } if (sfsp && count < maxcount) { sp = &mp->mnt_stat; /* * If MNT_NOWAIT or MNT_LAZY is specified, do not * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY * overrides MNT_WAIT. */ if (((SCARG(uap, flags) & (MNT_LAZY|MNT_NOWAIT)) == 0 || (SCARG(uap, flags) & MNT_WAIT)) && (error = VFS_STATFS(mp, sp, p))) { mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); continue; } sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); if (error) { vfs_unbusy(mp, p); return (error); } sfsp += sizeof(*sp); } count++; mtx_lock(&mountlist_mtx); nmp = TAILQ_NEXT(mp, mnt_list); vfs_unbusy(mp, p); } mtx_unlock(&mountlist_mtx); if (sfsp && count > maxcount) p->p_retval[0] = maxcount; else p->p_retval[0] = count; return (0); } /* * Change current working directory to a given file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchdir_args { int fd; }; #endif /* ARGSUSED */ int fchdir(p, uap) struct proc *p; struct fchdir_args /* { syscallarg(int) fd; } */ *uap; { register struct filedesc *fdp = p->p_fd; struct vnode *vp, *tdp; struct mount *mp; struct file *fp; int error; if ((error = getvnode(fdp, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; VREF(vp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); while (!error && (mp = vp->v_mountedhere) != NULL) { if (vfs_busy(mp, 0, 0, p)) continue; error = VFS_ROOT(mp, &tdp); vfs_unbusy(mp, p); if (error) break; vput(vp); vp = tdp; } if (error) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, p); vrele(fdp->fd_cdir); fdp->fd_cdir = vp; return (0); } /* * Change current working directory (``.''). */ #ifndef _SYS_SYSPROTO_H_ struct chdir_args { char *path; }; #endif /* ARGSUSED */ int chdir(p, uap) struct proc *p; struct chdir_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_cdir); fdp->fd_cdir = nd.ni_vp; return (0); } /* * Helper function for raised chroot(2) security function: Refuse if * any filedescriptors are open directories. */ static int chroot_refuse_vdir_fds(fdp) struct filedesc *fdp; { struct vnode *vp; struct file *fp; int error; int fd; for (fd = 0; fd < fdp->fd_nfiles ; fd++) { error = getvnode(fdp, fd, &fp); if (error) continue; vp = (struct vnode *)fp->f_data; if (vp->v_type != VDIR) continue; return(EPERM); } return (0); } /* * This sysctl determines if we will allow a process to chroot(2) if it * has a directory open: * 0: disallowed for all processes. * 1: allowed for processes that were not already chroot(2)'ed. * 2: allowed for all processes. */ static int chroot_allow_open_directories = 1; SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW, &chroot_allow_open_directories, 0, ""); /* * Change notion of root (``/'') directory. */ #ifndef _SYS_SYSPROTO_H_ struct chroot_args { char *path; }; #endif /* ARGSUSED */ int chroot(p, uap) struct proc *p; struct chroot_args /* { syscallarg(char *) path; } */ *uap; { register struct filedesc *fdp = p->p_fd; int error; struct nameidata nd; error = suser_xxx(0, p, PRISON_ROOT); if (error) return (error); if (chroot_allow_open_directories == 0 || (chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode)) error = chroot_refuse_vdir_fds(fdp); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = change_dir(&nd, p)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vrele(fdp->fd_rdir); fdp->fd_rdir = nd.ni_vp; if (!fdp->fd_jdir) { fdp->fd_jdir = nd.ni_vp; VREF(fdp->fd_jdir); } return (0); } /* * Common routine for chroot and chdir. */ static int change_dir(ndp, p) register struct nameidata *ndp; struct proc *p; { struct vnode *vp; int error; error = namei(ndp); if (error) return (error); vp = ndp->ni_vp; if (vp->v_type != VDIR) error = ENOTDIR; else error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); if (error) vput(vp); else VOP_UNLOCK(vp, 0, p); return (error); } /* * Check permissions, allocate an open file structure, * and call the device open routine if any. */ #ifndef _SYS_SYSPROTO_H_ struct open_args { char *path; int flags; int mode; }; #endif int open(p, uap) struct proc *p; register struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ *uap; { struct filedesc *fdp = p->p_fd; struct file *fp; struct vnode *vp; struct vattr vat; struct mount *mp; int cmode, flags, oflags; struct file *nfp; int type, indx, error; struct flock lf; struct nameidata nd; oflags = SCARG(uap, flags); if ((oflags & O_ACCMODE) == O_ACCMODE) return (EINVAL); flags = FFLAGS(oflags); error = falloc(p, &nfp, &indx); if (error) return (error); fp = nfp; cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); p->p_dupfd = -indx - 1; /* XXX check for fdopen */ /* * Bump the ref count to prevent another process from closing * the descriptor while we are blocked in vn_open() */ fhold(fp); error = vn_open(&nd, &flags, cmode); if (error) { /* * release our own reference */ fdrop(fp, p); /* * handle special fdopen() case. bleh. dupfdopen() is * responsible for dropping the old contents of ofiles[indx] * if it succeeds. */ if ((error == ENODEV || error == ENXIO) && p->p_dupfd >= 0 && /* XXX from fdopen */ (error = dupfdopen(p, fdp, indx, p->p_dupfd, flags, error)) == 0) { p->p_retval[0] = indx; return (0); } /* * Clean up the descriptor, but only if another thread hadn't * replaced or closed it. */ if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdrop(fp, p); } if (error == ERESTART) error = EINTR; return (error); } p->p_dupfd = 0; NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; /* * There should be 2 references on the file, one from the descriptor * table, and one for us. * * Handle the case where someone closed the file (via its file * descriptor) while we were blocked. The end result should look * like opening the file succeeded but it was immediately closed. */ if (fp->f_count == 1) { KASSERT(fdp->fd_ofiles[indx] != fp, ("Open file descriptor lost all refs")); VOP_UNLOCK(vp, 0, p); vn_close(vp, flags & FMASK, fp->f_cred, p); fdrop(fp, p); p->p_retval[0] = indx; return 0; } fp->f_data = (caddr_t)vp; fp->f_flag = flags & FMASK; fp->f_ops = &vnops; fp->f_type = (vp->v_type == VFIFO ? DTYPE_FIFO : DTYPE_VNODE); VOP_UNLOCK(vp, 0, p); 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 = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) goto bad; fp->f_flag |= FHASLOCK; } if (flags & O_TRUNC) { if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto bad; VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); VATTR_NULL(&vat); vat.va_size = 0; vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_SETATTR(vp, &vat, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); if (error) goto bad; } /* assert that vn_open created a backing object if one is needed */ KASSERT(!vn_canvmio(vp) || VOP_GETVOBJECT(vp, NULL) == 0, ("open: vmio vnode has no backing object after vn_open")); /* * Release our private reference, leaving the one associated with * the descriptor table intact. */ fdrop(fp, p); p->p_retval[0] = indx; return (0); bad: if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdrop(fp, p); } fdrop(fp, p); return (error); } #ifdef COMPAT_43 /* * Create a file. */ #ifndef _SYS_SYSPROTO_H_ struct ocreat_args { char *path; int mode; }; #endif int ocreat(p, uap) struct proc *p; register struct ocreat_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct open_args /* { syscallarg(char *) path; syscallarg(int) flags; syscallarg(int) mode; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, mode) = SCARG(uap, mode); SCARG(&nuap, flags) = O_WRONLY | O_CREAT | O_TRUNC; return (open(p, &nuap)); } #endif /* COMPAT_43 */ /* * Create a special file. */ #ifndef _SYS_SYSPROTO_H_ struct mknod_args { char *path; int mode; int dev; }; #endif /* ARGSUSED */ int mknod(p, uap) struct proc *p; register struct mknod_args /* { syscallarg(char *) path; syscallarg(int) mode; syscallarg(int) dev; } */ *uap; { struct vnode *vp; struct mount *mp; struct vattr vattr; int error; int whiteout = 0; struct nameidata nd; switch (SCARG(uap, mode) & S_IFMT) { case S_IFCHR: case S_IFBLK: error = suser(p); break; default: error = suser_xxx(0, p, PRISON_ROOT); break; } if (error) return (error); restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { vrele(vp); error = EEXIST; } else { VATTR_NULL(&vattr); vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; vattr.va_rdev = SCARG(uap, dev); whiteout = 0; switch (SCARG(uap, mode) & S_IFMT) { case S_IFMT: /* used by badsect to flag bad sectors */ vattr.va_type = VBAD; break; case S_IFCHR: vattr.va_type = VCHR; break; case S_IFBLK: vattr.va_type = VBLK; break; case S_IFWHT: whiteout = 1; break; default: error = EINVAL; break; } } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); if (whiteout) error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); else { error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); } } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mknod"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mknod"); return (error); } /* * Create a named pipe. */ #ifndef _SYS_SYSPROTO_H_ struct mkfifo_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkfifo(p, uap) struct proc *p; register struct mkfifo_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct mount *mp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VFIFO; vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); if (error == 0) vput(nd.ni_vp); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); return (error); } /* * Make a hard file link. */ #ifndef _SYS_SYSPROTO_H_ struct link_args { char *path; char *link; }; #endif /* ARGSUSED */ int link(p, uap) struct proc *p; register struct link_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { struct vnode *vp; struct mount *mp; struct nameidata nd; int error; bwillwrite(); NDINIT(&nd, LOOKUP, FOLLOW|NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type == VDIR) { vrele(vp); return (EPERM); /* POSIX */ } if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); if ((error = namei(&nd)) == 0) { if (nd.ni_vp != NULL) { vrele(nd.ni_vp); error = EEXIST; } else { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); } vrele(vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "link"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "link"); return (error); } /* * Make a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct symlink_args { char *path; char *link; }; #endif /* ARGSUSED */ int symlink(p, uap) struct proc *p; register struct symlink_args /* { syscallarg(char *) path; syscallarg(char *) link; } */ *uap; { struct mount *mp; struct vattr vattr; char *path; int error; struct nameidata nd; path = zalloc(namei_zone); if ((error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, NULL)) != 0) goto out; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT|NOOBJ, UIO_USERSPACE, SCARG(uap, link), p); if ((error = namei(&nd)) != 0) goto out; if (nd.ni_vp) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(nd.ni_vp); vput(nd.ni_dvp); error = EEXIST; goto out; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); NDFREE(&nd, NDF_ONLY_PNBUF); if (error == 0) vput(nd.ni_vp); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "symlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "symlink"); out: zfree(namei_zone, path); return (error); } /* * Delete a whiteout from the filesystem. */ /* ARGSUSED */ int undelete(p, uap) struct proc *p; register struct undelete_args /* { syscallarg(char *) path; } */ *uap; { int error; struct mount *mp; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, SCARG(uap, path), p); error = namei(&nd); if (error) return (error); if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_vp) vrele(nd.ni_vp); vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "undelete"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "undelete"); return (error); } /* * Delete a name from the filesystem. */ #ifndef _SYS_SYSPROTO_H_ struct unlink_args { char *path; }; #endif /* ARGSUSED */ int unlink(p, uap) struct proc *p; struct unlink_args /* { syscallarg(char *) path; } */ *uap; { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { /* * The root of a mounted filesystem cannot be deleted. * * XXX: can this only be a VDIR case? */ if (vp->v_flag & VROOT) error = EBUSY; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (!error) { VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); vput(vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "unlink"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "unlink"); return (error); } /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct lseek_args { int fd; int pad; off_t offset; int whence; }; #endif int lseek(p, uap) struct proc *p; register struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ *uap; { struct ucred *cred = p->p_ucred; register struct filedesc *fdp = p->p_fd; register struct file *fp; struct vattr vattr; int error; if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE) return (ESPIPE); switch (SCARG(uap, whence)) { case L_INCR: fp->f_offset += SCARG(uap, offset); break; case L_XTND: error=VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p); if (error) return (error); fp->f_offset = SCARG(uap, offset) + vattr.va_size; break; case L_SET: fp->f_offset = SCARG(uap, offset); break; default: return (EINVAL); } *(off_t *)(p->p_retval) = fp->f_offset; return (0); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Reposition read/write file offset. */ #ifndef _SYS_SYSPROTO_H_ struct olseek_args { int fd; long offset; int whence; }; #endif int olseek(p, uap) struct proc *p; register struct olseek_args /* { syscallarg(int) fd; syscallarg(long) offset; syscallarg(int) whence; } */ *uap; { struct lseek_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) offset; syscallarg(int) whence; } */ nuap; int error; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, offset) = SCARG(uap, offset); SCARG(&nuap, whence) = SCARG(uap, whence); error = lseek(p, &nuap); return (error); } #endif /* COMPAT_43 */ /* * Check access permissions. */ #ifndef _SYS_SYSPROTO_H_ struct access_args { char *path; int flags; }; #endif int access(p, uap) struct proc *p; register struct access_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { struct ucred *cred, *tmpcred; register struct vnode *vp; int error, flags; struct nameidata nd; cred = p->p_ucred; /* * Create and modify a temporary credential instead of one that * is potentially shared. This could also mess up socket * buffer accounting which can run in an interrupt context. * * XXX - Depending on how "threads" are finally implemented, it * may be better to explicitly pass the credential to namei() * rather than to modify the potentially shared process structure. */ tmpcred = crdup(cred); tmpcred->cr_uid = p->p_cred->p_ruid; tmpcred->cr_groups[0] = p->p_cred->p_rgid; p->p_ucred = tmpcred; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) goto out1; vp = nd.ni_vp; /* Flags == 0 means only check for existence. */ if (SCARG(uap, flags)) { flags = 0; if (SCARG(uap, flags) & R_OK) flags |= VREAD; if (SCARG(uap, flags) & W_OK) flags |= VWRITE; if (SCARG(uap, flags) & X_OK) flags |= VEXEC; if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) error = VOP_ACCESS(vp, flags, cred, p); } NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); out1: p->p_ucred = cred; crfree(tmpcred); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct ostat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int ostat(p, uap) struct proc *p; register struct ostat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct olstat_args { char *path; struct ostat *ub; }; #endif /* ARGSUSED */ int olstat(p, uap) struct proc *p; register struct olstat_args /* { syscallarg(char *) path; syscallarg(struct ostat *) ub; } */ *uap; { struct vnode *vp; struct stat sb; struct ostat osb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); cvtstat(&sb, &osb); error = copyout((caddr_t)&osb, (caddr_t)SCARG(uap, ub), sizeof (osb)); return (error); } /* * Convert from an old to a new stat structure. */ void cvtstat(st, ost) struct stat *st; struct ostat *ost; { ost->st_dev = st->st_dev; ost->st_ino = st->st_ino; ost->st_mode = st->st_mode; ost->st_nlink = st->st_nlink; ost->st_uid = st->st_uid; ost->st_gid = st->st_gid; ost->st_rdev = st->st_rdev; if (st->st_size < (quad_t)1 << 32) ost->st_size = st->st_size; else ost->st_size = -2; ost->st_atime = st->st_atime; ost->st_mtime = st->st_mtime; ost->st_ctime = st->st_ctime; ost->st_blksize = st->st_blksize; ost->st_blocks = st->st_blocks; ost->st_flags = st->st_flags; ost->st_gen = st->st_gen; } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Get file status; this version follows links. */ #ifndef _SYS_SYSPROTO_H_ struct stat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int stat(p, uap) struct proc *p; register struct stat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { struct stat sb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); error = vn_stat(nd.ni_vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } /* * Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int lstat(p, uap) struct proc *p; register struct lstat_args /* { syscallarg(char *) path; syscallarg(struct stat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; error = vn_stat(vp, &sb, p); NDFREE(&nd, NDF_ONLY_PNBUF); vput(vp); if (error) return (error); error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); return (error); } /* * Implementation of the NetBSD stat() function. * XXX This should probably be collapsed with the FreeBSD version, * as the differences are only due to vn_stat() clearing spares at * the end of the structures. vn_stat could be split to avoid this, * and thus collapse the following to close to zero code. */ void cvtnstat(sb, nsb) struct stat *sb; struct nstat *nsb; { nsb->st_dev = sb->st_dev; nsb->st_ino = sb->st_ino; nsb->st_mode = sb->st_mode; nsb->st_nlink = sb->st_nlink; nsb->st_uid = sb->st_uid; nsb->st_gid = sb->st_gid; nsb->st_rdev = sb->st_rdev; nsb->st_atimespec = sb->st_atimespec; nsb->st_mtimespec = sb->st_mtimespec; nsb->st_ctimespec = sb->st_ctimespec; nsb->st_size = sb->st_size; nsb->st_blocks = sb->st_blocks; nsb->st_blksize = sb->st_blksize; nsb->st_flags = sb->st_flags; nsb->st_gen = sb->st_gen; nsb->st_qspare[0] = sb->st_qspare[0]; nsb->st_qspare[1] = sb->st_qspare[1]; } #ifndef _SYS_SYSPROTO_H_ struct nstat_args { char *path; struct nstat *ub; }; #endif /* ARGSUSED */ int nstat(p, uap) struct proc *p; register struct nstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { struct stat sb; struct nstat nsb; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(nd.ni_vp, &sb, p); vput(nd.ni_vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * NetBSD lstat. Get file status; this version does not follow links. */ #ifndef _SYS_SYSPROTO_H_ struct lstat_args { char *path; struct stat *ub; }; #endif /* ARGSUSED */ int nlstat(p, uap) struct proc *p; register struct nlstat_args /* { syscallarg(char *) path; syscallarg(struct nstat *) ub; } */ *uap; { int error; struct vnode *vp; struct stat sb; struct nstat nsb; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); cvtnstat(&sb, &nsb); error = copyout((caddr_t)&nsb, (caddr_t)SCARG(uap, ub), sizeof (nsb)); return (error); } /* * Get configurable pathname variables. */ #ifndef _SYS_SYSPROTO_H_ struct pathconf_args { char *path; int name; }; #endif /* ARGSUSED */ int pathconf(p, uap) struct proc *p; register struct pathconf_args /* { syscallarg(char *) path; syscallarg(int) name; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), p->p_retval); vput(nd.ni_vp); return (error); } /* * Return target name of a symbolic link. */ #ifndef _SYS_SYSPROTO_H_ struct readlink_args { char *path; char *buf; int count; }; #endif /* ARGSUSED */ int readlink(p, uap) struct proc *p; register struct readlink_args /* { syscallarg(char *) path; syscallarg(char *) buf; syscallarg(int) count; } */ *uap; { register struct vnode *vp; struct iovec aiov; struct uio auio; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | NOOBJ, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; if (vp->v_type != VLNK) error = EINVAL; else { aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = 0; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); error = VOP_READLINK(vp, &auio, p->p_ucred); } vput(vp); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } /* * Common implementation code for chflags() and fchflags(). */ static int setfflags(p, vp, flags) struct proc *p; struct vnode *vp; int flags; { int error; struct mount *mp; struct vattr vattr; /* * Prevent non-root users from setting flags on devices. When * a device is reused, users can retain ownership of the device * if they are allowed to set flags and programs assume that * chown can't fail when done as root. */ if ((vp->v_type == VCHR || vp->v_type == VBLK) && ((error = suser_xxx(p->p_ucred, p, PRISON_ROOT)) != 0)) return (error); if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_flags = flags; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } /* * Change flags of a file given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chflags_args { char *path; int flags; }; #endif /* ARGSUSED */ int chflags(p, uap) struct proc *p; register struct chflags_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfflags(p, nd.ni_vp, SCARG(uap, flags)); vrele(nd.ni_vp); return error; } /* * Change flags of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchflags_args { int fd; int flags; }; #endif /* ARGSUSED */ int fchflags(p, uap) struct proc *p; register struct fchflags_args /* { syscallarg(int) fd; syscallarg(int) flags; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfflags(p, (struct vnode *) fp->f_data, SCARG(uap, flags)); } /* * Common implementation code for chmod(), lchmod() and fchmod(). */ static int setfmode(p, vp, mode) struct proc *p; struct vnode *vp; int mode; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_mode = mode & ALLPERMS; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return error; } /* * Change mode of a file given path name. */ #ifndef _SYS_SYSPROTO_H_ struct chmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int chmod(p, uap) struct proc *p; register struct chmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given path name (don't follow links.) */ #ifndef _SYS_SYSPROTO_H_ struct lchmod_args { char *path; int mode; }; #endif /* ARGSUSED */ int lchmod(p, uap) struct proc *p; register struct lchmod_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfmode(p, nd.ni_vp, SCARG(uap, mode)); vrele(nd.ni_vp); return error; } /* * Change mode of a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchmod_args { int fd; int mode; }; #endif /* ARGSUSED */ int fchmod(p, uap) struct proc *p; register struct fchmod_args /* { syscallarg(int) fd; syscallarg(int) mode; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfmode(p, (struct vnode *)fp->f_data, SCARG(uap, mode)); } /* * Common implementation for chown(), lchown(), and fchown() */ static int setfown(p, vp, uid, gid) struct proc *p; struct vnode *vp; uid_t uid; gid_t gid; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_uid = uid; vattr.va_gid = gid; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return error; } /* * Set ownership given a path name. */ #ifndef _SYS_SYSPROTO_H_ struct chown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int chown(p, uap) struct proc *p; register struct chown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a path name, do not cross symlinks. */ #ifndef _SYS_SYSPROTO_H_ struct lchown_args { char *path; int uid; int gid; }; #endif /* ARGSUSED */ int lchown(p, uap) struct proc *p; register struct lchown_args /* { syscallarg(char *) path; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { int error; struct nameidata nd; NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setfown(p, nd.ni_vp, SCARG(uap, uid), SCARG(uap, gid)); vrele(nd.ni_vp); return (error); } /* * Set ownership given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct fchown_args { int fd; int uid; int gid; }; #endif /* ARGSUSED */ int fchown(p, uap) struct proc *p; register struct fchown_args /* { syscallarg(int) fd; syscallarg(int) uid; syscallarg(int) gid; } */ *uap; { struct file *fp; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setfown(p, (struct vnode *)fp->f_data, SCARG(uap, uid), SCARG(uap, gid)); } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int getutimes(usrtvp, tsp) const struct timeval *usrtvp; struct timespec *tsp; { struct timeval tv[2]; int error; if (usrtvp == NULL) { microtime(&tv[0]); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); tsp[1] = tsp[0]; } else { if ((error = copyin(usrtvp, tv, sizeof (tv))) != 0) return (error); TIMEVAL_TO_TIMESPEC(&tv[0], &tsp[0]); TIMEVAL_TO_TIMESPEC(&tv[1], &tsp[1]); } return 0; } /* * Common implementation code for utimes(), lutimes(), and futimes(). */ static int setutimes(p, vp, ts, nullflag) struct proc *p; struct vnode *vp; const struct timespec *ts; int nullflag; { int error; struct mount *mp; struct vattr vattr; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); VATTR_NULL(&vattr); vattr.va_atime = ts[0]; vattr.va_mtime = ts[1]; if (nullflag) vattr.va_vaflags |= VA_UTIMES_NULL; error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return error; } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct utimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int utimes(p, uap) struct proc *p; register struct utimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct lutimes_args { char *path; struct timeval *tptr; }; #endif /* ARGSUSED */ int lutimes(p, uap) struct proc *p; register struct lutimes_args /* { syscallarg(char *) path; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct timeval *usrtvp; int error; struct nameidata nd; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); error = setutimes(p, nd.ni_vp, ts, usrtvp == NULL); vrele(nd.ni_vp); return (error); } /* * Set the access and modification times of a file. */ #ifndef _SYS_SYSPROTO_H_ struct futimes_args { int fd; struct timeval *tptr; }; #endif /* ARGSUSED */ int futimes(p, uap) struct proc *p; register struct futimes_args /* { syscallarg(int ) fd; syscallarg(struct timeval *) tptr; } */ *uap; { struct timespec ts[2]; struct file *fp; struct timeval *usrtvp; int error; usrtvp = SCARG(uap, tptr); if ((error = getutimes(usrtvp, ts)) != 0) return (error); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); return setutimes(p, (struct vnode *)fp->f_data, ts, usrtvp == NULL); } /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct truncate_args { char *path; int pad; off_t length; }; #endif /* ARGSUSED */ int truncate(p, uap) struct proc *p; register struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; if (uap->length < 0) return(EINVAL); NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } NDFREE(&nd, NDF_ONLY_PNBUF); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0 && (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); } vput(vp); vn_finished_write(mp); return (error); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct ftruncate_args { int fd; int pad; off_t length; }; #endif /* ARGSUSED */ int ftruncate(p, uap) struct proc *p; register struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ *uap; { struct mount *mp; struct vattr vattr; struct vnode *vp; struct file *fp; int error; if (uap->length < 0) return(EINVAL); if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FWRITE) == 0) return (EINVAL); vp = (struct vnode *)fp->f_data; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (vp->v_type == VDIR) error = EISDIR; else if ((error = vn_writechk(vp)) == 0) { VATTR_NULL(&vattr); vattr.va_size = SCARG(uap, length); error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); } VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) /* * Truncate a file given its path name. */ #ifndef _SYS_SYSPROTO_H_ struct otruncate_args { char *path; long length; }; #endif /* ARGSUSED */ int otruncate(p, uap) struct proc *p; register struct otruncate_args /* { syscallarg(char *) path; syscallarg(long) length; } */ *uap; { struct truncate_args /* { syscallarg(char *) path; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, path) = SCARG(uap, path); SCARG(&nuap, length) = SCARG(uap, length); return (truncate(p, &nuap)); } /* * Truncate a file given a file descriptor. */ #ifndef _SYS_SYSPROTO_H_ struct oftruncate_args { int fd; long length; }; #endif /* ARGSUSED */ int oftruncate(p, uap) struct proc *p; register struct oftruncate_args /* { syscallarg(int) fd; syscallarg(long) length; } */ *uap; { struct ftruncate_args /* { syscallarg(int) fd; syscallarg(int) pad; syscallarg(off_t) length; } */ nuap; SCARG(&nuap, fd) = SCARG(uap, fd); SCARG(&nuap, length) = SCARG(uap, length); return (ftruncate(p, &nuap)); } #endif /* COMPAT_43 || COMPAT_SUNOS */ /* * Sync an open file. */ #ifndef _SYS_SYSPROTO_H_ struct fsync_args { int fd; }; #endif /* ARGSUSED */ int fsync(p, uap) struct proc *p; struct fsync_args /* { syscallarg(int) fd; } */ *uap; { struct vnode *vp; struct mount *mp; struct file *fp; vm_object_t obj; int error; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); vp = (struct vnode *)fp->f_data; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if (VOP_GETVOBJECT(vp, &obj) == 0) vm_object_page_clean(obj, 0, 0, 0); error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); #ifdef SOFTUPDATES if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP)) error = softdep_fsync(vp); #endif VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } /* * Rename files. Source and destination must either both be directories, * or both not be directories. If target is a directory, it must be empty. */ #ifndef _SYS_SYSPROTO_H_ struct rename_args { char *from; char *to; }; #endif /* ARGSUSED */ int rename(p, uap) struct proc *p; register struct rename_args /* { syscallarg(char *) from; syscallarg(char *) to; } */ *uap; { struct mount *mp; struct vnode *tvp, *fvp, *tdvp; struct nameidata fromnd, tond; int error; bwillwrite(); NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, SCARG(uap, from), p); if ((error = namei(&fromnd)) != 0) return (error); fvp = fromnd.ni_vp; if ((error = vn_start_write(fvp, &mp, V_WAIT | PCATCH)) != 0) { NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); goto out1; } NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART | NOOBJ, UIO_USERSPACE, SCARG(uap, to), p); if (fromnd.ni_vp->v_type == VDIR) tond.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&tond)) != 0) { /* Translate error code for rename("dir1", "dir2/."). */ if (error == EISDIR && fvp->v_type == VDIR) error = EINVAL; NDFREE(&fromnd, NDF_ONLY_PNBUF); vrele(fromnd.ni_dvp); vrele(fvp); 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 out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { error = EISDIR; goto out; } } if (fvp == tdvp) error = EINVAL; /* * If source is the same as the destination (that is the * same inode number with the same name in the same directory), * then there is nothing to do. */ if (fvp == tvp && fromnd.ni_dvp == tdvp && 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)) error = -1; out: if (!error) { VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); if (fromnd.ni_dvp != tdvp) { VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); } if (tvp) { VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); } error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); } else { NDFREE(&fromnd, NDF_ONLY_PNBUF); NDFREE(&tond, NDF_ONLY_PNBUF); if (tdvp == tvp) vrele(tdvp); else vput(tdvp); if (tvp) vput(tvp); vrele(fromnd.ni_dvp); vrele(fvp); } vrele(tond.ni_startdir); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(fromnd.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(fromnd.ni_vp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_dvp, "rename"); ASSERT_VOP_UNLOCKED(tond.ni_vp, "rename"); out1: if (fromnd.ni_startdir) vrele(fromnd.ni_startdir); if (error == -1) return (0); return (error); } /* * Make a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct mkdir_args { char *path; int mode; }; #endif /* ARGSUSED */ int mkdir(p, uap) struct proc *p; register struct mkdir_args /* { syscallarg(char *) path; syscallarg(int) mode; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); nd.ni_cnd.cn_flags |= WILLBEDIR; if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); vrele(vp); vput(nd.ni_dvp); return (EEXIST); } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VATTR_NULL(&vattr); vattr.va_type = VDIR; vattr.va_mode = (SCARG(uap, mode) & ACCESSPERMS) &~ p->p_fd->fd_cmask; VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); if (!error) vput(nd.ni_vp); vn_finished_write(mp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "mkdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "mkdir"); return (error); } /* * Remove a directory file. */ #ifndef _SYS_SYSPROTO_H_ struct rmdir_args { char *path; }; #endif /* ARGSUSED */ int rmdir(p, uap) struct proc *p; struct rmdir_args /* { syscallarg(char *) path; } */ *uap; { struct mount *mp; struct vnode *vp; int error; struct nameidata nd; restart: bwillwrite(); NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ if (nd.ni_dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ if (vp->v_flag & VROOT) { error = EBUSY; goto out; } if (vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); vn_finished_write(mp); out: NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == vp) vrele(nd.ni_dvp); else vput(nd.ni_dvp); vput(vp); ASSERT_VOP_UNLOCKED(nd.ni_dvp, "rmdir"); ASSERT_VOP_UNLOCKED(nd.ni_vp, "rmdir"); return (error); } #ifdef COMPAT_43 /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct ogetdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int ogetdirentries(p, uap) struct proc *p; register struct ogetdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio, kuio; struct iovec aiov, kiov; struct dirent *dp, *edp; caddr_t dirbuf; int error, eofflag, readcnt; long loff; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; # if (BYTE_ORDER != LITTLE_ENDIAN) if (vp->v_mount->mnt_maxsymlinklen <= 0) { error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; } else # endif { kuio = auio; kuio.uio_iov = &kiov; kuio.uio_segflg = UIO_SYSSPACE; kiov.iov_len = SCARG(uap, count); MALLOC(dirbuf, caddr_t, SCARG(uap, count), M_TEMP, M_WAITOK); kiov.iov_base = dirbuf; error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = kuio.uio_offset; if (error == 0) { readcnt = SCARG(uap, count) - kuio.uio_resid; edp = (struct dirent *)&dirbuf[readcnt]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { # if (BYTE_ORDER == LITTLE_ENDIAN) /* * The expected low byte of * dp->d_namlen is our dp->d_type. * The high MBZ byte of dp->d_namlen * is our dp->d_namlen. */ dp->d_type = dp->d_namlen; dp->d_namlen = 0; # else /* * The dp->d_type is the high byte * of the expected dp->d_namlen, * so must be zero'ed. */ dp->d_type = 0; # endif if (dp->d_reclen > 0) { dp = (struct dirent *) ((char *)dp + dp->d_reclen); } else { error = EIO; break; } } if (dp >= edp) error = uiomove(dirbuf, readcnt, &auio); } FREE(dirbuf, M_TEMP); } VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #endif /* COMPAT_43 */ /* * Read a block of directory entries in a file system independent format. */ #ifndef _SYS_SYSPROTO_H_ struct getdirentries_args { int fd; char *buf; u_int count; long *basep; }; #endif int getdirentries(p, uap) struct proc *p; register struct getdirentries_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; syscallarg(long *) basep; } */ *uap; { struct vnode *vp; struct file *fp; struct uio auio; struct iovec aiov; long loff; int error, eofflag; if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) return (error); if ((fp->f_flag & FREAD) == 0) return (EBADF); vp = (struct vnode *)fp->f_data; unionread: if (vp->v_type != VDIR) return (EINVAL); aiov.iov_base = SCARG(uap, buf); aiov.iov_len = SCARG(uap, count); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_resid = SCARG(uap, count); /* vn_lock(vp, LK_SHARED | LK_RETRY, p); */ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); loff = auio.uio_offset = fp->f_offset; error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); fp->f_offset = auio.uio_offset; VOP_UNLOCK(vp, 0, p); if (error) return (error); if (SCARG(uap, count) == auio.uio_resid) { if (union_dircheckp) { error = union_dircheckp(p, &vp, fp); if (error == -1) goto unionread; if (error) return (error); } if ((vp->v_flag & VROOT) && (vp->v_mount->mnt_flag & MNT_UNION)) { struct vnode *tvp = vp; vp = vp->v_mount->mnt_vnodecovered; VREF(vp); fp->f_data = (caddr_t) vp; fp->f_offset = 0; vrele(tvp); goto unionread; } } if (SCARG(uap, basep) != NULL) { error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), sizeof(long)); } p->p_retval[0] = SCARG(uap, count) - auio.uio_resid; return (error); } #ifndef _SYS_SYSPROTO_H_ struct getdents_args { int fd; char *buf; size_t count; }; #endif int getdents(p, uap) struct proc *p; register struct getdents_args /* { syscallarg(int) fd; syscallarg(char *) buf; syscallarg(u_int) count; } */ *uap; { struct getdirentries_args ap; ap.fd = uap->fd; ap.buf = uap->buf; ap.count = uap->count; ap.basep = NULL; return getdirentries(p, &ap); } /* * Set the mode mask for creation of filesystem nodes. * * MP SAFE */ #ifndef _SYS_SYSPROTO_H_ struct umask_args { int newmask; }; #endif int umask(p, uap) struct proc *p; struct umask_args /* { syscallarg(int) newmask; } */ *uap; { register struct filedesc *fdp; fdp = p->p_fd; p->p_retval[0] = fdp->fd_cmask; fdp->fd_cmask = SCARG(uap, newmask) & ALLPERMS; return (0); } /* * Void all references to file by ripping underlying filesystem * away from vnode. */ #ifndef _SYS_SYSPROTO_H_ struct revoke_args { char *path; }; #endif /* ARGSUSED */ int revoke(p, uap) struct proc *p; register struct revoke_args /* { syscallarg(char *) path; } */ *uap; { struct mount *mp; struct vnode *vp; struct vattr vattr; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (vp->v_type != VCHR) { error = EINVAL; goto out; } if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) goto out; if (p->p_ucred->cr_uid != vattr.va_uid && (error = suser_xxx(0, p, PRISON_ROOT))) goto out; if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) goto out; if (vcount(vp) > 1) VOP_REVOKE(vp, REVOKEALL); vn_finished_write(mp); out: vrele(vp); return (error); } /* * Convert a user file descriptor to a kernel file entry. */ int getvnode(fdp, fd, fpp) struct filedesc *fdp; int fd; struct file **fpp; { struct file *fp; if ((u_int)fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) return (EBADF); if (fp->f_type != DTYPE_VNODE && fp->f_type != DTYPE_FIFO) return (EINVAL); *fpp = fp; return (0); } /* * Get (NFS) file handle */ #ifndef _SYS_SYSPROTO_H_ struct getfh_args { char *fname; fhandle_t *fhp; }; #endif int getfh(p, uap) struct proc *p; register struct getfh_args *uap; { struct nameidata nd; fhandle_t fh; register struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p); error = namei(&nd); if (error) return (error); NDFREE(&nd, NDF_ONLY_PNBUF); vp = nd.ni_vp; bzero(&fh, sizeof(fh)); fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; error = VFS_VPTOFH(vp, &fh.fh_fid); vput(vp); if (error) return (error); error = copyout(&fh, uap->fhp, sizeof (fh)); return (error); } /* * syscall for the rpc.lockd to use to translate a NFS file handle into * an open descriptor. * * warning: do not remove the suser() call or this becomes one giant * security hole. */ #ifndef _SYS_SYSPROTO_H_ struct fhopen_args { const struct fhandle *u_fhp; int flags; }; #endif int fhopen(p, uap) struct proc *p; struct fhopen_args /* { syscallarg(const struct fhandle *) u_fhp; syscallarg(int) flags; } */ *uap; { struct mount *mp; struct vnode *vp; struct fhandle fhp; struct vattr vat; struct vattr *vap = &vat; struct flock lf; struct file *fp; register struct filedesc *fdp = p->p_fd; int fmode, mode, error, type; struct file *nfp; int indx; /* * Must be super user */ error = suser(p); if (error) return (error); fmode = FFLAGS(SCARG(uap, flags)); /* why not allow a non-read/write open for our lockd? */ if (((fmode & (FREAD | FWRITE)) == 0) || (fmode & O_CREAT)) return (EINVAL); error = copyin(SCARG(uap,u_fhp), &fhp, sizeof(fhp)); if (error) return(error); /* find the mount point */ mp = vfs_getvfs(&fhp.fh_fsid); if (mp == NULL) return (ESTALE); /* now give me my vnode, it gets returned to me locked */ error = VFS_FHTOVP(mp, &fhp.fh_fid, &vp); if (error) return (error); /* * from now on we have to make sure not * to forget about the vnode * any error that causes an abort must vput(vp) * just set error = err and 'goto bad;'. */ /* * from vn_open */ if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (mode) { error = VOP_ACCESS(vp, mode, p->p_ucred, p); if (error) goto bad; } if (fmode & O_TRUNC) { VOP_UNLOCK(vp, 0, p); /* XXX */ if ((error = vn_start_write(NULL, &mp, V_WAIT | PCATCH)) != 0) { vrele(vp); return (error); } VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ VATTR_NULL(vap); vap->va_size = 0; error = VOP_SETATTR(vp, vap, p->p_ucred, p); vn_finished_write(mp); if (error) goto bad; } error = VOP_OPEN(vp, fmode, p->p_ucred, p); if (error) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, p, p->p_ucred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; /* * end of vn_open code */ if ((error = falloc(p, &nfp, &indx)) != 0) goto bad; fp = nfp; /* * Hold an extra reference to avoid having fp ripped out * from under us while we block in the lock op */ fhold(fp); nfp->f_data = (caddr_t)vp; nfp->f_flag = fmode & FMASK; nfp->f_ops = &vnops; nfp->f_type = DTYPE_VNODE; if (fmode & (O_EXLOCK | O_SHLOCK)) { lf.l_whence = SEEK_SET; lf.l_start = 0; lf.l_len = 0; if (fmode & O_EXLOCK) lf.l_type = F_WRLCK; else lf.l_type = F_RDLCK; type = F_FLOCK; if ((fmode & FNONBLOCK) == 0) type |= F_WAIT; VOP_UNLOCK(vp, 0, p); if ((error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) != 0) { /* * The lock request failed. Normally close the * descriptor but handle the case where someone might * have dup()d or close()d it when we weren't looking. */ if (fdp->fd_ofiles[indx] == fp) { fdp->fd_ofiles[indx] = NULL; fdrop(fp, p); } /* * release our private reference */ fdrop(fp, p); return(error); } vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); fp->f_flag |= FHASLOCK; } if ((vp->v_type == VREG) && (VOP_GETVOBJECT(vp, NULL) != 0)) vfs_object_create(vp, p, p->p_ucred); VOP_UNLOCK(vp, 0, p); fdrop(fp, p); p->p_retval[0] = indx; return (0); bad: vput(vp); return (error); } /* * Stat an (NFS) file handle. */ #ifndef _SYS_SYSPROTO_H_ struct fhstat_args { struct fhandle *u_fhp; struct stat *sb; }; #endif int fhstat(p, uap) struct proc *p; register struct fhstat_args /* { syscallarg(struct fhandle *) u_fhp; syscallarg(struct stat *) sb; } */ *uap; { struct stat sb; fhandle_t fh; struct mount *mp; struct vnode *vp; int error; /* * Must be super user */ error = suser(p); if (error) return (error); error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t)); if (error) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); error = vn_stat(vp, &sb, p); vput(vp); if (error) return (error); error = copyout(&sb, SCARG(uap, sb), sizeof(sb)); return (error); } /* * Implement fstatfs() for (NFS) file handles. */ #ifndef _SYS_SYSPROTO_H_ struct fhstatfs_args { struct fhandle *u_fhp; struct statfs *buf; }; #endif int fhstatfs(p, uap) struct proc *p; struct fhstatfs_args /* { syscallarg(struct fhandle) *u_fhp; syscallarg(struct statfs) *buf; } */ *uap; { struct statfs *sp; struct mount *mp; struct vnode *vp; struct statfs sb; fhandle_t fh; int error; /* * Must be super user */ if ((error = suser(p))) return (error); if ((error = copyin(SCARG(uap, u_fhp), &fh, sizeof(fhandle_t))) != 0) return (error); if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) return (ESTALE); if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) return (error); mp = vp->v_mount; sp = &mp->mnt_stat; vput(vp); if ((error = VFS_STATFS(mp, sp, p)) != 0) return (error); sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; if (suser_xxx(p->p_ucred, 0, 0)) { bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; sp = &sb; } return (copyout(sp, SCARG(uap, buf), sizeof(*sp))); } /* * Syscall to push extended attribute configuration information into the * VFS. Accepts a path, which it converts to a mountpoint, as well as * a command (int cmd), and attribute name and misc data. For now, the * attribute name is left in userspace for consumption by the VFS_op. * It will probably be changed to be copied into sysspace by the * syscall in the future, once issues with various consumers of the * attribute code have raised their hands. * * Currently this is used only by UFS Extended Attributes. */ int extattrctl(p, uap) struct proc *p; struct extattrctl_args *uap; { + struct vnode *filename_vp; struct nameidata nd; struct mount *mp; + char attrname[EXTATTR_MAXNAMELEN]; int error; + /* + * SCARG(uap, attrname) not always defined. We check again later + * when we invoke the VFS call so as to pass in NULL there if needed. + */ + if (SCARG(uap, attrname) != NULL) { + error = copyinstr(SCARG(uap, attrname), attrname, + EXTATTR_MAXNAMELEN, NULL); + if (error) + return (error); + } + + /* + * SCARG(uap, filename) not always defined. If it is, grab + * a vnode lock, which VFS_EXTATTRCTL() will later release. + */ + filename_vp = NULL; + if (SCARG(uap, filename) != NULL) { + NDINIT(&nd, LOOKUP | LOCKLEAF, FOLLOW, UIO_USERSPACE, + SCARG(uap, filename), p); + if ((error = namei(&nd)) != 0) + return (error); + filename_vp = nd.ni_vp; + NDFREE(&nd, NDF_NO_VP_RELE | NDF_NO_VP_UNLOCK); + } + + /* SCARG(uap, path) always defined. */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH); NDFREE(&nd, 0); - if (error) + if (error) { + if (filename_vp) + vrele(filename_vp); return (error); - error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), SCARG(uap, attrname), - SCARG(uap, arg), p); + } + + if (SCARG(uap, attrname) != NULL) { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, namespace), attrname, p); + } else { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, namespace), NULL, p); + } + vn_finished_write(mp); + /* + * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, + * filename_vp, so vrele it if it is defined. + */ + if (filename_vp != NULL) + vrele(filename_vp); + return (error); } /* * Syscall to set a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to the data to set. * The uio is consumed in the style of writev(). The real work happens * in VOP_SETEXTATTR(). */ int extattr_set_file(p, uap) struct proc *p; struct extattr_set_file_args *uap; { struct nameidata nd; struct mount *mp; struct uio auio; struct iovec *iov, *needfree = NULL, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); if ((error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH)) != 0) { NDFREE(&nd, 0); return (error); } iovlen = uap->iovcnt * sizeof(struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { error = EINVAL; goto done; } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else iov = aiov; auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; - error = VOP_SETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, - p); + error = VOP_SETEXTATTR(nd.ni_vp, SCARG(uap, namespace), attrname, + &auio, p->p_cred->pc_ucred, p); cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); vn_finished_write(mp); return (error); } /* * Syscall to get a named extended attribute on a file or directory. * Accepts attribute name, and a uio structure pointing to a buffer for the * data. The uio is consumed in the style of readv(). The real work * happens in VOP_GETEXTATTR(); */ int extattr_get_file(p, uap) struct proc *p; struct extattr_get_file_args *uap; { struct nameidata nd; struct uio auio; struct iovec *iov, *needfree, aiov[UIO_SMALLIOV]; char attrname[EXTATTR_MAXNAMELEN]; u_int iovlen, cnt; int error, i; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return (error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); iovlen = uap->iovcnt * sizeof (struct iovec); if (uap->iovcnt > UIO_SMALLIOV) { if (uap->iovcnt > UIO_MAXIOV) { NDFREE(&nd, 0); return (EINVAL); } MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK); needfree = iov; } else { iov = aiov; needfree = NULL; } auio.uio_iov = iov; auio.uio_iovcnt = uap->iovcnt; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_USERSPACE; auio.uio_procp = p; auio.uio_offset = 0; if ((error = copyin((caddr_t)uap->iovp, (caddr_t)iov, iovlen))) goto done; auio.uio_resid = 0; for (i = 0; i < uap->iovcnt; i++) { if (iov->iov_len > INT_MAX - auio.uio_resid) { error = EINVAL; goto done; } auio.uio_resid += iov->iov_len; iov++; } cnt = auio.uio_resid; - error = VOP_GETEXTATTR(nd.ni_vp, attrname, &auio, p->p_cred->pc_ucred, - p); + error = VOP_GETEXTATTR(nd.ni_vp, SCARG(uap, namespace), attrname, + &auio, p->p_cred->pc_ucred, p); cnt -= auio.uio_resid; p->p_retval[0] = cnt; done: if (needfree) FREE(needfree, M_IOV); NDFREE(&nd, 0); return(error); } /* * Syscall to delete a named extended attribute from a file or directory. * Accepts attribute name. The real work happens in VOP_SETEXTATTR(). */ int extattr_delete_file(p, uap) struct proc *p; struct extattr_delete_file_args *uap; { struct mount *mp; struct nameidata nd; char attrname[EXTATTR_MAXNAMELEN]; int error; error = copyin(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN); if (error) return(error); NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return(error); if ((error = vn_start_write(nd.ni_vp, &mp, V_WAIT | PCATCH)) != 0) { NDFREE(&nd, 0); return (error); } - error = VOP_SETEXTATTR(nd.ni_vp, attrname, NULL, p->p_cred->pc_ucred, - p); + error = VOP_SETEXTATTR(nd.ni_vp, SCARG(uap, namespace), attrname, + NULL, p->p_cred->pc_ucred, p); NDFREE(&nd, 0); vn_finished_write(mp); return(error); } Index: head/sys/kern/vfs_vnops.c =================================================================== --- head/sys/kern/vfs_vnops.c (revision 74272) +++ head/sys/kern/vfs_vnops.c (revision 74273) @@ -1,906 +1,907 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_vnops.c 8.2 (Berkeley) 1/21/94 * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int vn_closefile __P((struct file *fp, struct proc *p)); static int vn_ioctl __P((struct file *fp, u_long com, caddr_t data, struct proc *p)); static int vn_read __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); static int vn_poll __P((struct file *fp, int events, struct ucred *cred, struct proc *p)); static int vn_kqfilter __P((struct file *fp, struct knote *kn)); static int vn_statfile __P((struct file *fp, struct stat *sb, struct proc *p)); static int vn_write __P((struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct proc *p)); struct fileops vnops = { vn_read, vn_write, vn_ioctl, vn_poll, vn_kqfilter, vn_statfile, vn_closefile }; /* * Common code for vnode open operations. * Check permissions, and call the VOP_OPEN or VOP_CREATE routine. * * Note that this does NOT free nameidata for the successful case, * due to the NDINIT being done elsewhere. */ int vn_open(ndp, flagp, cmode) register struct nameidata *ndp; int *flagp, cmode; { struct vnode *vp; struct mount *mp; struct proc *p = ndp->ni_cnd.cn_proc; struct ucred *cred = p->p_ucred; struct vattr vat; struct vattr *vap = &vat; int mode, fmode, error; restart: fmode = *flagp; if (fmode & O_CREAT) { ndp->ni_cnd.cn_nameiop = CREATE; ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0) ndp->ni_cnd.cn_flags |= FOLLOW; bwillwrite(); if ((error = namei(ndp)) != 0) return (error); if (ndp->ni_vp == NULL) { VATTR_NULL(vap); vap->va_type = VREG; vap->va_mode = cmode; if (fmode & O_EXCL) vap->va_vaflags |= VA_EXCLUSIVE; if (vn_start_write(ndp->ni_dvp, &mp, V_NOWAIT) != 0) { NDFREE(ndp, NDF_ONLY_PNBUF); vput(ndp->ni_dvp); if ((error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH)) != 0) return (error); goto restart; } VOP_LEASE(ndp->ni_dvp, p, cred, LEASE_WRITE); error = VOP_CREATE(ndp->ni_dvp, &ndp->ni_vp, &ndp->ni_cnd, vap); vput(ndp->ni_dvp); vn_finished_write(mp); if (error) { NDFREE(ndp, NDF_ONLY_PNBUF); return (error); } ASSERT_VOP_UNLOCKED(ndp->ni_dvp, "create"); ASSERT_VOP_LOCKED(ndp->ni_vp, "create"); fmode &= ~O_TRUNC; vp = ndp->ni_vp; } else { if (ndp->ni_dvp == ndp->ni_vp) vrele(ndp->ni_dvp); else vput(ndp->ni_dvp); ndp->ni_dvp = NULL; vp = ndp->ni_vp; if (fmode & O_EXCL) { error = EEXIST; goto bad; } fmode &= ~O_CREAT; } } else { ndp->ni_cnd.cn_nameiop = LOOKUP; ndp->ni_cnd.cn_flags = ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF; if ((error = namei(ndp)) != 0) return (error); vp = ndp->ni_vp; } if (vp->v_type == VLNK) { error = EMLINK; goto bad; } if (vp->v_type == VSOCK) { error = EOPNOTSUPP; goto bad; } if ((fmode & O_CREAT) == 0) { mode = 0; if (fmode & (FWRITE | O_TRUNC)) { if (vp->v_type == VDIR) { error = EISDIR; goto bad; } error = vn_writechk(vp); if (error) goto bad; mode |= VWRITE; } if (fmode & FREAD) mode |= VREAD; if (mode) { error = VOP_ACCESS(vp, mode, cred, p); if (error) goto bad; } } if ((error = VOP_OPEN(vp, fmode, cred, p)) != 0) goto bad; /* * Make sure that a VM object is created for VMIO support. */ if (vn_canvmio(vp) == TRUE) { if ((error = vfs_object_create(vp, p, cred)) != 0) goto bad; } if (fmode & FWRITE) vp->v_writecount++; *flagp = fmode; return (0); bad: NDFREE(ndp, NDF_ONLY_PNBUF); vput(vp); *flagp = fmode; return (error); } /* * Check for write permissions on the specified vnode. * Prototype text segments cannot be written. */ int vn_writechk(vp) register struct vnode *vp; { /* * If there's shared text associated with * the vnode, try to free it up once. If * we fail, we can't allow writing. */ if (vp->v_flag & VTEXT) return (ETXTBSY); return (0); } /* * Vnode close call */ int vn_close(vp, flags, cred, p) register struct vnode *vp; int flags; struct ucred *cred; struct proc *p; { int error; if (flags & FWRITE) vp->v_writecount--; error = VOP_CLOSE(vp, flags, cred, p); vrele(vp); return (error); } static __inline int sequential_heuristic(struct uio *uio, struct file *fp) { /* * Sequential heuristic - detect sequential operation */ if ((uio->uio_offset == 0 && fp->f_seqcount > 0) || uio->uio_offset == fp->f_nextoff) { /* * XXX we assume that the filesystem block size is * the default. Not true, but still gives us a pretty * good indicator of how sequential the read operations * are. */ fp->f_seqcount += (uio->uio_resid + BKVASIZE - 1) / BKVASIZE; if (fp->f_seqcount >= 127) fp->f_seqcount = 127; return(fp->f_seqcount << 16); } /* * Not sequential, quick draw-down of seqcount */ if (fp->f_seqcount > 1) fp->f_seqcount = 1; else fp->f_seqcount = 0; return(0); } /* * Package up an I/O request on a vnode into a uio and do it. */ int vn_rdwr(rw, vp, base, len, offset, segflg, ioflg, cred, aresid, p) enum uio_rw rw; struct vnode *vp; caddr_t base; int len; off_t offset; enum uio_seg segflg; int ioflg; struct ucred *cred; int *aresid; struct proc *p; { struct uio auio; struct iovec aiov; struct mount *mp; int error; if ((ioflg & IO_NODELOCKED) == 0) { mp = NULL; if (rw == UIO_WRITE && vp->v_type != VCHR && (error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); } auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = base; aiov.iov_len = len; auio.uio_resid = len; auio.uio_offset = offset; auio.uio_segflg = segflg; auio.uio_rw = rw; auio.uio_procp = p; if (rw == UIO_READ) { error = VOP_READ(vp, &auio, ioflg, cred); } else { error = VOP_WRITE(vp, &auio, ioflg, cred); } if (aresid) *aresid = auio.uio_resid; else if (auio.uio_resid && error == 0) error = EIO; if ((ioflg & IO_NODELOCKED) == 0) { vn_finished_write(mp); VOP_UNLOCK(vp, 0, p); } return (error); } /* * File table vnode read routine. */ static int vn_read(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { struct vnode *vp; int error, ioflag; KASSERT(uio->uio_procp == p, ("uio_procp %p is not p %p", uio->uio_procp, p)); vp = (struct vnode *)fp->f_data; ioflag = 0; if (fp->f_flag & FNONBLOCK) ioflag |= IO_NDELAY; VOP_LEASE(vp, p, cred, LEASE_READ); vn_lock(vp, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; ioflag |= sequential_heuristic(uio, fp); error = VOP_READ(vp, uio, ioflag, cred); if ((flags & FOF_OFFSET) == 0) fp->f_offset = uio->uio_offset; fp->f_nextoff = uio->uio_offset; VOP_UNLOCK(vp, 0, p); return (error); } /* * File table vnode write routine. */ static int vn_write(fp, uio, cred, flags, p) struct file *fp; struct uio *uio; struct ucred *cred; struct proc *p; int flags; { struct vnode *vp; struct mount *mp; int error, ioflag; KASSERT(uio->uio_procp == p, ("uio_procp %p is not p %p", uio->uio_procp, p)); vp = (struct vnode *)fp->f_data; if (vp->v_type == VREG) bwillwrite(); vp = (struct vnode *)fp->f_data; /* XXX needed? */ ioflag = IO_UNIT; if (vp->v_type == VREG && (fp->f_flag & O_APPEND)) ioflag |= IO_APPEND; if (fp->f_flag & FNONBLOCK) ioflag |= IO_NDELAY; if ((fp->f_flag & O_FSYNC) || (vp->v_mount && (vp->v_mount->mnt_flag & MNT_SYNCHRONOUS))) ioflag |= IO_SYNC; mp = NULL; if (vp->v_type != VCHR && (error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) return (error); VOP_LEASE(vp, p, cred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); if ((flags & FOF_OFFSET) == 0) uio->uio_offset = fp->f_offset; ioflag |= sequential_heuristic(uio, fp); error = VOP_WRITE(vp, uio, ioflag, cred); if ((flags & FOF_OFFSET) == 0) fp->f_offset = uio->uio_offset; fp->f_nextoff = uio->uio_offset; VOP_UNLOCK(vp, 0, p); vn_finished_write(mp); return (error); } /* * File table vnode stat routine. */ static int vn_statfile(fp, sb, p) struct file *fp; struct stat *sb; struct proc *p; { struct vnode *vp = (struct vnode *)fp->f_data; return vn_stat(vp, sb, p); } int vn_stat(vp, sb, p) struct vnode *vp; register struct stat *sb; struct proc *p; { struct vattr vattr; register struct vattr *vap; int error; u_short mode; vap = &vattr; error = VOP_GETATTR(vp, vap, p->p_ucred, p); if (error) return (error); /* * Zero the spare stat fields */ sb->st_lspare = 0; sb->st_qspare[0] = 0; sb->st_qspare[1] = 0; /* * Copy from vattr table */ if (vap->va_fsid != VNOVAL) sb->st_dev = vap->va_fsid; else sb->st_dev = vp->v_mount->mnt_stat.f_fsid.val[0]; sb->st_ino = vap->va_fileid; mode = vap->va_mode; switch (vap->va_type) { case VREG: mode |= S_IFREG; break; case VDIR: mode |= S_IFDIR; break; case VBLK: mode |= S_IFBLK; break; case VCHR: mode |= S_IFCHR; break; case VLNK: mode |= S_IFLNK; /* This is a cosmetic change, symlinks do not have a mode. */ if (vp->v_mount->mnt_flag & MNT_NOSYMFOLLOW) sb->st_mode &= ~ACCESSPERMS; /* 0000 */ else sb->st_mode |= ACCESSPERMS; /* 0777 */ break; case VSOCK: mode |= S_IFSOCK; break; case VFIFO: mode |= S_IFIFO; break; default: return (EBADF); }; sb->st_mode = mode; sb->st_nlink = vap->va_nlink; sb->st_uid = vap->va_uid; sb->st_gid = vap->va_gid; sb->st_rdev = vap->va_rdev; sb->st_size = vap->va_size; sb->st_atimespec = vap->va_atime; sb->st_mtimespec = vap->va_mtime; sb->st_ctimespec = vap->va_ctime; /* * According to www.opengroup.org, the meaning of st_blksize is * "a filesystem-specific preferred I/O block size for this * object. In some filesystem types, this may vary from file * to file" * Default to zero to catch bogus uses of this field. */ if (vap->va_type == VREG) { sb->st_blksize = vap->va_blocksize; } else if (vn_isdisk(vp, NULL)) { sb->st_blksize = vp->v_rdev->si_bsize_best; if (sb->st_blksize < vp->v_rdev->si_bsize_phys) sb->st_blksize = vp->v_rdev->si_bsize_phys; if (sb->st_blksize < BLKDEV_IOSIZE) sb->st_blksize = BLKDEV_IOSIZE; } else { sb->st_blksize = 0; } sb->st_flags = vap->va_flags; if (suser_xxx(p->p_ucred, 0, 0)) sb->st_gen = 0; else sb->st_gen = vap->va_gen; #if (S_BLKSIZE == 512) /* Optimize this case */ sb->st_blocks = vap->va_bytes >> 9; #else sb->st_blocks = vap->va_bytes / S_BLKSIZE; #endif return (0); } /* * File table vnode ioctl routine. */ static int vn_ioctl(fp, com, data, p) struct file *fp; u_long com; caddr_t data; struct proc *p; { register struct vnode *vp = ((struct vnode *)fp->f_data); struct vattr vattr; int error; switch (vp->v_type) { case VREG: case VDIR: if (com == FIONREAD) { error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); if (error) return (error); *(int *)data = vattr.va_size - fp->f_offset; return (0); } if (com == FIONBIO || com == FIOASYNC) /* XXX */ return (0); /* XXX */ /* fall into ... */ default: #if 0 return (ENOTTY); #endif case VFIFO: case VCHR: case VBLK: if (com == FIODTYPE) { if (vp->v_type != VCHR && vp->v_type != VBLK) return (ENOTTY); *(int *)data = devsw(vp->v_rdev)->d_flags & D_TYPEMASK; return (0); } error = VOP_IOCTL(vp, com, data, fp->f_flag, p->p_ucred, p); if (error == 0 && com == TIOCSCTTY) { /* Do nothing if reassigning same control tty */ if (p->p_session->s_ttyvp == vp) return (0); /* Get rid of reference to old control tty */ if (p->p_session->s_ttyvp) vrele(p->p_session->s_ttyvp); p->p_session->s_ttyvp = vp; VREF(vp); } return (error); } } /* * File table vnode poll routine. */ static int vn_poll(fp, events, cred, p) struct file *fp; int events; struct ucred *cred; struct proc *p; { return (VOP_POLL(((struct vnode *)fp->f_data), events, cred, p)); } /* * Check that the vnode is still valid, and if so * acquire requested lock. */ int #ifndef DEBUG_LOCKS vn_lock(vp, flags, p) #else debug_vn_lock(vp, flags, p, filename, line) #endif struct vnode *vp; int flags; struct proc *p; #ifdef DEBUG_LOCKS const char *filename; int line; #endif { int error; do { if ((flags & LK_INTERLOCK) == 0) mtx_lock(&vp->v_interlock); if ((vp->v_flag & VXLOCK) && vp->v_vxproc != curproc) { vp->v_flag |= VXWANT; mtx_unlock(&vp->v_interlock); tsleep((caddr_t)vp, PINOD, "vn_lock", 0); error = ENOENT; } else { if (vp->v_vxproc != NULL) printf("VXLOCK interlock avoided in vn_lock\n"); #ifdef DEBUG_LOCKS vp->filename = filename; vp->line = line; #endif error = VOP_LOCK(vp, flags | LK_NOPAUSE | LK_INTERLOCK, p); if (error == 0) return (error); } flags &= ~LK_INTERLOCK; } while (flags & LK_RETRY); return (error); } /* * File table vnode close routine. */ static int vn_closefile(fp, p) struct file *fp; struct proc *p; { fp->f_ops = &badfileops; return (vn_close(((struct vnode *)fp->f_data), fp->f_flag, fp->f_cred, p)); } /* * Preparing to start a filesystem write operation. If the operation is * permitted, then we bump the count of operations in progress and * proceed. If a suspend request is in progress, we wait until the * suspension is over, and then proceed. */ int vn_start_write(vp, mpp, flags) struct vnode *vp; struct mount **mpp; int flags; { struct mount *mp; int error; /* * If a vnode is provided, get and return the mount point that * to which it will write. */ if (vp != NULL) { if ((error = VOP_GETWRITEMOUNT(vp, mpp)) != 0) { *mpp = NULL; if (error != EOPNOTSUPP) return (error); return (0); } } if ((mp = *mpp) == NULL) return (0); /* * Check on status of suspension. */ while ((mp->mnt_kern_flag & MNTK_SUSPEND) != 0) { if (flags & V_NOWAIT) return (EWOULDBLOCK); error = tsleep(&mp->mnt_flag, (PUSER - 1) | (flags & PCATCH), "suspfs", 0); if (error) return (error); } if (flags & V_XSLEEP) return (0); mp->mnt_writeopcount++; return (0); } /* * Secondary suspension. Used by operations such as vop_inactive * routines that are needed by the higher level functions. These * are allowed to proceed until all the higher level functions have * completed (indicated by mnt_writeopcount dropping to zero). At that * time, these operations are halted until the suspension is over. */ int vn_write_suspend_wait(vp, mp, flags) struct vnode *vp; struct mount *mp; int flags; { int error; if (vp != NULL) { if ((error = VOP_GETWRITEMOUNT(vp, &mp)) != 0) { if (error != EOPNOTSUPP) return (error); return (0); } } /* * If we are not suspended or have not yet reached suspended * mode, then let the operation proceed. */ if (mp == NULL || (mp->mnt_kern_flag & MNTK_SUSPENDED) == 0) return (0); if (flags & V_NOWAIT) return (EWOULDBLOCK); /* * Wait for the suspension to finish. */ return (tsleep(&mp->mnt_flag, (PUSER - 1) | (flags & PCATCH), "suspfs", 0)); } /* * Filesystem write operation has completed. If we are suspending and this * operation is the last one, notify the suspender that the suspension is * now in effect. */ void vn_finished_write(mp) struct mount *mp; { if (mp == NULL) return; mp->mnt_writeopcount--; if (mp->mnt_writeopcount < 0) panic("vn_finished_write: neg cnt"); if ((mp->mnt_kern_flag & MNTK_SUSPEND) != 0 && mp->mnt_writeopcount <= 0) wakeup(&mp->mnt_writeopcount); } /* * Request a filesystem to suspend write operations. */ void vfs_write_suspend(mp) struct mount *mp; { struct proc *p = curproc; if (mp->mnt_kern_flag & MNTK_SUSPEND) return; mp->mnt_kern_flag |= MNTK_SUSPEND; if (mp->mnt_writeopcount > 0) (void) tsleep(&mp->mnt_writeopcount, PUSER - 1, "suspwt", 0); VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p); mp->mnt_kern_flag |= MNTK_SUSPENDED; } /* * Request a filesystem to resume write operations. */ void vfs_write_resume(mp) struct mount *mp; { if ((mp->mnt_kern_flag & MNTK_SUSPEND) == 0) return; mp->mnt_kern_flag &= ~(MNTK_SUSPEND | MNTK_SUSPENDED); wakeup(&mp->mnt_writeopcount); wakeup(&mp->mnt_flag); } static int vn_kqfilter(struct file *fp, struct knote *kn) { return (VOP_KQFILTER(((struct vnode *)fp->f_data), kn)); } /* * Simplified in-kernel wrapper calls for extended attribute access. * Both calls pass in a NULL credential, authorizing as "kernel" access. * Set IO_NODELOCKED in ioflg if the vnode is already locked. */ int -vn_extattr_get(struct vnode *vp, int ioflg, const char *attrname, int *buflen, - char *buf, struct proc *p) +vn_extattr_get(struct vnode *vp, int ioflg, int namespace, + const char *attrname, int *buflen, char *buf, struct proc *p) { struct uio auio; struct iovec iov; int error; iov.iov_len = *buflen; iov.iov_base = buf; auio.uio_iov = &iov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = p; auio.uio_offset = 0; auio.uio_resid = *buflen; if ((ioflg & IO_NODELOCKED) == 0) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* authorize attribute retrieval as kernel */ - error = VOP_GETEXTATTR(vp, attrname, &auio, NULL, p); + error = VOP_GETEXTATTR(vp, namespace, attrname, &auio, NULL, p); if ((ioflg & IO_NODELOCKED) == 0) VOP_UNLOCK(vp, 0, p); if (error == 0) { *buflen = *buflen - auio.uio_resid; } return (error); } /* * XXX failure mode if partially written? */ int -vn_extattr_set(struct vnode *vp, int ioflg, const char *attrname, int buflen, - char *buf, struct proc *p) +vn_extattr_set(struct vnode *vp, int ioflg, int namespace, + const char *attrname, int buflen, char *buf, struct proc *p) { struct uio auio; struct iovec iov; struct mount *mp; int error; iov.iov_len = buflen; iov.iov_base = buf; auio.uio_iov = &iov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_WRITE; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = p; auio.uio_offset = 0; auio.uio_resid = buflen; if ((ioflg & IO_NODELOCKED) == 0) { if ((error = vn_start_write(vp, &mp, V_WAIT)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); } /* authorize attribute setting as kernel */ - error = VOP_SETEXTATTR(vp, attrname, &auio, NULL, p); + error = VOP_SETEXTATTR(vp, namespace, attrname, &auio, NULL, p); if ((ioflg & IO_NODELOCKED) == 0) { vn_finished_write(mp); VOP_UNLOCK(vp, 0, p); } return (error); } int -vn_extattr_rm(struct vnode *vp, int ioflg, const char *attrname, struct proc *p) +vn_extattr_rm(struct vnode *vp, int ioflg, int namespace, const char *attrname, + struct proc *p) { struct mount *mp; int error; if ((ioflg & IO_NODELOCKED) == 0) { if ((error = vn_start_write(vp, &mp, V_WAIT)) != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); } /* authorize attribute removal as kernel */ - error = VOP_SETEXTATTR(vp, attrname, NULL, NULL, p); + error = VOP_SETEXTATTR(vp, namespace, attrname, NULL, NULL, p); if ((ioflg & IO_NODELOCKED) == 0) { vn_finished_write(mp); VOP_UNLOCK(vp, 0, p); } return (error); } Index: head/sys/kern/vnode_if.src =================================================================== --- head/sys/kern/vnode_if.src (revision 74272) +++ head/sys/kern/vnode_if.src (revision 74273) @@ -1,571 +1,573 @@ # # Copyright (c) 1992, 1993 # The Regents of the University of California. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. All advertising materials mentioning features or use of this software # must display the following acknowledgement: # This product includes software developed by the University of # California, Berkeley and its contributors. # 4. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # @(#)vnode_if.src 8.12 (Berkeley) 5/14/95 # $FreeBSD$ # # # Above each of the vop descriptors is a specification of the locking # protocol used by each vop call. The first column is the name of # the variable, the remaining three columns are in, out and error # respectively. The "in" column defines the lock state on input, # the "out" column defines the state on succesful return, and the # "error" column defines the locking state on error exit. # # The locking value can take the following values: # L: locked; not converted to type of lock. # A: any lock type. # S: locked with shared lock. # E: locked with exclusive lock for this process. # O: locked with exclusive lock for other process. # U: unlocked. # -: not applicable. vnode does not yet (or no longer) exists. # =: the same on input and output, may be either L or U. # X: locked if not nil. # # #% islocked vp = = = # vop_islocked { IN struct vnode *vp; IN struct proc *p; }; # #% lookup dvp L ? ? #% lookup vpp - L - # # XXX - the lookup locking protocol defies simple description and depends # on the flags and operation fields in the (cnp) structure. Note # especially that *vpp may equal dvp and both may be locked. # vop_lookup { IN struct vnode *dvp; INOUT struct vnode **vpp; IN struct componentname *cnp; }; # #% cachedlookup dvp L ? ? #% cachedlookup vpp - L - # # This must be an exact copy of lookup. See kern/vfs_cache.c for details. # vop_cachedlookup { IN struct vnode *dvp; INOUT struct vnode **vpp; IN struct componentname *cnp; }; # #% create dvp L L L #% create vpp - L - # vop_create { IN struct vnode *dvp; OUT struct vnode **vpp; IN struct componentname *cnp; IN struct vattr *vap; }; # #% whiteout dvp L L L # vop_whiteout { IN struct vnode *dvp; IN struct componentname *cnp; IN int flags; }; # #% mknod dvp L L L #% mknod vpp - X - # vop_mknod { IN struct vnode *dvp; OUT struct vnode **vpp; IN struct componentname *cnp; IN struct vattr *vap; }; # #% open vp L L L # vop_open { IN struct vnode *vp; IN int mode; IN struct ucred *cred; IN struct proc *p; }; # #% close vp U U U # vop_close { IN struct vnode *vp; IN int fflag; IN struct ucred *cred; IN struct proc *p; }; # #% access vp L L L # vop_access { IN struct vnode *vp; IN int mode; IN struct ucred *cred; IN struct proc *p; }; # #% getattr vp = = = # vop_getattr { IN struct vnode *vp; OUT struct vattr *vap; IN struct ucred *cred; IN struct proc *p; }; # #% setattr vp L L L # vop_setattr { IN struct vnode *vp; IN struct vattr *vap; IN struct ucred *cred; IN struct proc *p; }; # #% read vp L L L # vop_read { IN struct vnode *vp; INOUT struct uio *uio; IN int ioflag; IN struct ucred *cred; }; # #% write vp L L L # vop_write { IN struct vnode *vp; INOUT struct uio *uio; IN int ioflag; IN struct ucred *cred; }; # #% lease vp = = = # vop_lease { IN struct vnode *vp; IN struct proc *p; IN struct ucred *cred; IN int flag; }; # #% ioctl vp U U U # vop_ioctl { IN struct vnode *vp; IN u_long command; IN caddr_t data; IN int fflag; IN struct ucred *cred; IN struct proc *p; }; # #% poll vp U U U # vop_poll { IN struct vnode *vp; IN int events; IN struct ucred *cred; IN struct proc *p; }; # #% kqfilter vp U U U # vop_kqfilter { IN struct vnode *vp; IN struct knote *kn; }; # #% revoke vp U U U # vop_revoke { IN struct vnode *vp; IN int flags; }; # #% fsync vp L L L # vop_fsync { IN struct vnode *vp; IN struct ucred *cred; IN int waitfor; IN struct proc *p; }; # #% remove dvp L L L #% remove vp L L L # vop_remove { IN struct vnode *dvp; IN struct vnode *vp; IN struct componentname *cnp; }; # #% link tdvp L L L #% link vp U U U # vop_link { IN struct vnode *tdvp; IN struct vnode *vp; IN struct componentname *cnp; }; # #% rename fdvp U U U #% rename fvp U U U #% rename tdvp L U U #% rename tvp X U U # vop_rename { IN WILLRELE struct vnode *fdvp; IN WILLRELE struct vnode *fvp; IN struct componentname *fcnp; IN WILLRELE struct vnode *tdvp; IN WILLRELE struct vnode *tvp; IN struct componentname *tcnp; }; # #% mkdir dvp L L L #% mkdir vpp - L - # vop_mkdir { IN struct vnode *dvp; OUT struct vnode **vpp; IN struct componentname *cnp; IN struct vattr *vap; }; # #% rmdir dvp L L L #% rmdir vp L L L # vop_rmdir { IN struct vnode *dvp; IN struct vnode *vp; IN struct componentname *cnp; }; # #% symlink dvp L L L #% symlink vpp - U - # vop_symlink { IN struct vnode *dvp; OUT struct vnode **vpp; IN struct componentname *cnp; IN struct vattr *vap; IN char *target; }; # #% readdir vp L L L # vop_readdir { IN struct vnode *vp; INOUT struct uio *uio; IN struct ucred *cred; INOUT int *eofflag; OUT int *ncookies; INOUT u_long **cookies; }; # #% readlink vp L L L # vop_readlink { IN struct vnode *vp; INOUT struct uio *uio; IN struct ucred *cred; }; # #% inactive vp L U U # vop_inactive { IN struct vnode *vp; IN struct proc *p; }; # #% reclaim vp U U U # vop_reclaim { IN struct vnode *vp; IN struct proc *p; }; # #% lock vp ? ? ? # vop_lock { IN struct vnode *vp; IN int flags; IN struct proc *p; }; # #% unlock vp L U L # vop_unlock { IN struct vnode *vp; IN int flags; IN struct proc *p; }; # #% bmap vp L L L #% bmap vpp - U - # vop_bmap { IN struct vnode *vp; IN daddr_t bn; OUT struct vnode **vpp; IN daddr_t *bnp; OUT int *runp; OUT int *runb; }; # #% strategy vp L L L # vop_strategy { IN struct vnode *vp; IN struct buf *bp; }; # #% getwritemount vp = = = # vop_getwritemount { IN struct vnode *vp; OUT struct mount **mpp; }; # #% print vp = = = # vop_print { IN struct vnode *vp; }; # #% pathconf vp L L L # vop_pathconf { IN struct vnode *vp; IN int name; OUT register_t *retval; }; # #% advlock vp U U U # vop_advlock { IN struct vnode *vp; IN caddr_t id; IN int op; IN struct flock *fl; IN int flags; }; # #% balloc vp L L L # vop_balloc { IN struct vnode *vp; IN off_t startoffset; IN int size; IN struct ucred *cred; IN int flags; OUT struct buf **bpp; }; # #% reallocblks vp L L L # vop_reallocblks { IN struct vnode *vp; IN struct cluster_save *buflist; }; # #% getpages vp L L L # vop_getpages { IN struct vnode *vp; IN vm_page_t *m; IN int count; IN int reqpage; IN vm_ooffset_t offset; }; # #% putpages vp L L L # vop_putpages { IN struct vnode *vp; IN vm_page_t *m; IN int count; IN int sync; IN int *rtvals; IN vm_ooffset_t offset; }; # #% freeblks vp - - - # # This call is used by the filesystem to release blocks back to # device-driver. This is useful if the driver has a lengthy # erase handling or similar. # vop_freeblks { IN struct vnode *vp; IN daddr_t addr; IN daddr_t length; }; # #% bwrite vp L L L # vop_bwrite { IN struct vnode *vp; IN struct buf *bp; }; # #% getacl vp L L L # vop_getacl { IN struct vnode *vp; IN acl_type_t type; OUT struct acl *aclp; IN struct ucred *cred; IN struct proc *p; }; # #% setacl vp L L L # vop_setacl { IN struct vnode *vp; IN acl_type_t type; IN struct acl *aclp; IN struct ucred *cred; IN struct proc *p; }; # #% aclcheck vp = = = # vop_aclcheck { IN struct vnode *vp; IN acl_type_t type; IN struct acl *aclp; IN struct ucred *cred; IN struct proc *p; }; # #% getextattr vp L L L # vop_getextattr { IN struct vnode *vp; + IN int namespace; IN const char *name; INOUT struct uio *uio; IN struct ucred *cred; IN struct proc *p; }; # #% setextattr vp L L L # vop_setextattr { IN struct vnode *vp; + IN int namespace; IN const char *name; INOUT struct uio *uio; IN struct ucred *cred; IN struct proc *p; }; # #% createvobject vp L L L # vop_createvobject { IN struct vnode *vp; IN struct ucred *cred; IN struct proc *p; }; # #% destroyvobject vp L L L # vop_destroyvobject { IN struct vnode *vp; }; # #% getvobject vp L L L # vop_getvobject { IN struct vnode *vp; OUT struct vm_object **objpp; }; Index: head/sys/miscfs/nullfs/null_vfsops.c =================================================================== --- head/sys/miscfs/nullfs/null_vfsops.c (revision 74272) +++ head/sys/miscfs/nullfs/null_vfsops.c (revision 74273) @@ -1,440 +1,443 @@ /* * Copyright (c) 1992, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94 * * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92 * $FreeBSD$ */ /* * Null Layer * (See null_vnops.c for a description of what this does.) */ #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_NULLFSMNT, "NULLFS mount", "NULLFS mount structure"); static int nullfs_fhtovp(struct mount *mp, struct fid *fidp, struct vnode **vpp); static int nullfs_checkexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp); static int nullfs_mount(struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p); static int nullfs_quotactl(struct mount *mp, int cmd, uid_t uid, caddr_t arg, struct proc *p); static int nullfs_root(struct mount *mp, struct vnode **vpp); static int nullfs_start(struct mount *mp, int flags, struct proc *p); static int nullfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p); static int nullfs_sync(struct mount *mp, int waitfor, struct ucred *cred, struct proc *p); static int nullfs_unmount(struct mount *mp, int mntflags, struct proc *p); static int nullfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp); static int nullfs_vptofh(struct vnode *vp, struct fid *fhp); static int nullfs_extattrctl(struct mount *mp, int cmd, - const char *attrname, caddr_t arg, struct proc *p); + struct vnode *filename_vp, + int namespace, const char *attrname, + struct proc *p); /* * Mount null layer */ static int nullfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { int error = 0; struct null_args args; struct vnode *lowerrootvp, *vp; struct vnode *nullm_rootvp; struct null_mount *xmp; u_int size; int isvnunlocked = 0; NULLFSDEBUG("nullfs_mount(mp = %p)\n", (void *)mp); /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/ } /* * Get argument */ error = copyin(data, (caddr_t)&args, sizeof(struct null_args)); if (error) return (error); /* * Unlock lower node to avoid deadlock. * (XXX) VOP_ISLOCKED is needed? */ if ((mp->mnt_vnodecovered->v_op == null_vnodeop_p) && VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) { VOP_UNLOCK(mp->mnt_vnodecovered, 0, p); isvnunlocked = 1; } /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); error = namei(ndp); /* * Re-lock vnode. */ if (isvnunlocked && !VOP_ISLOCKED(mp->mnt_vnodecovered, NULL)) vn_lock(mp->mnt_vnodecovered, LK_EXCLUSIVE | LK_RETRY, p); if (error) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; vrele(ndp->ni_dvp); ndp->ni_dvp = NULLVP; /* * Check multi null mount to avoid `lock against myself' panic. */ if (lowerrootvp == VTONULL(mp->mnt_vnodecovered)->null_lowervp) { NULLFSDEBUG("nullfs_mount: multi null mount?\n"); vput(lowerrootvp); return (EDEADLK); } xmp = (struct null_mount *) malloc(sizeof(struct null_mount), M_NULLFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ xmp->nullm_vfs = lowerrootvp->v_mount; /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = null_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp, 0, p); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(xmp, M_NULLFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in nullfs_unmount. */ nullm_rootvp = vp; nullm_rootvp->v_flag |= VROOT; xmp->nullm_rootvp = nullm_rootvp; if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) xmp; vfs_getnewfsid(mp); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)nullfs_statfs(mp, &mp->mnt_stat, p); NULLFSDEBUG("nullfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ static int nullfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */ } /* * Free reference to null layer */ static int nullfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; void *mntdata; int error; int flags = 0; NULLFSDEBUG("nullfs_unmount: mp = %p\n", (void *)mp); if (mntflags & MNT_FORCE) flags |= FORCECLOSE; error = VFS_ROOT(mp, &vp); if (error) return (error); if (vp->v_usecount > 2) { NULLFSDEBUG("nullfs_unmount: rootvp is busy(%d)\n", vp->v_usecount); vput(vp); return (EBUSY); } error = vflush(mp, vp, flags); if (error) return (error); #ifdef NULLFS_DEBUG vprint("alias root of lower", vp); #endif vput(vp); /* * Release reference on underlying root vnode */ vrele(vp); /* * And blow it away for future re-use */ vgone(vp); /* * Finally, throw away the null_mount structure */ mntdata = mp->mnt_data; mp->mnt_data = 0; free(mntdata, M_NULLFSMNT); return 0; } static int nullfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct proc *p = curproc; /* XXX */ struct vnode *vp; NULLFSDEBUG("nullfs_root(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); /* * Return locked reference to root. */ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp; VREF(vp); #ifdef NULLFS_DEBUG if (VOP_ISLOCKED(vp, NULL)) { Debugger("root vnode is locked.\n"); vrele(vp); return (EDEADLK); } #endif vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); *vpp = vp; return 0; } static int nullfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p); } static int nullfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } static int nullfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at null layer. */ return (0); } static int nullfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { int error; error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp); if (error) return (error); return (null_node_create(mp, *vpp, vpp)); } static int nullfs_fhtovp(mp, fidp, vpp) struct mount *mp; struct fid *fidp; struct vnode **vpp; { int error; error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, vpp); if (error) return (error); return (null_node_create(mp, *vpp, vpp)); } static int nullfs_checkexp(mp, nam, extflagsp, credanonp) struct mount *mp; struct sockaddr *nam; int *extflagsp; struct ucred **credanonp; { return VFS_CHECKEXP(MOUNTTONULLMOUNT(mp)->nullm_vfs, nam, extflagsp, credanonp); } static int nullfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp); } static int -nullfs_extattrctl(mp, cmd, attrname, arg, p) +nullfs_extattrctl(mp, cmd, filename_vp, namespace, attrname, p) struct mount *mp; int cmd; + struct vnode *filename_vp; + int namespace; const char *attrname; - caddr_t arg; struct proc *p; { - return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, attrname, - arg, p); + return VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, filename_vp, + namespace, attrname, p); } static struct vfsops null_vfsops = { nullfs_mount, nullfs_start, nullfs_unmount, nullfs_root, nullfs_quotactl, nullfs_statfs, nullfs_sync, nullfs_vget, nullfs_fhtovp, nullfs_checkexp, nullfs_vptofh, nullfs_init, nullfs_uninit, nullfs_extattrctl, }; VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK); Index: head/sys/miscfs/umapfs/umap_vfsops.c =================================================================== --- head/sys/miscfs/umapfs/umap_vfsops.c (revision 74272) +++ head/sys/miscfs/umapfs/umap_vfsops.c (revision 74273) @@ -1,462 +1,464 @@ /* * Copyright (c) 1992, 1993, 1995 * The Regents of the University of California. All rights reserved. * * This code is derived from software donated to Berkeley by * the UCLA Ficus project. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)umap_vfsops.c 8.8 (Berkeley) 5/14/95 * * $FreeBSD$ */ /* * Umap Layer * (See mount_umap(8) for a description of this layer.) */ #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_UMAPFSMNT, "UMAP mount", "UMAP mount structure"); static int umapfs_fhtovp __P((struct mount *mp, struct fid *fidp, struct vnode **vpp)); static int umapfs_checkexp __P((struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp)); static int umapfs_mount __P((struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p)); static int umapfs_quotactl __P((struct mount *mp, int cmd, uid_t uid, caddr_t arg, struct proc *p)); static int umapfs_root __P((struct mount *mp, struct vnode **vpp)); static int umapfs_start __P((struct mount *mp, int flags, struct proc *p)); static int umapfs_statfs __P((struct mount *mp, struct statfs *sbp, struct proc *p)); static int umapfs_sync __P((struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)); static int umapfs_unmount __P((struct mount *mp, int mntflags, struct proc *p)); static int umapfs_vget __P((struct mount *mp, ino_t ino, struct vnode **vpp)); static int umapfs_vptofh __P((struct vnode *vp, struct fid *fhp)); static int umapfs_extattrctl __P((struct mount *mp, int cmd, - const char *attrname, caddr_t arg, + struct vnode *filename_vp, + int namespace, const char *attrname, struct proc *p)); /* * Mount umap layer */ static int umapfs_mount(mp, path, data, ndp, p) struct mount *mp; char *path; caddr_t data; struct nameidata *ndp; struct proc *p; { struct umap_args args; struct vnode *lowerrootvp, *vp; struct vnode *umapm_rootvp; struct umap_mount *amp; u_int size; int error; #ifdef DEBUG int i; #endif /* * Only for root */ if ((error = suser(p)) != 0) return (error); #ifdef DEBUG printf("umapfs_mount(mp = %p)\n", (void *)mp); #endif /* * Update is a no-op */ if (mp->mnt_flag & MNT_UPDATE) { return (EOPNOTSUPP); /* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/ } /* * Get argument */ error = copyin(data, (caddr_t)&args, sizeof(struct umap_args)); if (error) return (error); /* * Find lower node */ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF, UIO_USERSPACE, args.target, p); error = namei(ndp); if (error) return (error); NDFREE(ndp, NDF_ONLY_PNBUF); /* * Sanity check on lower vnode */ lowerrootvp = ndp->ni_vp; #ifdef DEBUG printf("vp = %p, check for VDIR...\n", (void *)lowerrootvp); #endif vrele(ndp->ni_dvp); ndp->ni_dvp = 0; if (lowerrootvp->v_type != VDIR) { vput(lowerrootvp); return (EINVAL); } #ifdef DEBUG printf("mp = %p\n", (void *)mp); #endif amp = (struct umap_mount *) malloc(sizeof(struct umap_mount), M_UMAPFSMNT, M_WAITOK); /* XXX */ /* * Save reference to underlying FS */ amp->umapm_vfs = lowerrootvp->v_mount; /* * Now copy in the number of entries and maps for umap mapping. */ amp->info_nentries = args.nentries; amp->info_gnentries = args.gnentries; error = copyin(args.mapdata, (caddr_t)amp->info_mapdata, 2*sizeof(u_long)*args.nentries); if (error) return (error); #ifdef DEBUG printf("umap_mount:nentries %d\n",args.nentries); for (i = 0; i < args.nentries; i++) printf(" %lu maps to %lu\n", amp->info_mapdata[i][0], amp->info_mapdata[i][1]); #endif error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata, 2*sizeof(u_long)*args.gnentries); if (error) return (error); #ifdef DEBUG printf("umap_mount:gnentries %d\n",args.gnentries); for (i = 0; i < args.gnentries; i++) printf(" group %lu maps to %lu\n", amp->info_gmapdata[i][0], amp->info_gmapdata[i][1]); #endif /* * Save reference. Each mount also holds * a reference on the root vnode. */ error = umap_node_create(mp, lowerrootvp, &vp); /* * Unlock the node (either the lower or the alias) */ VOP_UNLOCK(vp, 0, p); /* * Make sure the node alias worked */ if (error) { vrele(lowerrootvp); free(amp, M_UMAPFSMNT); /* XXX */ return (error); } /* * Keep a held reference to the root vnode. * It is vrele'd in umapfs_unmount. */ umapm_rootvp = vp; umapm_rootvp->v_flag |= VROOT; amp->umapm_rootvp = umapm_rootvp; if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL) mp->mnt_flag |= MNT_LOCAL; mp->mnt_data = (qaddr_t) amp; vfs_getnewfsid(mp); (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); (void)umapfs_statfs(mp, &mp->mnt_stat, p); #ifdef DEBUG printf("umapfs_mount: lower %s, alias at %s\n", mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname); #endif return (0); } /* * VFS start. Nothing needed here - the start routine * on the underlying filesystem will have been called * when that filesystem was mounted. */ static int umapfs_start(mp, flags, p) struct mount *mp; int flags; struct proc *p; { return (0); /* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */ } /* * Free reference to umap layer */ static int umapfs_unmount(mp, mntflags, p) struct mount *mp; int mntflags; struct proc *p; { struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; int error; int flags = 0; #ifdef DEBUG printf("umapfs_unmount(mp = %p)\n", (void *)mp); #endif if (mntflags & MNT_FORCE) flags |= FORCECLOSE; /* * Clear out buffer cache. I don't think we * ever get anything cached at this level at the * moment, but who knows... */ #ifdef notyet mntflushbuf(mp, 0); if (mntinvalbuf(mp, 1)) return (EBUSY); #endif if (umapm_rootvp->v_usecount > 1) return (EBUSY); error = vflush(mp, umapm_rootvp, flags); if (error) return (error); #ifdef DEBUG vprint("alias root of lower", umapm_rootvp); #endif /* * Release reference on underlying root vnode */ vrele(umapm_rootvp); /* * And blow it away for future re-use */ vgone(umapm_rootvp); /* * Finally, throw away the umap_mount structure */ free(mp->mnt_data, M_UMAPFSMNT); /* XXX */ mp->mnt_data = 0; return (0); } static int umapfs_root(mp, vpp) struct mount *mp; struct vnode **vpp; { struct proc *p = curproc; /* XXX */ struct vnode *vp; #ifdef DEBUG printf("umapfs_root(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, (void *)UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)); #endif /* * Return locked reference to root. */ vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp; VREF(vp); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); *vpp = vp; return (0); } static int umapfs_quotactl(mp, cmd, uid, arg, p) struct mount *mp; int cmd; uid_t uid; caddr_t arg; struct proc *p; { return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p)); } static int umapfs_statfs(mp, sbp, p) struct mount *mp; struct statfs *sbp; struct proc *p; { int error; struct statfs mstat; #ifdef DEBUG printf("umapfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, (void *)MOUNTTOUMAPMOUNT(mp)->umapm_rootvp, (void *)UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)); #endif bzero(&mstat, sizeof(mstat)); error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p); if (error) return (error); /* now copy across the "interesting" information and fake the rest */ sbp->f_type = mstat.f_type; sbp->f_flags = mstat.f_flags; sbp->f_bsize = mstat.f_bsize; sbp->f_iosize = mstat.f_iosize; sbp->f_blocks = mstat.f_blocks; sbp->f_bfree = mstat.f_bfree; sbp->f_bavail = mstat.f_bavail; sbp->f_files = mstat.f_files; sbp->f_ffree = mstat.f_ffree; if (sbp != &mp->mnt_stat) { bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid)); bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN); bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } return (0); } static int umapfs_sync(mp, waitfor, cred, p) struct mount *mp; int waitfor; struct ucred *cred; struct proc *p; { /* * XXX - Assumes no data cached at umap layer. */ return (0); } static int umapfs_vget(mp, ino, vpp) struct mount *mp; ino_t ino; struct vnode **vpp; { return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp)); } static int umapfs_fhtovp(mp, fidp, vpp) struct mount *mp; struct fid *fidp; struct vnode **vpp; { return (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, vpp)); } static int umapfs_checkexp(mp, nam, exflagsp, credanonp) struct mount *mp; struct sockaddr *nam; int *exflagsp; struct ucred **credanonp; { return (VFS_CHECKEXP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, nam, exflagsp, credanonp)); } static int umapfs_vptofh(vp, fhp) struct vnode *vp; struct fid *fhp; { return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp)); } static int -umapfs_extattrctl(mp, cmd, attrname, arg, p) +umapfs_extattrctl(mp, cmd, filename_vp, namespace, attrname, p) struct mount *mp; int cmd; + struct vnode *filename_vp; + int namespace; const char *attrname; - caddr_t arg; struct proc *p; { - return (VFS_EXTATTRCTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, attrname, - arg, p)); + return (VFS_EXTATTRCTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, + filename_vp, namespace, attrname, p)); } static struct vfsops umap_vfsops = { umapfs_mount, umapfs_start, umapfs_unmount, umapfs_root, umapfs_quotactl, umapfs_statfs, umapfs_sync, umapfs_vget, umapfs_fhtovp, umapfs_checkexp, umapfs_vptofh, umapfs_init, vfs_stduninit, umapfs_extattrctl, }; VFS_SET(umap_vfsops, umap, VFCF_LOOPBACK); Index: head/sys/sys/acl.h =================================================================== --- head/sys/sys/acl.h (revision 74272) +++ head/sys/sys/acl.h (revision 74273) @@ -1,155 +1,157 @@ /*- - * Copyright (c) 1999-2001 Robert N. M. Watson + * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Developed by the TrustedBSD Project. * Support for POSIX.1e access control lists. */ #ifndef _SYS_ACL_H #define _SYS_ACL_H /* * POSIX.1e ACL types and related constants. */ -#define POSIX1E_ACL_ACCESS_EXTATTR_NAME "$posix1e.acl_access" -#define POSIX1E_ACL_DEFAULT_EXTATTR_NAME "$posix1e.acl_default" +#define POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define POSIX1E_ACL_ACCESS_EXTATTR_NAME "posix1e.acl_access" +#define POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define POSIX1E_ACL_DEFAULT_EXTATTR_NAME "posix1e.acl_default" #define ACL_MAX_ENTRIES 32 /* maximum entries in an ACL */ #define _POSIX_ACL_PATH_MAX ACL_MAX_ENTRIES typedef int acl_type_t; typedef int acl_tag_t; typedef mode_t acl_perm_t; struct acl_entry { acl_tag_t ae_tag; uid_t ae_id; acl_perm_t ae_perm; }; typedef struct acl_entry *acl_entry_t; struct acl { int acl_cnt; struct acl_entry acl_entry[ACL_MAX_ENTRIES]; }; typedef struct acl *acl_t; /* * Possible valid values for ae_tag field. */ #define ACL_USER_OBJ 0x00000001 #define ACL_USER 0x00000002 #define ACL_GROUP_OBJ 0x00000004 #define ACL_GROUP 0x00000008 #define ACL_MASK 0x00000010 #define ACL_OTHER 0x00000020 #define ACL_OTHER_OBJ ACL_OTHER /* * Possible valid values for acl_type_t arguments. */ #define ACL_TYPE_ACCESS 0x00000000 #define ACL_TYPE_DEFAULT 0x00000001 #define ACL_TYPE_AFS 0x00000002 #define ACL_TYPE_CODA 0x00000003 #define ACL_TYPE_NTFS 0x00000004 #define ACL_TYPE_NWFS 0x00000005 /* * Possible flags in ae_perm field. */ #define ACL_PERM_EXEC 0x0001 #define ACL_PERM_WRITE 0x0002 #define ACL_PERM_READ 0x0004 #define ACL_PERM_NONE 0x0000 #define ACL_PERM_BITS (ACL_PERM_EXEC | ACL_PERM_WRITE | ACL_PERM_READ) #define ACL_POSIX1E_BITS (ACL_PERM_EXEC | ACL_PERM_WRITE | ACL_PERM_READ) #ifdef _KERNEL /* * Storage for ACLs and support structures. */ #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_ACL); #endif acl_perm_t acl_posix1e_mode_to_perm __P((acl_tag_t tag, mode_t mode)); struct acl_entry acl_posix1e_mode_to_entry __P((acl_tag_t tag, uid_t uid, gid_t gid, mode_t mode)); mode_t acl_posix1e_perms_to_mode __P((struct acl_entry *acl_user_obj_entry, struct acl_entry *acl_group_obj_entry, struct acl_entry *acl_other_entry)); int acl_posix1e_check(struct acl *acl); #else /* !_KERNEL */ /* * Syscall interface -- use the library calls instead as the syscalls * have strict acl entry ordering requirements. */ __BEGIN_DECLS int __acl_aclcheck_fd(int _filedes, acl_type_t _type, struct acl *_aclp); int __acl_aclcheck_file(const char *_path, acl_type_t _type, struct acl *_aclp); int __acl_delete_fd(int _filedes, acl_type_t _type); int __acl_delete_file(const char *_path_p, acl_type_t _type); int __acl_get_fd(int _filedes, acl_type_t _type, struct acl *_aclp); int __acl_get_file(const char *_path, acl_type_t _type, struct acl *_aclp); int __acl_set_fd(int _filedes, acl_type_t _type, struct acl *_aclp); int __acl_set_file(const char *_path, acl_type_t _type, struct acl *_aclp); __END_DECLS /* * Supported POSIX.1e ACL manipulation and assignment/retrieval API * _np calls are local extensions that reflect an environment capable of * opening file descriptors of directories, and allowing additional * ACL type for different file systems (i.e., AFS). */ __BEGIN_DECLS int acl_delete_fd_np(int _filedes, acl_type_t _type); int acl_delete_file_np(const char *_path_p, acl_type_t _type); int acl_delete_def_file(const char *_path_p); acl_t acl_dup(acl_t _acl); int acl_free(void *_obj_p); acl_t acl_from_text(const char *_buf_p); acl_t acl_get_fd(int _fd); acl_t acl_get_fd_np(int fd, acl_type_t _type); acl_t acl_get_file(const char *_path_p, acl_type_t _type); acl_t acl_init(int _count); int acl_set_fd(int _fd, acl_t _acl); int acl_set_fd_np(int _fd, acl_t _acl, acl_type_t _type); int acl_set_file(const char *_path_p, acl_type_t _type, acl_t _acl); char *acl_to_text(acl_t _acl, ssize_t *_len_p); int acl_valid(acl_t _acl); int acl_valid_fd_np(int _fd, acl_type_t _type, acl_t _acl); int acl_valid_file_np(const char *_path_p, acl_type_t _type, acl_t _acl); __END_DECLS #endif /* !_KERNEL */ #endif /* !_SYS_ACL_H */ Index: head/sys/sys/capability.h =================================================================== --- head/sys/sys/capability.h (revision 74272) +++ head/sys/sys/capability.h (revision 74273) @@ -1,212 +1,213 @@ /*- - * Copyright (c) 2000 Robert N. M. Watson + * Copyright (c) 2000, 2001 Robert N. M. Watson * All rights reserved. * * Copyright (c) 1999 Ilmar S. Habibulin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Developed by the TrustedBSD Project. * Support for POSIX.1e process capabilities. */ #ifndef _SYS_CAPABILITY_H #define _SYS_CAPABILITY_H -#define POSIX1E_CAPABILITY_EXTATTR_NAME "$posix1e.cap" +#define POSIX1E_CAPABILITY_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define POSIX1E_CAPABILITY_EXTATTR_NAME "posix1e.cap" typedef int cap_flag_t; typedef int cap_flag_value_t; typedef u_int64_t cap_value_t; struct cap { u_int64_t c_effective; u_int64_t c_permitted; u_int64_t c_inheritable; }; typedef struct cap *cap_t; #define SET_CAPABILITY(mask, cap) do { \ (mask) |= cap; \ } while (0) #define UNSET_CAPABILITY(mask, cap) do { \ (mask) &= ~(cap); \ } while (0) #define IS_CAP_SET(mask, cap) \ ((mask) & (cap)) /* * Is (tcap) a logical subset of (scap)? */ #define CAP_SUBSET(scap,tcap) \ (((scap).c_permitted | (tcap).c_permitted == (scap).c_permitted) && \ ((scap).c_effective | (tcap).c_effective == (scap).c_effective) && \ ((scap).c_inheritable | (tcap).c_inheritable == (scap).c_inheritable)) /* * Possible flags for a particular capability. */ #define CAP_EFFECTIVE 0x01 #define CAP_INHERITABLE 0x02 #define CAP_PERMITTED 0x04 /* * Possible values for each capability flag. */ #define CAP_CLEAR 0 #define CAP_SET 1 /* * Possible capability values, both BSD/LINUX and POSIX.1e. */ #define CAP_CHOWN (0x0000000000000001) #define CAP_DAC_EXECUTE (0x0000000000000002) #define CAP_DAC_WRITE (0x0000000000000004) #define CAP_DAC_READ_SEARCH (0x0000000000000008) #define CAP_FOWNER (0x0000000000000010) #define CAP_FSETID (0x0000000000000020) #define CAP_KILL (0x0000000000000040) #define CAP_LINK_DIR (0x0000000000000080) #define CAP_SETFCAP (0x0000000000000100) #define CAP_SETGID (0x0000000000000200) #define CAP_SETUID (0x0000000000000400) #define CAP_MAC_DOWNGRADE (0x0000000000000800) #define CAP_MAC_READ (0x0000000000001000) #define CAP_MAC_RELABEL_SUBJ (0x0000000000002000) #define CAP_MAC_UPGRADE (0x0000000000004000) #define CAP_MAC_WRITE (0x0000000000008000) #define CAP_INF_NOFLOAT_OBJ (0x0000000000010000) #define CAP_INF_NOFLOAT_SUBJ (0x0000000000020000) #define CAP_INF_RELABEL_OBJ (0x0000000000040000) #define CAP_INF_RELABEL_SUBJ (0x0000000000080000) #define CAP_AUDIT_CONTROL (0x0000000000100000) #define CAP_AUDIT_WRITE (0x0000000000200000) /* * The following capability, borrowed from Linux, is unsafe */ #define CAP_SETPCAP (0x0000000000400000) /* This is unallocated: */ #define CAP_XXX_INVALID1 (0x0000000000800000) #define CAP_SYS_SETFFLAG (0x0000000001000000) /* * The CAP_LINUX_IMMUTABLE flag approximately maps into the * general file flag setting capability in BSD. Therfore, for * compatibility, map the constants. */ #define CAP_LINUX_IMMUTABLE CAP_SYS_SETFFLAG #define CAP_NET_BIND_SERVICE (0x0000000002000000) #define CAP_NET_BROADCAST (0x0000000004000000) #define CAP_NET_ADMIN (0x0000000008000000) #define CAP_NET_RAW (0x0000000010000000) #define CAP_IPC_LOCK (0x0000000020000000) #define CAP_IPC_OWNER (0x0000000040000000) /* * The following capabilities, borrowed from Linux, are unsafe in a * secure environment. * */ #define CAP_SYS_MODULE (0x0000000080000000) #define CAP_SYS_RAWIO (0x0000000100000000) #define CAP_SYS_CHROOT (0x0000000200000000) #define CAP_SYS_PTRACE (0x0000000400000000) #define CAP_SYS_PACCT (0x0000000800000000) #define CAP_SYS_ADMIN (0x0000001000000000) /* * Back to the safe ones, again */ #define CAP_SYS_BOOT (0x0000002000000000) #define CAP_SYS_NICE (0x0000004000000000) #define CAP_SYS_RESOURCE (0x0000008000000000) #define CAP_SYS_TIME (0x0000010000000000) #define CAP_SYS_TTY_CONFIG (0x0000020000000000) #define CAP_MKNOD (0x0000040000000000) #define CAP_MAX_ID CAP_MKNOD #define CAP_ALL_ON (CAP_CHOWN | CAP_DAC_EXECUTE | CAP_DAC_WRITE | \ CAP_DAC_READ_SEARCH | CAP_FOWNER | CAP_FSETID | CAP_KILL | CAP_LINK_DIR | \ CAP_SETFCAP | CAP_SETGID | CAP_SETUID | CAP_MAC_DOWNGRADE | \ CAP_MAC_READ | CAP_MAC_RELABEL_SUBJ | CAP_MAC_UPGRADE | \ CAP_MAC_WRITE | CAP_INF_NOFLOAT_OBJ | CAP_INF_NOFLOAT_SUBJ | \ CAP_INF_RELABEL_OBJ | CAP_INF_RELABEL_SUBJ | CAP_AUDIT_CONTROL | \ CAP_AUDIT_WRITE | CAP_SETPCAP | CAP_SYS_SETFFLAG | CAP_NET_BIND_SERVICE | \ CAP_NET_BROADCAST | CAP_NET_ADMIN | CAP_NET_RAW | CAP_IPC_LOCK | \ CAP_IPC_OWNER | CAP_SYS_MODULE | CAP_SYS_RAWIO | CAP_SYS_CHROOT | \ CAP_SYS_PTRACE | CAP_SYS_PACCT | CAP_SYS_ADMIN | CAP_SYS_BOOT | \ CAP_SYS_NICE | CAP_SYS_RESOURCE | CAP_SYS_TIME | CAP_SYS_TTY_CONFIG | \ CAP_MKNOD) #define CAP_ALL_OFF (0) #ifdef _KERNEL struct proc; struct ucred; struct vnode; int cap_check(struct ucred *, struct proc *, cap_value_t, int); int cap_change_on_inherit(struct cap *cap_p); int cap_inherit(struct vnode *vp, struct proc *p); void cap_init_proc0(struct cap *); void cap_init_proc1(struct cap *); #else /* !_KERNEL */ #define _POSIX_CAP #ifdef _BSD_SSIZE_T_ typedef _BSD_SSIZE_T_ ssize_t; #undef _BSD_SSIZE_T_ #endif int __cap_get_proc(struct cap *); int __cap_set_proc(struct cap *); int __cap_get_fd(int, struct cap *); int __cap_get_file(const char *, struct cap *); int __cap_set_fd(int, struct cap *); int __cap_set_file(const char *, struct cap *); int cap_clear(cap_t); ssize_t cap_copy_ext(void *, cap_t, ssize_t); cap_t cap_copy_int(const void *); cap_t cap_dup(cap_t); int cap_free(void *); cap_t cap_from_text(const char *); cap_t cap_get_fd(int); cap_t cap_get_file(const char *); int cap_get_flag(cap_t, cap_value_t, cap_flag_t, cap_flag_value_t *); cap_t cap_get_proc(void); cap_t cap_init(void); int cap_set_fd(int, cap_t); int cap_set_file(const char *, cap_t); int cap_set_flag(cap_t, cap_flag_t, int, cap_value_t[] , cap_flag_value_t); int cap_set_proc(cap_t); ssize_t cap_size(cap_t); char *cap_to_text(cap_t, ssize_t *); #endif /* !_KERNEL */ #endif /* !_SYS_CAPABILITY_H */ Index: head/sys/sys/extattr.h =================================================================== --- head/sys/sys/extattr.h (revision 74272) +++ head/sys/sys/extattr.h (revision 74273) @@ -1,63 +1,71 @@ /*- - * Copyright (c) 1999 Robert N. M. Watson + * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Userland/kernel interface for Extended File System Attributes * * This code from the FreeBSD POSIX.1e implementation. While the syscalls * are fully implemented, invoking the VFS vnops and VFS calls as necessary, * no file systems shipped with this version of FreeBSD implement these * calls. Extensions to UFS/FFS to support extended attributes are * available from the POSIX.1e implementation page, or possibly in a more * recent version of FreeBSD. * * The POSIX.1e implementation page may be reached at: * http://www.watson.org/fbsd-hardening/posix1e/ */ #ifndef _SYS_EXTATTR_H_ #define _SYS_EXTATTR_H_ + +#define EXTATTR_NAMESPACE_USER 0x00000001 +#define EXTATTR_NAMESPACE_USER_STRING "user" +#define EXTATTR_NAMESPACE_SYSTEM 0x00000002 +#define EXTATTR_NAMESPACE_SYSTEM_STRING "system" + #ifdef _KERNEL #define EXTATTR_MAXNAMELEN NAME_MAX #else #include struct iovec; __BEGIN_DECLS -int extattrctl(const char *path, int cmd, const char *attrname, char *arg); -int extattr_delete_file(const char *path, const char *attrname); -int extattr_get_file(const char *path, const char *attrname, - struct iovec *iovp, unsigned iovcnt); -int extattr_set_file(const char *path, const char *attrname, - struct iovec *iovp, unsigned iovcnt); +int extattrctl(const char *path, int cmd, const char *filename, + int namespace, const char *attrname); +int extattr_delete_file(const char *path, int namespace, + const char *attrname); +int extattr_get_file(const char *path, int namespace, + const char *attrname, struct iovec *iovp, unsigned iovcnt); +int extattr_set_file(const char *path, int namespace, + const char *attrname, struct iovec *iovp, unsigned iovcnt); __END_DECLS #endif /* !_KERNEL */ #endif /* !_SYS_EXTATTR_H_ */ Index: head/sys/sys/mount.h =================================================================== --- head/sys/sys/mount.h (revision 74272) +++ head/sys/sys/mount.h (revision 74273) @@ -1,494 +1,496 @@ /* * Copyright (c) 1989, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)mount.h 8.21 (Berkeley) 5/20/95 * $FreeBSD$ */ #ifndef _SYS_MOUNT_H_ #define _SYS_MOUNT_H_ #include #include #ifdef _KERNEL #include #endif typedef struct fsid { int32_t val[2]; } fsid_t; /* file system id type */ /* * File identifier. * These are unique per filesystem on a single machine. */ #define MAXFIDSZ 16 struct fid { u_short fid_len; /* length of data in bytes */ u_short fid_reserved; /* force longword alignment */ char fid_data[MAXFIDSZ]; /* data (variable length) */ }; /* * file system statistics */ #define MFSNAMELEN 16 /* length of fs type name, including null */ #ifdef __i386__ #define MNAMELEN 80 /* length of buffer for returned name */ #endif #if defined(__alpha__) || defined(__ia64__) #define MNAMELEN 72 /* length of buffer for returned name */ #endif struct statfs { long f_spare2; /* placeholder */ long f_bsize; /* fundamental file system block size */ long f_iosize; /* optimal transfer block size */ long f_blocks; /* total data blocks in file system */ long f_bfree; /* free blocks in fs */ long f_bavail; /* free blocks avail to non-superuser */ long f_files; /* total file nodes in file system */ long f_ffree; /* free file nodes in fs */ fsid_t f_fsid; /* file system id */ uid_t f_owner; /* user that mounted the filesystem */ int f_type; /* type of filesystem */ int f_flags; /* copy of mount exported flags */ long f_syncwrites; /* count of sync writes since mount */ long f_asyncwrites; /* count of async writes since mount */ char f_fstypename[MFSNAMELEN]; /* fs type name */ char f_mntonname[MNAMELEN]; /* directory on which mounted */ long f_syncreads; /* count of sync reads since mount */ long f_asyncreads; /* count of async reads since mount */ short f_spares1; /* unused spare */ char f_mntfromname[MNAMELEN];/* mounted filesystem */ short f_spares2; /* unused spare */ long f_spare[2]; /* unused spare */ }; #ifdef _KERNEL /* * Structure per mounted file system. Each mounted file system has an * array of operations and an instance record. The file systems are * put on a doubly linked list. */ LIST_HEAD(vnodelst, vnode); struct mount { TAILQ_ENTRY(mount) mnt_list; /* mount list */ struct vfsops *mnt_op; /* operations on fs */ struct vfsconf *mnt_vfc; /* configuration info */ struct vnode *mnt_vnodecovered; /* vnode we mounted on */ struct vnode *mnt_syncer; /* syncer vnode */ struct vnodelst mnt_vnodelist; /* list of vnodes this mount */ struct lock mnt_lock; /* mount structure lock */ int mnt_writeopcount; /* write syscalls in progress */ int mnt_flag; /* flags shared with user */ int mnt_kern_flag; /* kernel only flags */ int mnt_maxsymlinklen; /* max size of short symlink */ struct statfs mnt_stat; /* cache of filesystem stats */ qaddr_t mnt_data; /* private data */ time_t mnt_time; /* last time written*/ u_int mnt_iosize_max; /* max IO request size */ }; #endif /* _KERNEL */ /* * User specifiable flags. */ #define MNT_RDONLY 0x00000001 /* read only filesystem */ #define MNT_SYNCHRONOUS 0x00000002 /* file system written synchronously */ #define MNT_NOEXEC 0x00000004 /* can't exec from filesystem */ #define MNT_NOSUID 0x00000008 /* don't honor setuid bits on fs */ #define MNT_NODEV 0x00000010 /* don't interpret special files */ #define MNT_UNION 0x00000020 /* union with underlying filesystem */ #define MNT_ASYNC 0x00000040 /* file system written asynchronously */ #define MNT_SUIDDIR 0x00100000 /* special handling of SUID on dirs */ #define MNT_SOFTDEP 0x00200000 /* soft updates being done */ #define MNT_NOSYMFOLLOW 0x00400000 /* do not follow symlinks */ #define MNT_NOATIME 0x10000000 /* disable update of file access time */ #define MNT_NOCLUSTERR 0x40000000 /* disable cluster read */ #define MNT_NOCLUSTERW 0x80000000 /* disable cluster write */ /* * NFS export related mount flags. */ #define MNT_EXRDONLY 0x00000080 /* exported read only */ #define MNT_EXPORTED 0x00000100 /* file system is exported */ #define MNT_DEFEXPORTED 0x00000200 /* exported to the world */ #define MNT_EXPORTANON 0x00000400 /* use anon uid mapping for everyone */ #define MNT_EXKERB 0x00000800 /* exported with Kerberos uid mapping */ #define MNT_EXPUBLIC 0x20000000 /* public export (WebNFS) */ /* * Flags set by internal operations, * but visible to the user. * XXX some of these are not quite right.. (I've never seen the root flag set) */ #define MNT_LOCAL 0x00001000 /* filesystem is stored locally */ #define MNT_QUOTA 0x00002000 /* quotas are enabled on filesystem */ #define MNT_ROOTFS 0x00004000 /* identifies the root filesystem */ #define MNT_USER 0x00008000 /* mounted by a user */ #define MNT_IGNORE 0x00800000 /* do not show entry in df */ /* * Mask of flags that are visible to statfs() * XXX I think that this could now become (~(MNT_CMDFLAGS)) * but the 'mount' program may need changing to handle this. */ #define MNT_VISFLAGMASK (MNT_RDONLY | MNT_SYNCHRONOUS | MNT_NOEXEC | \ MNT_NOSUID | MNT_NODEV | MNT_UNION | \ MNT_ASYNC | MNT_EXRDONLY | MNT_EXPORTED | \ MNT_DEFEXPORTED | MNT_EXPORTANON| MNT_EXKERB | \ MNT_LOCAL | MNT_USER | MNT_QUOTA | \ MNT_ROOTFS | MNT_NOATIME | MNT_NOCLUSTERR| \ MNT_NOCLUSTERW | MNT_SUIDDIR | MNT_SOFTDEP | \ MNT_IGNORE | MNT_EXPUBLIC | MNT_NOSYMFOLLOW) /* * External filesystem command modifier flags. * Unmount can use the MNT_FORCE flag. * XXX These are not STATES and really should be somewhere else. */ #define MNT_UPDATE 0x00010000 /* not a real mount, just an update */ #define MNT_DELEXPORT 0x00020000 /* delete export host lists */ #define MNT_RELOAD 0x00040000 /* reload filesystem data */ #define MNT_FORCE 0x00080000 /* force unmount or readonly change */ #define MNT_SNAPSHOT 0x01000000 /* snapshot the filesystem */ #define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \ MNT_FORCE | MNT_SNAPSHOT) /* * Still available */ #define MNT_SPARE1 0x02000000 #define MNT_SPARE2 0x04000000 #define MNT_SPARE3 0x08000000 /* * Internal filesystem control flags stored in mnt_kern_flag. * * MNTK_UNMOUNT locks the mount entry so that name lookup cannot proceed * past the mount point. This keeps the subtree stable during mounts * and unmounts. */ #define MNTK_UNMOUNT 0x01000000 /* unmount in progress */ #define MNTK_MWAIT 0x02000000 /* waiting for unmount to finish */ #define MNTK_WANTRDWR 0x04000000 /* upgrade to read/write requested */ #define MNTK_SUSPEND 0x08000000 /* request write suspension */ #define MNTK_SUSPENDED 0x10000000 /* write operations are suspended */ /* * Sysctl CTL_VFS definitions. * * Second level identifier specifies which filesystem. Second level * identifier VFS_VFSCONF returns information about all filesystems. * Second level identifier VFS_GENERIC is non-terminal. */ #define VFS_VFSCONF 0 /* get configured filesystems */ #define VFS_GENERIC 0 /* generic filesystem information */ /* * Third level identifiers for VFS_GENERIC are given below; third * level identifiers for specific filesystems are given in their * mount specific header files. */ #define VFS_MAXTYPENUM 1 /* int: highest defined filesystem type */ #define VFS_CONF 2 /* struct: vfsconf for filesystem given as next argument */ /* * Flags for various system call interfaces. * * waitfor flags to vfs_sync() and getfsstat() */ #define MNT_WAIT 1 /* synchronously wait for I/O to complete */ #define MNT_NOWAIT 2 /* start all I/O, but do not wait for it */ #define MNT_LAZY 3 /* push data not written by filesystem syncer */ /* * Generic file handle */ struct fhandle { fsid_t fh_fsid; /* File system id of mount point */ struct fid fh_fid; /* File sys specific id */ }; typedef struct fhandle fhandle_t; /* * Export arguments for local filesystem mount calls. */ struct export_args { int ex_flags; /* export related flags */ uid_t ex_root; /* mapping for root uid */ struct xucred ex_anon; /* mapping for anonymous user */ struct sockaddr *ex_addr; /* net address to which exported */ u_char ex_addrlen; /* and the net address length */ struct sockaddr *ex_mask; /* mask of valid bits in saddr */ u_char ex_masklen; /* and the smask length */ char *ex_indexfile; /* index file for WebNFS URLs */ }; /* * Structure holding information for a publicly exported filesystem * (WebNFS). Currently the specs allow just for one such filesystem. */ struct nfs_public { int np_valid; /* Do we hold valid information */ fhandle_t np_handle; /* Filehandle for pub fs (internal) */ struct mount *np_mount; /* Mountpoint of exported fs */ char *np_index; /* Index file */ }; /* * Filesystem configuration information. One of these exists for each * type of filesystem supported by the kernel. These are searched at * mount time to identify the requested filesystem. */ struct vfsconf { struct vfsops *vfc_vfsops; /* filesystem operations vector */ char vfc_name[MFSNAMELEN]; /* filesystem type name */ int vfc_typenum; /* historic filesystem type number */ int vfc_refcount; /* number mounted of this type */ int vfc_flags; /* permanent flags */ struct vfsconf *vfc_next; /* next in list */ }; struct ovfsconf { void *vfc_vfsops; char vfc_name[32]; int vfc_index; int vfc_refcount; int vfc_flags; }; /* * NB: these flags refer to IMPLEMENTATION properties, not properties of * any actual mounts; i.e., it does not make sense to change the flags. */ #define VFCF_STATIC 0x00010000 /* statically compiled into kernel */ #define VFCF_NETWORK 0x00020000 /* may get data over the network */ #define VFCF_READONLY 0x00040000 /* writes are not implemented */ #define VFCF_SYNTHETIC 0x00080000 /* data does not represent real files */ #define VFCF_LOOPBACK 0x00100000 /* aliases some other mounted FS */ #define VFCF_UNICODE 0x00200000 /* stores file names as Unicode*/ #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_MOUNT); #endif extern int maxvfsconf; /* highest defined filesystem type */ extern int nfs_mount_type; /* vfc_typenum for nfs, or -1 */ extern struct vfsconf *vfsconf; /* head of list of filesystem types */ /* * Operations supported on mounted file system. */ #ifdef __STDC__ struct nameidata; struct mbuf; struct mount_args; #endif struct vfsops { int (*vfs_mount) __P((struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p)); int (*vfs_start) __P((struct mount *mp, int flags, struct proc *p)); int (*vfs_unmount) __P((struct mount *mp, int mntflags, struct proc *p)); int (*vfs_root) __P((struct mount *mp, struct vnode **vpp)); int (*vfs_quotactl) __P((struct mount *mp, int cmds, uid_t uid, caddr_t arg, struct proc *p)); int (*vfs_statfs) __P((struct mount *mp, struct statfs *sbp, struct proc *p)); int (*vfs_sync) __P((struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)); int (*vfs_vget) __P((struct mount *mp, ino_t ino, struct vnode **vpp)); int (*vfs_fhtovp) __P((struct mount *mp, struct fid *fhp, struct vnode **vpp)); int (*vfs_checkexp) __P((struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp)); int (*vfs_vptofh) __P((struct vnode *vp, struct fid *fhp)); int (*vfs_init) __P((struct vfsconf *)); int (*vfs_uninit) __P((struct vfsconf *)); int (*vfs_extattrctl) __P((struct mount *mp, int cmd, - const char *attrname, caddr_t arg, + struct vnode *filename_vp, + int namespace, const char *attrname, struct proc *p)); }; #define VFS_MOUNT(MP, PATH, DATA, NDP, P) \ (*(MP)->mnt_op->vfs_mount)(MP, PATH, DATA, NDP, P) #define VFS_START(MP, FLAGS, P) (*(MP)->mnt_op->vfs_start)(MP, FLAGS, P) #define VFS_UNMOUNT(MP, FORCE, P) (*(MP)->mnt_op->vfs_unmount)(MP, FORCE, P) #define VFS_ROOT(MP, VPP) (*(MP)->mnt_op->vfs_root)(MP, VPP) #define VFS_QUOTACTL(MP,C,U,A,P) (*(MP)->mnt_op->vfs_quotactl)(MP, C, U, A, P) #define VFS_STATFS(MP, SBP, P) (*(MP)->mnt_op->vfs_statfs)(MP, SBP, P) #define VFS_SYNC(MP, WAIT, C, P) (*(MP)->mnt_op->vfs_sync)(MP, WAIT, C, P) #define VFS_VGET(MP, INO, VPP) (*(MP)->mnt_op->vfs_vget)(MP, INO, VPP) #define VFS_FHTOVP(MP, FIDP, VPP) \ (*(MP)->mnt_op->vfs_fhtovp)(MP, FIDP, VPP) #define VFS_VPTOFH(VP, FIDP) (*(VP)->v_mount->mnt_op->vfs_vptofh)(VP, FIDP) #define VFS_CHECKEXP(MP, NAM, EXFLG, CRED) \ (*(MP)->mnt_op->vfs_checkexp)(MP, NAM, EXFLG, CRED) -#define VFS_EXTATTRCTL(MP, C, N, A, P) \ - (*(MP)->mnt_op->vfs_extattrctl)(MP, C, N, A, P) +#define VFS_EXTATTRCTL(MP, C, FN, NS, N, P) \ + (*(MP)->mnt_op->vfs_extattrctl)(MP, C, FN, NS, N, P) #include #define VFS_SET(vfsops, fsname, flags) \ static struct vfsconf fsname ## _vfsconf = { \ &vfsops, \ #fsname, \ -1, \ 0, \ flags \ }; \ static moduledata_t fsname ## _mod = { \ #fsname, \ vfs_modevent, \ & fsname ## _vfsconf \ }; \ DECLARE_MODULE(fsname, fsname ## _mod, SI_SUB_VFS, SI_ORDER_MIDDLE) #include #define AF_MAX 33 /* XXX */ /* * Network address lookup element */ struct netcred { struct radix_node netc_rnodes[2]; int netc_exflags; struct ucred netc_anon; }; /* * Network export information */ struct netexport { struct netcred ne_defexported; /* Default export */ struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */ }; extern char *mountrootfsname; /* * exported vnode operations */ int dounmount __P((struct mount *, int, struct proc *)); int vfs_mount __P((struct proc *p, char *type, char *path, int flags, void *data)); int vfs_setpublicfs /* set publicly exported fs */ __P((struct mount *, struct netexport *, struct export_args *)); int vfs_lock __P((struct mount *)); /* lock a vfs */ void vfs_msync __P((struct mount *, int)); void vfs_unlock __P((struct mount *)); /* unlock a vfs */ int vfs_busy __P((struct mount *, int, struct mtx *, struct proc *)); int vfs_export /* process mount export info */ __P((struct mount *, struct netexport *, struct export_args *)); struct netcred *vfs_export_lookup /* lookup host in fs export list */ __P((struct mount *, struct netexport *, struct sockaddr *)); int vfs_allocate_syncvnode __P((struct mount *)); void vfs_getnewfsid __P((struct mount *)); dev_t vfs_getrootfsid __P((struct mount *)); struct mount *vfs_getvfs __P((fsid_t *)); /* return vfs given fsid */ int vfs_modevent __P((module_t, int, void *)); int vfs_mountedon __P((struct vnode *)); /* is a vfs mounted on vp */ int vfs_rootmountalloc __P((char *, char *, struct mount **)); void vfs_unbusy __P((struct mount *, struct proc *)); void vfs_unmountall __P((void)); int vfs_register __P((struct vfsconf *)); int vfs_unregister __P((struct vfsconf *)); extern TAILQ_HEAD(mntlist, mount) mountlist; /* mounted filesystem list */ extern struct mtx mountlist_mtx; extern struct nfs_public nfs_pub; /* * Declarations for these vfs default operations are located in * kern/vfs_default.c, they should be used instead of making "dummy" * functions or casting entries in the VFS op table to "enopnotsupp()". */ int vfs_stdmount __P((struct mount *mp, char *path, caddr_t data, struct nameidata *ndp, struct proc *p)); int vfs_stdstart __P((struct mount *mp, int flags, struct proc *p)); int vfs_stdunmount __P((struct mount *mp, int mntflags, struct proc *p)); int vfs_stdroot __P((struct mount *mp, struct vnode **vpp)); int vfs_stdquotactl __P((struct mount *mp, int cmds, uid_t uid, caddr_t arg, struct proc *p)); int vfs_stdstatfs __P((struct mount *mp, struct statfs *sbp, struct proc *p)); int vfs_stdsync __P((struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)); int vfs_stdvget __P((struct mount *mp, ino_t ino, struct vnode **vpp)); int vfs_stdfhtovp __P((struct mount *mp, struct fid *fhp, struct vnode **vpp)); int vfs_stdcheckexp __P((struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp)); int vfs_stdvptofh __P((struct vnode *vp, struct fid *fhp)); int vfs_stdinit __P((struct vfsconf *)); int vfs_stduninit __P((struct vfsconf *)); -int vfs_stdextattrctl __P((struct mount *mp, int cmd, const char *attrname, - caddr_t arg, struct proc *p)); +int vfs_stdextattrctl __P((struct mount *mp, int cmd, + struct vnode *filename_vp, int namespace, const char *attrname, + struct proc *p)); /* XXX - these should be indirect functions!!! */ int softdep_process_worklist __P((struct mount *)); int softdep_fsync __P((struct vnode *)); #else /* !_KERNEL */ #include struct stat; __BEGIN_DECLS int fhopen __P((const struct fhandle *, int)); int fhstat __P((const struct fhandle *, struct stat *)); int fhstatfs __P((const struct fhandle *, struct statfs *)); int fstatfs __P((int, struct statfs *)); int getfh __P((const char *, fhandle_t *)); int getfsstat __P((struct statfs *, long, int)); int getmntinfo __P((struct statfs **, int)); int mount __P((const char *, const char *, int, void *)); int statfs __P((const char *, struct statfs *)); int unmount __P((const char *, int)); /* C library stuff */ void endvfsent __P((void)); struct ovfsconf *getvfsbyname __P((const char *)); struct ovfsconf *getvfsbytype __P((int)); struct ovfsconf *getvfsent __P((void)); #define getvfsbyname new_getvfsbyname int new_getvfsbyname __P((const char *, struct vfsconf *)); void setvfsent __P((int)); int vfsisloadable __P((const char *)); int vfsload __P((const char *)); __END_DECLS #endif /* _KERNEL */ #endif /* !_SYS_MOUNT_H_ */ Index: head/sys/sys/vnode.h =================================================================== --- head/sys/sys/vnode.h (revision 74272) +++ head/sys/sys/vnode.h (revision 74273) @@ -1,659 +1,659 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vnode.h 8.7 (Berkeley) 2/4/94 * $FreeBSD$ */ #ifndef _SYS_VNODE_H_ #define _SYS_VNODE_H_ #include #include #include #include #include #include /* * The vnode is the focus of all file activity in UNIX. There is a * unique vnode allocated for each active file, each current directory, * each mounted-on file, text file, and the root. */ /* * Vnode types. VNON means no type. */ enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD }; /* * Vnode tag types. * These are for the benefit of external programs only (e.g., pstat) * and should NEVER be inspected by the kernel. */ enum vtagtype { VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC, VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS, VT_UNION, VT_MSDOSFS, VT_DEVFS, VT_TFS, VT_VFS, VT_CODA, VT_NTFS, VT_HPFS, VT_NWFS }; /* * Each underlying filesystem allocates its own private area and hangs * it from v_data. If non-null, this area is freed in getnewvnode(). */ TAILQ_HEAD(buflists, buf); typedef int vop_t __P((void *)); struct namecache; /* * Reading or writing any of these items requires holding the appropriate lock. * v_freelist is locked by the global vnode_free_list mutex. * v_mntvnodes is locked by the global mntvnodes mutex. * v_flag, v_usecount, v_holdcount and v_writecount are * locked by the v_interlock mutex. * v_pollinfo is locked by the lock contained inside it. */ struct vnode { u_long v_flag; /* vnode flags (see below) */ int v_usecount; /* reference count of users */ int v_writecount; /* reference count of writers */ int v_holdcnt; /* page & buffer references */ u_long v_id; /* capability identifier */ struct mount *v_mount; /* ptr to vfs we are in */ vop_t **v_op; /* vnode operations vector */ TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ LIST_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ struct buflists v_cleanblkhd; /* clean blocklist head */ struct buflists v_dirtyblkhd; /* dirty blocklist head */ LIST_ENTRY(vnode) v_synclist; /* vnodes with dirty buffers */ long v_numoutput; /* num of writes in progress */ enum vtype v_type; /* vnode type */ union { struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */ struct socket *vu_socket; /* unix ipc (VSOCK) */ struct { struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */ SLIST_ENTRY(vnode) vu_specnext; } vu_spec; struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */ } v_un; struct nqlease *v_lease; /* Soft reference to lease */ daddr_t v_lastw; /* last write (write cluster) */ daddr_t v_cstart; /* start block of cluster */ daddr_t v_lasta; /* last allocation */ int v_clen; /* length of current cluster */ struct vm_object *v_object; /* Place to store VM object */ struct mtx v_interlock; /* lock on usecount and flag */ struct lock v_lock; /* used if fs don't have one */ struct lock *v_vnlock; /* pointer to vnode lock */ enum vtagtype v_tag; /* type of underlying data */ void *v_data; /* private data for fs */ LIST_HEAD(, namecache) v_cache_src; /* Cache entries from us */ TAILQ_HEAD(, namecache) v_cache_dst; /* Cache entries to us */ struct vnode *v_dd; /* .. vnode */ u_long v_ddid; /* .. capability identifier */ struct { struct mtx vpi_lock; /* lock to protect below */ struct selinfo vpi_selinfo; /* identity of poller(s) */ short vpi_events; /* what they are looking for */ short vpi_revents; /* what has happened */ } v_pollinfo; struct proc *v_vxproc; /* proc owning VXLOCK */ #ifdef DEBUG_LOCKS const char *filename; /* Source file doing locking */ int line; /* Line number doing locking */ #endif }; #define v_mountedhere v_un.vu_mountedhere #define v_socket v_un.vu_socket #define v_rdev v_un.vu_spec.vu_specinfo #define v_specnext v_un.vu_spec.vu_specnext #define v_fifoinfo v_un.vu_fifoinfo #define VN_POLLEVENT(vp, events) \ do { \ if ((vp)->v_pollinfo.vpi_events & (events)) \ vn_pollevent((vp), (events)); \ } while (0) /* * Vnode flags. */ #define VROOT 0x00001 /* root of its file system */ #define VTEXT 0x00002 /* vnode is a pure text prototype */ #define VSYSTEM 0x00004 /* vnode being used by kernel */ #define VISTTY 0x00008 /* vnode represents a tty */ #define VXLOCK 0x00100 /* vnode is locked to change underlying type */ #define VXWANT 0x00200 /* process is waiting for vnode */ #define VBWAIT 0x00400 /* waiting for output to complete */ /* open for business 0x00800 */ /* open for business 0x01000 */ #define VOBJBUF 0x02000 /* Allocate buffers in VM object */ #define VCOPYONWRITE 0x04000 /* vnode is doing copy-on-write */ #define VAGE 0x08000 /* Insert vnode at head of free list */ #define VOLOCK 0x10000 /* vnode is locked waiting for an object */ #define VOWANT 0x20000 /* a process is waiting for VOLOCK */ #define VDOOMED 0x40000 /* This vnode is being recycled */ #define VFREE 0x80000 /* This vnode is on the freelist */ /* open for business 0x100000 */ #define VONWORKLST 0x200000 /* On syncer work-list */ #define VMOUNT 0x400000 /* Mount in progress */ /* * Vnode attributes. A field value of VNOVAL represents a field whose value * is unavailable (getattr) or which is not to be changed (setattr). */ struct vattr { enum vtype va_type; /* vnode type (for create) */ u_short va_mode; /* files access mode and type */ short va_nlink; /* number of references to file */ uid_t va_uid; /* owner user id */ gid_t va_gid; /* owner group id */ udev_t va_fsid; /* file system id */ long va_fileid; /* file id */ u_quad_t va_size; /* file size in bytes */ long va_blocksize; /* blocksize preferred for i/o */ struct timespec va_atime; /* time of last access */ struct timespec va_mtime; /* time of last modification */ struct timespec va_ctime; /* time file changed */ u_long va_gen; /* generation number of file */ u_long va_flags; /* flags defined for file */ udev_t va_rdev; /* device the special file represents */ u_quad_t va_bytes; /* bytes of disk space held by file */ u_quad_t va_filerev; /* file modification number */ u_int va_vaflags; /* operations flags, see below */ long va_spare; /* remain quad aligned */ }; /* * Flags for va_vaflags. */ #define VA_UTIMES_NULL 0x01 /* utimes argument was NULL */ #define VA_EXCLUSIVE 0x02 /* exclusive create request */ /* * Flags for ioflag. (high 16 bits used to ask for read-ahead and * help with write clustering) */ #define IO_UNIT 0x01 /* do I/O as atomic unit */ #define IO_APPEND 0x02 /* append write to end */ #define IO_SYNC 0x04 /* do I/O synchronously */ #define IO_NODELOCKED 0x08 /* underlying node already locked */ #define IO_NDELAY 0x10 /* FNDELAY flag set in file table */ #define IO_VMIO 0x20 /* data already in VMIO space */ #define IO_INVAL 0x40 /* invalidate after I/O */ #define IO_ASYNC 0x80 /* bawrite rather then bdwrite */ /* * Modes. Some values same as Ixxx entries from inode.h for now. */ #define VADMIN 010000 /* permission to administer vnode */ #define VSUID 004000 /* set user id on execution */ #define VSGID 002000 /* set group id on execution */ #define VSVTX 001000 /* save swapped text even after use */ #define VREAD 000400 /* read, write, execute permissions */ #define VWRITE 000200 #define VEXEC 000100 /* * Token indicating no attribute value yet assigned. */ #define VNOVAL (-1) #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_VNODE); #endif /* * Convert between vnode types and inode formats (since POSIX.1 * defines mode word of stat structure in terms of inode formats). */ extern enum vtype iftovt_tab[]; extern int vttoif_tab[]; #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12]) #define VTTOIF(indx) (vttoif_tab[(int)(indx)]) #define MAKEIMODE(indx, mode) (int)(VTTOIF(indx) | (mode)) /* * Flags to various vnode functions. */ #define SKIPSYSTEM 0x0001 /* vflush: skip vnodes marked VSYSTEM */ #define FORCECLOSE 0x0002 /* vflush: force file closure */ #define WRITECLOSE 0x0004 /* vflush: only close writable files */ #define DOCLOSE 0x0008 /* vclean: close active files */ #define V_SAVE 0x0001 /* vinvalbuf: sync file first */ #define REVOKEALL 0x0001 /* vop_revoke: revoke all aliases */ #define V_WAIT 0x0001 /* vn_start_write: sleep for suspend */ #define V_NOWAIT 0x0002 /* vn_start_write: don't sleep for suspend */ #define V_XSLEEP 0x0004 /* vn_start_write: just return after sleep */ #define VREF(vp) vref(vp) #ifdef DIAGNOSTIC #define VATTR_NULL(vap) vattr_null(vap) #else #define VATTR_NULL(vap) (*(vap) = va_null) /* initialize a vattr */ #endif /* DIAGNOSTIC */ #define NULLVP ((struct vnode *)NULL) #define VNODEOP_SET(f) \ C_SYSINIT(f##init, SI_SUB_VFS, SI_ORDER_SECOND, vfs_add_vnodeops, &f); \ C_SYSUNINIT(f##uninit, SI_SUB_VFS, SI_ORDER_SECOND, vfs_rm_vnodeops, &f); /* * Global vnode data. */ extern struct vnode *rootvnode; /* root (i.e. "/") vnode */ extern int desiredvnodes; /* number of vnodes desired */ extern time_t syncdelay; /* max time to delay syncing data */ extern time_t filedelay; /* time to delay syncing files */ extern time_t dirdelay; /* time to delay syncing directories */ extern time_t metadelay; /* time to delay syncing metadata */ extern struct vm_zone *namei_zone; extern int prtactive; /* nonzero to call vprint() */ extern struct vattr va_null; /* predefined null vattr structure */ extern int vfs_ioopt; /* * Macro/function to check for client cache inconsistency w.r.t. leasing. */ #define LEASE_READ 0x1 /* Check lease for readers */ #define LEASE_WRITE 0x2 /* Check lease for modifiers */ extern void (*lease_updatetime) __P((int deltat)); #define VSHOULDFREE(vp) \ (!((vp)->v_flag & (VFREE|VDOOMED)) && \ !(vp)->v_holdcnt && !(vp)->v_usecount && \ (!(vp)->v_object || \ !((vp)->v_object->ref_count || (vp)->v_object->resident_page_count))) #define VSHOULDBUSY(vp) \ (((vp)->v_flag & VFREE) && \ ((vp)->v_holdcnt || (vp)->v_usecount)) #define VI_LOCK(vp) mtx_lock(&(vp)->v_interlock) #define VI_TRYLOCK(vp) mtx_trylock(&(vp)->v_interlock) #define VI_UNLOCK(vp) mtx_unlock(&(vp)->v_interlock) #endif /* _KERNEL */ /* * Mods for extensibility. */ /* * Flags for vdesc_flags: */ #define VDESC_MAX_VPS 16 /* Low order 16 flag bits are reserved for willrele flags for vp arguments. */ #define VDESC_VP0_WILLRELE 0x0001 #define VDESC_VP1_WILLRELE 0x0002 #define VDESC_VP2_WILLRELE 0x0004 #define VDESC_VP3_WILLRELE 0x0008 #define VDESC_NOMAP_VPP 0x0100 #define VDESC_VPP_WILLRELE 0x0200 /* * VDESC_NO_OFFSET is used to identify the end of the offset list * and in places where no such field exists. */ #define VDESC_NO_OFFSET -1 /* * This structure describes the vnode operation taking place. */ struct vnodeop_desc { int vdesc_offset; /* offset in vector--first for speed */ char *vdesc_name; /* a readable name for debugging */ int vdesc_flags; /* VDESC_* flags */ /* * These ops are used by bypass routines to map and locate arguments. * Creds and procs are not needed in bypass routines, but sometimes * they are useful to (for example) transport layers. * Nameidata is useful because it has a cred in it. */ int *vdesc_vp_offsets; /* list ended by VDESC_NO_OFFSET */ int vdesc_vpp_offset; /* return vpp location */ int vdesc_cred_offset; /* cred location, if any */ int vdesc_proc_offset; /* proc location, if any */ int vdesc_componentname_offset; /* if any */ /* * Finally, we've got a list of private data (about each operation) * for each transport layer. (Support to manage this list is not * yet part of BSD.) */ caddr_t *vdesc_transports; }; #ifdef _KERNEL /* * A list of all the operation descs. */ extern struct vnodeop_desc *vnodeop_descs[]; /* * Interlock for scanning list of vnodes attached to a mountpoint */ extern struct mtx mntvnode_mtx; /* * This macro is very helpful in defining those offsets in the vdesc struct. * * This is stolen from X11R4. I ignored all the fancy stuff for * Crays, so if you decide to port this to such a serious machine, * you might want to consult Intrinsic.h's XtOffset{,Of,To}. */ #define VOPARG_OFFSET(p_type,field) \ ((int) (((char *) (&(((p_type)NULL)->field))) - ((char *) NULL))) #define VOPARG_OFFSETOF(s_type,field) \ VOPARG_OFFSET(s_type*,field) #define VOPARG_OFFSETTO(S_TYPE,S_OFFSET,STRUCT_P) \ ((S_TYPE)(((char*)(STRUCT_P))+(S_OFFSET))) /* * This structure is used to configure the new vnodeops vector. */ struct vnodeopv_entry_desc { struct vnodeop_desc *opve_op; /* which operation this is */ vop_t *opve_impl; /* code implementing this operation */ }; struct vnodeopv_desc { /* ptr to the ptr to the vector where op should go */ vop_t ***opv_desc_vector_p; struct vnodeopv_entry_desc *opv_desc_ops; /* null terminated list */ }; /* * A generic structure. * This can be used by bypass routines to identify generic arguments. */ struct vop_generic_args { struct vnodeop_desc *a_desc; /* other random data follows, presumably */ }; #ifdef DEBUG_VFS_LOCKS /* * Macros to aid in tracing VFS locking problems. Not totally * reliable since if the process sleeps between changing the lock * state and checking it with the assert, some other process could * change the state. They are good enough for debugging a single * filesystem using a single-threaded test. I find that 'cvs co src' * is a pretty good test. */ /* * [dfr] Kludge until I get around to fixing all the vfs locking. */ #define IS_LOCKING_VFS(vp) ((vp)->v_tag == VT_UFS \ || (vp)->v_tag == VT_MFS \ || (vp)->v_tag == VT_NFS \ || (vp)->v_tag == VT_LFS \ || (vp)->v_tag == VT_ISOFS \ || (vp)->v_tag == VT_MSDOSFS \ || (vp)->v_tag == VT_DEVFS) #define ASSERT_VOP_LOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && !VOP_ISLOCKED(_vp, NULL)) \ panic("%s: %p is not locked but should be", str, _vp); \ } while (0) #define ASSERT_VOP_UNLOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ int lockstate; \ \ if (_vp && IS_LOCKING_VFS(_vp)) { \ lockstate = VOP_ISLOCKED(_vp, curproc); \ if (lockstate == LK_EXCLUSIVE) \ panic("%s: %p is locked but should not be", \ str, _vp); \ } \ } while (0) #define ASSERT_VOP_ELOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, curproc) != LK_EXCLUSIVE) \ panic("%s: %p is not exclusive locked but should be", \ str, _vp); \ } while (0) #define ASSERT_VOP_ELOCKED_OTHER(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, curproc) != LK_EXCLOTHER) \ panic("%s: %p is not exclusive locked by another proc", \ str, _vp); \ } while (0) #define ASSERT_VOP_SLOCKED(vp, str) \ do { \ struct vnode *_vp = (vp); \ \ if (_vp && IS_LOCKING_VFS(_vp) && \ VOP_ISLOCKED(_vp, NULL) != LK_SHARED) \ panic("%s: %p is not locked shared but should be", \ str, _vp); \ } while (0) #else #define ASSERT_VOP_LOCKED(vp, str) #define ASSERT_VOP_UNLOCKED(vp, str) #endif /* * VOCALL calls an op given an ops vector. We break it out because BSD's * vclean changes the ops vector and then wants to call ops with the old * vector. */ #define VOCALL(OPSV,OFF,AP) (( *((OPSV)[(OFF)])) (AP)) /* * This call works for vnodes in the kernel. */ #define VCALL(VP,OFF,AP) VOCALL((VP)->v_op,(OFF),(AP)) #define VDESC(OP) (& __CONCAT(OP,_desc)) #define VOFFSET(OP) (VDESC(OP)->vdesc_offset) /* * VMIO support inline */ extern int vmiodirenable; static __inline int vn_canvmio(struct vnode *vp) { if (vp && (vp->v_type == VREG || (vmiodirenable && vp->v_type == VDIR))) return(TRUE); return(FALSE); } /* * Finally, include the default set of vnode operations. */ #include "vnode_if.h" /* * Public vnode manipulation functions. */ struct componentname; struct file; struct mount; struct nameidata; struct ostat; struct proc; struct stat; struct nstat; struct ucred; struct uio; struct vattr; struct vnode; struct vop_bwrite_args; extern int (*lease_check_hook) __P((struct vop_lease_args *)); struct vnode *addaliasu __P((struct vnode *vp, udev_t nvp_rdev)); int bdevvp __P((dev_t dev, struct vnode **vpp)); /* cache_* may belong in namei.h. */ void cache_enter __P((struct vnode *dvp, struct vnode *vp, struct componentname *cnp)); int cache_lookup __P((struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)); void cache_purge __P((struct vnode *vp)); void cache_purgevfs __P((struct mount *mp)); void cvtstat __P((struct stat *st, struct ostat *ost)); void cvtnstat __P((struct stat *sb, struct nstat *nsb)); int getnewvnode __P((enum vtagtype tag, struct mount *mp, vop_t **vops, struct vnode **vpp)); int lease_check __P((struct vop_lease_args *ap)); int spec_vnoperate __P((struct vop_generic_args *)); int speedup_syncer __P((void)); int textvp_fullpath __P((struct proc *p, char **retbuf, char **retfreebuf)); int vaccess __P((enum vtype type, mode_t file_mode, uid_t uid, gid_t gid, mode_t acc_mode, struct ucred *cred, int *privused)); int vaccess_acl_posix1e __P((enum vtype type, struct acl *acl, mode_t acc_mode, struct ucred *cred, int *privused)); void vattr_null __P((struct vattr *vap)); int vcount __P((struct vnode *vp)); void vdrop __P((struct vnode *)); int vfinddev __P((dev_t dev, enum vtype type, struct vnode **vpp)); void vfs_add_vnodeops __P((const void *)); void vfs_rm_vnodeops __P((const void *)); int vflush __P((struct mount *mp, struct vnode *skipvp, int flags)); int vget __P((struct vnode *vp, int lockflag, struct proc *p)); void vgone __P((struct vnode *vp)); void vgonel __P((struct vnode *vp, struct proc *p)); void vhold __P((struct vnode *)); int vinvalbuf __P((struct vnode *vp, int save, struct ucred *cred, struct proc *p, int slpflag, int slptimeo)); int vtruncbuf __P((struct vnode *vp, struct ucred *cred, struct proc *p, off_t length, int blksize)); void vprint __P((char *label, struct vnode *vp)); int vrecycle __P((struct vnode *vp, struct mtx *inter_lkp, struct proc *p)); int vn_close __P((struct vnode *vp, int flags, struct ucred *cred, struct proc *p)); void vn_finished_write __P((struct mount *mp)); int vn_isdisk __P((struct vnode *vp, int *errp)); int vn_lock __P((struct vnode *vp, int flags, struct proc *p)); #ifdef DEBUG_LOCKS int debug_vn_lock __P((struct vnode *vp, int flags, struct proc *p, const char *filename, int line)); #define vn_lock(vp,flags,p) debug_vn_lock(vp,flags,p,__FILE__,__LINE__) #endif int vn_open __P((struct nameidata *ndp, int *flagp, int cmode)); void vn_pollevent __P((struct vnode *vp, int events)); void vn_pollgone __P((struct vnode *vp)); int vn_pollrecord __P((struct vnode *vp, struct proc *p, int events)); int vn_rdwr __P((enum uio_rw rw, struct vnode *vp, caddr_t base, int len, off_t offset, enum uio_seg segflg, int ioflg, struct ucred *cred, int *aresid, struct proc *p)); int vn_stat __P((struct vnode *vp, struct stat *sb, struct proc *p)); int vn_start_write __P((struct vnode *vp, struct mount **mpp, int flags)); dev_t vn_todev __P((struct vnode *vp)); int vn_write_suspend_wait __P((struct vnode *vp, struct mount *mp, int flags)); int vn_writechk __P((struct vnode *vp)); -int vn_extattr_get __P((struct vnode *vp, int ioflg, const char *attrname, - int *buflen, char *buf, struct proc *p)); -int vn_extattr_set __P((struct vnode *vp, int ioflg, const char *attrname, - int buflen, char *buf, struct proc *p)); -int vn_extattr_rm(struct vnode *vp, int ioflg, const char *attrname, - struct proc *p); +int vn_extattr_get __P((struct vnode *vp, int ioflg, int namespace, + const char *attrname, int *buflen, char *buf, struct proc *p)); +int vn_extattr_set __P((struct vnode *vp, int ioflg, int namespace, + const char *attrname, int buflen, char *buf, struct proc *p)); +int vn_extattr_rm(struct vnode *vp, int ioflg, int namespace, + const char *attrname, struct proc *p); int vfs_cache_lookup __P((struct vop_lookup_args *ap)); int vfs_object_create __P((struct vnode *vp, struct proc *p, struct ucred *cred)); void vfs_timestamp __P((struct timespec *)); void vfs_write_resume __P((struct mount *mp)); void vfs_write_suspend __P((struct mount *mp)); int vop_stdbwrite __P((struct vop_bwrite_args *ap)); int vop_stdgetwritemount __P((struct vop_getwritemount_args *)); int vop_stdinactive __P((struct vop_inactive_args *)); int vop_stdislocked __P((struct vop_islocked_args *)); int vop_stdlock __P((struct vop_lock_args *)); int vop_stdunlock __P((struct vop_unlock_args *)); int vop_noislocked __P((struct vop_islocked_args *)); int vop_nolock __P((struct vop_lock_args *)); int vop_nopoll __P((struct vop_poll_args *)); int vop_nounlock __P((struct vop_unlock_args *)); int vop_stdpathconf __P((struct vop_pathconf_args *)); int vop_stdpoll __P((struct vop_poll_args *)); int vop_revoke __P((struct vop_revoke_args *)); int vop_sharedlock __P((struct vop_lock_args *)); int vop_eopnotsupp __P((struct vop_generic_args *ap)); int vop_ebadf __P((struct vop_generic_args *ap)); int vop_einval __P((struct vop_generic_args *ap)); int vop_enotty __P((struct vop_generic_args *ap)); int vop_defaultop __P((struct vop_generic_args *ap)); int vop_null __P((struct vop_generic_args *ap)); int vop_panic __P((struct vop_generic_args *ap)); int vop_stdcreatevobject __P((struct vop_createvobject_args *ap)); int vop_stddestroyvobject __P((struct vop_destroyvobject_args *ap)); int vop_stdgetvobject __P((struct vop_getvobject_args *ap)); void vfree __P((struct vnode *)); void vput __P((struct vnode *vp)); void vrele __P((struct vnode *vp)); void vref __P((struct vnode *vp)); void vbusy __P((struct vnode *vp)); extern vop_t **default_vnodeop_p; extern vop_t **spec_vnodeop_p; extern vop_t **dead_vnodeop_p; #endif /* _KERNEL */ #endif /* !_SYS_VNODE_H_ */ Index: head/sys/ufs/ufs/extattr.h =================================================================== --- head/sys/ufs/ufs/extattr.h (revision 74272) +++ head/sys/ufs/ufs/extattr.h (revision 74273) @@ -1,104 +1,105 @@ /*- - * Copyright (c) 1999, 2000 Robert N. M. Watson + * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * TrustedBSD Project - extended attribute support for UFS-like file systems */ #ifndef _UFS_UFS_EXTATTR_H_ #define _UFS_UFS_EXTATTR_H_ #define UFS_EXTATTR_MAGIC 0x00b5d5ec #define UFS_EXTATTR_VERSION 0x00000003 #define UFS_EXTATTR_FSROOTSUBDIR ".attribute" #define UFS_EXTATTR_MAXEXTATTRNAME 65 /* including null */ #define UFS_EXTATTR_ATTR_FLAG_INUSE 0x00000001 /* attr has been set */ #define UFS_EXTATTR_PERM_KERNEL 0x00000000 #define UFS_EXTATTR_PERM_ROOT 0x00000001 #define UFS_EXTATTR_PERM_OWNER 0x00000002 #define UFS_EXTATTR_PERM_ANYONE 0x00000003 #define UFS_EXTATTR_UEPM_INITIALIZED 0x00000001 #define UFS_EXTATTR_UEPM_STARTED 0x00000002 #define UFS_EXTATTR_CMD_START 0x00000001 #define UFS_EXTATTR_CMD_STOP 0x00000002 #define UFS_EXTATTR_CMD_ENABLE 0x00000003 #define UFS_EXTATTR_CMD_DISABLE 0x00000004 struct ufs_extattr_fileheader { u_int uef_magic; /* magic number for sanity checking */ u_int uef_version; /* version of attribute file */ u_int uef_size; /* size of attributes, w/o header */ }; struct ufs_extattr_header { u_int ueh_flags; /* flags for attribute */ u_int ueh_len; /* local defined length; <= uef_size */ u_int32_t ueh_i_gen; /* generation number for sanity */ /* data follows the header */ }; #ifdef _KERNEL #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_EXTATTR); #endif struct vnode; LIST_HEAD(ufs_extattr_list_head, ufs_extattr_list_entry); struct ufs_extattr_list_entry { - LIST_ENTRY(ufs_extattr_list_entry) uele_entries; - struct ufs_extattr_fileheader uele_fileheader; + LIST_ENTRY(ufs_extattr_list_entry) uele_entries; + struct ufs_extattr_fileheader uele_fileheader; + int uele_namespace; char uele_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; struct vnode *uele_backing_vnode; }; struct lock; struct ucred; struct ufs_extattr_per_mount { struct lock uepm_lock; struct ufs_extattr_list_head uepm_list; struct ucred *uepm_ucred; int uepm_flags; }; void ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm); void ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm); int ufs_extattr_start(struct mount *mp, struct proc *p); int ufs_extattr_autostart(struct mount *mp, struct proc *p); int ufs_extattr_stop(struct mount *mp, struct proc *p); -int ufs_extattrctl(struct mount *mp, int cmd, const char *attrname, - caddr_t arg, struct proc *p); +int ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename, + int namespace, const char *attrname, struct proc *p); int ufs_vop_getextattr(struct vop_getextattr_args *ap); int ufs_vop_setextattr(struct vop_setextattr_args *ap); void ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p); #endif /* !_KERNEL */ #endif /* !_UFS_UFS_EXTATTR_H_ */ Index: head/sys/ufs/ufs/ufs_extattr.c =================================================================== --- head/sys/ufs/ufs/ufs_extattr.c (revision 74272) +++ head/sys/ufs/ufs/ufs_extattr.c (revision 74273) @@ -1,1197 +1,1239 @@ /*- * Copyright (c) 1999, 2000, 2001 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * TrustedBSD Project - extended attribute support for UFS-like file systems */ #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include "opt_ffs.h" +#ifdef FFS_EXTATTR + #define MIN(a,b) (((a)<(b))?(a):(b)) static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); static int ufs_extattr_valid_attrname(const char *attrname); static int ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele, struct ucred *cred, struct proc *p, int access); -static int ufs_extattr_enable(struct ufsmount *ump, const char *attrname, - struct vnode *backing_vnode, struct proc *p); -static int ufs_extattr_disable(struct ufsmount *ump, const char *attrname, - struct proc *p); -static int ufs_extattr_get(struct vnode *vp, const char *name, - struct uio *uio, struct ucred *cred, struct proc *p); -static int ufs_extattr_set(struct vnode *vp, const char *name, - struct uio *uio, struct ucred *cred, struct proc *p); -static int ufs_extattr_rm(struct vnode *vp, const char *name, - struct ucred *cred, struct proc *p); +static int ufs_extattr_enable_with_open(struct ufsmount *ump, + struct vnode *vp, int namespace, const char *attrname, struct proc *p); +static int ufs_extattr_enable(struct ufsmount *ump, int namespace, + const char *attrname, struct vnode *backing_vnode, struct proc *p); +static int ufs_extattr_disable(struct ufsmount *ump, int namespace, + const char *attrname, struct proc *p); +static int ufs_extattr_get(struct vnode *vp, int namespace, + const char *name, struct uio *uio, struct ucred *cred, struct proc *p); +static int ufs_extattr_set(struct vnode *vp, int namespace, + const char *name, struct uio *uio, struct ucred *cred, struct proc *p); +static int ufs_extattr_rm(struct vnode *vp, int namespace, + const char *name, struct ucred *cred, struct proc *p); /* * Per-FS attribute lock protecting attribute operations. * XXX Right now there is a lot of lock contention due to having a single * lock per-FS; really, this should be far more fine-grained. */ static void ufs_extattr_uepm_lock(struct ufsmount *ump, struct proc *p) { /* Ideally, LK_CANRECURSE would not be used, here. */ lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE, 0, p); } static void ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p) { lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, p); } /* * Determine whether the name passed is a valid name for an actual * attribute. * * Invalid currently consists of: * NULL pointer for attrname * zero-length attrname (used to retrieve application attribute list) * attrname consisting of "$" (used to treive system attribute list) */ static int ufs_extattr_valid_attrname(const char *attrname) { if (attrname == NULL) return (0); if (strlen(attrname) == 0) return (0); if (strlen(attrname) == 1 && attrname[0] == '$') return (0); return (1); } /* * Locate an attribute given a name and mountpoint. * Must be holding uepm lock for the mount point. */ static struct ufs_extattr_list_entry * -ufs_extattr_find_attr(struct ufsmount *ump, const char *attrname) +ufs_extattr_find_attr(struct ufsmount *ump, int namespace, + const char *attrname) { struct ufs_extattr_list_entry *search_attribute; for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list); search_attribute; search_attribute = LIST_NEXT(search_attribute, uele_entries)) { if (!(strncmp(attrname, search_attribute->uele_attrname, - UFS_EXTATTR_MAXEXTATTRNAME))) { + UFS_EXTATTR_MAXEXTATTRNAME)) && + (namespace == search_attribute->uele_namespace)) { return (search_attribute); } } return (0); } /* * Initialize per-FS structures supporting extended attributes. Do not * start extended attributes yet. */ void ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) { uepm->uepm_flags = 0; LIST_INIT(&uepm->uepm_list); /* XXX is PVFS right, here? */ lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0); uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; } /* * Destroy per-FS structures supporting extended attributes. Assumes * that EAs have already been stopped, and will panic if not. */ void ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm) { if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) panic("ufs_extattr_uepm_destroy: not initialized"); if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED)) panic("ufs_extattr_uepm_destroy: called while still started"); /* * XXX: It's not clear that either order for the next two lines is * ideal, and it should never be a problem if this is only called * during unmount, and with vfs_busy(). */ uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED; lockdestroy(&uepm->uepm_lock); } /* * Start extended attribute support on an FS. */ int ufs_extattr_start(struct mount *mp, struct proc *p) { struct ufsmount *ump; int error = 0; ump = VFSTOUFS(mp); ufs_extattr_uepm_lock(ump, p); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) { error = EOPNOTSUPP; goto unlock; } if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) { error = EBUSY; goto unlock; } ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; crhold(p->p_ucred); ump->um_extattr.uepm_ucred = p->p_ucred; unlock: ufs_extattr_uepm_unlock(ump, p); return (error); } #ifdef FFS_EXTATTR_AUTOSTART /* * Helper routine: given a locked parent directory and filename, return * the locked vnode of the inode associated with the name. Will not * follow symlinks, may return any type of vnode. Lock on parent will * be released even in the event of a failure. In the event that the * target is the parent (i.e., "."), there will be two references and * one lock, requiring the caller to possibly special-case. */ #define UE_GETDIR_LOCKPARENT 1 #define UE_GETDIR_LOCKPARENT_DONT 2 static int ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, struct vnode **vp, struct proc *p) { struct vop_cachedlookup_args vargs; struct componentname cnp; struct vnode *target_vp; int error; bzero(&cnp, sizeof(cnp)); cnp.cn_nameiop = LOOKUP; cnp.cn_flags = ISLASTCN; if (lockparent == UE_GETDIR_LOCKPARENT) cnp.cn_flags |= LOCKPARENT; cnp.cn_proc = p; cnp.cn_cred = p->p_ucred; cnp.cn_pnbuf = zalloc(namei_zone); cnp.cn_nameptr = cnp.cn_pnbuf; error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN, (size_t *) &cnp.cn_namelen); if (error) { if (lockparent == UE_GETDIR_LOCKPARENT_DONT) { VOP_UNLOCK(start_dvp, 0, p); } zfree(namei_zone, cnp.cn_pnbuf); printf("ufs_extattr_lookup: copystr failed\n"); return (error); } cnp.cn_namelen--; /* trim nul termination */ vargs.a_desc = NULL; vargs.a_dvp = start_dvp; vargs.a_vpp = &target_vp; vargs.a_cnp = &cnp; error = ufs_lookup(&vargs); zfree(namei_zone, cnp.cn_pnbuf); if (error) { /* * Error condition, may have to release the lock on the parent * if ufs_lookup() didn't. */ if (!(cnp.cn_flags & PDIRUNLOCK) && (lockparent == UE_GETDIR_LOCKPARENT_DONT)) VOP_UNLOCK(start_dvp, 0, p); /* * Check that ufs_lookup() didn't release the lock when we * didn't want it to. */ if ((cnp.cn_flags & PDIRUNLOCK) && (lockparent == UE_GETDIR_LOCKPARENT)) panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); printf("ufs_extattr_lookup: ufs_lookup failed (%d)\n", error); return (error); } /* if (target_vp == start_dvp) panic("ufs_extattr_lookup: target_vp == start_dvp"); */ if (target_vp != start_dvp && !(cnp.cn_flags & PDIRUNLOCK) && (lockparent == UE_GETDIR_LOCKPARENT_DONT)) panic("ufs_extattr_lookup: !lockparent but !PDIRUNLOCK"); if ((cnp.cn_flags & PDIRUNLOCK) && (lockparent == UE_GETDIR_LOCKPARENT)) panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); /* printf("ufs_extattr_lookup: success\n"); */ *vp = target_vp; return (0); } +#endif /* !FFS_EXTATTR_AUTOSTART */ /* * Enable an EA using the passed file system, backing vnode, attribute name, - * and proc. Will perform a VOP_OPEN() on the vp, so expects vp to be locked - * when passed in. Will unlock vp, and grab its own reference, so the caller - * needs to vrele(), just not vput(). If the call fails, the lock is not - * released. + * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp + * to be locked when passed in. Will unlock vp, and grab its own reference, + * so the caller needs to vrele(), just not vput(). The unlock the vnode + * regardless of call success or failure. */ static int ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, - char *attrname, struct proc *p) + int namespace, const char *attrname, struct proc *p) { int error; error = VOP_OPEN(vp, FREAD|FWRITE, p->p_ucred, p); if (error) { printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " "with %d\n", error); + VOP_UNLOCK(vp, 0, p); return (error); } /* * XXX: Note, should VOP_CLOSE() if vfs_object_create() fails, but due * to a similar piece of code in vn_open(), we don't. */ if (vn_canvmio(vp) == TRUE) if ((error = vfs_object_create(vp, p, p->p_ucred)) != 0) { /* * XXX: bug replicated from vn_open(): should * VOP_CLOSE() here. */ + VOP_UNLOCK(vp, 0, p); return (error); } vp->v_writecount++; vref(vp); VOP_UNLOCK(vp, 0, p); - return (ufs_extattr_enable(ump, attrname, vp, p)); + return (ufs_extattr_enable(ump, namespace, attrname, vp, p)); } +#ifdef FFS_EXTATTR_AUTOSTART /* * Given a locked directory vnode, iterate over the names in the directory * and use ufs_extattr_lookup() to retrieve locked vnodes of potential * attribute files. Then invoke ufs_extattr_enable_with_open() on each * to attempt to start the attribute. Leaves the directory locked on * exit. * XXX: Add a EA namespace argument */ static int ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, - struct proc *p) + int namespace, struct proc *p) { struct vop_readdir_args vargs; struct dirent *dp, *edp; struct vnode *attr_vp; struct uio auio; struct iovec aiov; char *dirbuf; int error, eofflag = 0; if (dvp->v_type != VDIR) return (ENOTDIR); MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_rw = UIO_READ; auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = p; auio.uio_offset = 0; vargs.a_desc = NULL; vargs.a_vp = dvp; vargs.a_uio = &auio; vargs.a_cred = p->p_ucred; vargs.a_eofflag = &eofflag; vargs.a_ncookies = NULL; vargs.a_cookies = NULL; while (!eofflag) { auio.uio_resid = DIRBLKSIZ; aiov.iov_base = dirbuf; aiov.iov_len = DIRBLKSIZ; error = ufs_readdir(&vargs); if (error) { printf("ufs_extattr_iterate_directory: ufs_readdir " "%d\n", error); return (error); } edp = (struct dirent *)&dirbuf[DIRBLKSIZ]; for (dp = (struct dirent *)dirbuf; dp < edp; ) { #if (BYTE_ORDER == LITTLE_ENDIAN) dp->d_type = dp->d_namlen; dp->d_namlen = 0; #else dp->d_type = 0; #endif if (dp->d_reclen == 0) break; error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, dp->d_name, &attr_vp, p); if (error) { printf("ufs_extattr_iterate_directory: lookup " "%s %d\n", dp->d_name, error); } else if (attr_vp == dvp) { vrele(attr_vp); } else if (attr_vp->v_type != VREG) { /* * Eventually, this will be uncommented, but in the mean time, the ".." * entry causes unnecessary console warnings. printf("ufs_extattr_iterate_directory: " "%s not VREG\n", dp->d_name); */ vput(attr_vp); } else { error = ufs_extattr_enable_with_open(ump, - attr_vp, dp->d_name, p); + attr_vp, namespace, dp->d_name, p); + vrele(attr_vp); if (error) { printf("ufs_extattr_iterate_directory: " "enable %s %d\n", dp->d_name, error); - vput(attr_vp); } else { /* * While it's nice to have some visual output here, skip for the time-being. * Probably should be enabled by -v at boot. printf("Autostarted %s\n", dp->d_name); */ - vrele(attr_vp); } } dp = (struct dirent *) ((char *)dp + dp->d_reclen); if (dp >= edp) break; } } FREE(dirbuf, M_TEMP); return (0); } /* * Auto-start of extended attributes, to be executed (optionally) at * mount-time. */ int ufs_extattr_autostart(struct mount *mp, struct proc *p) { struct vnode *attr_dvp, /**attr_vp,*/ *rvp; int error; /* - * Does ".attribute" exist off the file system root? If so, - * automatically start EA's. + * Does UFS_EXTATTR_FSROOTSUBDIR exist off the file system root? + * If so, automatically start EA's. */ error = VFS_ROOT(mp, &rvp); if (error) { printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", error); return (error); } error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, - ".attribute", &attr_dvp, p); + UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, p); if (error) { /* rvp ref'd but now unlocked */ vrele(rvp); return (error); } if (rvp == attr_dvp) { /* Should never happen. */ vrele(attr_dvp); vput(rvp); return (EINVAL); } vrele(rvp); if (attr_dvp->v_type != VDIR) { - printf("ufs_extattr_autostart: .attribute != VDIR\n"); + printf("ufs_extattr_autostart: %s != VDIR\n", + UFS_EXTATTR_FSROOTSUBDIR); goto return_vput; } error = ufs_extattr_start(mp, p); if (error) { printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", error); goto return_vput; } /* - * Iterate over the directory. Eventually we may lookup sub-directories - * and iterate over them independently. + * Iterate over the directory. Eventually we will lookup sub- + * directories and iterate over them independently with different + * EA namespaces. + * + * XXX: Right now, assert that all attributes are in the system + * namespace. */ - error = ufs_extattr_iterate_directory(VFSTOUFS(mp), attr_dvp, p); + error = ufs_extattr_iterate_directory(VFSTOUFS(mp), attr_dvp, + EXTATTR_NAMESPACE_SYSTEM, p); if (error) printf("ufs_extattr_iterate_directory returned %d\n", error); /* Mask startup failures. */ error = 0; return_vput: vput(attr_dvp); return (error); } #endif /* !FFS_EXTATTR_AUTOSTART */ /* * Stop extended attribute support on an FS. */ int ufs_extattr_stop(struct mount *mp, struct proc *p) { struct ufs_extattr_list_entry *uele; struct ufsmount *ump = VFSTOUFS(mp); int error = 0; ufs_extattr_uepm_lock(ump, p); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { error = EOPNOTSUPP; goto unlock; } while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) { uele = LIST_FIRST(&ump->um_extattr.uepm_list); - ufs_extattr_disable(ump, uele->uele_attrname, p); + ufs_extattr_disable(ump, uele->uele_namespace, + uele->uele_attrname, p); } ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; crfree(ump->um_extattr.uepm_ucred); ump->um_extattr.uepm_ucred = NULL; unlock: ufs_extattr_uepm_unlock(ump, p); return (error); } /* - * Enable a named attribute on the specified file system; provide a - * backing vnode to hold the attribute data. + * Enable a named attribute on the specified file system; provide an + * unlocked backing vnode to hold the attribute data. */ static int -ufs_extattr_enable(struct ufsmount *ump, const char *attrname, +ufs_extattr_enable(struct ufsmount *ump, int namespace, const char *attrname, struct vnode *backing_vnode, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct iovec aiov; struct uio auio; int error = 0; if (!ufs_extattr_valid_attrname(attrname)) return (EINVAL); if (backing_vnode->v_type != VREG) return (EINVAL); MALLOC(attribute, struct ufs_extattr_list_entry *, sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); if (attribute == NULL) return (ENOMEM); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { error = EOPNOTSUPP; goto free_exit; } - if (ufs_extattr_find_attr(ump, attrname)) { + if (ufs_extattr_find_attr(ump, namespace, attrname)) { error = EEXIST; goto free_exit; } strncpy(attribute->uele_attrname, attrname, UFS_EXTATTR_MAXEXTATTRNAME); + attribute->uele_namespace = namespace; bzero(&attribute->uele_fileheader, sizeof(struct ufs_extattr_fileheader)); attribute->uele_backing_vnode = backing_vnode; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; aiov.iov_base = (caddr_t) &attribute->uele_fileheader; aiov.iov_len = sizeof(struct ufs_extattr_fileheader); auio.uio_resid = sizeof(struct ufs_extattr_fileheader); auio.uio_offset = (off_t) 0; auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_procp = (struct proc *) p; VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE); vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, ump->um_extattr.uepm_ucred); VOP_UNLOCK(backing_vnode, 0, p); - if (error) { + if (error) goto free_exit; - } if (auio.uio_resid != 0) { printf("ufs_extattr_enable: malformed attribute header\n"); error = EINVAL; goto free_exit; } if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { printf("ufs_extattr_enable: invalid attribute header magic\n"); error = EINVAL; goto free_exit; } if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { printf("ufs_extattr_enable: incorrect attribute header " "version\n"); error = EINVAL; goto free_exit; } backing_vnode->v_flag |= VSYSTEM; LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries); return (0); free_exit: FREE(attribute, M_UFS_EXTATTR); return (error); } /* * Disable extended attribute support on an FS. */ static int -ufs_extattr_disable(struct ufsmount *ump, const char *attrname, struct proc *p) +ufs_extattr_disable(struct ufsmount *ump, int namespace, const char *attrname, + struct proc *p) { struct ufs_extattr_list_entry *uele; int error = 0; if (!ufs_extattr_valid_attrname(attrname)) return (EINVAL); - uele = ufs_extattr_find_attr(ump, attrname); + uele = ufs_extattr_find_attr(ump, namespace, attrname); if (!uele) return (ENOENT); LIST_REMOVE(uele, uele_entries); uele->uele_backing_vnode->v_flag &= ~VSYSTEM; error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p); FREE(uele, M_UFS_EXTATTR); return (error); } /* - * VFS call to manage extended attributes in UFS. - * attrname, arg are userspace pointers from the syscall. + * VFS call to manage extended attributes in UFS. If filename_vp is + * non-NULL, it must be passed in locked, and regardless of errors in + * processing, will be unlocked. */ int -ufs_extattrctl(struct mount *mp, int cmd, const char *attrname, - caddr_t arg, struct proc *p) +ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, + int namespace, const char *attrname, struct proc *p) { - struct nameidata nd; struct ufsmount *ump = VFSTOUFS(mp); - struct vnode *vp; - char local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* Incl. null. */ - char *filename; - int error, flags; - size_t len; + int error; /* * Processes with privilege, but in jail, are not allowed to * configure extended attributes. */ - if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0))) + if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0))) { + if (filename_vp != NULL) + VOP_UNLOCK(filename_vp, 0, p); return (error); + } switch(cmd) { case UFS_EXTATTR_CMD_START: + if (filename_vp != NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } + if (attrname != NULL) + return (EINVAL); + error = ufs_extattr_start(mp, p); return (error); case UFS_EXTATTR_CMD_STOP: - return (ufs_extattr_stop(mp, p)); + if (filename_vp != NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } + if (attrname != NULL) + return (EINVAL); + error = ufs_extattr_stop(mp, p); + + return (error); + case UFS_EXTATTR_CMD_ENABLE: - error = copyinstr(attrname, local_attrname, - UFS_EXTATTR_MAXEXTATTRNAME, &len); - if (error) - return (error); - filename = (char *) arg; - NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p); - flags = FREAD | FWRITE; - error = vn_open(&nd, &flags, 0); - if (error) - return (error); + if (filename_vp == NULL) + return (EINVAL); + if (attrname == NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } - vp = nd.ni_vp; - VOP_UNLOCK(vp, 0, p); - + /* + * ufs_extattr_enable_with_open() will always unlock the + * vnode, regardless of failure. + */ ufs_extattr_uepm_lock(ump, p); - error = ufs_extattr_enable(ump, local_attrname, vp, p); + error = ufs_extattr_enable_with_open(ump, filename_vp, + namespace, attrname, p); ufs_extattr_uepm_unlock(ump, p); return (error); case UFS_EXTATTR_CMD_DISABLE: - error = copyinstr(attrname, local_attrname, - UFS_EXTATTR_MAXEXTATTRNAME, &len); + if (filename_vp != NULL) { + VOP_UNLOCK(filename_vp, 0, p); + return (EINVAL); + } + if (attrname == NULL) + return (EINVAL); + ufs_extattr_uepm_lock(ump, p); - error = ufs_extattr_disable(ump, local_attrname, p); + error = ufs_extattr_disable(ump, namespace, attrname, p); ufs_extattr_uepm_unlock(ump, p); return (error); default: return (EINVAL); } } /* * Credential check based on process requesting service, and per-attribute * permissions. */ static int ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele, struct ucred *cred, struct proc *p, int access) { - int system_namespace; - system_namespace = (strlen(uele->uele_attrname) >= 1 && - uele->uele_attrname[0] == '$'); - /* * Kernel-invoked always succeeds. */ if (cred == NULL) return (0); /* * Do not allow privileged processes in jail to directly * manipulate system attributes. * * XXX What capability should apply here? * Probably CAP_SYS_SETFFLAG. */ - if (system_namespace) + switch (uele->uele_namespace) { + case EXTATTR_NAMESPACE_SYSTEM: return (suser_xxx(cred, p, 0)); - else + case EXTATTR_NAMESPACE_USER: return (VOP_ACCESS(vp, access, cred, p)); + default: + return (EPERM); + } } /* * Vnode operating to retrieve a named extended attribute. */ int ufs_vop_getextattr(struct vop_getextattr_args *ap) /* vop_getextattr { IN struct vnode *a_vp; + IN int a_namespace; IN const char *a_name; INOUT struct uio *a_uio; IN struct ucred *a_cred; IN struct proc *a_p; }; */ { struct mount *mp = ap->a_vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); int error; ufs_extattr_uepm_lock(ump, ap->a_p); - error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred, - ap->a_p); + error = ufs_extattr_get(ap->a_vp, ap->a_namespace, ap->a_name, + ap->a_uio, ap->a_cred, ap->a_p); ufs_extattr_uepm_unlock(ump, ap->a_p); return (error); } /* * Real work associated with retrieving a named attribute--assumes that * the attribute lock has already been grabbed. */ static int -ufs_extattr_get(struct vnode *vp, const char *name, struct uio *uio, - struct ucred *cred, struct proc *p) +ufs_extattr_get(struct vnode *vp, int namespace, const char *name, + struct uio *uio, struct ucred *cred, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; size_t size, old_size; int error = 0; if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); - if (strlen(name) == 0 || (strlen(name) == 1 && name[0] == '$')) { + if (strlen(name) == 0) { /* XXX retrieve attribute lists. */ + /* XXX should probably be checking for name == NULL? */ return (EINVAL); } - attribute = ufs_extattr_find_attr(ump, name); + attribute = ufs_extattr_find_attr(ump, namespace, name); if (!attribute) return (ENOENT); if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IREAD))) return (error); /* * Allow only offsets of zero to encourage the read/replace * extended attribute semantic. Otherwise we can't guarantee * atomicity, as we don't provide locks for extended attributes. */ if (uio->uio_offset != 0) return (ENXIO); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number. */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Read in the data header to see if the data is defined, and if so * how much. */ bzero(&ueh, sizeof(struct ufs_extattr_header)); local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_READ; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_procp = p; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); /* * Acquire locks. */ VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ); /* * Don't need to get a lock on the backing file if the getattr is * being applied to the backing file, as the lock is already held. */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); error = VOP_READ(attribute->uele_backing_vnode, &local_aio, IO_NODELOCKED, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; /* Defined? */ if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { error = ENOENT; goto vopunlock_exit; } /* Valid for the current inode generation? */ if (ueh.ueh_i_gen != ip->i_gen) { /* * The inode itself has a different generation number * than the attribute data. For now, the best solution * is to coerce this to undefined, and let it get cleaned * up by the next write or extattrctl clean. */ printf("ufs_extattr_get: inode number inconsistency (%d, %d)\n", ueh.ueh_i_gen, ip->i_gen); error = ENOENT; goto vopunlock_exit; } /* Local size consistency check. */ if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { error = ENXIO; goto vopunlock_exit; } /* Allow for offset into the attribute data. */ uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); /* * Figure out maximum to transfer -- use buffer size and local data * limit. */ size = MIN(uio->uio_resid, ueh.ueh_len); old_size = uio->uio_resid; uio->uio_resid = size; error = VOP_READ(attribute->uele_backing_vnode, uio, IO_NODELOCKED, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; uio->uio_resid = old_size - (size - uio->uio_resid); vopunlock_exit: uio->uio_offset = 0; if (attribute->uele_backing_vnode != vp) VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); return (error); } /* * Vnode operation to set a named attribute. */ int ufs_vop_setextattr(struct vop_setextattr_args *ap) /* vop_setextattr { IN struct vnode *a_vp; + IN int a_namespace; IN const char *a_name; INOUT struct uio *a_uio; IN struct ucred *a_cred; IN struct proc *a_p; }; */ { struct mount *mp = ap->a_vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); int error; ufs_extattr_uepm_lock(ump, ap->a_p); if (ap->a_uio != NULL) - error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio, - ap->a_cred, ap->a_p); + error = ufs_extattr_set(ap->a_vp, ap->a_namespace, ap->a_name, + ap->a_uio, ap->a_cred, ap->a_p); else - error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred, - ap->a_p); + error = ufs_extattr_rm(ap->a_vp, ap->a_namespace, ap->a_name, + ap->a_cred, ap->a_p); ufs_extattr_uepm_unlock(ump, ap->a_p); return (error); } /* * Real work associated with setting a vnode's extended attributes; * assumes that the attribute lock has already been grabbed. */ static int -ufs_extattr_set(struct vnode *vp, const char *name, struct uio *uio, - struct ucred *cred, struct proc *p) +ufs_extattr_set(struct vnode *vp, int namespace, const char *name, + struct uio *uio, struct ucred *cred, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; int error = 0; if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); if (!ufs_extattr_valid_attrname(name)) return (EINVAL); - attribute = ufs_extattr_find_attr(ump, name); + attribute = ufs_extattr_find_attr(ump, namespace, name); if (!attribute) return (ENOENT); if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) return (error); /* * Early rejection of invalid offsets/length. * Reject: any offset but 0 (replace) * Any size greater than attribute size limit */ if (uio->uio_offset != 0 || uio->uio_resid > attribute->uele_fileheader.uef_size) return (ENXIO); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number. */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Write out a data header for the data. */ ueh.ueh_len = uio->uio_resid; ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; ueh.ueh_i_gen = ip->i_gen; local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_WRITE; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_procp = p; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); /* * Acquire locks. */ VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); /* * Don't need to get a lock on the backing file if the setattr is * being applied to the backing file, as the lock is already held. */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; if (local_aio.uio_resid != 0) { error = ENXIO; goto vopunlock_exit; } /* * Write out user data. */ uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); error = VOP_WRITE(attribute->uele_backing_vnode, uio, IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); vopunlock_exit: uio->uio_offset = 0; if (attribute->uele_backing_vnode != vp) VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); return (error); } /* * Real work associated with removing an extended attribute from a vnode. * Assumes the attribute lock has already been grabbed. */ static int -ufs_extattr_rm(struct vnode *vp, const char *name, struct ucred *cred, - struct proc *p) +ufs_extattr_rm(struct vnode *vp, int namespace, const char *name, + struct ucred *cred, struct proc *p) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; int error = 0; if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); if (!ufs_extattr_valid_attrname(name)) return (EINVAL); - attribute = ufs_extattr_find_attr(ump, name); + attribute = ufs_extattr_find_attr(ump, namespace, name); if (!attribute) return (ENOENT); if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) return (error); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number. */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Check to see if currently defined. */ bzero(&ueh, sizeof(struct ufs_extattr_header)); local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_READ; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_procp = p; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); /* * Don't need to get the lock on the backing vnode if the vnode we're * modifying is it, as we already hold the lock. */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); error = VOP_READ(attribute->uele_backing_vnode, &local_aio, IO_NODELOCKED, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; /* Defined? */ if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { error = ENOENT; goto vopunlock_exit; } /* Valid for the current inode generation? */ if (ueh.ueh_i_gen != ip->i_gen) { /* * The inode itself has a different generation number than * the attribute data. For now, the best solution is to * coerce this to undefined, and let it get cleaned up by * the next write or extattrctl clean. */ printf("ufs_extattr_rm: inode number inconsistency (%d, %d)\n", ueh.ueh_i_gen, ip->i_gen); error = ENOENT; goto vopunlock_exit; } /* Flag it as not in use. */ ueh.ueh_flags = 0; ueh.ueh_len = 0; local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_WRITE; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_procp = p; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; if (local_aio.uio_resid != 0) error = ENXIO; vopunlock_exit: VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); return (error); } /* * Called by UFS when an inode is no longer active and should have its * attributes stripped. */ void ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) { struct ufs_extattr_list_entry *uele; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); ufs_extattr_uepm_lock(ump, p); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { ufs_extattr_uepm_unlock(ump, p); return; } LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) - ufs_extattr_rm(vp, uele->uele_attrname, NULL, p); + ufs_extattr_rm(vp, uele->uele_namespace, uele->uele_attrname, + NULL, p); ufs_extattr_uepm_unlock(ump, p); } + +#endif /* !FFS_EXTATTR */