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