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