xref: /xnu-12377.61.12/bsd/miscfs/devfs/devfs_vnops.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 /*
2  * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /*
29  * Copyright 1997,1998 Julian Elischer.  All rights reserved.
30  * [email protected]
31  *
32  * Redistribution and use in source and binary forms, with or without
33  * modification, are permitted provided that the following conditions are
34  * met:
35  *  1. Redistributions of source code must retain the above copyright
36  *     notice, this list of conditions and the following disclaimer.
37  *  2. Redistributions in binary form must reproduce the above copyright notice,
38  *     this list of conditions and the following disclaimer in the documentation
39  *     and/or other materials provided with the distribution.
40  *
41  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
42  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
43  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44  * DISCLAIMED.  IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
45  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
47  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
48  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  * devfs_vnops.c
54  */
55 
56 /*
57  * HISTORY
58  *  Clark Warner ([email protected]) Tue Feb 10 2000
59  *  - Added err_copyfile to the vnode operations table
60  *  Dieter Siegmund ([email protected]) Thu Apr  8 14:08:19 PDT 1999
61  *  - instead of duplicating specfs here, created a vnode-ops table
62  *    that redirects most operations to specfs (as is done with ufs);
63  *  - removed routines that made no sense
64  *  - cleaned up reclaim: replaced devfs_vntodn() with a macro VTODN()
65  *  - cleaned up symlink, link locking
66  *  - added the devfs_lock to protect devfs data structures against
67  *    driver's calling devfs_add_devswf()/etc.
68  *  Dieter Siegmund ([email protected]) Wed Jul 14 13:37:59 PDT 1999
69  *  - free the devfs devnode in devfs_inactive(), not just in devfs_reclaim()
70  *    to free up kernel memory as soon as it's available
71  *  - got rid of devfsspec_{read, write}
72  *  Dieter Siegmund ([email protected]) Fri Sep 17 09:58:38 PDT 1999
73  *  - update the mod/access times
74  */
75 /*
76  * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
77  * support for mandatory and extensible security protections.  This notice
78  * is included in support of clause 2.2 (b) of the Apple Public License,
79  * Version 2.0.
80  */
81 
82 #include <sys/param.h>
83 #include <sys/systm.h>
84 #include <sys/namei.h>
85 #include <sys/kernel.h>
86 #include <sys/fcntl.h>
87 #include <sys/conf.h>
88 #include <sys/disklabel.h>
89 #include <sys/lock.h>
90 #include <sys/stat.h>
91 #include <sys/mount_internal.h>
92 #include <sys/proc.h>
93 #include <sys/kauth.h>
94 #include <sys/time.h>
95 #include <sys/vnode_internal.h>
96 #include <miscfs/specfs/specdev.h>
97 #include <sys/dirent.h>
98 #include <sys/vmmeter.h>
99 #include <sys/vm.h>
100 #include <sys/uio_internal.h>
101 
102 #if CONFIG_MACF
103 #include <security/mac_framework.h>
104 #endif
105 
106 #include "devfsdefs.h"
107 #include "devfs.h"
108 
109 #if FDESC
110 #include "fdesc.h"
111 #endif /* FDESC */
112 
113 static int              devfs_update(struct vnode *vp, struct timeval *access,
114     struct timeval *modify);
115 void                    devfs_rele_node(devnode_t *);
116 static void             devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags);
117 static boolean_t        devfs_update_needed(long now_s, long last_s);
118 static boolean_t        devfs_is_name_protected(struct vnode *dvp, const char *name);
119 static boolean_t        devfs_is_vnode_protected(struct vnode *vp);
120 void                    dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags);
121 void                    dn_times_now(devnode_t *dnp, uint32_t just_changed_flags);
122 void                    dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags);
123 
124 void
dn_times_locked(devnode_t * dnp,struct timeval * t1,struct timeval * t2,struct timeval * t3,uint32_t just_changed_flags)125 dn_times_locked(devnode_t * dnp, struct timeval *t1, struct timeval *t2, struct timeval *t3, uint32_t just_changed_flags)
126 {
127 	lck_mtx_assert(&devfs_attr_mutex, LCK_MTX_ASSERT_OWNED);
128 
129 	if (just_changed_flags & DEVFS_UPDATE_ACCESS) {
130 		dnp->dn_atime.tv_sec = t1->tv_sec;
131 		dnp->dn_atime.tv_nsec = t1->tv_usec * 1000;
132 		dnp->dn_access = 0;
133 	} else if (dnp->dn_access) {
134 		dnp->dn_atime.tv_sec = MIN(t1->tv_sec, dnp->dn_atime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
135 		dnp->dn_atime.tv_nsec = t1->tv_usec * 1000;
136 		dnp->dn_access = 0;
137 	}
138 
139 	if (just_changed_flags & DEVFS_UPDATE_MOD) {
140 		dnp->dn_mtime.tv_sec = t2->tv_sec;
141 		dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000;
142 		dnp->dn_update = 0;
143 	} else if (dnp->dn_update) {
144 		dnp->dn_mtime.tv_sec = MIN(t2->tv_sec, dnp->dn_mtime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
145 		dnp->dn_mtime.tv_nsec = t2->tv_usec * 1000;
146 		dnp->dn_update = 0;
147 	}
148 
149 	if (just_changed_flags & DEVFS_UPDATE_CHANGE) {
150 		dnp->dn_ctime.tv_sec = t3->tv_sec;
151 		dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000;
152 		dnp->dn_change = 0;
153 	} else if (dnp->dn_change) {
154 		dnp->dn_ctime.tv_sec = MIN(t3->tv_sec, dnp->dn_ctime.tv_sec + DEVFS_LAZY_UPDATE_SECONDS);
155 		dnp->dn_ctime.tv_nsec = t3->tv_usec * 1000;
156 		dnp->dn_change = 0;
157 	}
158 }
159 
160 void
dn_mark_for_delayed_times_update(devnode_t * dnp,uint32_t just_changed_flags)161 dn_mark_for_delayed_times_update(devnode_t *dnp, uint32_t just_changed_flags)
162 {
163 	if (just_changed_flags & DEVFS_UPDATE_CHANGE) {
164 		dnp->dn_change = 1;
165 	}
166 	if (just_changed_flags & DEVFS_UPDATE_ACCESS) {
167 		dnp->dn_access = 1;
168 	}
169 	if (just_changed_flags & DEVFS_UPDATE_MOD) {
170 		dnp->dn_update = 1;
171 	}
172 }
173 
174 /*
175  * Update times based on pending updates and optionally a set of new changes.
176  */
177 void
dn_times_now(devnode_t * dnp,uint32_t just_changed_flags)178 dn_times_now(devnode_t * dnp, uint32_t just_changed_flags)
179 {
180 	struct timeval now;
181 
182 	DEVFS_ATTR_LOCK_SPIN();
183 	microtime(&now);
184 	dn_times_locked(dnp, &now, &now, &now, just_changed_flags);
185 	DEVFS_ATTR_UNLOCK();
186 }
187 
188 /*
189  * Critical devfs devices cannot be renamed or removed.
190  * However, links to them may be moved/unlinked. So we block
191  * remove/rename on a per-name basis, rather than per-node.
192  */
193 static boolean_t
devfs_is_name_protected(struct vnode * dvp,const char * name)194 devfs_is_name_protected(struct vnode *dvp, const char *name)
195 {
196 	/*
197 	 * Only names in root are protected. E.g. /dev/null is protected,
198 	 * but /dev/foo/null isn't.
199 	 */
200 	if (!vnode_isvroot(dvp)) {
201 		return FALSE;
202 	}
203 
204 	if ((strcmp("console", name) == 0) ||
205 	    (strcmp("tty", name) == 0) ||
206 	    (strcmp("null", name) == 0) ||
207 	    (strcmp("zero", name) == 0) ||
208 	    (strcmp("fd", name) == 0) ||
209 	    (strcmp("klog", name) == 0)) {
210 		return TRUE;
211 	}
212 
213 	return FALSE;
214 }
215 
216 /*
217  * These devfs devices cannot have their permissions updated.
218  */
219 static boolean_t
devfs_is_vnode_protected(struct vnode * vp)220 devfs_is_vnode_protected(struct vnode *vp)
221 {
222 	struct vnode *dvp = NULLVP;
223 	const char *vname = NULL;
224 	boolean_t ret = FALSE;
225 	vnode_getparent_and_name(vp, &dvp, &vname);
226 	if (!dvp || !vname) {
227 		ret = FALSE;
228 		goto out;
229 	}
230 
231 	ret = devfs_is_name_protected(dvp, vname);
232 
233 out:
234 	if (vname) {
235 		vnode_putname(vname);
236 	}
237 	if (dvp != NULLVP) {
238 		vnode_put(dvp);
239 	}
240 
241 	return ret;
242 }
243 
244 /* Walk up to the root to find out the depth of this nested directory. */
245 static int
devfs_nested_dirs_depth(devnode_t * dir_p)246 devfs_nested_dirs_depth(devnode_t *dir_p)
247 {
248 	devdirent_t *dirent_p;
249 	int depth = 0;
250 
251 	while ((dir_p = dir_p->dn_typeinfo.Dir.parent) != NULL) {
252 		dirent_p = dir_p->dn_typeinfo.Dir.myname;
253 		/* If 'de_parent' is NULL, then we are at root. */
254 		if (dirent_p->de_parent == NULL) {
255 			break;
256 		}
257 		depth++;
258 	}
259 
260 	return depth;
261 }
262 
263 /*
264  * Recurse down to all subdirs to figure out the max depths of the deepest
265  * subdir.
266  */
267 static int
devfs_subdirs_depth(devdirent_t * parent_dirent_p)268 devfs_subdirs_depth(devdirent_t *parent_dirent_p)
269 {
270 	devnode_t *parent_dnp = parent_dirent_p->de_dnp;
271 	devdirent_t *dirent_p;
272 	int max_depths = 0;
273 
274 	/* Return depth of 0 when there is no more subdir(s). */
275 	if (parent_dnp->dn_typeinfo.Dir.entrycount == 0) {
276 		return 0;
277 	}
278 
279 	/*
280 	 * Traverse each subdir at this level to find out the max depth of its
281 	 * subdirs.
282 	 */
283 	for (dirent_p = parent_dnp->dn_typeinfo.Dir.dirlist; dirent_p;
284 	    dirent_p = dirent_p->de_next) {
285 		if (dirent_p->de_dnp->dn_type == DEV_DIR) {
286 			int depths;
287 
288 			depths = devfs_subdirs_depth(dirent_p);
289 			max_depths = MAX(depths, max_depths);
290 		}
291 	}
292 
293 	return max_depths + 1;
294 }
295 
296 /*
297  * Convert a component of a pathname into a pointer to a locked node.
298  * This is a very central and rather complicated routine.
299  * If the file system is not maintained in a strict tree hierarchy,
300  * this can result in a deadlock situation (see comments in code below).
301  *
302  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
303  * whether the name is to be looked up, created, renamed, or deleted.
304  * When CREATE, RENAME, or DELETE is specified, information usable in
305  * creating, renaming, or deleting a directory entry may be calculated.
306  * If flag has LOCKPARENT or'ed into it and the target of the pathname
307  * exists, lookup returns both the target and its parent directory locked.
308  * When creating or renaming and LOCKPARENT is specified, the target may
309  * not be ".".  When deleting and LOCKPARENT is specified, the target may
310  * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
311  * instead of two DNUNLOCKs.
312  *
313  * Overall outline of devfs_lookup:
314  *
315  *	check accessibility of directory
316  *	null terminate the component (lookup leaves the whole string alone)
317  *	look for name in cache, if found, then if at end of path
318  *	  and deleting or creating, drop it, else return name
319  *	search for name in directory, to found or notfound
320  * notfound:
321  *	if creating, return locked directory,
322  *	else return error
323  * found:
324  *	if at end of path and deleting, return information to allow delete
325  *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
326  *	  node and return info to allow rewrite
327  *	if not at end, add name to cache; if at end and neither creating
328  *	  nor deleting, add name to cache
329  * On return to lookup, remove the null termination we put in at the start.
330  *
331  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
332  */
333 static int
devfs_lookup(struct vnop_lookup_args * ap)334 devfs_lookup(struct vnop_lookup_args *ap)
335 /*struct vnop_lookup_args {
336  *       struct vnode * a_dvp; directory vnode ptr
337  *       struct vnode ** a_vpp; where to put the result
338  *       struct componentname * a_cnp; the name we want
339  *       vfs_context_t a_context;
340  *  };*/
341 {
342 	struct componentname *cnp = ap->a_cnp;
343 	vfs_context_t ctx = cnp->cn_context;
344 	struct proc *p = vfs_context_proc(ctx);
345 	struct vnode *dir_vnode = ap->a_dvp;
346 	struct vnode **result_vnode = ap->a_vpp;
347 	devnode_t *   dir_node;       /* the directory we are searching */
348 	devnode_t *   node = NULL;       /* the node we are searching for */
349 	devdirent_t * nodename;
350 	int flags = cnp->cn_flags;
351 	int op = cnp->cn_nameiop;       /* LOOKUP, CREATE, RENAME, or DELETE */
352 	int wantparent = flags & (LOCKPARENT | WANTPARENT);
353 	int error = 0;
354 	char    heldchar;       /* the char at the end of the name componet */
355 
356 retry:
357 
358 	*result_vnode = NULL; /* safe not sorry */ /*XXX*/
359 
360 	/*  okay to look at directory vnodes ourside devfs lock as they are not aliased */
361 	dir_node = VTODN(dir_vnode);
362 
363 	/*
364 	 * Make sure that our node is a directory as well.
365 	 */
366 	if (dir_node->dn_type != DEV_DIR) {
367 		return ENOTDIR;
368 	}
369 
370 	DEVFS_LOCK();
371 	/*
372 	 * temporarily terminate string component
373 	 */
374 	heldchar = cnp->cn_nameptr[cnp->cn_namelen];
375 	cnp->cn_nameptr[cnp->cn_namelen] = '\0';
376 
377 	nodename = dev_findname(dir_node, cnp->cn_nameptr);
378 	/*
379 	 * restore saved character
380 	 */
381 	cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
382 
383 	if (nodename) {
384 		/* entry exists */
385 		node = nodename->de_dnp;
386 
387 		/* Do potential vnode allocation here inside the lock
388 		 * to make sure that our device node has a non-NULL dn_vn
389 		 * associated with it.  The device node might otherwise
390 		 * get deleted out from under us (see devfs_dn_free()).
391 		 */
392 		error = devfs_dntovn(node, result_vnode, p);
393 	}
394 	DEVFS_UNLOCK();
395 
396 	if (error) {
397 		if (error == EAGAIN) {
398 			goto retry;
399 		}
400 		return error;
401 	}
402 	if (!nodename) {
403 		/*
404 		 * we haven't called devfs_dntovn if we get here
405 		 * we have not taken a reference on the node.. no
406 		 * vnode_put is necessary on these error returns
407 		 *
408 		 * If it doesn't exist and we're not the last component,
409 		 * or we're at the last component, but we're not creating
410 		 * or renaming, return ENOENT.
411 		 */
412 		if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
413 			return ENOENT;
414 		}
415 		/*
416 		 * We return with the directory locked, so that
417 		 * the parameters we set up above will still be
418 		 * valid if we actually decide to add a new entry.
419 		 * We return ni_vp == NULL to indicate that the entry
420 		 * does not currently exist; we leave a pointer to
421 		 * the (locked) directory vnode in namei_data->ni_dvp.
422 		 *
423 		 * NB - if the directory is unlocked, then this
424 		 * information cannot be used.
425 		 */
426 		return EJUSTRETURN;
427 	}
428 	/*
429 	 * from this point forward, we need to vnode_put the reference
430 	 * picked up in devfs_dntovn if we decide to return an error
431 	 */
432 
433 	/*
434 	 * If deleting, and at end of pathname, return
435 	 * parameters which can be used to remove file.
436 	 * If the wantparent flag isn't set, we return only
437 	 * the directory (in namei_data->ni_dvp), otherwise we go
438 	 * on and lock the node, being careful with ".".
439 	 */
440 	if (op == DELETE && (flags & ISLASTCN)) {
441 		/*
442 		 * we are trying to delete '.'.  What does this mean? XXX
443 		 */
444 		if (dir_node == node) {
445 			if (*result_vnode) {
446 				vnode_put(*result_vnode);
447 				*result_vnode = NULL;
448 			}
449 			if (((error = vnode_get(dir_vnode)) == 0)) {
450 				*result_vnode = dir_vnode;
451 			}
452 			return error;
453 		}
454 		return 0;
455 	}
456 
457 	/*
458 	 * If rewriting (RENAME), return the vnode and the
459 	 * information required to rewrite the present directory
460 	 * Must get node of directory entry to verify it's a
461 	 * regular file, or empty directory.
462 	 */
463 	if (op == RENAME && wantparent && (flags & ISLASTCN)) {
464 		/*
465 		 * Careful about locking second node.
466 		 * This can only occur if the target is ".".
467 		 */
468 		if (dir_node == node) {
469 			error = EISDIR;
470 			goto drop_ref;
471 		}
472 		return 0;
473 	}
474 
475 	/*
476 	 * Step through the translation in the name.  We do not unlock the
477 	 * directory because we may need it again if a symbolic link
478 	 * is relative to the current directory.  Instead we save it
479 	 * unlocked as "saved_dir_node" XXX.  We must get the target
480 	 * node before unlocking
481 	 * the directory to insure that the node will not be removed
482 	 * before we get it.  We prevent deadlock by always fetching
483 	 * nodes from the root, moving down the directory tree. Thus
484 	 * when following backward pointers ".." we must unlock the
485 	 * parent directory before getting the requested directory.
486 	 * There is a potential race condition here if both the current
487 	 * and parent directories are removed before the lock for the
488 	 * node associated with ".." returns.  We hope that this occurs
489 	 * infrequently since we cannot avoid this race condition without
490 	 * implementing a sophisticated deadlock detection algorithm.
491 	 * Note also that this simple deadlock detection scheme will not
492 	 * work if the file system has any hard links other than ".."
493 	 * that point backwards in the directory structure.
494 	 */
495 	if ((flags & ISDOTDOT) == 0 && dir_node == node) {
496 		if (*result_vnode) {
497 			vnode_put(*result_vnode);
498 			*result_vnode = NULL;
499 		}
500 		if ((error = vnode_get(dir_vnode))) {
501 			return error;
502 		}
503 		*result_vnode = dir_vnode;
504 	}
505 	return 0;
506 
507 drop_ref:
508 	if (*result_vnode) {
509 		vnode_put(*result_vnode);
510 		*result_vnode = NULL;
511 	}
512 	return error;
513 }
514 
515 static int
devfs_getattr(struct vnop_getattr_args * ap)516 devfs_getattr(struct vnop_getattr_args *ap)
517 /*struct vnop_getattr_args {
518  *       struct vnode *a_vp;
519  *       struct vnode_attr *a_vap;
520  *       kauth_cred_t a_cred;
521  *       struct proc *a_p;
522  *  } */
523 {
524 	struct vnode *vp = ap->a_vp;
525 	struct vnode_attr *vap = ap->a_vap;
526 	devnode_t *     file_node;
527 	struct timeval now;
528 
529 
530 	DEVFS_LOCK();
531 	file_node = VTODN(vp);
532 
533 	VATTR_RETURN(vap, va_mode, file_node->dn_mode);
534 
535 	/*
536 	 * Note: for DEV_CDEV and DEV_BDEV, we return the device from
537 	 * the vp, not the file_node; if we getting information on a
538 	 * cloning device, we want the cloned information, not the template.
539 	 */
540 	switch (file_node->dn_type) {
541 	case    DEV_DIR:
542 #if FDESC
543 	case    DEV_DEVFD:      /* Like a directory */
544 #endif /* FDESC */
545 		VATTR_RETURN(vap, va_rdev, 0);
546 		vap->va_mode |= (S_IFDIR);
547 		break;
548 	case    DEV_CDEV:
549 		VATTR_RETURN(vap, va_rdev, vp->v_rdev);
550 		vap->va_mode |= (S_IFCHR);
551 		break;
552 	case    DEV_BDEV:
553 		VATTR_RETURN(vap, va_rdev, vp->v_rdev);
554 		vap->va_mode |= (S_IFBLK);
555 		break;
556 	case    DEV_SLNK:
557 		VATTR_RETURN(vap, va_rdev, 0);
558 		vap->va_mode |= (S_IFLNK);
559 		break;
560 	default:
561 		VATTR_RETURN(vap, va_rdev, 0);  /* default value only */
562 	}
563 	VATTR_RETURN(vap, va_type, vp->v_type);
564 	VATTR_RETURN(vap, va_nlink, file_node->dn_links);
565 	VATTR_RETURN(vap, va_uid, file_node->dn_uid);
566 	VATTR_RETURN(vap, va_gid, file_node->dn_gid);
567 	VATTR_RETURN(vap, va_fsid, (uint32_t)VM_KERNEL_ADDRHASH(file_node->dn_dvm));
568 	VATTR_RETURN(vap, va_fileid, (uintptr_t)file_node->dn_ino);
569 	VATTR_RETURN(vap, va_data_size, file_node->dn_len);
570 
571 	/* return an override block size (advisory) */
572 	if (vp->v_type == VBLK) {
573 		VATTR_RETURN(vap, va_iosize, BLKDEV_IOSIZE);
574 	} else if (vp->v_type == VCHR) {
575 		VATTR_RETURN(vap, va_iosize, MAXPHYSIO);
576 	} else {
577 		VATTR_RETURN(vap, va_iosize, (uint32_t)vp->v_mount->mnt_vfsstat.f_iosize);
578 	}
579 
580 
581 	DEVFS_ATTR_LOCK_SPIN();
582 
583 	microtime(&now);
584 	dn_times_locked(file_node, &now, &now, &now, 0);
585 
586 	/* if the time is bogus, set it to the boot time */
587 	if (file_node->dn_ctime.tv_sec == 0) {
588 		file_node->dn_ctime.tv_sec = boottime_sec();
589 		file_node->dn_ctime.tv_nsec = 0;
590 	}
591 	if (file_node->dn_mtime.tv_sec == 0) {
592 		file_node->dn_mtime = file_node->dn_ctime;
593 	}
594 	if (file_node->dn_atime.tv_sec == 0) {
595 		file_node->dn_atime = file_node->dn_ctime;
596 	}
597 	VATTR_RETURN(vap, va_change_time, file_node->dn_ctime);
598 	VATTR_RETURN(vap, va_modify_time, file_node->dn_mtime);
599 	VATTR_RETURN(vap, va_access_time, file_node->dn_atime);
600 
601 	DEVFS_ATTR_UNLOCK();
602 
603 	VATTR_RETURN(vap, va_gen, 0);
604 	VATTR_RETURN(vap, va_filerev, 0);
605 	VATTR_RETURN(vap, va_acl, NULL);
606 
607 	/* Hide the root so Finder doesn't display it */
608 	if (vnode_isvroot(vp)) {
609 		VATTR_RETURN(vap, va_flags, UF_HIDDEN);
610 	} else {
611 		VATTR_RETURN(vap, va_flags, 0);
612 	}
613 
614 	DEVFS_UNLOCK();
615 
616 	return 0;
617 }
618 
619 static int
devfs_setattr(struct vnop_setattr_args * ap)620 devfs_setattr(struct vnop_setattr_args *ap)
621 /*struct vnop_setattr_args  {
622  *  struct vnode *a_vp;
623  *  struct vnode_attr *a_vap;
624  *  vfs_context_t a_context;
625  *  } */
626 {
627 	struct vnode *vp = ap->a_vp;
628 	struct vnode_attr *vap = ap->a_vap;
629 	int error = 0;
630 	devnode_t *     file_node;
631 	struct timeval atimeval, mtimeval;
632 
633 	DEVFS_LOCK();
634 
635 	file_node = VTODN(vp);
636 	/*
637 	 * Go through the fields and update if set.
638 	 */
639 	if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) {
640 		if (VATTR_IS_ACTIVE(vap, va_access_time)) {
641 			file_node->dn_access = 1;
642 		}
643 		if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
644 			file_node->dn_change = 1;
645 			file_node->dn_update = 1;
646 		}
647 		atimeval.tv_sec = vap->va_access_time.tv_sec;
648 		atimeval.tv_usec = (suseconds_t)(vap->va_access_time.tv_nsec / 1000);
649 		mtimeval.tv_sec = vap->va_modify_time.tv_sec;
650 		mtimeval.tv_usec = (suseconds_t)(vap->va_modify_time.tv_nsec / 1000);
651 
652 		if ((error = devfs_update(vp, &atimeval, &mtimeval))) {
653 			goto exit;
654 		}
655 	}
656 	VATTR_SET_SUPPORTED(vap, va_access_time);
657 	VATTR_SET_SUPPORTED(vap, va_change_time);
658 
659 	/*
660 	 * Change the permissions.
661 	 */
662 	if (VATTR_IS_ACTIVE(vap, va_mode)) {
663 		/*
664 		 * Don't allow permission updates of critical devfs devices
665 		 */
666 		if (devfs_is_vnode_protected(vp)) {
667 			error = EPERM;
668 			goto exit;
669 		}
670 		file_node->dn_mode &= ~07777;
671 		file_node->dn_mode |= vap->va_mode & 07777;
672 	}
673 	VATTR_SET_SUPPORTED(vap, va_mode);
674 
675 	/*
676 	 * Change the owner.
677 	 */
678 	if (VATTR_IS_ACTIVE(vap, va_uid)) {
679 		file_node->dn_uid = vap->va_uid;
680 	}
681 	VATTR_SET_SUPPORTED(vap, va_uid);
682 
683 	/*
684 	 * Change the group.
685 	 */
686 	if (VATTR_IS_ACTIVE(vap, va_gid)) {
687 		file_node->dn_gid = vap->va_gid;
688 	}
689 	VATTR_SET_SUPPORTED(vap, va_gid);
690 exit:
691 	DEVFS_UNLOCK();
692 
693 	return error;
694 }
695 
696 #if CONFIG_MACF
697 static int
devfs_setlabel(struct vnop_setlabel_args * ap)698 devfs_setlabel(struct vnop_setlabel_args *ap)
699 /* struct vnop_setlabel_args {
700  *               struct vnodeop_desc *a_desc;
701  *               struct vnode *a_vp;
702  *               struct label *a_vl;
703  *       vfs_context_t a_context;
704  *       } */
705 {
706 	struct vnode *vp;
707 	struct devnode *de;
708 
709 	vp = ap->a_vp;
710 	de = VTODN(vp);
711 
712 	mac_vnode_label_update(ap->a_context, vp, ap->a_vl);
713 	mac_devfs_label_update(vp->v_mount, de, vp);
714 
715 	return 0;
716 }
717 #endif
718 
719 static int
devfs_read(struct vnop_read_args * ap)720 devfs_read(struct vnop_read_args *ap)
721 /* struct vnop_read_args {
722  *       struct vnode *a_vp;
723  *       struct uio *a_uio;
724  *       int  a_ioflag;
725  *       vfs_context_t a_context;
726  *  } */
727 {
728 	devnode_t * dn_p = VTODN(ap->a_vp);
729 
730 	switch (ap->a_vp->v_type) {
731 	case VDIR: {
732 		dn_p->dn_access = 1;
733 
734 		return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context);
735 	}
736 	default: {
737 		printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
738 		return EINVAL;
739 	}
740 	}
741 }
742 
743 static int
devfs_close(struct vnop_close_args * ap)744 devfs_close(struct vnop_close_args *ap)
745 /* struct vnop_close_args {
746  *       struct vnode *a_vp;
747  *       int  a_fflag;
748  *       vfs_context_t a_context;
749  *  } */
750 {
751 	struct vnode *          vp = ap->a_vp;
752 	devnode_t *     dnp;
753 
754 	if (vnode_isinuse(vp, 1)) {
755 		DEVFS_LOCK();
756 		dnp = VTODN(vp);
757 		if (dnp) {
758 			dn_times_now(dnp, 0);
759 		}
760 		DEVFS_UNLOCK();
761 	}
762 	return 0;
763 }
764 
765 static int
devfsspec_close(struct vnop_close_args * ap)766 devfsspec_close(struct vnop_close_args *ap)
767 /* struct vnop_close_args {
768  *       struct vnode *a_vp;
769  *       int  a_fflag;
770  *       vfs_context_t a_context;
771  *  } */
772 {
773 	struct vnode *          vp = ap->a_vp;
774 	devnode_t *     dnp;
775 
776 	if (vnode_isinuse(vp, 0)) {
777 		DEVFS_LOCK();
778 		dnp = VTODN(vp);
779 		if (dnp) {
780 			dn_times_now(dnp, 0);
781 		}
782 		DEVFS_UNLOCK();
783 	}
784 
785 	return VOCALL(spec_vnodeop_p, VOFFSET(vnop_close), ap);
786 }
787 
788 static boolean_t
devfs_update_needed(long now_s,long last_s)789 devfs_update_needed(long now_s, long last_s)
790 {
791 	if (now_s > last_s) {
792 		if (now_s - last_s >= DEVFS_LAZY_UPDATE_SECONDS) {
793 			return TRUE;
794 		}
795 	}
796 
797 	return FALSE;
798 }
799 
800 /*
801  * Given a set of time updates required [to happen at some point], check
802  * either make those changes (and resolve other pending updates) or mark
803  * the devnode for a subsequent update.
804  */
805 static void
devfs_consider_time_update(devnode_t * dnp,uint32_t just_changed_flags)806 devfs_consider_time_update(devnode_t *dnp, uint32_t just_changed_flags)
807 {
808 	struct timeval          now;
809 	long now_s;
810 
811 	microtime(&now);
812 	now_s = now.tv_sec;
813 
814 	if (dnp->dn_change || (just_changed_flags & DEVFS_UPDATE_CHANGE)) {
815 		if (devfs_update_needed(now_s, dnp->dn_ctime.tv_sec)) {
816 			dn_times_now(dnp, just_changed_flags);
817 			return;
818 		}
819 	}
820 	if (dnp->dn_access || (just_changed_flags & DEVFS_UPDATE_ACCESS)) {
821 		if (devfs_update_needed(now_s, dnp->dn_atime.tv_sec)) {
822 			dn_times_now(dnp, just_changed_flags);
823 			return;
824 		}
825 	}
826 	if (dnp->dn_update || (just_changed_flags & DEVFS_UPDATE_MOD)) {
827 		if (devfs_update_needed(now_s, dnp->dn_mtime.tv_sec)) {
828 			dn_times_now(dnp, just_changed_flags);
829 			return;
830 		}
831 	}
832 
833 	/* Not going to do anything now--mark for later update */
834 	dn_mark_for_delayed_times_update(dnp, just_changed_flags);
835 
836 	return;
837 }
838 
839 static int
devfsspec_read(struct vnop_read_args * ap)840 devfsspec_read(struct vnop_read_args *ap)
841 /* struct vnop_read_args {
842  *       struct vnode *a_vp;
843  *       struct uio *a_uio;
844  *       int  a_ioflag;
845  *       kauth_cred_t a_cred;
846  *  } */
847 {
848 	devnode_t *     dnp = VTODN(ap->a_vp);
849 
850 	devfs_consider_time_update(dnp, DEVFS_UPDATE_ACCESS);
851 
852 	return VOCALL(spec_vnodeop_p, VOFFSET(vnop_read), ap);
853 }
854 
855 static int
devfsspec_write(struct vnop_write_args * ap)856 devfsspec_write(struct vnop_write_args *ap)
857 /* struct vnop_write_args  {
858  *       struct vnode *a_vp;
859  *       struct uio *a_uio;
860  *       int  a_ioflag;
861  *       vfs_context_t a_context;
862  *  } */
863 {
864 	devnode_t *     dnp = VTODN(ap->a_vp);
865 
866 	devfs_consider_time_update(dnp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
867 
868 	return VOCALL(spec_vnodeop_p, VOFFSET(vnop_write), ap);
869 }
870 
871 /*
872  *  Write data to a file or directory.
873  */
874 static int
devfs_write(struct vnop_write_args * ap)875 devfs_write(struct vnop_write_args *ap)
876 /* struct vnop_write_args  {
877  *       struct vnode *a_vp;
878  *       struct uio *a_uio;
879  *       int  a_ioflag;
880  *       kauth_cred_t a_cred;
881  *  } */
882 {
883 	switch (ap->a_vp->v_type) {
884 	case VDIR:
885 		return EISDIR;
886 	default:
887 		printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
888 		return EINVAL;
889 	}
890 }
891 
892 /*
893  * Deviates from UFS naming convention because there is a KPI function
894  * called devfs_remove().
895  */
896 static int
devfs_vnop_remove(struct vnop_remove_args * ap)897 devfs_vnop_remove(struct vnop_remove_args *ap)
898 /* struct vnop_remove_args  {
899  *       struct vnode *a_dvp;
900  *       struct vnode *a_vp;
901  *       struct componentname *a_cnp;
902  *  } */
903 {
904 	struct vnode *vp = ap->a_vp;
905 	struct vnode *dvp = ap->a_dvp;
906 	struct componentname *cnp = ap->a_cnp;
907 	devnode_t *  tp;
908 	devnode_t *  tdp;
909 	devdirent_t * tnp;
910 	int doingdirectory = 0;
911 	int error = 0;
912 
913 	/*
914 	 * assume that the name is null terminated as they
915 	 * are the end of the path. Get pointers to all our
916 	 * devfs structures.
917 	 */
918 
919 	DEVFS_LOCK();
920 
921 	tp = VTODN(vp);
922 	tdp = VTODN(dvp);
923 
924 
925 	tnp = dev_findname(tdp, cnp->cn_nameptr);
926 
927 	if (tnp == NULL) {
928 		error = ENOENT;
929 		goto abort;
930 	}
931 
932 	/*
933 	 * Don't allow removing critical devfs devices
934 	 */
935 	if (devfs_is_name_protected(dvp, cnp->cn_nameptr)) {
936 		error = EINVAL;
937 		goto abort;
938 	}
939 
940 	/*
941 	 * Make sure that we don't try do something stupid
942 	 */
943 	if ((tp->dn_type) == DEV_DIR) {
944 		/*
945 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
946 		 */
947 		if ((cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
948 		    || (cnp->cn_flags & ISDOTDOT)) {
949 			error = EINVAL;
950 			goto abort;
951 		}
952 		doingdirectory++;
953 	}
954 
955 	/***********************************
956 	* Start actually doing things.... *
957 	***********************************/
958 	devfs_consider_time_update(tdp, DEVFS_UPDATE_CHANGE | DEVFS_UPDATE_MOD);
959 
960 	/*
961 	 * Target must be empty if a directory and have no links
962 	 * to it. Also, ensure source and target are compatible
963 	 * (both directories, or both not directories).
964 	 */
965 	if ((doingdirectory) && (tp->dn_links > 2)) {
966 		error = ENOTEMPTY;
967 		goto abort;
968 	}
969 	dev_free_name(tnp);
970 abort:
971 	DEVFS_UNLOCK();
972 
973 	return error;
974 }
975 
976 /*
977  */
978 static int
devfs_link(struct vnop_link_args * ap)979 devfs_link(struct vnop_link_args *ap)
980 /*struct vnop_link_args  {
981  *       struct vnode *a_tdvp;
982  *       struct vnode *a_vp;
983  *       struct componentname *a_cnp;
984  *       vfs_context_t a_context;
985  *  } */
986 {
987 	struct vnode *vp = ap->a_vp;
988 	struct vnode *tdvp = ap->a_tdvp;
989 	struct componentname *cnp = ap->a_cnp;
990 	devnode_t * fp;
991 	devnode_t * tdp;
992 	devdirent_t * tnp;
993 	int error = 0;
994 
995 	/*
996 	 * First catch an arbitrary restriction for this FS
997 	 */
998 	if (cnp->cn_namelen > DEVMAXNAMESIZE) {
999 		error = ENAMETOOLONG;
1000 		goto out1;
1001 	}
1002 
1003 	/*
1004 	 * Lock our directories and get our name pointers
1005 	 * assume that the names are null terminated as they
1006 	 * are the end of the path. Get pointers to all our
1007 	 * devfs structures.
1008 	 */
1009 	/* can lookup dnode safely for tdvp outside of devfs lock as it is not aliased */
1010 	tdp = VTODN(tdvp);
1011 
1012 	if (tdvp->v_mount != vp->v_mount) {
1013 		return EXDEV;
1014 	}
1015 	DEVFS_LOCK();
1016 
1017 	fp = VTODN(vp);
1018 
1019 	/***********************************
1020 	* Start actually doing things.... *
1021 	***********************************/
1022 	dn_times_now(fp, DEVFS_UPDATE_CHANGE);
1023 
1024 	if (!error) {
1025 		error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
1026 	}
1027 out1:
1028 	DEVFS_UNLOCK();
1029 
1030 	return error;
1031 }
1032 
1033 #define MAX_NESTED_DIRS  16
1034 
1035 /*
1036  * Rename system call. Seems overly complicated to me...
1037  *      rename("foo", "bar");
1038  * is essentially
1039  *	unlink("bar");
1040  *	link("foo", "bar");
1041  *	unlink("foo");
1042  * but ``atomically''.
1043  *
1044  * When the target exists, both the directory
1045  * and target vnodes are locked.
1046  * the source and source-parent vnodes are referenced
1047  *
1048  *
1049  * Basic algorithm is:
1050  *
1051  * 1) Bump link count on source while we're linking it to the
1052  *    target.  This also ensure the inode won't be deleted out
1053  *    from underneath us while we work (it may be truncated by
1054  *    a concurrent `trunc' or `open' for creation).
1055  * 2) Link source to destination.  If destination already exists,
1056  *    delete it first.
1057  * 3) Unlink source reference to node if still around. If a
1058  *    directory was moved and the parent of the destination
1059  *    is different from the source, patch the ".." entry in the
1060  *    directory.
1061  */
1062 static int
devfs_rename(struct vnop_rename_args * ap)1063 devfs_rename(struct vnop_rename_args *ap)
1064 /*struct vnop_rename_args  {
1065  *       struct vnode *a_fdvp;
1066  *       struct vnode *a_fvp;
1067  *       struct componentname *a_fcnp;
1068  *       struct vnode *a_tdvp;
1069  *       struct vnode *a_tvp;
1070  *       struct componentname *a_tcnp;
1071  *       vfs_context_t a_context;
1072  *  } */
1073 {
1074 	struct vnode *tvp = ap->a_tvp;
1075 	struct vnode *tdvp = ap->a_tdvp;
1076 	struct vnode *fvp = ap->a_fvp;
1077 	struct vnode *fdvp = ap->a_fdvp;
1078 	struct componentname *tcnp = ap->a_tcnp;
1079 	struct componentname *fcnp = ap->a_fcnp;
1080 	devnode_t *fp, *fdp, *tp, *tdp;
1081 	devdirent_t *fnp, *tnp;
1082 	int doingdirectory = 0;
1083 	int error = 0;
1084 
1085 	DEVFS_LOCK();
1086 	/*
1087 	 * First catch an arbitrary restriction for this FS
1088 	 */
1089 	if (tcnp->cn_namelen > DEVMAXNAMESIZE) {
1090 		error = ENAMETOOLONG;
1091 		goto out;
1092 	}
1093 
1094 	/*
1095 	 * assume that the names are null terminated as they
1096 	 * are the end of the path. Get pointers to all our
1097 	 * devfs structures.
1098 	 */
1099 	tdp = VTODN(tdvp);
1100 	fdp = VTODN(fdvp);
1101 	fp = VTODN(fvp);
1102 
1103 	fnp = dev_findname(fdp, fcnp->cn_nameptr);
1104 
1105 	if (fnp == NULL) {
1106 		error = ENOENT;
1107 		goto out;
1108 	}
1109 	tp = NULL;
1110 	tnp = NULL;
1111 
1112 	if (tvp) {
1113 		tnp = dev_findname(tdp, tcnp->cn_nameptr);
1114 
1115 		if (tnp == NULL) {
1116 			error = ENOENT;
1117 			goto out;
1118 		}
1119 		tp = VTODN(tvp);
1120 	}
1121 
1122 	/*
1123 	 * Make sure that we don't try do something stupid
1124 	 */
1125 	if ((fp->dn_type) == DEV_DIR) {
1126 		/*
1127 		 * Avoid ".", "..", and aliases of "." for obvious reasons.
1128 		 */
1129 		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
1130 		    || (fcnp->cn_flags & ISDOTDOT)
1131 		    || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')
1132 		    || (tcnp->cn_flags & ISDOTDOT)
1133 		    || (tdp == fp)) {
1134 			error = EINVAL;
1135 			goto out;
1136 		}
1137 		doingdirectory++;
1138 	}
1139 
1140 	/*
1141 	 * Don't allow renaming critical devfs devices
1142 	 */
1143 	if (devfs_is_name_protected(fdvp, fcnp->cn_nameptr) ||
1144 	    devfs_is_name_protected(tdvp, tcnp->cn_nameptr)) {
1145 		error = EINVAL;
1146 		goto out;
1147 	}
1148 
1149 	/*
1150 	 * If ".." must be changed (ie the directory gets a new
1151 	 * parent) then the source directory must not be in the
1152 	 * directory hierarchy above the target, as this would
1153 	 * orphan everything below the source directory. Also
1154 	 * the user must have write permission in the source so
1155 	 * as to be able to change "..".
1156 	 */
1157 	if (doingdirectory && (tdp != fdp)) {
1158 		devnode_t * tmp, *ntmp;
1159 		tmp = tdp;
1160 		do {
1161 			if (tmp == fp) {
1162 				/* XXX unlock stuff here probably */
1163 				error = EINVAL;
1164 				goto out;
1165 			}
1166 			ntmp = tmp;
1167 		} while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
1168 	}
1169 
1170 	/*
1171 	 * If we are renaming a directory, fail the rename if the deepest subdir
1172 	 * in the target after rename is going to exceed the MAX_NESTED_DIRS limit.
1173 	 */
1174 	if (doingdirectory) {
1175 		if ((devfs_subdirs_depth(fnp) + devfs_nested_dirs_depth(tdp)) >=
1176 		    MAX_NESTED_DIRS) {
1177 			error = EMLINK;
1178 			goto out;
1179 		}
1180 	}
1181 
1182 	/***********************************
1183 	* Start actually doing things.... *
1184 	***********************************/
1185 	dn_times_now(fp, DEVFS_UPDATE_CHANGE);
1186 
1187 	/*
1188 	 * Check if just deleting a link name.
1189 	 */
1190 	if (fvp == tvp) {
1191 		if (fvp->v_type == VDIR) {
1192 			error = EINVAL;
1193 			goto out;
1194 		}
1195 		/* Release destination completely. */
1196 		dev_free_name(fnp);
1197 
1198 		DEVFS_UNLOCK();
1199 		return 0;
1200 	}
1201 	/*
1202 	 * 1) Bump link count while we're moving stuff
1203 	 *    around.  If we crash somewhere before
1204 	 *    completing our work,  too bad :)
1205 	 */
1206 	fp->dn_links++;
1207 	/*
1208 	 * If the target exists zap it (unless it's a non-empty directory)
1209 	 * We could do that as well but won't
1210 	 */
1211 	if (tp) {
1212 		/*
1213 		 * Target must be empty if a directory and have no links
1214 		 * to it. Also, ensure source and target are compatible
1215 		 * (both directories, or both not directories).
1216 		 */
1217 		if ((doingdirectory) && (tp->dn_links > 2)) {
1218 			error = ENOTEMPTY;
1219 			goto bad;
1220 		}
1221 		dev_free_name(tnp);
1222 		tp = NULL;
1223 	}
1224 	dev_add_name(tcnp->cn_nameptr, tdp, NULL, fp, &tnp);
1225 	fnp->de_dnp = NULL;
1226 	fp->dn_links--; /* one less link to it.. */
1227 
1228 	dev_free_name(fnp);
1229 bad:
1230 	fp->dn_links--; /* we added one earlier*/
1231 out:
1232 	DEVFS_UNLOCK();
1233 	return error;
1234 }
1235 
1236 static int
devfs_mkdir(struct vnop_mkdir_args * ap)1237 devfs_mkdir(struct vnop_mkdir_args *ap)
1238 /*struct vnop_mkdir_args {
1239  *       struct vnode *a_dvp;
1240  *       struct vnode **a_vpp;
1241  *       struct componentname *a_cnp;
1242  *       struct vnode_attr *a_vap;
1243  *       vfs_context_t a_context;
1244  *  } */
1245 {
1246 	struct componentname * cnp = ap->a_cnp;
1247 	vfs_context_t ctx = cnp->cn_context;
1248 	struct proc *p = vfs_context_proc(ctx);
1249 	int error = 0;
1250 	devnode_t * dir_p;
1251 	devdirent_t * nm_p;
1252 	devnode_t * dev_p;
1253 	struct vnode_attr * vap = ap->a_vap;
1254 	struct vnode * * vpp = ap->a_vpp;
1255 
1256 	DEVFS_LOCK();
1257 
1258 	dir_p = VTODN(ap->a_dvp);
1259 
1260 	/* Fail the mkdir if the depth of parent dir is already at the limit. */
1261 	if (devfs_nested_dirs_depth(dir_p) >= MAX_NESTED_DIRS) {
1262 		error = EMLINK;
1263 		goto failure;
1264 	}
1265 
1266 	error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_DIR,
1267 	    NULL, NULL, NULL, &nm_p);
1268 	if (error) {
1269 		goto failure;
1270 	}
1271 	dev_p = nm_p->de_dnp;
1272 	dev_p->dn_uid = dir_p->dn_uid;
1273 	dev_p->dn_gid = dir_p->dn_gid;
1274 	dev_p->dn_mode = vap->va_mode;
1275 	dn_copy_times(dev_p, dir_p);
1276 
1277 	error = devfs_dntovn(dev_p, vpp, p);
1278 failure:
1279 	DEVFS_UNLOCK();
1280 
1281 	return error;
1282 }
1283 
1284 /*
1285  * An rmdir is a special type of remove, which we already support; we wrap
1286  * and reexpress the arguments to call devfs_remove directly.  The only
1287  * different argument is flags, which we do not set, since it's ignored.
1288  */
1289 static int
devfs_rmdir(struct vnop_rmdir_args * ap)1290 devfs_rmdir(struct vnop_rmdir_args *ap)
1291 /* struct vnop_rmdir_args {
1292  *       struct vnode *a_dvp;
1293  *       struct vnode *a_vp;
1294  *       struct componentname *a_cnp;
1295  *       vfs_context_t a_context;
1296  *  } */
1297 {
1298 	struct vnop_remove_args ra;
1299 
1300 	ra.a_dvp = ap->a_dvp;
1301 	ra.a_vp = ap->a_vp;
1302 	ra.a_cnp = ap->a_cnp;
1303 	ra.a_flags = 0;         /* XXX */
1304 	ra.a_context = ap->a_context;
1305 
1306 	return devfs_vnop_remove(&ra);
1307 }
1308 
1309 
1310 static int
devfs_symlink(struct vnop_symlink_args * ap)1311 devfs_symlink(struct vnop_symlink_args *ap)
1312 /*struct vnop_symlink_args {
1313  *       struct vnode *a_dvp;
1314  *       struct vnode **a_vpp;
1315  *       struct componentname *a_cnp;
1316  *       struct vnode_attr *a_vap;
1317  *       char *a_target;
1318  *       vfs_context_t a_context;
1319  *  } */
1320 {
1321 	int error;
1322 	devdirent_t *newent;
1323 
1324 	DEVFS_LOCK();
1325 	error = devfs_make_symlink(VTODN(ap->a_dvp), ap->a_cnp->cn_nameptr, ap->a_vap->va_mode, ap->a_target, &newent);
1326 
1327 	if (error == 0) {
1328 		error = devfs_dntovn(newent->de_dnp, ap->a_vpp, vfs_context_proc(ap->a_context));
1329 	}
1330 
1331 	DEVFS_UNLOCK();
1332 
1333 	return error;
1334 }
1335 
1336 /* Called with devfs locked */
1337 int
devfs_make_symlink(devnode_t * dir_p,char * name,mode_t mode,char * target,devdirent_t ** newent)1338 devfs_make_symlink(devnode_t *dir_p, char *name, mode_t mode, char *target, devdirent_t **newent)
1339 {
1340 	int error = 0;
1341 	devnode_type_t typeinfo;
1342 	devdirent_t * nm_p;
1343 	devnode_t * dev_p;
1344 
1345 	typeinfo.Slnk.name = target;
1346 	typeinfo.Slnk.namelen = strlen(target);
1347 
1348 	error = dev_add_entry(name, dir_p, DEV_SLNK,
1349 	    &typeinfo, NULL, NULL, &nm_p);
1350 	if (error) {
1351 		goto failure;
1352 	}
1353 	dev_p = nm_p->de_dnp;
1354 	dev_p->dn_uid = dir_p->dn_uid;
1355 	dev_p->dn_gid = dir_p->dn_gid;
1356 	dev_p->dn_mode = mode;
1357 	dn_copy_times(dev_p, dir_p);
1358 
1359 	if (newent) {
1360 		*newent = nm_p;
1361 	}
1362 
1363 failure:
1364 
1365 	return error;
1366 }
1367 
1368 /*
1369  * Mknod vnode call
1370  */
1371 static int
devfs_mknod(struct vnop_mknod_args * ap)1372 devfs_mknod(struct vnop_mknod_args *ap)
1373 /* struct vnop_mknod_args {
1374  *       struct vnode *a_dvp;
1375  *       struct vnode **a_vpp;
1376  *       struct componentname *a_cnp;
1377  *       struct vnode_attr *a_vap;
1378  *       vfs_context_t a_context;
1379  *  } */
1380 {
1381 	struct componentname * cnp = ap->a_cnp;
1382 	vfs_context_t ctx = cnp->cn_context;
1383 	struct proc *p = vfs_context_proc(ctx);
1384 	devnode_t *     dev_p;
1385 	devdirent_t *   devent;
1386 	devnode_t *     dir_p;  /* devnode for parent directory */
1387 	struct vnode *  dvp = ap->a_dvp;
1388 	int             error = 0;
1389 	devnode_type_t  typeinfo;
1390 	struct vnode_attr *     vap = ap->a_vap;
1391 	struct vnode ** vpp = ap->a_vpp;
1392 
1393 	*vpp = NULL;
1394 	if (!(vap->va_type == VBLK) && !(vap->va_type == VCHR)) {
1395 		return EINVAL; /* only support mknod of special files */
1396 	}
1397 	typeinfo.dev = vap->va_rdev;
1398 
1399 	DEVFS_LOCK();
1400 
1401 	dir_p = VTODN(dvp);
1402 
1403 	error = dev_add_entry(cnp->cn_nameptr, dir_p,
1404 	    (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
1405 	    &typeinfo, NULL, NULL, &devent);
1406 	if (error) {
1407 		goto failure;
1408 	}
1409 	dev_p = devent->de_dnp;
1410 	error = devfs_dntovn(dev_p, vpp, p);
1411 	if (error) {
1412 		goto failure;
1413 	}
1414 	dev_p->dn_uid = vap->va_uid;
1415 	dev_p->dn_gid = vap->va_gid;
1416 	dev_p->dn_mode = vap->va_mode;
1417 	VATTR_SET_SUPPORTED(vap, va_uid);
1418 	VATTR_SET_SUPPORTED(vap, va_gid);
1419 	VATTR_SET_SUPPORTED(vap, va_mode);
1420 failure:
1421 	DEVFS_UNLOCK();
1422 
1423 	return error;
1424 }
1425 
1426 /*
1427  * Vnode op for readdir
1428  */
1429 static int
devfs_readdir(struct vnop_readdir_args * ap)1430 devfs_readdir(struct vnop_readdir_args *ap)
1431 /*struct vnop_readdir_args {
1432  *       struct vnode *a_vp;
1433  *       struct uio *a_uio;
1434  *       int a_flags;
1435  *       int *a_eofflag;
1436  *       int *a_numdirent;
1437  *       vfs_context_t a_context;
1438  *  } */
1439 {
1440 	struct vnode *vp = ap->a_vp;
1441 	struct uio *uio = ap->a_uio;
1442 	struct dirent dirent;
1443 	devnode_t * dir_node;
1444 	devdirent_t *   name_node;
1445 	const char *name;
1446 	int error = 0;
1447 	int reclen;
1448 	int nodenumber;
1449 	off_t startpos, pos;
1450 
1451 	if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF)) {
1452 		return EINVAL;
1453 	}
1454 
1455 	/*  set up refs to dir */
1456 	dir_node = VTODN(vp);
1457 	if (dir_node->dn_type != DEV_DIR) {
1458 		return ENOTDIR;
1459 	}
1460 	pos = 0;
1461 	startpos = uio->uio_offset;
1462 
1463 	DEVFS_LOCK();
1464 
1465 	name_node = dir_node->dn_typeinfo.Dir.dirlist;
1466 	nodenumber = 0;
1467 
1468 	while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0)) {
1469 		switch (nodenumber) {
1470 		case    0:
1471 			dirent.d_fileno = dir_node->dn_ino;
1472 			name = ".";
1473 			dirent.d_namlen = 1;
1474 			dirent.d_type = DT_DIR;
1475 			break;
1476 		case    1:
1477 			if (dir_node->dn_typeinfo.Dir.parent) {
1478 				dirent.d_fileno = dir_node->dn_typeinfo.Dir.parent->dn_ino;
1479 			} else {
1480 				dirent.d_fileno = dir_node->dn_ino;
1481 			}
1482 			name = "..";
1483 			dirent.d_namlen = 2;
1484 			dirent.d_type = DT_DIR;
1485 			break;
1486 		default:
1487 			dirent.d_fileno = name_node->de_dnp->dn_ino;
1488 			dirent.d_namlen = (__uint8_t) strlen(name_node->de_name);
1489 			name = name_node->de_name;
1490 			switch (name_node->de_dnp->dn_type) {
1491 			case DEV_BDEV:
1492 				dirent.d_type = DT_BLK;
1493 				break;
1494 			case DEV_CDEV:
1495 				dirent.d_type = DT_CHR;
1496 				break;
1497 			case DEV_DIR:
1498 				dirent.d_type = DT_DIR;
1499 				break;
1500 			case DEV_SLNK:
1501 				dirent.d_type = DT_LNK;
1502 				break;
1503 			default:
1504 				dirent.d_type = DT_UNKNOWN;
1505 			}
1506 		}
1507 #define GENERIC_DIRSIZ(dp) \
1508     ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1509 
1510 		reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
1511 
1512 		if (pos >= startpos) {   /* made it to the offset yet? */
1513 			if (uio_resid(uio) < reclen) { /* will it fit? */
1514 				break;
1515 			}
1516 			strlcpy(dirent.d_name, name, DEVMAXNAMESIZE);
1517 			if ((error = uiomove((caddr_t)&dirent,
1518 			    dirent.d_reclen, uio)) != 0) {
1519 				break;
1520 			}
1521 		}
1522 		pos += reclen;
1523 		if ((nodenumber > 1) && name_node) {
1524 			name_node = name_node->de_next;
1525 		}
1526 		nodenumber++;
1527 	}
1528 	DEVFS_UNLOCK();
1529 	uio->uio_offset = pos;
1530 
1531 	devfs_consider_time_update(dir_node, DEVFS_UPDATE_ACCESS);
1532 
1533 	return error;
1534 }
1535 
1536 
1537 /*
1538  */
1539 static int
devfs_readlink(struct vnop_readlink_args * ap)1540 devfs_readlink(struct vnop_readlink_args *ap)
1541 /*struct vnop_readlink_args {
1542  *       struct vnode *a_vp;
1543  *       struct uio *a_uio;
1544  *       vfs_context_t a_context;
1545  *  } */
1546 {
1547 	struct vnode *vp = ap->a_vp;
1548 	struct uio *uio = ap->a_uio;
1549 	devnode_t * lnk_node;
1550 	int error = 0;
1551 
1552 	/*  set up refs to dir */
1553 	lnk_node = VTODN(vp);
1554 
1555 	if (lnk_node->dn_type != DEV_SLNK) {
1556 		error = EINVAL;
1557 		goto out;
1558 	}
1559 	error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
1560 	    (int)lnk_node->dn_typeinfo.Slnk.namelen, uio);
1561 out:
1562 	return error;
1563 }
1564 
1565 static int
devfs_reclaim(struct vnop_reclaim_args * ap)1566 devfs_reclaim(struct vnop_reclaim_args *ap)
1567 /*struct vnop_reclaim_args {
1568  *       struct vnode *a_vp;
1569  *  } */
1570 {
1571 	struct vnode *      vp = ap->a_vp;
1572 	devnode_t *         dnp;
1573 
1574 	DEVFS_LOCK();
1575 
1576 	dnp = VTODN(vp);
1577 
1578 	if (dnp) {
1579 		/* If this is a cloning device, it didn't have a dn_vn anyway */
1580 		dnp->dn_vn = NULL;
1581 		vnode_clearfsnode(vp);
1582 
1583 		/* This could delete the node, if we are the last vnode */
1584 		devfs_rele_node(dnp);
1585 	}
1586 	DEVFS_UNLOCK();
1587 
1588 	return 0;
1589 }
1590 
1591 
1592 /*
1593  * Get configurable pathname variables.
1594  */
1595 static int
devs_vnop_pathconf(struct vnop_pathconf_args * ap)1596 devs_vnop_pathconf(
1597 	struct vnop_pathconf_args /* {
1598                                    *  struct vnode *a_vp;
1599                                    *  int a_name;
1600                                    *  int *a_retval;
1601                                    *  vfs_context_t a_context;
1602                                    *  } */*ap)
1603 {
1604 	switch (ap->a_name) {
1605 	case _PC_LINK_MAX:
1606 		/* arbitrary limit matching HFS; devfs has no hard limit */
1607 		*ap->a_retval = 32767;
1608 		break;
1609 	case _PC_NAME_MAX:
1610 		*ap->a_retval = DEVMAXNAMESIZE - 1;     /* includes NUL */
1611 		break;
1612 	case _PC_PATH_MAX:
1613 		*ap->a_retval = DEVMAXPATHSIZE - 1;     /* XXX nonconformant */
1614 		break;
1615 	case _PC_CHOWN_RESTRICTED:
1616 		*ap->a_retval = 200112;         /* _POSIX_CHOWN_RESTRICTED */
1617 		break;
1618 	case _PC_NO_TRUNC:
1619 		*ap->a_retval = 0;
1620 		break;
1621 	case _PC_CASE_SENSITIVE:
1622 		*ap->a_retval = 1;
1623 		break;
1624 	case _PC_CASE_PRESERVING:
1625 		*ap->a_retval = 1;
1626 		break;
1627 	default:
1628 		return EINVAL;
1629 	}
1630 
1631 	return 0;
1632 }
1633 
1634 
1635 
1636 /**************************************************************************\
1637 * pseudo ops *
1638 \**************************************************************************/
1639 
1640 /*
1641  *
1642  *	struct vnop_inactive_args {
1643  *		struct vnode *a_vp;
1644  *		vfs_context_t a_context;
1645  *	}
1646  */
1647 
1648 static int
devfs_inactive(__unused struct vnop_inactive_args * ap)1649 devfs_inactive(__unused struct vnop_inactive_args *ap)
1650 {
1651 	vnode_t vp = ap->a_vp;
1652 	devnode_t *dnp = VTODN(vp);
1653 
1654 	/*
1655 	 * Cloned vnodes are not linked in anywhere, so they
1656 	 * can just be recycled.
1657 	 */
1658 	if (dnp->dn_clone != NULL) {
1659 		vnode_recycle(vp);
1660 	}
1661 
1662 	return 0;
1663 }
1664 
1665 /*
1666  * called with DEVFS_LOCK held
1667  */
1668 static int
devfs_update(struct vnode * vp,struct timeval * access,struct timeval * modify)1669 devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify)
1670 {
1671 	devnode_t * ip;
1672 	struct timeval now;
1673 
1674 	ip = VTODN(vp);
1675 	if (vp->v_mount->mnt_flag & MNT_RDONLY) {
1676 		ip->dn_access = 0;
1677 		ip->dn_change = 0;
1678 		ip->dn_update = 0;
1679 
1680 		return 0;
1681 	}
1682 
1683 	DEVFS_ATTR_LOCK_SPIN();
1684 	microtime(&now);
1685 	dn_times_locked(ip, access, modify, &now, DEVFS_UPDATE_ACCESS | DEVFS_UPDATE_MOD);
1686 	DEVFS_ATTR_UNLOCK();
1687 
1688 	return 0;
1689 }
1690 
1691 #define VOPFUNC int (*)(void *)
1692 
1693 #define devfs_default_error (void (*)(void))vn_default_error
1694 
1695 /* The following ops are used by directories and symlinks */
1696 int(**devfs_vnodeop_p)(void *);
1697 const static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
1698 	{ .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)devfs_default_error },
1699 	{ .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)devfs_lookup },           /* lookup */
1700 	{ .opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC)err_create },             /* create */
1701 	{ .opve_op = &vnop_whiteout_desc, .opve_impl = (VOPFUNC)err_whiteout },         /* whiteout */
1702 	{ .opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC)devfs_mknod },             /* mknod */
1703 	{ .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)nop_open },                 /* open */
1704 	{ .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfs_close },             /* close */
1705 	{ .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr },         /* getattr */
1706 	{ .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr },         /* setattr */
1707 	{ .opve_op = &vnop_read_desc, .opve_impl = (VOPFUNC)devfs_read },               /* read */
1708 	{ .opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC)devfs_write },             /* write */
1709 	{ .opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC)err_ioctl },               /* ioctl */
1710 	{ .opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)err_select },             /* select */
1711 	{ .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)err_revoke },             /* revoke */
1712 	{ .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC)err_mmap },                 /* mmap */
1713 	{ .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)nop_fsync },               /* fsync */
1714 	{ .opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC)devfs_vnop_remove },      /* remove */
1715 	{ .opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC)devfs_link },               /* link */
1716 	{ .opve_op = &vnop_rename_desc, .opve_impl = (VOPFUNC)devfs_rename },           /* rename */
1717 	{ .opve_op = &vnop_mkdir_desc, .opve_impl = (VOPFUNC)devfs_mkdir },             /* mkdir */
1718 	{ .opve_op = &vnop_rmdir_desc, .opve_impl = (VOPFUNC)devfs_rmdir },             /* rmdir */
1719 	{ .opve_op = &vnop_symlink_desc, .opve_impl = (VOPFUNC)devfs_symlink },         /* symlink */
1720 	{ .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)devfs_readdir },         /* readdir */
1721 	{ .opve_op = &vnop_readlink_desc, .opve_impl = (VOPFUNC)devfs_readlink },       /* readlink */
1722 	{ .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive },       /* inactive */
1723 	{ .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim },         /* reclaim */
1724 	{ .opve_op = &vnop_strategy_desc, .opve_impl = (VOPFUNC)err_strategy },         /* strategy */
1725 	{ .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)devs_vnop_pathconf },   /* pathconf */
1726 	{ .opve_op = &vnop_advlock_desc, .opve_impl = (VOPFUNC)err_advlock },           /* advlock */
1727 	{ .opve_op = &vnop_bwrite_desc, .opve_impl = (VOPFUNC)err_bwrite },
1728 	{ .opve_op = &vnop_pagein_desc, .opve_impl = (VOPFUNC)err_pagein },             /* Pagein */
1729 	{ .opve_op = &vnop_pageout_desc, .opve_impl = (VOPFUNC)err_pageout },           /* Pageout */
1730 	{ .opve_op = &vnop_copyfile_desc, .opve_impl = (VOPFUNC)err_copyfile },         /* Copyfile */
1731 	{ .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)err_blktooff },         /* blktooff */
1732 	{ .opve_op = &vnop_offtoblk_desc, .opve_impl = (VOPFUNC)err_offtoblk },         /* offtoblk */
1733 	{ .opve_op = &vnop_blockmap_desc, .opve_impl = (VOPFUNC)err_blockmap },         /* blockmap */
1734 #if CONFIG_MACF
1735 	{ .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel },       /* setlabel */
1736 #endif
1737 	{ .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL }
1738 };
1739 const struct vnodeopv_desc devfs_vnodeop_opv_desc =
1740 { .opv_desc_vector_p = &devfs_vnodeop_p, .opv_desc_ops = devfs_vnodeop_entries };
1741 
1742 /* The following ops are used by the device nodes */
1743 int(**devfs_spec_vnodeop_p)(void *);
1744 const static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
1745 	{ .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)devfs_default_error },
1746 	{ .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)spec_lookup },            /* lookup */
1747 	{ .opve_op = &vnop_create_desc, .opve_impl = (VOPFUNC)spec_create },            /* create */
1748 	{ .opve_op = &vnop_mknod_desc, .opve_impl = (VOPFUNC)spec_mknod },              /* mknod */
1749 	{ .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)spec_open },                        /* open */
1750 	{ .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfsspec_close },         /* close */
1751 	{ .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr },         /* getattr */
1752 	{ .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr },         /* setattr */
1753 	{ .opve_op = &vnop_read_desc, .opve_impl = (VOPFUNC)devfsspec_read },           /* read */
1754 	{ .opve_op = &vnop_write_desc, .opve_impl = (VOPFUNC)devfsspec_write },         /* write */
1755 	{ .opve_op = &vnop_ioctl_desc, .opve_impl = (VOPFUNC)spec_ioctl },              /* ioctl */
1756 	{ .opve_op = &vnop_select_desc, .opve_impl = (VOPFUNC)spec_select },            /* select */
1757 	{ .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)spec_revoke },            /* revoke */
1758 	{ .opve_op = &vnop_mmap_desc, .opve_impl = (VOPFUNC)spec_mmap },                        /* mmap */
1759 	{ .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)spec_fsync },              /* fsync */
1760 	{ .opve_op = &vnop_remove_desc, .opve_impl = (VOPFUNC)devfs_vnop_remove },      /* remove */
1761 	{ .opve_op = &vnop_link_desc, .opve_impl = (VOPFUNC)devfs_link },               /* link */
1762 	{ .opve_op = &vnop_rename_desc, .opve_impl = (VOPFUNC)spec_rename },            /* rename */
1763 	{ .opve_op = &vnop_mkdir_desc, .opve_impl = (VOPFUNC)spec_mkdir },              /* mkdir */
1764 	{ .opve_op = &vnop_rmdir_desc, .opve_impl = (VOPFUNC)spec_rmdir },              /* rmdir */
1765 	{ .opve_op = &vnop_symlink_desc, .opve_impl = (VOPFUNC)spec_symlink },          /* symlink */
1766 	{ .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)spec_readdir },          /* readdir */
1767 	{ .opve_op = &vnop_readlink_desc, .opve_impl = (VOPFUNC)spec_readlink },                /* readlink */
1768 	{ .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive },       /* inactive */
1769 	{ .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim },         /* reclaim */
1770 	{ .opve_op = &vnop_strategy_desc, .opve_impl = (VOPFUNC)spec_strategy },                /* strategy */
1771 	{ .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)spec_pathconf },                /* pathconf */
1772 	{ .opve_op = &vnop_advlock_desc, .opve_impl = (VOPFUNC)spec_advlock },          /* advlock */
1773 	{ .opve_op = &vnop_bwrite_desc, .opve_impl = (VOPFUNC)vn_bwrite },
1774 	{ .opve_op = &vnop_pagein_desc, .opve_impl = (VOPFUNC)err_pagein },             /* Pagein */
1775 	{ .opve_op = &vnop_pageout_desc, .opve_impl = (VOPFUNC)err_pageout },           /* Pageout */
1776 	{ .opve_op = &vnop_copyfile_desc, .opve_impl = (VOPFUNC)err_copyfile },         /* Copyfile */
1777 	{ .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)spec_blktooff },        /* blktooff */
1778 	{ .opve_op = &vnop_blktooff_desc, .opve_impl = (VOPFUNC)spec_offtoblk  },       /* blkofftoblk */
1779 	{ .opve_op = &vnop_blockmap_desc, .opve_impl = (VOPFUNC)spec_blockmap },        /* blockmap */
1780 #if CONFIG_MACF
1781 	{ .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel },       /* setlabel */
1782 #endif
1783 	{ .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL }
1784 };
1785 const struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
1786 { .opv_desc_vector_p = &devfs_spec_vnodeop_p, .opv_desc_ops = devfs_spec_vnodeop_entries };
1787 
1788 
1789 #if FDESC
1790 int(**devfs_devfd_vnodeop_p)(void*);
1791 const static struct vnodeopv_entry_desc devfs_devfd_vnodeop_entries[] = {
1792 	{ .opve_op = &vnop_default_desc, .opve_impl = (VOPFUNC)devfs_default_error },
1793 	{ .opve_op = &vnop_lookup_desc, .opve_impl = (VOPFUNC)devfs_devfd_lookup},      /* lookup */
1794 	{ .opve_op = &vnop_open_desc, .opve_impl = (VOPFUNC)nop_open },                 /* open */
1795 	{ .opve_op = &vnop_close_desc, .opve_impl = (VOPFUNC)devfs_close },             /* close */
1796 	{ .opve_op = &vnop_getattr_desc, .opve_impl = (VOPFUNC)devfs_getattr },         /* getattr */
1797 	{ .opve_op = &vnop_setattr_desc, .opve_impl = (VOPFUNC)devfs_setattr },         /* setattr */
1798 	{ .opve_op = &vnop_revoke_desc, .opve_impl = (VOPFUNC)err_revoke },             /* revoke */
1799 	{ .opve_op = &vnop_fsync_desc, .opve_impl = (VOPFUNC)nop_fsync },               /* fsync */
1800 	{ .opve_op = &vnop_readdir_desc, .opve_impl = (VOPFUNC)devfs_devfd_readdir},            /* readdir */
1801 	{ .opve_op = &vnop_inactive_desc, .opve_impl = (VOPFUNC)devfs_inactive },       /* inactive */
1802 	{ .opve_op = &vnop_reclaim_desc, .opve_impl = (VOPFUNC)devfs_reclaim },         /* reclaim */
1803 	{ .opve_op = &vnop_pathconf_desc, .opve_impl = (VOPFUNC)devs_vnop_pathconf },   /* pathconf */
1804 #if CONFIG_MACF
1805 	{ .opve_op = &vnop_setlabel_desc, .opve_impl = (VOPFUNC)devfs_setlabel },       /* setlabel */
1806 #endif
1807 	{ .opve_op = (struct vnodeop_desc*)NULL, .opve_impl = (int (*)(void *))NULL }
1808 };
1809 const struct vnodeopv_desc devfs_devfd_vnodeop_opv_desc =
1810 { .opv_desc_vector_p = &devfs_devfd_vnodeop_p, .opv_desc_ops = devfs_devfd_vnodeop_entries};
1811 #endif /* FDESC */
1812