/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
- * @APPLE_LICENSE_HEADER_START@
- *
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
- *
* 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. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
+ * 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,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright 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:
* 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
* 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
*/
* 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 <sys/param.h>
#include <sys/systm.h>
-#include <sys/buf.h>
#include <sys/namei.h>
#include <sys/kernel.h>
#include <sys/fcntl.h>
#include <sys/disklabel.h>
#include <sys/lock.h>
#include <sys/stat.h>
-#include <sys/mount.h>
+#include <sys/mount_internal.h>
#include <sys/proc.h>
+#include <sys/kauth.h>
#include <sys/time.h>
-#include <sys/vnode.h>
+#include <sys/vnode_internal.h>
#include <miscfs/specfs/specdev.h>
#include <sys/dirent.h>
#include <sys/vmmeter.h>
#include <sys/vm.h>
+#include <sys/uio_internal.h>
+
+#if CONFIG_MACF
+#include <security/mac_framework.h>
+#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.
* 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 */
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
* 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
* 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;
}
/*
* 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;
}
/*
* 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;
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;
}
/*
/*
* 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;
}
}
/***********************************
- * 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
* 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");
* 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;
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.
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);
}
/*
/*
* 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;
}
/***********************************
- * 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
/*
* 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;
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;
/* 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 */