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