X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..c6bf4f310a33a9262d455ea4d3f0630b1255e3fe:/bsd/miscfs/devfs/devfs_vnops.c?ds=sidebyside diff --git a/bsd/miscfs/devfs/devfs_vnops.c b/bsd/miscfs/devfs/devfs_vnops.c index 6c25d8044..b9de4b101 100644 --- a/bsd/miscfs/devfs/devfs_vnops.c +++ b/bsd/miscfs/devfs/devfs_vnops.c @@ -1,28 +1,34 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2019 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * Copyright 1997,1998 Julian Elischer. All rights reserved. * julian@freebsd.org - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: @@ -31,7 +37,7 @@ * 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 COPYRIGHT HOLDER ``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 @@ -43,7 +49,7 @@ * 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. - * + * * devfs_vnops.c */ @@ -66,10 +72,15 @@ * Dieter Siegmund (dieter@apple.com) Fri Sep 17 09:58:38 PDT 1999 * - update the mod/access times */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + */ #include #include -#include #include #include #include @@ -77,16 +88,129 @@ #include #include #include -#include +#include #include +#include #include -#include +#include #include #include #include #include +#include + +#if CONFIG_MACF +#include +#endif #include "devfsdefs.h" +#include "devfs.h" + +#if FDESC +#include "fdesc.h" +#endif /* FDESC */ + +static int devfs_update(struct vnode *vp, struct timeval *access, + struct timeval *modify); +void devfs_rele_node(devnode_t *); +static void devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags); +static boolean_t devfs_update_needed(long now_s, long last_s); +static boolean_t devfs_is_name_protected(struct vnode *dvp, const char *name); +void dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags); +void dn_times_now(devnode_t *dnp, uint32_t just_changed_flags); +void dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags); + +void +dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags) +{ + lck_mtx_assert(&devfs_attr_mutex, LCK_MTX_ASSERT_OWNED); + + if (just_changed_flags & DEVFS_UPDATE_ACCESS) { + dnp->dn_atime.tv_sec = t1->tv_sec; + dnp->dn_atime.tv_nsec = t1->tv_usec * 1000; + dnp->dn_access = 0; + } else if (dnp->dn_access) { + dnp->dn_atime.tv_sec = MIN(t1->tv_sec, dnp->dn_atime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS); + dnp->dn_atime.tv_nsec = t1->tv_usec * 1000; + dnp->dn_access = 0; + } + + if (just_changed_flags & DEVFS_UPDATE_MOD) { + dnp->dn_mtime.tv_sec = t2->tv_sec; + dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000; + dnp->dn_update = 0; + } else if (dnp->dn_update) { + dnp->dn_mtime.tv_sec = MIN(t2->tv_sec, dnp->dn_mtime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS); + dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000; + dnp->dn_update = 0; + } + + if (just_changed_flags & DEVFS_UPDATE_CHANGE) { + dnp->dn_ctime.tv_sec = t3->tv_sec; + dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000; + dnp->dn_change = 0; + } else if (dnp->dn_change) { + dnp->dn_ctime.tv_sec = MIN(t3->tv_sec, dnp->dn_ctime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS); + dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000; + dnp->dn_change = 0; + } +} + +void +dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags) +{ + if (just_changed_flags & DEVFS_UPDATE_CHANGE) { + dnp->dn_change = 1; + } + if (just_changed_flags & DEVFS_UPDATE_ACCESS) { + dnp->dn_access = 1; + } + if (just_changed_flags & DEVFS_UPDATE_MOD) { + dnp->dn_update = 1; + } +} + +/* + * Update times based on pending updates and optionally a set of new changes. + */ +void +dn_times_now(devnode_t * dnp, uint32_t just_changed_flags) +{ + struct timeval now; + + DEVFS_ATTR_LOCK_SPIN(); + microtime(&now); + dn_times_locked(dnp, &now, &now, &now, just_changed_flags); + DEVFS_ATTR_UNLOCK(); +} + +/* + * Critical devfs devices cannot be renamed or removed. + * However, links to them may be moved/unlinked. So we block + * remove/rename on a per-name basis, rather than per-node. + */ +static boolean_t +devfs_is_name_protected(struct vnode *dvp, const char *name) +{ + /* + * Only names in root are protected. E.g. /dev/null is protected, + * but /dev/foo/null isn't. + */ + if (!vnode_isvroot(dvp)) { + return FALSE; + } + + if ((strcmp("console", name) == 0) || + (strcmp("tty", name) == 0) || + (strcmp("null", name) == 0) || + (strcmp("zero", name) == 0) || + (strcmp("klog", name) == 0)) { + return TRUE; + } + + return FALSE; +} + /* * Convert a component of a pathname into a pointer to a locked node. @@ -126,14 +250,17 @@ * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked. */ static int -devfs_lookup(struct vop_lookup_args *ap) - /*struct vop_lookup_args { - struct vnode * a_dvp; directory vnode ptr - struct vnode ** a_vpp; where to put the result - struct componentname * a_cnp; the name we want - };*/ +devfs_lookup(struct vnop_lookup_args *ap) +/*struct vnop_lookup_args { + * struct vnode * a_dvp; directory vnode ptr + * struct vnode ** a_vpp; where to put the result + * struct componentname * a_cnp; the name we want + * vfs_context_t a_context; + * };*/ { struct componentname *cnp = ap->a_cnp; + vfs_context_t ctx = cnp->cn_context; + struct proc *p = vfs_context_proc(ctx); struct vnode *dir_vnode = ap->a_dvp; struct vnode **result_vnode = ap->a_vpp; devnode_t * dir_node; /* the directory we are searching */ @@ -141,68 +268,68 @@ devfs_lookup(struct vop_lookup_args *ap) devdirent_t * nodename; int flags = cnp->cn_flags; int op = cnp->cn_nameiop; /* LOOKUP, CREATE, RENAME, or DELETE */ - int lockparent = flags & LOCKPARENT; - int wantparent = flags & (LOCKPARENT|WANTPARENT); + int wantparent = flags & (LOCKPARENT | WANTPARENT); int error = 0; - struct proc *p = cnp->cn_proc; - char heldchar; /* the char at the end of the name componet */ + char heldchar; /* the char at the end of the name componet */ + +retry: *result_vnode = NULL; /* safe not sorry */ /*XXX*/ - if (dir_vnode->v_usecount == 0) - printf("devfs_lookup: dir had no refs "); + /* okay to look at directory vnodes ourside devfs lock as they are not aliased */ dir_node = VTODN(dir_vnode); /* - * Check accessiblity of directory. + * Make sure that our node is a directory as well. */ if (dir_node->dn_type != DEV_DIR) { - return (ENOTDIR); - } - - if ((error = VOP_ACCESS(dir_vnode, VEXEC, cnp->cn_cred, p)) != 0) { - return (error); + return ENOTDIR; } - /* temporarily terminate string component */ + DEVFS_LOCK(); + /* + * temporarily terminate string component + */ heldchar = cnp->cn_nameptr[cnp->cn_namelen]; cnp->cn_nameptr[cnp->cn_namelen] = '\0'; - DEVFS_LOCK(p); - nodename = dev_findname(dir_node,cnp->cn_nameptr); - if (nodename) { - /* entry exists */ - node = nodename->de_dnp; - node->dn_last_lookup = nodename; /* for unlink */ - /* Do potential vnode allocation here inside the lock - * to make sure that our device node has a non-NULL dn_vn - * associated with it. The device node might otherwise - * get deleted out from under us (see devfs_dn_free()). - */ - error = devfs_dntovn(node, result_vnode, p); - } - DEVFS_UNLOCK(p); - /* restore saved character */ + + nodename = dev_findname(dir_node, cnp->cn_nameptr); + /* + * restore saved character + */ cnp->cn_nameptr[cnp->cn_namelen] = heldchar; - if (error) - return (error); + if (nodename) { + /* entry exists */ + node = nodename->de_dnp; - if (!nodename) { /* no entry */ - /* If it doesn't exist and we're not the last component, - * or we're at the last component, but we're not creating - * or renaming, return ENOENT. + /* Do potential vnode allocation here inside the lock + * to make sure that our device node has a non-NULL dn_vn + * associated with it. The device node might otherwise + * get deleted out from under us (see devfs_dn_free()). */ - if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) { - return ENOENT; + error = devfs_dntovn(node, result_vnode, p); + } + DEVFS_UNLOCK(); + + if (error) { + if (error == EAGAIN) { + goto retry; } + return error; + } + if (!nodename) { /* - * Access for write is interpreted as allowing - * creation of files in the directory. + * we haven't called devfs_dntovn if we get here + * we have not taken a reference on the node.. no + * vnode_put is necessary on these error returns + * + * If it doesn't exist and we're not the last component, + * or we're at the last component, but we're not creating + * or renaming, return ENOENT. */ - if ((error = VOP_ACCESS(dir_vnode, VWRITE, - cnp->cn_cred, p)) != 0) - { - return (error); + if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) { + return ENOENT; } /* * We return with the directory locked, so that @@ -211,17 +338,16 @@ devfs_lookup(struct vop_lookup_args *ap) * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory vnode in namei_data->ni_dvp. - * The pathname buffer is saved so that the name - * can be obtained later. * * NB - if the directory is unlocked, then this * information cannot be used. */ - cnp->cn_flags |= SAVENAME; - if (!lockparent) - VOP_UNLOCK(dir_vnode, 0, p); - return (EJUSTRETURN); + return EJUSTRETURN; } + /* + * from this point forward, we need to vnode_put the reference + * picked up in devfs_dntovn if we decide to return an error + */ /* * If deleting, and at end of pathname, return @@ -231,38 +357,20 @@ devfs_lookup(struct vop_lookup_args *ap) * on and lock the node, being careful with ".". */ if (op == DELETE && (flags & ISLASTCN)) { - /* - * Write access to directory required to delete files. - */ - if ((error = VOP_ACCESS(dir_vnode, VWRITE, - cnp->cn_cred, p)) != 0) - return (error); /* * we are trying to delete '.'. What does this mean? XXX */ if (dir_node == node) { - VREF(dir_vnode); - *result_vnode = dir_vnode; - return (0); - } -#ifdef NOTYET - /* - * If directory is "sticky", then user must own - * the directory, or the file in it, else she - * may not delete it (unless she's root). This - * implements append-only directories. - */ - if ((dir_node->mode & ISVTX) && - cnp->cn_cred->cr_uid != 0 && - cnp->cn_cred->cr_uid != dir_node->uid && - cnp->cn_cred->cr_uid != node->uid) { - VOP_UNLOCK(*result_vnode, 0, p); - return (EPERM); + if (*result_vnode) { + vnode_put(*result_vnode); + *result_vnode = NULL; + } + if (((error = vnode_get(dir_vnode)) == 0)) { + *result_vnode = dir_vnode; + } + return error; } -#endif - if (!lockparent) - VOP_UNLOCK(dir_vnode, 0, p); - return (0); + return 0; } /* @@ -272,23 +380,15 @@ devfs_lookup(struct vop_lookup_args *ap) * regular file, or empty directory. */ if (op == RENAME && wantparent && (flags & ISLASTCN)) { - /* - * Are we allowed to change the holding directory? - */ - if ((error = VOP_ACCESS(dir_vnode, VWRITE, - cnp->cn_cred, p)) != 0) - return (error); /* * Careful about locking second node. * This can only occur if the target is ".". */ - if (dir_node == node) - return (EISDIR); - /* hmm save the 'from' name (we need to delete it) */ - cnp->cn_flags |= SAVENAME; - if (!lockparent) - VOP_UNLOCK(dir_vnode, 0, p); - return (0); + if (dir_node == node) { + error = EISDIR; + goto drop_ref; + } + return 0; } /* @@ -311,397 +411,407 @@ devfs_lookup(struct vop_lookup_args *ap) * work if the file system has any hard links other than ".." * that point backwards in the directory structure. */ - if (flags & ISDOTDOT) { - VOP_UNLOCK(dir_vnode, 0, p); /* race to get the node */ - if (lockparent && (flags & ISLASTCN)) - vn_lock(dir_vnode, LK_EXCLUSIVE | LK_RETRY, p); - } else if (dir_node == node) { -#if 0 - /* - * this next statement is wrong: we already did a vget in - * devfs_dntovn(); DWS 4/16/1999 - */ - VREF(dir_vnode); /* we want ourself, ie "." */ -#endif + if ((flags & ISDOTDOT) == 0 && dir_node == node) { + if (*result_vnode) { + vnode_put(*result_vnode); + *result_vnode = NULL; + } + if ((error = vnode_get(dir_vnode))) { + return error; + } *result_vnode = dir_vnode; - } else { - if (!lockparent || (flags & ISLASTCN)) - VOP_UNLOCK(dir_vnode, 0, p); } + return 0; - return (0); +drop_ref: + if (*result_vnode) { + vnode_put(*result_vnode); + *result_vnode = NULL; + } + return error; } static int -devfs_access(struct vop_access_args *ap) - /*struct vop_access_args { - struct vnode *a_vp; - int a_mode; - struct ucred *a_cred; - struct proc *a_p; - } */ +devfs_getattr(struct vnop_getattr_args *ap) +/*struct vnop_getattr_args { + * struct vnode *a_vp; + * struct vnode_attr *a_vap; + * kauth_cred_t a_cred; + * struct proc *a_p; + * } */ { - /* - * mode is filled with a combination of VREAD, VWRITE, - * and/or VEXEC bits turned on. In an octal number these - * are the Y in 0Y00. - */ struct vnode *vp = ap->a_vp; - int mode = ap->a_mode; - struct ucred *cred = ap->a_cred; - devnode_t * file_node; - gid_t *gp; - int i; - struct proc *p = ap->a_p; + struct vnode_attr *vap = ap->a_vap; + devnode_t * file_node; + struct timeval now; + + DEVFS_LOCK(); file_node = VTODN(vp); - /* - * if we are not running as a process, we are in the - * kernel and we DO have permission - */ - if (p == NULL) - return 0; + + VATTR_RETURN(vap, va_mode, file_node->dn_mode); /* - * Access check is based on only one of owner, group, public. - * If not owner, then check group. If not a member of the - * group, then check public access. - */ - if (cred->cr_uid != file_node->dn_uid) - { - /* failing that.. try groups */ - mode >>= 3; - gp = cred->cr_groups; - for (i = 0; i < cred->cr_ngroups; i++, gp++) - { - if (file_node->dn_gid == *gp) - { - goto found; - } - } - /* failing that.. try general access */ - mode >>= 3; -found: - ; - } - if ((file_node->dn_mode & mode) == mode) - return (0); - /* - * Root gets to do anything. - * but only use suser prives as a last resort - * (Use of super powers is recorded in ap->a_p->p_acflag) + * Note: for DEV_CDEV and DEV_BDEV, we return the device from + * the vp, not the file_node; if we getting information on a + * cloning device, we want the cloned information, not the template. */ - if( suser(cred, &ap->a_p->p_acflag) == 0) /* XXX what if no proc? */ - return 0; - return (EACCES); -} - -static int -devfs_getattr(struct vop_getattr_args *ap) - /*struct vop_getattr_args { - struct vnode *a_vp; - struct vattr *a_vap; - struct ucred *a_cred; - struct proc *a_p; - } */ -{ - struct vnode *vp = ap->a_vp; - struct vattr *vap = ap->a_vap; - devnode_t * file_node; - struct timeval tv; - - file_node = VTODN(vp); - tv = time; - dn_times(file_node, tv, tv); - vap->va_rdev = 0;/* default value only */ - vap->va_mode = file_node->dn_mode; - switch (file_node->dn_type) - { - case DEV_DIR: - vap->va_rdev = (dev_t)file_node->dn_dvm; + switch (file_node->dn_type) { + case DEV_DIR: +#if FDESC + case DEV_DEVFD: /* Like a directory */ +#endif /* FDESC */ + VATTR_RETURN(vap, va_rdev, 0); vap->va_mode |= (S_IFDIR); break; - case DEV_CDEV: - vap->va_rdev = file_node->dn_typeinfo.dev; + case DEV_CDEV: + VATTR_RETURN(vap, va_rdev, vp->v_rdev); vap->va_mode |= (S_IFCHR); break; - case DEV_BDEV: - vap->va_rdev = file_node->dn_typeinfo.dev; + case DEV_BDEV: + VATTR_RETURN(vap, va_rdev, vp->v_rdev); vap->va_mode |= (S_IFBLK); break; - case DEV_SLNK: + case DEV_SLNK: + VATTR_RETURN(vap, va_rdev, 0); vap->va_mode |= (S_IFLNK); break; + default: + VATTR_RETURN(vap, va_rdev, 0); /* default value only */ } - vap->va_type = vp->v_type; - vap->va_nlink = file_node->dn_links; - vap->va_uid = file_node->dn_uid; - vap->va_gid = file_node->dn_gid; - vap->va_fsid = (int32_t)(void *)file_node->dn_dvm; - vap->va_fileid = (int32_t)(void *)file_node; - vap->va_size = file_node->dn_len; /* now a u_quad_t */ - /* this doesn't belong here */ - if (vp->v_type == VBLK) - vap->va_blocksize = BLKDEV_IOSIZE; - else if (vp->v_type == VCHR) - vap->va_blocksize = MAXPHYSIO; - else - vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize; + VATTR_RETURN(vap, va_type, vp->v_type); + VATTR_RETURN(vap, va_nlink, file_node->dn_links); + VATTR_RETURN(vap, va_uid, file_node->dn_uid); + VATTR_RETURN(vap, va_gid, file_node->dn_gid); + VATTR_RETURN(vap, va_fsid, (uintptr_t)file_node->dn_dvm); + VATTR_RETURN(vap, va_fileid, (uintptr_t)file_node->dn_ino); + VATTR_RETURN(vap, va_data_size, file_node->dn_len); + + /* return an override block size (advisory) */ + if (vp->v_type == VBLK) { + VATTR_RETURN(vap, va_iosize, BLKDEV_IOSIZE); + } else if (vp->v_type == VCHR) { + VATTR_RETURN(vap, va_iosize, MAXPHYSIO); + } else { + VATTR_RETURN(vap, va_iosize, vp->v_mount->mnt_vfsstat.f_iosize); + } + + + DEVFS_ATTR_LOCK_SPIN(); + + microtime(&now); + dn_times_locked(file_node, &now, &now, &now, 0); + /* if the time is bogus, set it to the boot time */ - if (file_node->dn_ctime.tv_sec == 0) - file_node->dn_ctime.tv_sec = boottime.tv_sec; - if (file_node->dn_mtime.tv_sec == 0) - file_node->dn_mtime.tv_sec = boottime.tv_sec; - if (file_node->dn_atime.tv_sec == 0) - file_node->dn_atime.tv_sec = boottime.tv_sec; - vap->va_ctime = file_node->dn_ctime; - vap->va_mtime = file_node->dn_mtime; - vap->va_atime = file_node->dn_atime; - vap->va_gen = 0; - vap->va_flags = 0; - vap->va_bytes = file_node->dn_len; /* u_quad_t */ - vap->va_filerev = 0; /* XXX */ /* u_quad_t */ - vap->va_vaflags = 0; /* XXX */ + if (file_node->dn_ctime.tv_sec == 0) { + file_node->dn_ctime.tv_sec = boottime_sec(); + file_node->dn_ctime.tv_nsec = 0; + } + if (file_node->dn_mtime.tv_sec == 0) { + file_node->dn_mtime = file_node->dn_ctime; + } + if (file_node->dn_atime.tv_sec == 0) { + file_node->dn_atime = file_node->dn_ctime; + } + VATTR_RETURN(vap, va_change_time, file_node->dn_ctime); + VATTR_RETURN(vap, va_modify_time, file_node->dn_mtime); + VATTR_RETURN(vap, va_access_time, file_node->dn_atime); + + DEVFS_ATTR_UNLOCK(); + + VATTR_RETURN(vap, va_gen, 0); + VATTR_RETURN(vap, va_filerev, 0); + VATTR_RETURN(vap, va_acl, NULL); + + /* Hide the root so Finder doesn't display it */ + if (vnode_isvroot(vp)) { + VATTR_RETURN(vap, va_flags, UF_HIDDEN); + } else { + VATTR_RETURN(vap, va_flags, 0); + } + + DEVFS_UNLOCK(); + return 0; } static int -devfs_setattr(struct vop_setattr_args *ap) - /*struct vop_setattr_args { - struct vnode *a_vp; - struct vattr *a_vap; - struct ucred *a_cred; - struct proc *a_p; - } */ +devfs_setattr(struct vnop_setattr_args *ap) +/*struct vnop_setattr_args { + * struct vnode *a_vp; + * struct vnode_attr *a_vap; + * vfs_context_t a_context; + * } */ { struct vnode *vp = ap->a_vp; - struct vattr *vap = ap->a_vap; - struct ucred *cred = ap->a_cred; - struct proc *p = ap->a_p; + struct vnode_attr *vap = ap->a_vap; int error = 0; - gid_t *gp; - int i; - devnode_t * file_node; + devnode_t * file_node; struct timeval atimeval, mtimeval; - if (vap->va_flags != VNOVAL) /* XXX needs to be implemented */ - return (EOPNOTSUPP); + DEVFS_LOCK(); file_node = VTODN(vp); - - if ((vap->va_type != VNON) || - (vap->va_nlink != VNOVAL) || - (vap->va_fsid != VNOVAL) || - (vap->va_fileid != VNOVAL) || - (vap->va_blocksize != VNOVAL) || - (vap->va_rdev != VNOVAL) || - (vap->va_bytes != VNOVAL) || - (vap->va_gen != VNOVAL )) - { - return EINVAL; - } - /* - * Go through the fields and update iff not VNOVAL. + * Go through the fields and update if set. */ - if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) { - if (cred->cr_uid != file_node->dn_uid && - (error = suser(cred, &p->p_acflag)) && - ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || - (error = VOP_ACCESS(vp, VWRITE, cred, p)))) - return (error); - if (vap->va_atime.tv_sec != VNOVAL) - file_node->dn_flags |= DN_ACCESS; - if (vap->va_mtime.tv_sec != VNOVAL) - file_node->dn_flags |= DN_CHANGE | DN_UPDATE; - atimeval.tv_sec = vap->va_atime.tv_sec; - atimeval.tv_usec = vap->va_atime.tv_nsec / 1000; - mtimeval.tv_sec = vap->va_mtime.tv_sec; - mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000; - if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1)) - return (error); + if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) { + if (VATTR_IS_ACTIVE(vap, va_access_time)) { + file_node->dn_access = 1; + } + if (VATTR_IS_ACTIVE(vap, va_modify_time)) { + file_node->dn_change = 1; + file_node->dn_update = 1; + } + atimeval.tv_sec = vap->va_access_time.tv_sec; + atimeval.tv_usec = vap->va_access_time.tv_nsec / 1000; + mtimeval.tv_sec = vap->va_modify_time.tv_sec; + mtimeval.tv_usec = vap->va_modify_time.tv_nsec / 1000; + + if ((error = devfs_update(vp, &atimeval, &mtimeval))) { + goto exit; + } } + VATTR_SET_SUPPORTED(vap, va_access_time); + VATTR_SET_SUPPORTED(vap, va_change_time); /* - * Change the permissions.. must be root or owner to do this. + * Change the permissions. */ - if (vap->va_mode != (u_short)VNOVAL) { - if ((cred->cr_uid != file_node->dn_uid) - && (error = suser(cred, &p->p_acflag))) - return (error); + if (VATTR_IS_ACTIVE(vap, va_mode)) { file_node->dn_mode &= ~07777; file_node->dn_mode |= vap->va_mode & 07777; } + VATTR_SET_SUPPORTED(vap, va_mode); /* - * Change the owner.. must be root to do this. + * Change the owner. */ - if (vap->va_uid != (uid_t)VNOVAL) { - if (error = suser(cred, &p->p_acflag)) - return (error); + if (VATTR_IS_ACTIVE(vap, va_uid)) { file_node->dn_uid = vap->va_uid; } + VATTR_SET_SUPPORTED(vap, va_uid); /* - * Change the group.. must be root or owner to do this. - * If we are the owner, we must be in the target group too. - * don't use suser() unless you have to as it reports - * whether you needed suser powers or not. + * Change the group. */ - if (vap->va_gid != (gid_t)VNOVAL) { - if (cred->cr_uid == file_node->dn_uid){ - gp = cred->cr_groups; - for (i = 0; i < cred->cr_ngroups; i++, gp++) { - if (vap->va_gid == *gp) - goto cando; - } - } - /* - * we can't do it with normal privs, - * do we have an ace up our sleeve? - */ - if (error = suser(cred, &p->p_acflag)) - return (error); -cando: + if (VATTR_IS_ACTIVE(vap, va_gid)) { file_node->dn_gid = vap->va_gid; } -#if 0 - /* - * Copied from somewhere else - * but only kept as a marker and reminder of the fact that - * flags should be handled some day - */ - if (vap->va_flags != VNOVAL) { - if (error = suser(cred, &p->p_acflag)) - return error; - if (cred->cr_uid == 0) - ; - else { - } - } -#endif + VATTR_SET_SUPPORTED(vap, va_gid); +exit: + DEVFS_UNLOCK(); + return error; } +#if CONFIG_MACF static int -devfs_read(struct vop_read_args *ap) - /*struct vop_read_args { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ +devfs_setlabel(struct vnop_setlabel_args *ap) +/* struct vnop_setlabel_args { + * struct vnodeop_desc *a_desc; + * struct vnode *a_vp; + * struct label *a_vl; + * vfs_context_t a_context; + * } */ { - devnode_t * dn_p = VTODN(ap->a_vp); + struct vnode *vp; + struct devnode *de; + + vp = ap->a_vp; + de = VTODN(vp); + + mac_vnode_label_update(ap->a_context, vp, ap->a_vl); + mac_devfs_label_update(vp->v_mount, de, vp); + + return 0; +} +#endif + +static int +devfs_read(struct vnop_read_args *ap) +/* struct vnop_read_args { + * struct vnode *a_vp; + * struct uio *a_uio; + * int a_ioflag; + * vfs_context_t a_context; + * } */ +{ + devnode_t * dn_p = VTODN(ap->a_vp); switch (ap->a_vp->v_type) { - case VDIR: { - dn_p->dn_flags |= DN_ACCESS; - return VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, - NULL, NULL, NULL); - } - default: { - printf("devfs_read(): bad file type %d", ap->a_vp->v_type); - return(EINVAL); - break; - } - } - return (0); /* not reached */ + case VDIR: { + dn_p->dn_access = 1; + + return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context); + } + default: { + printf("devfs_read(): bad file type %d", ap->a_vp->v_type); + return EINVAL; + } + } } static int -devfs_close(ap) - struct vop_close_args /* { - struct vnode *a_vp; - int a_fflag; - struct ucred *a_cred; - struct proc *a_p; - } */ *ap; +devfs_close(struct vnop_close_args *ap) +/* struct vnop_close_args { + * struct vnode *a_vp; + * int a_fflag; + * vfs_context_t a_context; + * } */ { - struct vnode * vp = ap->a_vp; - register devnode_t * dnp = VTODN(vp); - - simple_lock(&vp->v_interlock); - if (vp->v_usecount > 1) - dn_times(dnp, time, time); - simple_unlock(&vp->v_interlock); - return (0); + struct vnode * vp = ap->a_vp; + devnode_t * dnp; + + if (vnode_isinuse(vp, 1)) { + DEVFS_LOCK(); + dnp = VTODN(vp); + if (dnp) { + dn_times_now(dnp, 0); + } + DEVFS_UNLOCK(); + } + return 0; } static int -devfsspec_close(ap) - struct vop_close_args /* { - struct vnode *a_vp; - int a_fflag; - struct ucred *a_cred; - struct proc *a_p; - } */ *ap; +devfsspec_close(struct vnop_close_args *ap) +/* struct vnop_close_args { + * struct vnode *a_vp; + * int a_fflag; + * vfs_context_t a_context; + * } */ { - struct vnode * vp = ap->a_vp; - register devnode_t * dnp = VTODN(vp); - - simple_lock(&vp->v_interlock); - if (vp->v_usecount > 1) - dn_times(dnp, time, time); - simple_unlock(&vp->v_interlock); - return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap)); + struct vnode * vp = ap->a_vp; + devnode_t * dnp; + + if (vnode_isinuse(vp, 0)) { + DEVFS_LOCK(); + dnp = VTODN(vp); + if (dnp) { + dn_times_now(dnp, 0); + } + DEVFS_UNLOCK(); + } + + return VOCALL(spec_vnodeop_p, VOFFSET(vnop_close), ap); +} + +static boolean_t +devfs_update_needed(long now_s, long last_s) +{ + if (now_s > last_s) { + if (now_s - last_s >= DEVFS_LAZY_UPDATE_SECONDS) { + return TRUE; + } + } + + return FALSE; +} + +/* + * Given a set of time updates required [to happen at some point], check + * either make those changes (and resolve other pending updates) or mark + * the devnode for a subsequent update. + */ +static void +devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags) +{ + struct timeval now; + long now_s; + + microtime(&now); + now_s = now.tv_sec; + + if (dnp->dn_change || (just_changed_flags & DEVFS_UPDATE_CHANGE)) { + if (devfs_update_needed(now_s, dnp->dn_ctime.tv_sec)) { + dn_times_now(dnp, just_changed_flags); + return; + } + } + if (dnp->dn_access || (just_changed_flags & DEVFS_UPDATE_ACCESS)) { + if (devfs_update_needed(now_s, dnp->dn_atime.tv_sec)) { + dn_times_now(dnp, just_changed_flags); + return; + } + } + if (dnp->dn_update || (just_changed_flags & DEVFS_UPDATE_MOD)) { + if (devfs_update_needed(now_s, dnp->dn_mtime.tv_sec)) { + dn_times_now(dnp, just_changed_flags); + return; + } + } + + /* Not going to do anything now--mark for later update */ + dn_mark_for_delayed_times_update(dnp, just_changed_flags); + + return; } static int -devfsspec_read(struct vop_read_args *ap) - /*struct vop_read_args { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ +devfsspec_read(struct vnop_read_args *ap) +/* struct vnop_read_args { + * struct vnode *a_vp; + * struct uio *a_uio; + * int a_ioflag; + * kauth_cred_t a_cred; + * } */ { - VTODN(ap->a_vp)->dn_flags |= DN_ACCESS; - return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap)); + devnode_t * dnp = VTODN(ap->a_vp); + + devfs_consider_time_update(dnp, DEVFS_UPDATE_ACCESS); + + return VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap); } static int -devfsspec_write(struct vop_write_args *ap) - /*struct vop_write_args { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ +devfsspec_write(struct vnop_write_args *ap) +/* struct vnop_write_args { + * struct vnode *a_vp; + * struct uio *a_uio; + * int a_ioflag; + * vfs_context_t a_context; + * } */ { - VTODN(ap->a_vp)->dn_flags |= DN_CHANGE | DN_UPDATE; - return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap)); + devnode_t * dnp = VTODN(ap->a_vp); + + devfs_consider_time_update(dnp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD); + + return VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap); } /* * Write data to a file or directory. */ static int -devfs_write(struct vop_write_args *ap) - /*struct vop_write_args { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ +devfs_write(struct vnop_write_args *ap) +/* struct vnop_write_args { + * struct vnode *a_vp; + * struct uio *a_uio; + * int a_ioflag; + * kauth_cred_t a_cred; + * } */ { switch (ap->a_vp->v_type) { case VDIR: - return(EISDIR); + return EISDIR; default: printf("devfs_write(): bad file type %d", ap->a_vp->v_type); - return (EINVAL); + return EINVAL; } - return 0; /* not reached */ } +/* + * Deviates from UFS naming convention because there is a KPI function + * called devfs_remove(). + */ static int -devfs_remove(struct vop_remove_args *ap) - /*struct vop_remove_args { - struct vnode *a_dvp; - struct vnode *a_vp; - struct componentname *a_cnp; - } */ +devfs_vnop_remove(struct vnop_remove_args *ap) +/* struct vnop_remove_args { + * struct vnode *a_dvp; + * struct vnode *a_vp; + * struct componentname *a_cnp; + * } */ { struct vnode *vp = ap->a_vp; struct vnode *dvp = ap->a_dvp; @@ -711,29 +821,32 @@ devfs_remove(struct vop_remove_args *ap) devdirent_t * tnp; int doingdirectory = 0; int error = 0; - uid_t ouruid = cnp->cn_cred->cr_uid; - struct proc *p = cnp->cn_proc; /* - * Lock our directories and get our name pointers - * assume that the names are null terminated as they + * assume that the name is null terminated as they * are the end of the path. Get pointers to all our * devfs structures. */ + + DEVFS_LOCK(); + tp = VTODN(vp); tdp = VTODN(dvp); - /* - * Assuming we are atomic, dev_lookup left this for us - */ - tnp = tp->dn_last_lookup; + + + tnp = dev_findname(tdp, cnp->cn_nameptr); + + if (tnp == NULL) { + error = ENOENT; + goto abort; + } /* - * Check we are doing legal things WRT the new flags + * Don't allow removing critical devfs devices */ - if ((tp->dn_flags & (IMMUTABLE | APPEND)) - || (tdp->dn_flags & APPEND) /*XXX eh?*/ ) { - error = EPERM; - goto abort; + if (devfs_is_name_protected(dvp, cnp->cn_nameptr)) { + error = EINVAL; + goto abort; } /* @@ -743,8 +856,8 @@ devfs_remove(struct vop_remove_args *ap) /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ - if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') - || (cnp->cn_flags&ISDOTDOT) ) { + if ((cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') + || (cnp->cn_flags & ISDOTDOT)) { error = EINVAL; goto abort; } @@ -752,63 +865,44 @@ devfs_remove(struct vop_remove_args *ap) } /*********************************** - * Start actually doing things.... * - ***********************************/ - tdp->dn_flags |= DN_CHANGE | DN_UPDATE; + * Start actually doing things.... * + ***********************************/ + devfs_consider_time_update(tdp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD); - /* - * own the parent directory, or the destination of the rename, - * otherwise the destination may not be changed (except by - * root). This implements append-only directories. - * XXX shoudn't this be in generic code? - */ - if ((tdp->dn_mode & S_ISTXT) - && ouruid != 0 - && ouruid != tdp->dn_uid - && ouruid != tp->dn_uid ) { - error = EPERM; - goto abort; - } /* * Target must be empty if a directory and have no links * to it. Also, ensure source and target are compatible * (both directories, or both not directories). */ - if (( doingdirectory) && (tp->dn_links > 2)) { - error = ENOTEMPTY; - goto abort; + if ((doingdirectory) && (tp->dn_links > 2)) { + error = ENOTEMPTY; + goto abort; } - DEVFS_LOCK(p); dev_free_name(tnp); - DEVFS_UNLOCK(p); - abort: - if (dvp == vp) - vrele(vp); - else - vput(vp); - vput(dvp); - return (error); +abort: + DEVFS_UNLOCK(); + + return error; } /* */ static int -devfs_link(struct vop_link_args *ap) - /*struct vop_link_args { - struct vnode *a_tdvp; - struct vnode *a_vp; - struct componentname *a_cnp; - } */ +devfs_link(struct vnop_link_args *ap) +/*struct vnop_link_args { + * struct vnode *a_tdvp; + * struct vnode *a_vp; + * struct componentname *a_cnp; + * vfs_context_t a_context; + * } */ { struct vnode *vp = ap->a_vp; struct vnode *tdvp = ap->a_tdvp; struct componentname *cnp = ap->a_cnp; - struct proc *p = cnp->cn_proc; devnode_t * fp; devnode_t * tdp; devdirent_t * tnp; int error = 0; - struct timeval tv; /* * First catch an arbitrary restriction for this FS @@ -824,80 +918,33 @@ devfs_link(struct vop_link_args *ap) * are the end of the path. Get pointers to all our * devfs structures. */ + /* can lookup dnode safely for tdvp outside of devfs lock as it is not aliased */ tdp = VTODN(tdvp); - fp = VTODN(vp); - + if (tdvp->v_mount != vp->v_mount) { - error = EXDEV; - VOP_ABORTOP(tdvp, cnp); - goto out2; - } - if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) { - VOP_ABORTOP(tdvp, cnp); - goto out2; + return EXDEV; } + DEVFS_LOCK(); - /* - * Check we are doing legal things WRT the new flags - */ - if (fp->dn_flags & (IMMUTABLE | APPEND)) { - VOP_ABORTOP(tdvp, cnp); - error = EPERM; - goto out1; - } + fp = VTODN(vp); /*********************************** - * Start actually doing things.... * - ***********************************/ - fp->dn_flags |= DN_CHANGE; - tv = time; - error = VOP_UPDATE(vp, &tv, &tv, 1); + * Start actually doing things.... * + ***********************************/ + dn_times_now(fp, DEVFS_UPDATE_CHANGE); + if (!error) { - DEVFS_LOCK(p); - error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp); - DEVFS_UNLOCK(p); + error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp); } out1: - if (tdvp != vp) - VOP_UNLOCK(vp, 0, p); -out2: - vput(tdvp); - return (error); -} - -/* - * Check if source directory is in the path of the target directory. - * Target is supplied locked, source is unlocked. - * The target is always vput before returning. - */ -int -devfs_checkpath(source, target) - devnode_t *source, *target; -{ - int error = 0; - devnode_t * ntmp; - devnode_t * tmp; - struct vnode *vp; - - vp = target->dn_vn; - tmp = target; - - do { - if (tmp == source) { - error = EINVAL; - break; - } - ntmp = tmp; - } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp); + DEVFS_UNLOCK(); - if (vp != NULL) - vput(vp); - return (error); + return error; } /* * Rename system call. Seems overly complicated to me... - * rename("foo", "bar"); + * rename("foo", "bar"); * is essentially * unlink("bar"); * link("foo", "bar"); @@ -923,15 +970,16 @@ devfs_checkpath(source, target) * directory. */ static int -devfs_rename(struct vop_rename_args *ap) - /*struct vop_rename_args { - struct vnode *a_fdvp; - struct vnode *a_fvp; - struct componentname *a_fcnp; - struct vnode *a_tdvp; - struct vnode *a_tvp; - struct componentname *a_tcnp; - } */ +devfs_rename(struct vnop_rename_args *ap) +/*struct vnop_rename_args { + * struct vnode *a_fdvp; + * struct vnode *a_fvp; + * struct componentname *a_fcnp; + * struct vnode *a_tdvp; + * struct vnode *a_tvp; + * struct componentname *a_tcnp; + * vfs_context_t a_context; + * } */ { struct vnode *tvp = ap->a_tvp; struct vnode *tdvp = ap->a_tdvp; @@ -939,23 +987,21 @@ devfs_rename(struct vop_rename_args *ap) struct vnode *fdvp = ap->a_fdvp; struct componentname *tcnp = ap->a_tcnp; struct componentname *fcnp = ap->a_fcnp; - struct proc *p = fcnp->cn_proc; devnode_t *fp, *fdp, *tp, *tdp; - devdirent_t *fnp,*tnp; + devdirent_t *fnp, *tnp; int doingdirectory = 0; int error = 0; - struct timeval tv; + DEVFS_LOCK(); /* * First catch an arbitrary restriction for this FS */ - if(tcnp->cn_namelen > DEVMAXNAMESIZE) { + if (tcnp->cn_namelen > DEVMAXNAMESIZE) { error = ENAMETOOLONG; - goto abortit; + goto out; } /* - * Lock our directories and get our name pointers * assume that the names are null terminated as they * are the end of the path. Get pointers to all our * devfs structures. @@ -963,45 +1009,24 @@ devfs_rename(struct vop_rename_args *ap) tdp = VTODN(tdvp); fdp = VTODN(fdvp); fp = VTODN(fvp); - fnp = fp->dn_last_lookup; + + fnp = dev_findname(fdp, fcnp->cn_nameptr); + + if (fnp == NULL) { + error = ENOENT; + goto out; + } tp = NULL; tnp = NULL; + if (tvp) { - tp = VTODN(tvp); - tnp = tp->dn_last_lookup; - } - - /* - * trying to move it out of devfs? - * if we move a dir across mnt points. we need to fix all - * the mountpoint pointers! XXX - * so for now keep dirs within the same mount - */ - if ((fvp->v_mount != tdvp->v_mount) || - (tvp && (fvp->v_mount != tvp->v_mount))) { - error = EXDEV; -abortit: - VOP_ABORTOP(tdvp, tcnp); - if (tdvp == tvp) /* eh? */ - vrele(tdvp); - else - vput(tdvp); - if (tvp) - vput(tvp); - VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */ - vrele(fdvp); - vrele(fvp); - return (error); - } + tnp = dev_findname(tdp, tcnp->cn_nameptr); - /* - * Check we are doing legal things WRT the new flags - */ - if ((tp && (tp->dn_flags & (IMMUTABLE | APPEND))) - || (fp->dn_flags & (IMMUTABLE | APPEND)) - || (fdp->dn_flags & APPEND)) { - error = EPERM; - goto abortit; + if (tnp == NULL) { + error = ENOENT; + goto out; + } + tp = VTODN(tvp); } /* @@ -1011,31 +1036,39 @@ abortit: /* * Avoid ".", "..", and aliases of "." for obvious reasons. */ - if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') - || (fcnp->cn_flags&ISDOTDOT) - || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.') - || (tcnp->cn_flags&ISDOTDOT) - || (tdp == fp )) { + if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') + || (fcnp->cn_flags & ISDOTDOT) + || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.') + || (tcnp->cn_flags & ISDOTDOT) + || (tdp == fp)) { error = EINVAL; - goto abortit; + goto out; } doingdirectory++; } + /* + * Don't allow renaming critical devfs devices + */ + if (devfs_is_name_protected(fdvp, fcnp->cn_nameptr) || + devfs_is_name_protected(tdvp, tcnp->cn_nameptr)) { + error = EINVAL; + goto out; + } + /* * If ".." must be changed (ie the directory gets a new * parent) then the source directory must not be in the * directory hierarchy above the target, as this would * orphan everything below the source directory. Also * the user must have write permission in the source so - * as to be able to change "..". + * as to be able to change "..". */ if (doingdirectory && (tdp != fdp)) { devnode_t * tmp, *ntmp; - error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc); tmp = tdp; do { - if(tmp == fp) { + if (tmp == fp) { /* XXX unlock stuff here probably */ error = EINVAL; goto out; @@ -1045,38 +1078,24 @@ abortit: } /*********************************** - * Start actually doing things.... * - ***********************************/ - fp->dn_flags |= DN_CHANGE; - tv = time; - if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) { - VOP_UNLOCK(fvp, 0, p); - goto bad; - } + * Start actually doing things.... * + ***********************************/ + dn_times_now(fp, DEVFS_UPDATE_CHANGE); + /* * Check if just deleting a link name. */ if (fvp == tvp) { if (fvp->v_type == VDIR) { error = EINVAL; - goto abortit; + goto out; } - /* Release destination completely. */ - VOP_ABORTOP(tdvp, tcnp); - vput(tdvp); - vput(tvp); - - /* Delete source. */ - VOP_ABORTOP(fdvp, fcnp); /*XXX*/ - vrele(fdvp); - vrele(fvp); dev_free_name(fnp); + + DEVFS_UNLOCK(); return 0; } - - vrele(fdvp); - /* * 1) Bump link count while we're moving stuff * around. If we crash somewhere before @@ -1086,230 +1105,280 @@ abortit: /* * If the target exists zap it (unless it's a non-empty directory) * We could do that as well but won't - */ + */ if (tp) { - int ouruid = tcnp->cn_cred->cr_uid; - /* - * If the parent directory is "sticky", then the user must - * own the parent directory, or the destination of the rename, - * otherwise the destination may not be changed (except by - * root). This implements append-only directories. - * XXX shoudn't this be in generic code? - */ - if ((tdp->dn_mode & S_ISTXT) - && ouruid != 0 - && ouruid != tdp->dn_uid - && ouruid != tp->dn_uid ) { - error = EPERM; - goto bad; - } /* * Target must be empty if a directory and have no links * to it. Also, ensure source and target are compatible * (both directories, or both not directories). */ - if (( doingdirectory) && (tp->dn_links > 2)) { - error = ENOTEMPTY; - goto bad; + if ((doingdirectory) && (tp->dn_links > 2)) { + error = ENOTEMPTY; + goto bad; } dev_free_name(tnp); tp = NULL; } - dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp); + dev_add_name(tcnp->cn_nameptr, tdp, NULL, fp, &tnp); fnp->de_dnp = NULL; fp->dn_links--; /* one less link to it.. */ - dev_free_name(fnp); - fp->dn_links--; /* we added one earlier*/ - if (tdp) - vput(tdvp); - if (tp) - vput(fvp); - vrele(fvp); - return (error); + dev_free_name(fnp); bad: - if (tp) - vput(tvp); - vput(tdvp); + fp->dn_links--; /* we added one earlier*/ out: - if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) { - fp->dn_links--; /* we added one earlier*/ - vput(fvp); - } else - vrele(fvp); - return (error); + DEVFS_UNLOCK(); + return error; } static int -devfs_symlink(struct vop_symlink_args *ap) - /*struct vop_symlink_args { - struct vnode *a_dvp; - struct vnode **a_vpp; - struct componentname *a_cnp; - struct vattr *a_vap; - char *a_target; - } */ +devfs_mkdir(struct vnop_mkdir_args *ap) +/*struct vnop_mkdir_args { + * struct vnode *a_dvp; + * struct vnode **a_vpp; + * struct componentname *a_cnp; + * struct vnode_attr *a_vap; + * vfs_context_t a_context; + * } */ { - struct componentname * cnp = ap->a_cnp; - struct vnode *vp = NULL; + struct componentname * cnp = ap->a_cnp; + vfs_context_t ctx = cnp->cn_context; + struct proc *p = vfs_context_proc(ctx); int error = 0; devnode_t * dir_p; - devnode_type_t typeinfo; devdirent_t * nm_p; devnode_t * dev_p; - struct vattr * vap = ap->a_vap; + struct vnode_attr * vap = ap->a_vap; struct vnode * * vpp = ap->a_vpp; - struct proc *p = cnp->cn_proc; - struct timeval tv; + + DEVFS_LOCK(); dir_p = VTODN(ap->a_dvp); - typeinfo.Slnk.name = ap->a_target; - typeinfo.Slnk.namelen = strlen(ap->a_target); - DEVFS_LOCK(p); - error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK, - &typeinfo, NULL, NULL, &nm_p); - DEVFS_UNLOCK(p); + error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_DIR, + NULL, NULL, NULL, &nm_p); if (error) { - goto failure; + goto failure; } - dev_p = nm_p->de_dnp; dev_p->dn_uid = dir_p->dn_uid; dev_p->dn_gid = dir_p->dn_gid; dev_p->dn_mode = vap->va_mode; dn_copy_times(dev_p, dir_p); + error = devfs_dntovn(dev_p, vpp, p); - if (error) - goto failure; - vp = *vpp; - vput(vp); - failure: - if ((cnp->cn_flags & SAVESTART) == 0) - FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); - vput(ap->a_dvp); +failure: + DEVFS_UNLOCK(); + return error; } /* - * Mknod vnode call + * An rmdir is a special type of remove, which we already support; we wrap + * and reexpress the arguments to call devfs_remove directly. The only + * different argument is flags, which we do not set, since it's ignored. */ -/* ARGSUSED */ +static int +devfs_rmdir(struct vnop_rmdir_args *ap) +/* struct vnop_rmdir_args { + * struct vnode *a_dvp; + * struct vnode *a_vp; + * struct componentname *a_cnp; + * vfs_context_t a_context; + * } */ +{ + struct vnop_remove_args ra; + + ra.a_dvp = ap->a_dvp; + ra.a_vp = ap->a_vp; + ra.a_cnp = ap->a_cnp; + ra.a_flags = 0; /* XXX */ + ra.a_context = ap->a_context; + + return devfs_vnop_remove(&ra); +} + + +static int +devfs_symlink(struct vnop_symlink_args *ap) +/*struct vnop_symlink_args { + * struct vnode *a_dvp; + * struct vnode **a_vpp; + * struct componentname *a_cnp; + * struct vnode_attr *a_vap; + * char *a_target; + * vfs_context_t a_context; + * } */ +{ + int error; + devdirent_t *newent; + + DEVFS_LOCK(); + error = devfs_make_symlink(VTODN(ap->a_dvp), ap->a_cnp->cn_nameptr, ap->a_vap->va_mode, ap->a_target, &newent); + + if (error == 0) { + error = devfs_dntovn(newent->de_dnp, ap->a_vpp, vfs_context_proc(ap->a_context)); + } + + DEVFS_UNLOCK(); + + return error; +} + +/* Called with devfs locked */ int -devfs_mknod(ap) - struct vop_mknod_args /* { - struct vnode *a_dvp; - struct vnode **a_vpp; - struct componentname *a_cnp; - struct vattr *a_vap; - } */ *ap; +devfs_make_symlink(devnode_t *dir_p, char *name, int mode, char *target, devdirent_t **newent) { - struct componentname * cnp = ap->a_cnp; - devnode_t * dev_p; - devdirent_t * devent; - devnode_t * dir_p; /* devnode for parent directory */ - struct vnode * dvp = ap->a_dvp; - int error = 0; - devnode_type_t typeinfo; - struct vattr * vap = ap->a_vap; + int error = 0; + devnode_type_t typeinfo; + devdirent_t * nm_p; + devnode_t * dev_p; + + typeinfo.Slnk.name = target; + typeinfo.Slnk.namelen = strlen(target); + + error = dev_add_entry(name, dir_p, DEV_SLNK, + &typeinfo, NULL, NULL, &nm_p); + if (error) { + goto failure; + } + dev_p = nm_p->de_dnp; + dev_p->dn_uid = dir_p->dn_uid; + dev_p->dn_gid = dir_p->dn_gid; + dev_p->dn_mode = mode; + dn_copy_times(dev_p, dir_p); + + if (newent) { + *newent = nm_p; + } + +failure: + + return error; +} + +/* + * Mknod vnode call + */ +static int +devfs_mknod(struct vnop_mknod_args *ap) +/* struct vnop_mknod_args { + * struct vnode *a_dvp; + * struct vnode **a_vpp; + * struct componentname *a_cnp; + * struct vnode_attr *a_vap; + * vfs_context_t a_context; + * } */ +{ + struct componentname * cnp = ap->a_cnp; + vfs_context_t ctx = cnp->cn_context; + struct proc *p = vfs_context_proc(ctx); + devnode_t * dev_p; + devdirent_t * devent; + devnode_t * dir_p; /* devnode for parent directory */ + struct vnode * dvp = ap->a_dvp; + int error = 0; + devnode_type_t typeinfo; + struct vnode_attr * vap = ap->a_vap; struct vnode ** vpp = ap->a_vpp; - struct proc * p = cnp->cn_proc; *vpp = NULL; - if (!vap->va_type == VBLK && !vap->va_type == VCHR) { - error = EINVAL; /* only support mknod of special files */ - goto failure; + if (!(vap->va_type == VBLK) && !(vap->va_type == VCHR)) { + return EINVAL; /* only support mknod of special files */ } - dir_p = VTODN(dvp); typeinfo.dev = vap->va_rdev; - DEVFS_LOCK(p); - error = dev_add_entry(cnp->cn_nameptr, dir_p, - (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV, - &typeinfo, NULL, NULL, &devent); - DEVFS_UNLOCK(p); + + DEVFS_LOCK(); + + dir_p = VTODN(dvp); + + error = dev_add_entry(cnp->cn_nameptr, dir_p, + (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV, + &typeinfo, NULL, NULL, &devent); if (error) { - goto failure; + goto failure; } dev_p = devent->de_dnp; error = devfs_dntovn(dev_p, vpp, p); - if (error) - goto failure; - dev_p->dn_uid = cnp->cn_cred->cr_uid; - dev_p->dn_gid = dir_p->dn_gid; + if (error) { + goto failure; + } + dev_p->dn_uid = vap->va_uid; + dev_p->dn_gid = vap->va_gid; dev_p->dn_mode = vap->va_mode; - failure: - if (*vpp) { - vput(*vpp); - *vpp = 0; - } - if ((cnp->cn_flags & SAVESTART) == 0) - FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI); - vput(dvp); - return (error); + VATTR_SET_SUPPORTED(vap, va_uid); + VATTR_SET_SUPPORTED(vap, va_gid); + VATTR_SET_SUPPORTED(vap, va_mode); +failure: + DEVFS_UNLOCK(); + + return error; } /* * Vnode op for readdir */ static int -devfs_readdir(struct vop_readdir_args *ap) - /*struct vop_readdir_args { - struct vnode *a_vp; - struct uio *a_uio; - struct ucred *a_cred; - int *eofflag; - int *ncookies; - u_int **cookies; - } */ +devfs_readdir(struct vnop_readdir_args *ap) +/*struct vnop_readdir_args { + * struct vnode *a_vp; + * struct uio *a_uio; + * int a_flags; + * int *a_eofflag; + * int *a_numdirent; + * vfs_context_t a_context; + * } */ { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; struct dirent dirent; devnode_t * dir_node; - devdirent_t * name_node; - char *name; + devdirent_t * name_node; + const char *name; int error = 0; int reclen; int nodenumber; - int startpos,pos; - struct proc * p = uio->uio_procp; + int startpos, pos; + + if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF)) { + return EINVAL; + } /* set up refs to dir */ dir_node = VTODN(vp); - if(dir_node->dn_type != DEV_DIR) - return(ENOTDIR); - + if (dir_node->dn_type != DEV_DIR) { + return ENOTDIR; + } pos = 0; startpos = uio->uio_offset; - DEVFS_LOCK(p); + + DEVFS_LOCK(); + name_node = dir_node->dn_typeinfo.Dir.dirlist; nodenumber = 0; - dir_node->dn_flags |= DN_ACCESS; - - while ((name_node || (nodenumber < 2)) && (uio->uio_resid > 0)) - { - switch(nodenumber) - { - case 0: - dirent.d_fileno = (int32_t)(void *)dir_node; + + while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0)) { + switch (nodenumber) { + case 0: + dirent.d_fileno = dir_node->dn_ino; name = "."; dirent.d_namlen = 1; dirent.d_type = DT_DIR; break; - case 1: - if(dir_node->dn_typeinfo.Dir.parent) - dirent.d_fileno - = (int32_t)dir_node->dn_typeinfo.Dir.parent; - else - dirent.d_fileno = (u_int32_t)dir_node; + case 1: + if (dir_node->dn_typeinfo.Dir.parent) { + dirent.d_fileno = dir_node->dn_typeinfo.Dir.parent->dn_ino; + } else { + dirent.d_fileno = dir_node->dn_ino; + } name = ".."; dirent.d_namlen = 2; dirent.d_type = DT_DIR; break; default: - dirent.d_fileno = (int32_t)(void *)name_node->de_dnp; + dirent.d_fileno = name_node->de_dnp->dn_ino; dirent.d_namlen = strlen(name_node->de_name); name = name_node->de_name; - switch(name_node->de_dnp->dn_type) { + switch (name_node->de_dnp->dn_type) { case DEV_BDEV: dirent.d_type = DT_BLK; break; @@ -1326,41 +1395,45 @@ devfs_readdir(struct vop_readdir_args *ap) dirent.d_type = DT_UNKNOWN; } } -#define GENERIC_DIRSIZ(dp) \ +#define GENERIC_DIRSIZ(dp) \ ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3)) reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent); - if(pos >= startpos) /* made it to the offset yet? */ - { - if (uio->uio_resid < reclen) /* will it fit? */ + if (pos >= startpos) { /* made it to the offset yet? */ + if (uio_resid(uio) < reclen) { /* will it fit? */ break; - strcpy( dirent.d_name,name); - if ((error = uiomove ((caddr_t)&dirent, - dirent.d_reclen, uio)) != 0) + } + strlcpy(dirent.d_name, name, DEVMAXNAMESIZE); + if ((error = uiomove((caddr_t)&dirent, + dirent.d_reclen, uio)) != 0) { break; + } } pos += reclen; - if((nodenumber >1) && name_node) + if ((nodenumber > 1) && name_node) { name_node = name_node->de_next; + } nodenumber++; } - DEVFS_UNLOCK(p); + DEVFS_UNLOCK(); uio->uio_offset = pos; - return (error); + devfs_consider_time_update(dir_node, DEVFS_UPDATE_ACCESS); + + return error; } /* */ static int -devfs_readlink(struct vop_readlink_args *ap) - /*struct vop_readlink_args { - struct vnode *a_vp; - struct uio *a_uio; - struct ucred *a_cred; - } */ +devfs_readlink(struct vnop_readlink_args *ap) +/*struct vnop_readlink_args { + * struct vnode *a_vp; + * struct uio *a_uio; + * vfs_context_t a_context; + * } */ { struct vnode *vp = ap->a_vp; struct uio *uio = ap->a_uio; @@ -1369,238 +1442,259 @@ devfs_readlink(struct vop_readlink_args *ap) /* set up refs to dir */ lnk_node = VTODN(vp); - if(lnk_node->dn_type != DEV_SLNK) - return(EINVAL); - if ((error = VOP_ACCESS(vp, VREAD, ap->a_cred, NULL)) != 0) { /* XXX */ - return error; + + if (lnk_node->dn_type != DEV_SLNK) { + error = EINVAL; + goto out; } - error = uiomove(lnk_node->dn_typeinfo.Slnk.name, - lnk_node->dn_typeinfo.Slnk.namelen, uio); + error = uiomove(lnk_node->dn_typeinfo.Slnk.name, + lnk_node->dn_typeinfo.Slnk.namelen, uio); +out: return error; } static int -devfs_abortop(struct vop_abortop_args *ap) - /*struct vop_abortop_args { - struct vnode *a_dvp; - struct componentname *a_cnp; - } */ +devfs_reclaim(struct vnop_reclaim_args *ap) +/*struct vnop_reclaim_args { + * struct vnode *a_vp; + * } */ { - if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) { - FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI); - } - return 0; -} + struct vnode * vp = ap->a_vp; + devnode_t * dnp; + DEVFS_LOCK(); -static int -devfs_reclaim(struct vop_reclaim_args *ap) - /*struct vop_reclaim_args { - struct vnode *a_vp; - } */ -{ - struct vnode * vp = ap->a_vp; - devnode_t * dnp = VTODN(vp); - - if (dnp) { - /* - * do the same as devfs_inactive in case it is not called - * before us (can that ever happen?) - */ - dnp->dn_vn = NULL; - vp->v_data = NULL; - if (dnp->dn_delete) { - devnode_free(dnp); + dnp = VTODN(vp); + + if (dnp) { + /* If this is a cloning device, it didn't have a dn_vn anyway */ + dnp->dn_vn = NULL; + vnode_clearfsnode(vp); + + /* This could delete the node, if we are the last vnode */ + devfs_rele_node(dnp); } - } - return(0); + DEVFS_UNLOCK(); + + return 0; } + /* - * Print out the contents of a /devfs vnode. + * Get configurable pathname variables. */ static int -devfs_print(struct vop_print_args *ap) - /*struct vop_print_args { - struct vnode *a_vp; - } */ +devs_vnop_pathconf( + struct vnop_pathconf_args /* { + * struct vnode *a_vp; + * int a_name; + * int *a_retval; + * vfs_context_t a_context; + * } */*ap) { + switch (ap->a_name) { + case _PC_LINK_MAX: + /* arbitrary limit matching HFS; devfs has no hard limit */ + *ap->a_retval = 32767; + break; + case _PC_NAME_MAX: + *ap->a_retval = DEVMAXNAMESIZE - 1; /* includes NUL */ + break; + case _PC_PATH_MAX: + *ap->a_retval = DEVMAXPATHSIZE - 1; /* XXX nonconformant */ + break; + case _PC_CHOWN_RESTRICTED: + *ap->a_retval = 200112; /* _POSIX_CHOWN_RESTRICTED */ + break; + case _PC_NO_TRUNC: + *ap->a_retval = 0; + break; + case _PC_CASE_SENSITIVE: + *ap->a_retval = 1; + break; + case _PC_CASE_PRESERVING: + *ap->a_retval = 1; + break; + default: + return EINVAL; + } - return (0); + return 0; } + + /**************************************************************************\ * pseudo ops * \**************************************************************************/ /* * - * struct vop_inactive_args { + * struct vnop_inactive_args { * struct vnode *a_vp; - * struct proc *a_p; - * } + * vfs_context_t a_context; + * } */ static int -devfs_inactive(struct vop_inactive_args *ap) +devfs_inactive(__unused struct vnop_inactive_args *ap) { - struct vnode * vp = ap->a_vp; - devnode_t * dnp = VTODN(vp); - - if (dnp) { - dnp->dn_vn = NULL; - vp->v_data = NULL; - if (dnp->dn_delete) { - devnode_free(dnp); - } - } - VOP_UNLOCK(vp, 0, ap->a_p); - return (0); + vnode_t vp = ap->a_vp; + devnode_t *dnp = VTODN(vp); + + /* + * Cloned vnodes are not linked in anywhere, so they + * can just be recycled. + */ + if (dnp->dn_clone != NULL) { + vnode_recycle(vp); + } + + return 0; } -int -devfs_update(ap) - struct vop_update_args /* { - struct vnode *a_vp; - struct timeval *a_access; - struct timeval *a_modify; - int a_waitfor; - } */ *ap; +/* + * called with DEVFS_LOCK held + */ +static int +devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify) { - register struct fs *fs; - int error; devnode_t * ip; + struct timeval now; - ip = VTODN(ap->a_vp); - if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) { - ip->dn_flags &= - ~(DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE); - return (0); - } - if ((ip->dn_flags & - (DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE)) == 0) - return (0); - dn_times(ip, time, time); - return (0); + ip = VTODN(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) { + ip->dn_access = 0; + ip->dn_change = 0; + ip->dn_update = 0; + + return 0; + } + + DEVFS_ATTR_LOCK_SPIN(); + microtime(&now); + dn_times_locked(ip, access, modify, &now, DEVFS_UPDATE_ACCESS | DEVFS_UPDATE_MOD); + DEVFS_ATTR_UNLOCK(); + + return 0; } #define VOPFUNC int (*)(void *) /* The following ops are used by directories and symlinks */ -int (**devfs_vnodeop_p)(void *); -static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { - { &vop_default_desc, (VOPFUNC)vn_default_error }, - { &vop_lookup_desc, (VOPFUNC)devfs_lookup }, /* lookup */ - { &vop_create_desc, (VOPFUNC)err_create }, /* create */ - { &vop_whiteout_desc, (VOPFUNC)err_whiteout }, /* whiteout */ - { &vop_mknod_desc, (VOPFUNC)devfs_mknod }, /* mknod */ - { &vop_open_desc, (VOPFUNC)nop_open }, /* open */ - { &vop_close_desc, (VOPFUNC)devfs_close }, /* close */ - { &vop_access_desc, (VOPFUNC)devfs_access }, /* access */ - { &vop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */ - { &vop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */ - { &vop_read_desc, (VOPFUNC)devfs_read }, /* read */ - { &vop_write_desc, (VOPFUNC)devfs_write }, /* write */ - { &vop_lease_desc, (VOPFUNC)nop_lease }, /* lease */ - { &vop_ioctl_desc, (VOPFUNC)err_ioctl }, /* ioctl */ - { &vop_select_desc, (VOPFUNC)err_select }, /* select */ - { &vop_revoke_desc, (VOPFUNC)err_revoke }, /* revoke */ - { &vop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */ - { &vop_fsync_desc, (VOPFUNC)nop_fsync }, /* fsync */ - { &vop_seek_desc, (VOPFUNC)err_seek }, /* seek */ - { &vop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */ - { &vop_link_desc, (VOPFUNC)devfs_link }, /* link */ - { &vop_rename_desc, (VOPFUNC)devfs_rename }, /* rename */ - { &vop_mkdir_desc, (VOPFUNC)err_mkdir }, /* mkdir */ - { &vop_rmdir_desc, (VOPFUNC)err_rmdir }, /* rmdir */ - { &vop_symlink_desc, (VOPFUNC)devfs_symlink }, /* symlink */ - { &vop_readdir_desc, (VOPFUNC)devfs_readdir }, /* readdir */ - { &vop_readlink_desc, (VOPFUNC)devfs_readlink }, /* readlink */ - { &vop_abortop_desc, (VOPFUNC)devfs_abortop }, /* abortop */ - { &vop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */ - { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */ - { &vop_lock_desc, (VOPFUNC)nop_lock }, /* lock */ - { &vop_unlock_desc, (VOPFUNC)nop_unlock }, /* unlock */ - { &vop_bmap_desc, (VOPFUNC)err_bmap }, /* bmap */ - { &vop_strategy_desc, (VOPFUNC)err_strategy }, /* strategy */ - { &vop_print_desc, (VOPFUNC)err_print }, /* print */ - { &vop_islocked_desc, (VOPFUNC)nop_islocked }, /* islocked */ - { &vop_pathconf_desc, (VOPFUNC)err_pathconf }, /* pathconf */ - { &vop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */ - { &vop_blkatoff_desc, (VOPFUNC)err_blkatoff }, /* blkatoff */ - { &vop_valloc_desc, (VOPFUNC)err_valloc }, /* valloc */ - { &vop_reallocblks_desc, (VOPFUNC)err_reallocblks }, /* reallocblks */ - { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */ - { &vop_truncate_desc, (VOPFUNC)err_truncate }, /* truncate */ - { &vop_update_desc, (VOPFUNC)devfs_update }, /* update */ - { &vop_bwrite_desc, (VOPFUNC)err_bwrite }, - { &vop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */ - { &vop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */ - { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */ - { &vop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff */ - { &vop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */ - { &vop_cmap_desc, (VOPFUNC)err_cmap }, /* cmap */ - { (struct vnodeop_desc*)NULL, (int(*)())NULL } +int(**devfs_vnodeop_p)(void *); +const static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = { + { .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)vn_default_error }, + { .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)devfs_lookup }, /* lookup */ + { .opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC)err_create }, /* create */ + { .opve_op = &vnop_whiteout_desc, .opve_impl = (VOPFUNC)err_whiteout }, /* whiteout */ + { .opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC)devfs_mknod }, /* mknod */ + { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)nop_open }, /* open */ + { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfs_close }, /* close */ + { .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr }, /* getattr */ + { .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr }, /* setattr */ + { .opve_op = &vnop_read_desc, .opve_impl = (VOPFUNC)devfs_read }, /* read */ + { .opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC)devfs_write }, /* write */ + { .opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC)err_ioctl }, /* ioctl */ + { .opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)err_select }, /* select */ + { .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)err_revoke }, /* revoke */ + { .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC)err_mmap }, /* mmap */ + { .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)nop_fsync }, /* fsync */ + { .opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC)devfs_vnop_remove }, /* remove */ + { .opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC)devfs_link }, /* link */ + { .opve_op = &vnop_rename_desc, .opve_impl = (VOPFUNC)devfs_rename }, /* rename */ + { .opve_op = &vnop_mkdir_desc, .opve_impl = (VOPFUNC)devfs_mkdir }, /* mkdir */ + { .opve_op = &vnop_rmdir_desc, .opve_impl = (VOPFUNC)devfs_rmdir }, /* rmdir */ + { .opve_op = &vnop_symlink_desc, .opve_impl = (VOPFUNC)devfs_symlink }, /* symlink */ + { .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)devfs_readdir }, /* readdir */ + { .opve_op = &vnop_readlink_desc, .opve_impl = (VOPFUNC)devfs_readlink }, /* readlink */ + { .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive }, /* inactive */ + { .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim }, /* reclaim */ + { .opve_op = &vnop_strategy_desc, .opve_impl = (VOPFUNC)err_strategy }, /* strategy */ + { .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)devs_vnop_pathconf }, /* pathconf */ + { .opve_op = &vnop_advlock_desc, .opve_impl = (VOPFUNC)err_advlock }, /* advlock */ + { .opve_op = &vnop_bwrite_desc, .opve_impl = (VOPFUNC)err_bwrite }, + { .opve_op = &vnop_pagein_desc, .opve_impl = (VOPFUNC)err_pagein }, /* Pagein */ + { .opve_op = &vnop_pageout_desc, .opve_impl = (VOPFUNC)err_pageout }, /* Pageout */ + { .opve_op = &vnop_copyfile_desc, .opve_impl = (VOPFUNC)err_copyfile }, /* Copyfile */ + { .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)err_blktooff }, /* blktooff */ + { .opve_op = &vnop_offtoblk_desc, .opve_impl = (VOPFUNC)err_offtoblk }, /* offtoblk */ + { .opve_op = &vnop_blockmap_desc, .opve_impl = (VOPFUNC)err_blockmap }, /* blockmap */ +#if CONFIG_MACF + { .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel }, /* setlabel */ +#endif + { .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL } }; -struct vnodeopv_desc devfs_vnodeop_opv_desc = - { &devfs_vnodeop_p, devfs_vnodeop_entries }; +const struct vnodeopv_desc devfs_vnodeop_opv_desc = +{ .opv_desc_vector_p = &devfs_vnodeop_p, .opv_desc_ops = devfs_vnodeop_entries }; /* The following ops are used by the device nodes */ -int (**devfs_spec_vnodeop_p)(void *); -static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = { - { &vop_default_desc, (VOPFUNC)vn_default_error }, - { &vop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */ - { &vop_create_desc, (VOPFUNC)spec_create }, /* create */ - { &vop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */ - { &vop_open_desc, (VOPFUNC)spec_open }, /* open */ - { &vop_close_desc, (VOPFUNC)devfsspec_close }, /* close */ - { &vop_access_desc, (VOPFUNC)devfs_access }, /* access */ - { &vop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */ - { &vop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */ - { &vop_read_desc, (VOPFUNC)devfsspec_read }, /* read */ - { &vop_write_desc, (VOPFUNC)devfsspec_write }, /* write */ - { &vop_lease_desc, (VOPFUNC)spec_lease_check }, /* lease */ - { &vop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */ - { &vop_select_desc, (VOPFUNC)spec_select }, /* select */ - { &vop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */ - { &vop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */ - { &vop_fsync_desc, (VOPFUNC)spec_fsync }, /* fsync */ - { &vop_seek_desc, (VOPFUNC)spec_seek }, /* seek */ - { &vop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */ - { &vop_link_desc, (VOPFUNC)devfs_link }, /* link */ - { &vop_rename_desc, (VOPFUNC)spec_rename }, /* rename */ - { &vop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */ - { &vop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */ - { &vop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */ - { &vop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */ - { &vop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */ - { &vop_abortop_desc, (VOPFUNC)spec_abortop }, /* abortop */ - { &vop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */ - { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */ - { &vop_lock_desc, (VOPFUNC)nop_lock }, /* lock */ - { &vop_unlock_desc, (VOPFUNC)nop_unlock }, /* unlock */ - { &vop_bmap_desc, (VOPFUNC)spec_bmap }, /* bmap */ - { &vop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */ - { &vop_print_desc, (VOPFUNC)devfs_print }, /* print */ - { &vop_islocked_desc, (VOPFUNC)nop_islocked }, /* islocked */ - { &vop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */ - { &vop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */ - { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff }, /* blkatoff */ - { &vop_valloc_desc, (VOPFUNC)spec_valloc }, /* valloc */ - { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks }, /* reallocblks */ - { &vop_vfree_desc, (VOPFUNC)nop_vfree }, /* vfree */ - { &vop_truncate_desc, (VOPFUNC)spec_truncate }, /* truncate */ - { &vop_update_desc, (VOPFUNC)devfs_update }, /* update */ - { &vop_bwrite_desc, (VOPFUNC)vn_bwrite }, - { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */ - { &vop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */ - { &vop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */ - { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */ - { &vop_blktooff_desc, (VOPFUNC)spec_blktooff }, /* blktooff */ - { &vop_blktooff_desc, (VOPFUNC)spec_offtoblk }, /* blkofftoblk */ - { &vop_cmap_desc, (VOPFUNC)spec_cmap }, /* cmap */ - { (struct vnodeop_desc*)NULL, (int(*)())NULL } +int(**devfs_spec_vnodeop_p)(void *); +const static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = { + { .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)vn_default_error }, + { .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)spec_lookup }, /* lookup */ + { .opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC)spec_create }, /* create */ + { .opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC)spec_mknod }, /* mknod */ + { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)spec_open }, /* open */ + { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfsspec_close }, /* close */ + { .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr }, /* getattr */ + { .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr }, /* setattr */ + { .opve_op = &vnop_read_desc, .opve_impl = (VOPFUNC)devfsspec_read }, /* read */ + { .opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC)devfsspec_write }, /* write */ + { .opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC)spec_ioctl }, /* ioctl */ + { .opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)spec_select }, /* select */ + { .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)spec_revoke }, /* revoke */ + { .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC)spec_mmap }, /* mmap */ + { .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)spec_fsync }, /* fsync */ + { .opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC)devfs_vnop_remove }, /* remove */ + { .opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC)devfs_link }, /* link */ + { .opve_op = &vnop_rename_desc, .opve_impl = (VOPFUNC)spec_rename }, /* rename */ + { .opve_op = &vnop_mkdir_desc, .opve_impl = (VOPFUNC)spec_mkdir }, /* mkdir */ + { .opve_op = &vnop_rmdir_desc, .opve_impl = (VOPFUNC)spec_rmdir }, /* rmdir */ + { .opve_op = &vnop_symlink_desc, .opve_impl = (VOPFUNC)spec_symlink }, /* symlink */ + { .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)spec_readdir }, /* readdir */ + { .opve_op = &vnop_readlink_desc, .opve_impl = (VOPFUNC)spec_readlink }, /* readlink */ + { .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive }, /* inactive */ + { .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim }, /* reclaim */ + { .opve_op = &vnop_strategy_desc, .opve_impl = (VOPFUNC)spec_strategy }, /* strategy */ + { .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)spec_pathconf }, /* pathconf */ + { .opve_op = &vnop_advlock_desc, .opve_impl = (VOPFUNC)spec_advlock }, /* advlock */ + { .opve_op = &vnop_bwrite_desc, .opve_impl = (VOPFUNC)vn_bwrite }, + { .opve_op = &vnop_pagein_desc, .opve_impl = (VOPFUNC)err_pagein }, /* Pagein */ + { .opve_op = &vnop_pageout_desc, .opve_impl = (VOPFUNC)err_pageout }, /* Pageout */ + { .opve_op = &vnop_copyfile_desc, .opve_impl = (VOPFUNC)err_copyfile }, /* Copyfile */ + { .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)spec_blktooff }, /* blktooff */ + { .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)spec_offtoblk }, /* blkofftoblk */ + { .opve_op = &vnop_blockmap_desc, .opve_impl = (VOPFUNC)spec_blockmap }, /* blockmap */ +#if CONFIG_MACF + { .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel }, /* setlabel */ +#endif + { .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL } }; -struct vnodeopv_desc devfs_spec_vnodeop_opv_desc = - { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries }; - +const struct vnodeopv_desc devfs_spec_vnodeop_opv_desc = +{ .opv_desc_vector_p = &devfs_spec_vnodeop_p, .opv_desc_ops = devfs_spec_vnodeop_entries }; + + +#if FDESC +int(**devfs_devfd_vnodeop_p)(void*); +const static struct vnodeopv_entry_desc devfs_devfd_vnodeop_entries[] = { + { .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)vn_default_error }, + { .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)devfs_devfd_lookup}, /* lookup */ + { .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)nop_open }, /* open */ + { .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfs_close }, /* close */ + { .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr }, /* getattr */ + { .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr }, /* setattr */ + { .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)err_revoke }, /* revoke */ + { .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)nop_fsync }, /* fsync */ + { .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)devfs_devfd_readdir}, /* readdir */ + { .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive }, /* inactive */ + { .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim }, /* reclaim */ + { .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)devs_vnop_pathconf }, /* pathconf */ +#if CONFIG_MACF + { .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel }, /* setlabel */ +#endif + { .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL } +}; +const struct vnodeopv_desc devfs_devfd_vnodeop_opv_desc = +{ .opv_desc_vector_p = &devfs_devfd_vnodeop_p, .opv_desc_ops = devfs_devfd_vnodeop_entries}; +#endif /* FDESC */