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