2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 * Copyright 1997,1998 Julian Elischer. All rights reserved.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions are
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.
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
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
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,
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>
88 #include <sys/disklabel.h>
91 #include <sys/mount_internal.h>
93 #include <sys/kauth.h>
95 #include <sys/vnode_internal.h>
96 #include <miscfs/specfs/specdev.h>
97 #include <sys/dirent.h>
98 #include <sys/vmmeter.h>
100 #include <sys/uio_internal.h>
103 #include <security/mac_framework.h>
106 #include "devfsdefs.h"
113 static int devfs_update(struct vnode
*vp
, struct timeval
*access
,
114 struct timeval
*modify
);
115 void devfs_rele_node(devnode_t
*);
116 static void devfs_consider_time_update(devnode_t
*dnp
, uint32_t just_changed_flags
);
117 static boolean_t
devfs_update_needed(long now_s
, long last_s
);
118 static boolean_t
devfs_is_name_protected(struct vnode
*dvp
, const char *name
);
119 void dn_times_locked(devnode_t
* dnp
, struct timeval
*t1
, struct timeval
*t2
, struct timeval
*t3
, uint32_t just_changed_flags
);
120 void dn_times_now(devnode_t
*dnp
, uint32_t just_changed_flags
);
121 void dn_mark_for_delayed_times_update(devnode_t
*dnp
, uint32_t just_changed_flags
);
124 dn_times_locked(devnode_t
* dnp
, struct timeval
*t1
, struct timeval
*t2
, struct timeval
*t3
, uint32_t just_changed_flags
)
127 lck_mtx_assert(&devfs_attr_mutex
, LCK_MTX_ASSERT_OWNED
);
129 if (just_changed_flags
& DEVFS_UPDATE_ACCESS
) {
130 dnp
->dn_atime
.tv_sec
= t1
->tv_sec
;
131 dnp
->dn_atime
.tv_nsec
= t1
->tv_usec
* 1000;
133 } else if (dnp
->dn_access
) {
134 dnp
->dn_atime
.tv_sec
= MIN(t1
->tv_sec
, dnp
->dn_atime
.tv_sec
+ DEVFS_LAZY_UPDATE_SECONDS
);
135 dnp
->dn_atime
.tv_nsec
= t1
->tv_usec
* 1000;
139 if (just_changed_flags
& DEVFS_UPDATE_MOD
) {
140 dnp
->dn_mtime
.tv_sec
= t2
->tv_sec
;
141 dnp
->dn_mtime
.tv_nsec
= t2
->tv_usec
* 1000;
143 } else if (dnp
->dn_update
) {
144 dnp
->dn_mtime
.tv_sec
= MIN(t2
->tv_sec
, dnp
->dn_mtime
.tv_sec
+ DEVFS_LAZY_UPDATE_SECONDS
);
145 dnp
->dn_mtime
.tv_nsec
= t2
->tv_usec
* 1000;
149 if (just_changed_flags
& DEVFS_UPDATE_CHANGE
) {
150 dnp
->dn_ctime
.tv_sec
= t3
->tv_sec
;
151 dnp
->dn_ctime
.tv_nsec
= t3
->tv_usec
* 1000;
153 } else if (dnp
->dn_change
) {
154 dnp
->dn_ctime
.tv_sec
= MIN(t3
->tv_sec
, dnp
->dn_ctime
.tv_sec
+ DEVFS_LAZY_UPDATE_SECONDS
);
155 dnp
->dn_ctime
.tv_nsec
= t3
->tv_usec
* 1000;
161 dn_mark_for_delayed_times_update(devnode_t
*dnp
, uint32_t just_changed_flags
)
163 if (just_changed_flags
& DEVFS_UPDATE_CHANGE
) {
166 if (just_changed_flags
& DEVFS_UPDATE_ACCESS
) {
169 if (just_changed_flags
& DEVFS_UPDATE_MOD
) {
175 * Update times based on pending updates and optionally a set of new changes.
178 dn_times_now(devnode_t
* dnp
, uint32_t just_changed_flags
)
182 DEVFS_ATTR_LOCK_SPIN();
184 dn_times_locked(dnp
, &now
, &now
, &now
, just_changed_flags
);
189 * Critical devfs devices cannot be renamed or removed.
190 * However, links to them may be moved/unlinked. So we block
191 * remove/rename on a per-name basis, rather than per-node.
194 devfs_is_name_protected(struct vnode
*dvp
, const char *name
)
197 * Only names in root are protected. E.g. /dev/null is protected,
198 * but /dev/foo/null isn't.
200 if (!vnode_isvroot(dvp
))
203 if ((strcmp("console", name
) == 0) ||
204 (strcmp("tty", name
) == 0) ||
205 (strcmp("null", name
) == 0) ||
206 (strcmp("zero", name
) == 0) ||
207 (strcmp("klog", name
) == 0)) {
217 * Convert a component of a pathname into a pointer to a locked node.
218 * This is a very central and rather complicated routine.
219 * If the file system is not maintained in a strict tree hierarchy,
220 * this can result in a deadlock situation (see comments in code below).
222 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
223 * whether the name is to be looked up, created, renamed, or deleted.
224 * When CREATE, RENAME, or DELETE is specified, information usable in
225 * creating, renaming, or deleting a directory entry may be calculated.
226 * If flag has LOCKPARENT or'ed into it and the target of the pathname
227 * exists, lookup returns both the target and its parent directory locked.
228 * When creating or renaming and LOCKPARENT is specified, the target may
229 * not be ".". When deleting and LOCKPARENT is specified, the target may
230 * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
231 * instead of two DNUNLOCKs.
233 * Overall outline of devfs_lookup:
235 * check accessibility of directory
236 * null terminate the component (lookup leaves the whole string alone)
237 * look for name in cache, if found, then if at end of path
238 * and deleting or creating, drop it, else return name
239 * search for name in directory, to found or notfound
241 * if creating, return locked directory,
244 * if at end of path and deleting, return information to allow delete
245 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
246 * node and return info to allow rewrite
247 * if not at end, add name to cache; if at end and neither creating
248 * nor deleting, add name to cache
249 * On return to lookup, remove the null termination we put in at the start.
251 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
254 devfs_lookup(struct vnop_lookup_args
*ap
)
255 /*struct vnop_lookup_args {
256 struct vnode * a_dvp; directory vnode ptr
257 struct vnode ** a_vpp; where to put the result
258 struct componentname * a_cnp; the name we want
259 vfs_context_t a_context;
262 struct componentname
*cnp
= ap
->a_cnp
;
263 vfs_context_t ctx
= cnp
->cn_context
;
264 struct proc
*p
= vfs_context_proc(ctx
);
265 struct vnode
*dir_vnode
= ap
->a_dvp
;
266 struct vnode
**result_vnode
= ap
->a_vpp
;
267 devnode_t
* dir_node
; /* the directory we are searching */
268 devnode_t
* node
= NULL
; /* the node we are searching for */
269 devdirent_t
* nodename
;
270 int flags
= cnp
->cn_flags
;
271 int op
= cnp
->cn_nameiop
; /* LOOKUP, CREATE, RENAME, or DELETE */
272 int wantparent
= flags
& (LOCKPARENT
|WANTPARENT
);
274 char heldchar
; /* the char at the end of the name componet */
278 *result_vnode
= NULL
; /* safe not sorry */ /*XXX*/
280 /* okay to look at directory vnodes ourside devfs lock as they are not aliased */
281 dir_node
= VTODN(dir_vnode
);
284 * Make sure that our node is a directory as well.
286 if (dir_node
->dn_type
!= DEV_DIR
) {
292 * temporarily terminate string component
294 heldchar
= cnp
->cn_nameptr
[cnp
->cn_namelen
];
295 cnp
->cn_nameptr
[cnp
->cn_namelen
] = '\0';
297 nodename
= dev_findname(dir_node
, cnp
->cn_nameptr
);
299 * restore saved character
301 cnp
->cn_nameptr
[cnp
->cn_namelen
] = heldchar
;
305 node
= nodename
->de_dnp
;
307 /* Do potential vnode allocation here inside the lock
308 * to make sure that our device node has a non-NULL dn_vn
309 * associated with it. The device node might otherwise
310 * get deleted out from under us (see devfs_dn_free()).
312 error
= devfs_dntovn(node
, result_vnode
, p
);
323 * we haven't called devfs_dntovn if we get here
324 * we have not taken a reference on the node.. no
325 * vnode_put is necessary on these error returns
327 * If it doesn't exist and we're not the last component,
328 * or we're at the last component, but we're not creating
329 * or renaming, return ENOENT.
331 if (!(flags
& ISLASTCN
) || !(op
== CREATE
|| op
== RENAME
)) {
335 * We return with the directory locked, so that
336 * the parameters we set up above will still be
337 * valid if we actually decide to add a new entry.
338 * We return ni_vp == NULL to indicate that the entry
339 * does not currently exist; we leave a pointer to
340 * the (locked) directory vnode in namei_data->ni_dvp.
342 * NB - if the directory is unlocked, then this
343 * information cannot be used.
345 return (EJUSTRETURN
);
348 * from this point forward, we need to vnode_put the reference
349 * picked up in devfs_dntovn if we decide to return an error
353 * If deleting, and at end of pathname, return
354 * parameters which can be used to remove file.
355 * If the wantparent flag isn't set, we return only
356 * the directory (in namei_data->ni_dvp), otherwise we go
357 * on and lock the node, being careful with ".".
359 if (op
== DELETE
&& (flags
& ISLASTCN
)) {
362 * we are trying to delete '.'. What does this mean? XXX
364 if (dir_node
== node
) {
366 vnode_put(*result_vnode
);
367 *result_vnode
= NULL
;
369 if ( ((error
= vnode_get(dir_vnode
)) == 0) ) {
370 *result_vnode
= dir_vnode
;
378 * If rewriting (RENAME), return the vnode and the
379 * information required to rewrite the present directory
380 * Must get node of directory entry to verify it's a
381 * regular file, or empty directory.
383 if (op
== RENAME
&& wantparent
&& (flags
& ISLASTCN
)) {
386 * Careful about locking second node.
387 * This can only occur if the target is ".".
389 if (dir_node
== node
) {
397 * Step through the translation in the name. We do not unlock the
398 * directory because we may need it again if a symbolic link
399 * is relative to the current directory. Instead we save it
400 * unlocked as "saved_dir_node" XXX. We must get the target
401 * node before unlocking
402 * the directory to insure that the node will not be removed
403 * before we get it. We prevent deadlock by always fetching
404 * nodes from the root, moving down the directory tree. Thus
405 * when following backward pointers ".." we must unlock the
406 * parent directory before getting the requested directory.
407 * There is a potential race condition here if both the current
408 * and parent directories are removed before the lock for the
409 * node associated with ".." returns. We hope that this occurs
410 * infrequently since we cannot avoid this race condition without
411 * implementing a sophisticated deadlock detection algorithm.
412 * Note also that this simple deadlock detection scheme will not
413 * work if the file system has any hard links other than ".."
414 * that point backwards in the directory structure.
416 if ((flags
& ISDOTDOT
) == 0 && dir_node
== node
) {
418 vnode_put(*result_vnode
);
419 *result_vnode
= NULL
;
421 if ( (error
= vnode_get(dir_vnode
)) ) {
424 *result_vnode
= dir_vnode
;
430 vnode_put(*result_vnode
);
431 *result_vnode
= NULL
;
437 devfs_getattr(struct vnop_getattr_args
*ap
)
438 /*struct vnop_getattr_args {
440 struct vnode_attr *a_vap;
445 struct vnode
*vp
= ap
->a_vp
;
446 struct vnode_attr
*vap
= ap
->a_vap
;
447 devnode_t
* file_node
;
452 file_node
= VTODN(vp
);
454 VATTR_RETURN(vap
, va_mode
, file_node
->dn_mode
);
457 * Note: for DEV_CDEV and DEV_BDEV, we return the device from
458 * the vp, not the file_node; if we getting information on a
459 * cloning device, we want the cloned information, not the template.
461 switch (file_node
->dn_type
)
465 case DEV_DEVFD
: /* Like a directory */
467 VATTR_RETURN(vap
, va_rdev
, 0);
468 vap
->va_mode
|= (S_IFDIR
);
471 VATTR_RETURN(vap
, va_rdev
, vp
->v_rdev
);
472 vap
->va_mode
|= (S_IFCHR
);
475 VATTR_RETURN(vap
, va_rdev
, vp
->v_rdev
);
476 vap
->va_mode
|= (S_IFBLK
);
479 VATTR_RETURN(vap
, va_rdev
, 0);
480 vap
->va_mode
|= (S_IFLNK
);
483 VATTR_RETURN(vap
, va_rdev
, 0); /* default value only */
485 VATTR_RETURN(vap
, va_type
, vp
->v_type
);
486 VATTR_RETURN(vap
, va_nlink
, file_node
->dn_links
);
487 VATTR_RETURN(vap
, va_uid
, file_node
->dn_uid
);
488 VATTR_RETURN(vap
, va_gid
, file_node
->dn_gid
);
489 VATTR_RETURN(vap
, va_fsid
, (uintptr_t)file_node
->dn_dvm
);
490 VATTR_RETURN(vap
, va_fileid
, (uintptr_t)file_node
->dn_ino
);
491 VATTR_RETURN(vap
, va_data_size
, file_node
->dn_len
);
493 /* return an override block size (advisory) */
494 if (vp
->v_type
== VBLK
)
495 VATTR_RETURN(vap
, va_iosize
, BLKDEV_IOSIZE
);
496 else if (vp
->v_type
== VCHR
)
497 VATTR_RETURN(vap
, va_iosize
, MAXPHYSIO
);
499 VATTR_RETURN(vap
, va_iosize
, vp
->v_mount
->mnt_vfsstat
.f_iosize
);
502 DEVFS_ATTR_LOCK_SPIN();
505 dn_times_locked(file_node
, &now
, &now
, &now
, 0);
507 /* if the time is bogus, set it to the boot time */
508 if (file_node
->dn_ctime
.tv_sec
== 0) {
509 file_node
->dn_ctime
.tv_sec
= boottime_sec();
510 file_node
->dn_ctime
.tv_nsec
= 0;
512 if (file_node
->dn_mtime
.tv_sec
== 0)
513 file_node
->dn_mtime
= file_node
->dn_ctime
;
514 if (file_node
->dn_atime
.tv_sec
== 0)
515 file_node
->dn_atime
= file_node
->dn_ctime
;
516 VATTR_RETURN(vap
, va_change_time
, file_node
->dn_ctime
);
517 VATTR_RETURN(vap
, va_modify_time
, file_node
->dn_mtime
);
518 VATTR_RETURN(vap
, va_access_time
, file_node
->dn_atime
);
522 VATTR_RETURN(vap
, va_gen
, 0);
523 VATTR_RETURN(vap
, va_filerev
, 0);
524 VATTR_RETURN(vap
, va_acl
, NULL
);
526 /* Hide the root so Finder doesn't display it */
527 if (vnode_isvroot(vp
)) {
528 VATTR_RETURN(vap
, va_flags
, UF_HIDDEN
);
530 VATTR_RETURN(vap
, va_flags
, 0);
539 devfs_setattr(struct vnop_setattr_args
*ap
)
540 /*struct vnop_setattr_args {
542 struct vnode_attr *a_vap;
543 vfs_context_t a_context;
546 struct vnode
*vp
= ap
->a_vp
;
547 struct vnode_attr
*vap
= ap
->a_vap
;
549 devnode_t
* file_node
;
550 struct timeval atimeval
, mtimeval
;
554 file_node
= VTODN(vp
);
556 * Go through the fields and update if set.
558 if (VATTR_IS_ACTIVE(vap
, va_access_time
) || VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
561 if (VATTR_IS_ACTIVE(vap
, va_access_time
))
562 file_node
->dn_access
= 1;
563 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
564 file_node
->dn_change
= 1;
565 file_node
->dn_update
= 1;
567 atimeval
.tv_sec
= vap
->va_access_time
.tv_sec
;
568 atimeval
.tv_usec
= vap
->va_access_time
.tv_nsec
/ 1000;
569 mtimeval
.tv_sec
= vap
->va_modify_time
.tv_sec
;
570 mtimeval
.tv_usec
= vap
->va_modify_time
.tv_nsec
/ 1000;
572 if ( (error
= devfs_update(vp
, &atimeval
, &mtimeval
)) )
575 VATTR_SET_SUPPORTED(vap
, va_access_time
);
576 VATTR_SET_SUPPORTED(vap
, va_change_time
);
579 * Change the permissions.
581 if (VATTR_IS_ACTIVE(vap
, va_mode
)) {
582 file_node
->dn_mode
&= ~07777;
583 file_node
->dn_mode
|= vap
->va_mode
& 07777;
585 VATTR_SET_SUPPORTED(vap
, va_mode
);
590 if (VATTR_IS_ACTIVE(vap
, va_uid
))
591 file_node
->dn_uid
= vap
->va_uid
;
592 VATTR_SET_SUPPORTED(vap
, va_uid
);
597 if (VATTR_IS_ACTIVE(vap
, va_gid
))
598 file_node
->dn_gid
= vap
->va_gid
;
599 VATTR_SET_SUPPORTED(vap
, va_gid
);
608 devfs_setlabel(struct vnop_setlabel_args
*ap
)
609 /* struct vnop_setlabel_args {
610 struct vnodeop_desc *a_desc;
613 vfs_context_t a_context;
622 mac_vnode_label_update(ap
->a_context
, vp
, ap
->a_vl
);
623 mac_devfs_label_update(vp
->v_mount
, de
, vp
);
630 devfs_read(struct vnop_read_args
*ap
)
631 /* struct vnop_read_args {
635 vfs_context_t a_context;
638 devnode_t
* dn_p
= VTODN(ap
->a_vp
);
640 switch (ap
->a_vp
->v_type
) {
644 return VNOP_READDIR(ap
->a_vp
, ap
->a_uio
, 0, NULL
, NULL
, ap
->a_context
);
647 printf("devfs_read(): bad file type %d", ap
->a_vp
->v_type
);
654 devfs_close(struct vnop_close_args
*ap
)
655 /* struct vnop_close_args {
658 vfs_context_t a_context;
661 struct vnode
* vp
= ap
->a_vp
;
664 if (vnode_isinuse(vp
, 1)) {
668 dn_times_now(dnp
, 0);
675 devfsspec_close(struct vnop_close_args
*ap
)
676 /* struct vnop_close_args {
679 vfs_context_t a_context;
682 struct vnode
* vp
= ap
->a_vp
;
685 if (vnode_isinuse(vp
, 0)) {
689 dn_times_now(dnp
, 0);
693 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_close
), ap
));
697 devfs_update_needed(long now_s
, long last_s
)
699 if (now_s
> last_s
) {
700 if (now_s
- last_s
>= DEVFS_LAZY_UPDATE_SECONDS
) {
709 * Given a set of time updates required [to happen at some point], check
710 * either make those changes (and resolve other pending updates) or mark
711 * the devnode for a subsequent update.
714 devfs_consider_time_update(devnode_t
*dnp
, uint32_t just_changed_flags
)
722 if (dnp
->dn_change
|| (just_changed_flags
& DEVFS_UPDATE_CHANGE
)) {
723 if (devfs_update_needed(now_s
, dnp
->dn_ctime
.tv_sec
)) {
724 dn_times_now(dnp
, just_changed_flags
);
728 if (dnp
->dn_access
|| (just_changed_flags
& DEVFS_UPDATE_ACCESS
)) {
729 if (devfs_update_needed(now_s
, dnp
->dn_atime
.tv_sec
)) {
730 dn_times_now(dnp
, just_changed_flags
);
734 if (dnp
->dn_update
|| (just_changed_flags
& DEVFS_UPDATE_MOD
)) {
735 if (devfs_update_needed(now_s
, dnp
->dn_mtime
.tv_sec
)) {
736 dn_times_now(dnp
, just_changed_flags
);
741 /* Not going to do anything now--mark for later update */
742 dn_mark_for_delayed_times_update(dnp
, just_changed_flags
);
748 devfsspec_read(struct vnop_read_args
*ap
)
749 /* struct vnop_read_args {
756 devnode_t
* dnp
= VTODN(ap
->a_vp
);
758 devfs_consider_time_update(dnp
, DEVFS_UPDATE_ACCESS
);
760 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_read
), ap
));
764 devfsspec_write(struct vnop_write_args
*ap
)
765 /* struct vnop_write_args {
769 vfs_context_t a_context;
772 devnode_t
* dnp
= VTODN(ap
->a_vp
);
774 devfs_consider_time_update(dnp
, DEVFS_UPDATE_CHANGE
| DEVFS_UPDATE_MOD
);
776 return (VOCALL (spec_vnodeop_p
, VOFFSET(vnop_write
), ap
));
780 * Write data to a file or directory.
783 devfs_write(struct vnop_write_args
*ap
)
784 /* struct vnop_write_args {
791 switch (ap
->a_vp
->v_type
) {
795 printf("devfs_write(): bad file type %d", ap
->a_vp
->v_type
);
801 * Deviates from UFS naming convention because there is a KPI function
802 * called devfs_remove().
805 devfs_vnop_remove(struct vnop_remove_args
*ap
)
806 /* struct vnop_remove_args {
809 struct componentname *a_cnp;
812 struct vnode
*vp
= ap
->a_vp
;
813 struct vnode
*dvp
= ap
->a_dvp
;
814 struct componentname
*cnp
= ap
->a_cnp
;
818 int doingdirectory
= 0;
822 * assume that the name is null terminated as they
823 * are the end of the path. Get pointers to all our
833 tnp
= dev_findname(tdp
, cnp
->cn_nameptr
);
841 * Don't allow removing critical devfs devices
843 if (devfs_is_name_protected(dvp
, cnp
->cn_nameptr
)) {
849 * Make sure that we don't try do something stupid
851 if ((tp
->dn_type
) == DEV_DIR
) {
853 * Avoid ".", "..", and aliases of "." for obvious reasons.
855 if ( (cnp
->cn_namelen
== 1 && cnp
->cn_nameptr
[0] == '.')
856 || (cnp
->cn_flags
&ISDOTDOT
) ) {
863 /***********************************
864 * Start actually doing things.... *
865 ***********************************/
866 devfs_consider_time_update(tdp
, DEVFS_UPDATE_CHANGE
| DEVFS_UPDATE_MOD
);
869 * Target must be empty if a directory and have no links
870 * to it. Also, ensure source and target are compatible
871 * (both directories, or both not directories).
873 if (( doingdirectory
) && (tp
->dn_links
> 2)) {
887 devfs_link(struct vnop_link_args
*ap
)
888 /*struct vnop_link_args {
889 struct vnode *a_tdvp;
891 struct componentname *a_cnp;
892 vfs_context_t a_context;
895 struct vnode
*vp
= ap
->a_vp
;
896 struct vnode
*tdvp
= ap
->a_tdvp
;
897 struct componentname
*cnp
= ap
->a_cnp
;
904 * First catch an arbitrary restriction for this FS
906 if (cnp
->cn_namelen
> DEVMAXNAMESIZE
) {
907 error
= ENAMETOOLONG
;
912 * Lock our directories and get our name pointers
913 * assume that the names are null terminated as they
914 * are the end of the path. Get pointers to all our
917 /* can lookup dnode safely for tdvp outside of devfs lock as it is not aliased */
920 if (tdvp
->v_mount
!= vp
->v_mount
) {
927 /***********************************
928 * Start actually doing things.... *
929 ***********************************/
930 dn_times_now(fp
, DEVFS_UPDATE_CHANGE
);
933 error
= dev_add_name(cnp
->cn_nameptr
, tdp
, NULL
, fp
, &tnp
);
942 * Rename system call. Seems overly complicated to me...
943 * rename("foo", "bar");
946 * link("foo", "bar");
948 * but ``atomically''.
950 * When the target exists, both the directory
951 * and target vnodes are locked.
952 * the source and source-parent vnodes are referenced
955 * Basic algorithm is:
957 * 1) Bump link count on source while we're linking it to the
958 * target. This also ensure the inode won't be deleted out
959 * from underneath us while we work (it may be truncated by
960 * a concurrent `trunc' or `open' for creation).
961 * 2) Link source to destination. If destination already exists,
963 * 3) Unlink source reference to node if still around. If a
964 * directory was moved and the parent of the destination
965 * is different from the source, patch the ".." entry in the
969 devfs_rename(struct vnop_rename_args
*ap
)
970 /*struct vnop_rename_args {
971 struct vnode *a_fdvp;
973 struct componentname *a_fcnp;
974 struct vnode *a_tdvp;
976 struct componentname *a_tcnp;
977 vfs_context_t a_context;
980 struct vnode
*tvp
= ap
->a_tvp
;
981 struct vnode
*tdvp
= ap
->a_tdvp
;
982 struct vnode
*fvp
= ap
->a_fvp
;
983 struct vnode
*fdvp
= ap
->a_fdvp
;
984 struct componentname
*tcnp
= ap
->a_tcnp
;
985 struct componentname
*fcnp
= ap
->a_fcnp
;
986 devnode_t
*fp
, *fdp
, *tp
, *tdp
;
987 devdirent_t
*fnp
,*tnp
;
988 int doingdirectory
= 0;
993 * First catch an arbitrary restriction for this FS
995 if (tcnp
->cn_namelen
> DEVMAXNAMESIZE
) {
996 error
= ENAMETOOLONG
;
1001 * assume that the names are null terminated as they
1002 * are the end of the path. Get pointers to all our
1009 fnp
= dev_findname(fdp
, fcnp
->cn_nameptr
);
1019 tnp
= dev_findname(tdp
, tcnp
->cn_nameptr
);
1029 * Make sure that we don't try do something stupid
1031 if ((fp
->dn_type
) == DEV_DIR
) {
1033 * Avoid ".", "..", and aliases of "." for obvious reasons.
1035 if ((fcnp
->cn_namelen
== 1 && fcnp
->cn_nameptr
[0] == '.')
1036 || (fcnp
->cn_flags
&ISDOTDOT
)
1037 || (tcnp
->cn_namelen
== 1 && tcnp
->cn_nameptr
[0] == '.')
1038 || (tcnp
->cn_flags
&ISDOTDOT
)
1047 * Don't allow renaming critical devfs devices
1049 if (devfs_is_name_protected(fdvp
, fcnp
->cn_nameptr
) ||
1050 devfs_is_name_protected(tdvp
, tcnp
->cn_nameptr
)) {
1056 * If ".." must be changed (ie the directory gets a new
1057 * parent) then the source directory must not be in the
1058 * directory hierarchy above the target, as this would
1059 * orphan everything below the source directory. Also
1060 * the user must have write permission in the source so
1061 * as to be able to change "..".
1063 if (doingdirectory
&& (tdp
!= fdp
)) {
1064 devnode_t
* tmp
, *ntmp
;
1068 /* XXX unlock stuff here probably */
1073 } while ((tmp
= tmp
->dn_typeinfo
.Dir
.parent
) != ntmp
);
1076 /***********************************
1077 * Start actually doing things.... *
1078 ***********************************/
1079 dn_times_now(fp
, DEVFS_UPDATE_CHANGE
);
1082 * Check if just deleting a link name.
1085 if (fvp
->v_type
== VDIR
) {
1089 /* Release destination completely. */
1096 * 1) Bump link count while we're moving stuff
1097 * around. If we crash somewhere before
1098 * completing our work, too bad :)
1102 * If the target exists zap it (unless it's a non-empty directory)
1103 * We could do that as well but won't
1107 * Target must be empty if a directory and have no links
1108 * to it. Also, ensure source and target are compatible
1109 * (both directories, or both not directories).
1111 if (( doingdirectory
) && (tp
->dn_links
> 2)) {
1118 dev_add_name(tcnp
->cn_nameptr
,tdp
,NULL
,fp
,&tnp
);
1120 fp
->dn_links
--; /* one less link to it.. */
1124 fp
->dn_links
--; /* we added one earlier*/
1131 devfs_mkdir(struct vnop_mkdir_args
*ap
)
1132 /*struct vnop_mkdir_args {
1133 struct vnode *a_dvp;
1134 struct vnode **a_vpp;
1135 struct componentname *a_cnp;
1136 struct vnode_attr *a_vap;
1137 vfs_context_t a_context;
1140 struct componentname
* cnp
= ap
->a_cnp
;
1141 vfs_context_t ctx
= cnp
->cn_context
;
1142 struct proc
*p
= vfs_context_proc(ctx
);
1147 struct vnode_attr
* vap
= ap
->a_vap
;
1148 struct vnode
* * vpp
= ap
->a_vpp
;
1152 dir_p
= VTODN(ap
->a_dvp
);
1153 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
, DEV_DIR
,
1154 NULL
, NULL
, NULL
, &nm_p
);
1158 dev_p
= nm_p
->de_dnp
;
1159 dev_p
->dn_uid
= dir_p
->dn_uid
;
1160 dev_p
->dn_gid
= dir_p
->dn_gid
;
1161 dev_p
->dn_mode
= vap
->va_mode
;
1162 dn_copy_times(dev_p
, dir_p
);
1164 error
= devfs_dntovn(dev_p
, vpp
, p
);
1172 * An rmdir is a special type of remove, which we already support; we wrap
1173 * and reexpress the arguments to call devfs_remove directly. The only
1174 * different argument is flags, which we do not set, since it's ignored.
1177 devfs_rmdir(struct vnop_rmdir_args
*ap
)
1178 /* struct vnop_rmdir_args {
1179 struct vnode *a_dvp;
1181 struct componentname *a_cnp;
1182 vfs_context_t a_context;
1185 struct vnop_remove_args ra
;
1187 ra
.a_dvp
= ap
->a_dvp
;
1189 ra
.a_cnp
= ap
->a_cnp
;
1190 ra
.a_flags
= 0; /* XXX */
1191 ra
.a_context
= ap
->a_context
;
1193 return devfs_vnop_remove(&ra
);
1198 devfs_symlink(struct vnop_symlink_args
*ap
)
1199 /*struct vnop_symlink_args {
1200 struct vnode *a_dvp;
1201 struct vnode **a_vpp;
1202 struct componentname *a_cnp;
1203 struct vnode_attr *a_vap;
1205 vfs_context_t a_context;
1209 devdirent_t
*newent
;
1212 error
= devfs_make_symlink(VTODN(ap
->a_dvp
), ap
->a_cnp
->cn_nameptr
, ap
->a_vap
->va_mode
, ap
->a_target
, &newent
);
1215 error
= devfs_dntovn(newent
->de_dnp
, ap
->a_vpp
, vfs_context_proc(ap
->a_context
));
1224 /* Called with devfs locked */
1226 devfs_make_symlink(devnode_t
*dir_p
, char *name
, int mode
, char *target
, devdirent_t
**newent
)
1229 devnode_type_t typeinfo
;
1233 typeinfo
.Slnk
.name
= target
;
1234 typeinfo
.Slnk
.namelen
= strlen(target
);
1236 error
= dev_add_entry(name
, dir_p
, DEV_SLNK
,
1237 &typeinfo
, NULL
, NULL
, &nm_p
);
1241 dev_p
= nm_p
->de_dnp
;
1242 dev_p
->dn_uid
= dir_p
->dn_uid
;
1243 dev_p
->dn_gid
= dir_p
->dn_gid
;
1244 dev_p
->dn_mode
= mode
;
1245 dn_copy_times(dev_p
, dir_p
);
1260 devfs_mknod(struct vnop_mknod_args
*ap
)
1261 /* struct vnop_mknod_args {
1262 struct vnode *a_dvp;
1263 struct vnode **a_vpp;
1264 struct componentname *a_cnp;
1265 struct vnode_attr *a_vap;
1266 vfs_context_t a_context;
1269 struct componentname
* cnp
= ap
->a_cnp
;
1270 vfs_context_t ctx
= cnp
->cn_context
;
1271 struct proc
*p
= vfs_context_proc(ctx
);
1273 devdirent_t
* devent
;
1274 devnode_t
* dir_p
; /* devnode for parent directory */
1275 struct vnode
* dvp
= ap
->a_dvp
;
1277 devnode_type_t typeinfo
;
1278 struct vnode_attr
* vap
= ap
->a_vap
;
1279 struct vnode
** vpp
= ap
->a_vpp
;
1282 if (!(vap
->va_type
== VBLK
) && !(vap
->va_type
== VCHR
)) {
1283 return (EINVAL
); /* only support mknod of special files */
1285 typeinfo
.dev
= vap
->va_rdev
;
1291 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
,
1292 (vap
->va_type
== VBLK
) ? DEV_BDEV
: DEV_CDEV
,
1293 &typeinfo
, NULL
, NULL
, &devent
);
1297 dev_p
= devent
->de_dnp
;
1298 error
= devfs_dntovn(dev_p
, vpp
, p
);
1301 dev_p
->dn_uid
= vap
->va_uid
;
1302 dev_p
->dn_gid
= vap
->va_gid
;
1303 dev_p
->dn_mode
= vap
->va_mode
;
1304 VATTR_SET_SUPPORTED(vap
, va_uid
);
1305 VATTR_SET_SUPPORTED(vap
, va_gid
);
1306 VATTR_SET_SUPPORTED(vap
, va_mode
);
1314 * Vnode op for readdir
1317 devfs_readdir(struct vnop_readdir_args
*ap
)
1318 /*struct vnop_readdir_args {
1324 vfs_context_t a_context;
1327 struct vnode
*vp
= ap
->a_vp
;
1328 struct uio
*uio
= ap
->a_uio
;
1329 struct dirent dirent
;
1330 devnode_t
* dir_node
;
1331 devdirent_t
* name_node
;
1338 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
))
1341 /* set up refs to dir */
1342 dir_node
= VTODN(vp
);
1343 if (dir_node
->dn_type
!= DEV_DIR
)
1346 startpos
= uio
->uio_offset
;
1350 name_node
= dir_node
->dn_typeinfo
.Dir
.dirlist
;
1353 while ((name_node
|| (nodenumber
< 2)) && (uio_resid(uio
) > 0))
1358 dirent
.d_fileno
= dir_node
->dn_ino
;
1360 dirent
.d_namlen
= 1;
1361 dirent
.d_type
= DT_DIR
;
1364 if(dir_node
->dn_typeinfo
.Dir
.parent
)
1365 dirent
.d_fileno
= dir_node
->dn_typeinfo
.Dir
.parent
->dn_ino
;
1367 dirent
.d_fileno
= dir_node
->dn_ino
;
1369 dirent
.d_namlen
= 2;
1370 dirent
.d_type
= DT_DIR
;
1373 dirent
.d_fileno
= name_node
->de_dnp
->dn_ino
;
1374 dirent
.d_namlen
= strlen(name_node
->de_name
);
1375 name
= name_node
->de_name
;
1376 switch(name_node
->de_dnp
->dn_type
) {
1378 dirent
.d_type
= DT_BLK
;
1381 dirent
.d_type
= DT_CHR
;
1384 dirent
.d_type
= DT_DIR
;
1387 dirent
.d_type
= DT_LNK
;
1390 dirent
.d_type
= DT_UNKNOWN
;
1393 #define GENERIC_DIRSIZ(dp) \
1394 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1396 reclen
= dirent
.d_reclen
= GENERIC_DIRSIZ(&dirent
);
1398 if(pos
>= startpos
) /* made it to the offset yet? */
1400 if (uio_resid(uio
) < reclen
) /* will it fit? */
1402 strlcpy(dirent
.d_name
, name
, DEVMAXNAMESIZE
);
1403 if ((error
= uiomove ((caddr_t
)&dirent
,
1404 dirent
.d_reclen
, uio
)) != 0)
1408 if((nodenumber
>1) && name_node
)
1409 name_node
= name_node
->de_next
;
1413 uio
->uio_offset
= pos
;
1415 devfs_consider_time_update(dir_node
, DEVFS_UPDATE_ACCESS
);
1424 devfs_readlink(struct vnop_readlink_args
*ap
)
1425 /*struct vnop_readlink_args {
1428 vfs_context_t a_context;
1431 struct vnode
*vp
= ap
->a_vp
;
1432 struct uio
*uio
= ap
->a_uio
;
1433 devnode_t
* lnk_node
;
1436 /* set up refs to dir */
1437 lnk_node
= VTODN(vp
);
1439 if (lnk_node
->dn_type
!= DEV_SLNK
) {
1443 error
= uiomove(lnk_node
->dn_typeinfo
.Slnk
.name
,
1444 lnk_node
->dn_typeinfo
.Slnk
.namelen
, uio
);
1450 devfs_reclaim(struct vnop_reclaim_args
*ap
)
1451 /*struct vnop_reclaim_args {
1455 struct vnode
* vp
= ap
->a_vp
;
1463 /* If this is a cloning device, it didn't have a dn_vn anyway */
1465 vnode_clearfsnode(vp
);
1467 /* This could delete the node, if we are the last vnode */
1468 devfs_rele_node(dnp
);
1477 * Get configurable pathname variables.
1481 struct vnop_pathconf_args
/* {
1485 vfs_context_t a_context;
1488 switch (ap
->a_name
) {
1490 /* arbitrary limit matching HFS; devfs has no hard limit */
1491 *ap
->a_retval
= 32767;
1494 *ap
->a_retval
= DEVMAXNAMESIZE
- 1; /* includes NUL */
1497 *ap
->a_retval
= DEVMAXPATHSIZE
- 1; /* XXX nonconformant */
1499 case _PC_CHOWN_RESTRICTED
:
1500 *ap
->a_retval
= 200112; /* _POSIX_CHOWN_RESTRICTED */
1505 case _PC_CASE_SENSITIVE
:
1508 case _PC_CASE_PRESERVING
:
1520 /**************************************************************************\
1522 \**************************************************************************/
1526 * struct vnop_inactive_args {
1527 * struct vnode *a_vp;
1528 * vfs_context_t a_context;
1533 devfs_inactive(__unused
struct vnop_inactive_args
*ap
)
1535 vnode_t vp
= ap
->a_vp
;
1536 devnode_t
*dnp
= VTODN(vp
);
1539 * Cloned vnodes are not linked in anywhere, so they
1540 * can just be recycled.
1542 if (dnp
->dn_clone
!= NULL
) {
1550 * called with DEVFS_LOCK held
1553 devfs_update(struct vnode
*vp
, struct timeval
*access
, struct timeval
*modify
)
1559 if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) {
1567 DEVFS_ATTR_LOCK_SPIN();
1569 dn_times_locked(ip
, access
, modify
, &now
, DEVFS_UPDATE_ACCESS
| DEVFS_UPDATE_MOD
);
1570 DEVFS_ATTR_UNLOCK();
1575 #define VOPFUNC int (*)(void *)
1577 /* The following ops are used by directories and symlinks */
1578 int (**devfs_vnodeop_p
)(void *);
1579 static struct vnodeopv_entry_desc devfs_vnodeop_entries
[] = {
1580 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
1581 { &vnop_lookup_desc
, (VOPFUNC
)devfs_lookup
}, /* lookup */
1582 { &vnop_create_desc
, (VOPFUNC
)err_create
}, /* create */
1583 { &vnop_whiteout_desc
, (VOPFUNC
)err_whiteout
}, /* whiteout */
1584 { &vnop_mknod_desc
, (VOPFUNC
)devfs_mknod
}, /* mknod */
1585 { &vnop_open_desc
, (VOPFUNC
)nop_open
}, /* open */
1586 { &vnop_close_desc
, (VOPFUNC
)devfs_close
}, /* close */
1587 { &vnop_getattr_desc
, (VOPFUNC
)devfs_getattr
}, /* getattr */
1588 { &vnop_setattr_desc
, (VOPFUNC
)devfs_setattr
}, /* setattr */
1589 { &vnop_read_desc
, (VOPFUNC
)devfs_read
}, /* read */
1590 { &vnop_write_desc
, (VOPFUNC
)devfs_write
}, /* write */
1591 { &vnop_ioctl_desc
, (VOPFUNC
)err_ioctl
}, /* ioctl */
1592 { &vnop_select_desc
, (VOPFUNC
)err_select
}, /* select */
1593 { &vnop_revoke_desc
, (VOPFUNC
)err_revoke
}, /* revoke */
1594 { &vnop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
1595 { &vnop_fsync_desc
, (VOPFUNC
)nop_fsync
}, /* fsync */
1596 { &vnop_remove_desc
, (VOPFUNC
)devfs_vnop_remove
}, /* remove */
1597 { &vnop_link_desc
, (VOPFUNC
)devfs_link
}, /* link */
1598 { &vnop_rename_desc
, (VOPFUNC
)devfs_rename
}, /* rename */
1599 { &vnop_mkdir_desc
, (VOPFUNC
)devfs_mkdir
}, /* mkdir */
1600 { &vnop_rmdir_desc
, (VOPFUNC
)devfs_rmdir
}, /* rmdir */
1601 { &vnop_symlink_desc
, (VOPFUNC
)devfs_symlink
}, /* symlink */
1602 { &vnop_readdir_desc
, (VOPFUNC
)devfs_readdir
}, /* readdir */
1603 { &vnop_readlink_desc
, (VOPFUNC
)devfs_readlink
}, /* readlink */
1604 { &vnop_inactive_desc
, (VOPFUNC
)devfs_inactive
}, /* inactive */
1605 { &vnop_reclaim_desc
, (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1606 { &vnop_strategy_desc
, (VOPFUNC
)err_strategy
}, /* strategy */
1607 { &vnop_pathconf_desc
, (VOPFUNC
)devs_vnop_pathconf
}, /* pathconf */
1608 { &vnop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
1609 { &vnop_bwrite_desc
, (VOPFUNC
)err_bwrite
},
1610 { &vnop_pagein_desc
, (VOPFUNC
)err_pagein
}, /* Pagein */
1611 { &vnop_pageout_desc
, (VOPFUNC
)err_pageout
}, /* Pageout */
1612 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1613 { &vnop_blktooff_desc
, (VOPFUNC
)err_blktooff
}, /* blktooff */
1614 { &vnop_offtoblk_desc
, (VOPFUNC
)err_offtoblk
}, /* offtoblk */
1615 { &vnop_blockmap_desc
, (VOPFUNC
)err_blockmap
}, /* blockmap */
1617 { &vnop_setlabel_desc
, (VOPFUNC
)devfs_setlabel
}, /* setlabel */
1619 { (struct vnodeop_desc
*)NULL
, (int(*)(void *))NULL
}
1621 struct vnodeopv_desc devfs_vnodeop_opv_desc
=
1622 { &devfs_vnodeop_p
, devfs_vnodeop_entries
};
1624 /* The following ops are used by the device nodes */
1625 int (**devfs_spec_vnodeop_p
)(void *);
1626 static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries
[] = {
1627 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
1628 { &vnop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
1629 { &vnop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
1630 { &vnop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
1631 { &vnop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
1632 { &vnop_close_desc
, (VOPFUNC
)devfsspec_close
}, /* close */
1633 { &vnop_getattr_desc
, (VOPFUNC
)devfs_getattr
}, /* getattr */
1634 { &vnop_setattr_desc
, (VOPFUNC
)devfs_setattr
}, /* setattr */
1635 { &vnop_read_desc
, (VOPFUNC
)devfsspec_read
}, /* read */
1636 { &vnop_write_desc
, (VOPFUNC
)devfsspec_write
}, /* write */
1637 { &vnop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
1638 { &vnop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
1639 { &vnop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
1640 { &vnop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
1641 { &vnop_fsync_desc
, (VOPFUNC
)spec_fsync
}, /* fsync */
1642 { &vnop_remove_desc
, (VOPFUNC
)devfs_vnop_remove
}, /* remove */
1643 { &vnop_link_desc
, (VOPFUNC
)devfs_link
}, /* link */
1644 { &vnop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
1645 { &vnop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
1646 { &vnop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
1647 { &vnop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
1648 { &vnop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
1649 { &vnop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
1650 { &vnop_inactive_desc
, (VOPFUNC
)devfs_inactive
}, /* inactive */
1651 { &vnop_reclaim_desc
, (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1652 { &vnop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
1653 { &vnop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
1654 { &vnop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
1655 { &vnop_bwrite_desc
, (VOPFUNC
)vn_bwrite
},
1656 { &vnop_pagein_desc
, (VOPFUNC
)err_pagein
}, /* Pagein */
1657 { &vnop_pageout_desc
, (VOPFUNC
)err_pageout
}, /* Pageout */
1658 { &vnop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1659 { &vnop_blktooff_desc
, (VOPFUNC
)spec_blktooff
}, /* blktooff */
1660 { &vnop_blktooff_desc
, (VOPFUNC
)spec_offtoblk
}, /* blkofftoblk */
1661 { &vnop_blockmap_desc
, (VOPFUNC
)spec_blockmap
}, /* blockmap */
1663 { &vnop_setlabel_desc
, (VOPFUNC
)devfs_setlabel
}, /* setlabel */
1665 { (struct vnodeop_desc
*)NULL
, (int(*)(void *))NULL
}
1667 struct vnodeopv_desc devfs_spec_vnodeop_opv_desc
=
1668 { &devfs_spec_vnodeop_p
, devfs_spec_vnodeop_entries
};
1672 int (**devfs_devfd_vnodeop_p
)(void*);
1673 static struct vnodeopv_entry_desc devfs_devfd_vnodeop_entries
[] = {
1674 { &vnop_default_desc
, (VOPFUNC
)vn_default_error
},
1675 { &vnop_lookup_desc
, (VOPFUNC
)devfs_devfd_lookup
}, /* lookup */
1676 { &vnop_open_desc
, (VOPFUNC
)nop_open
}, /* open */
1677 { &vnop_close_desc
, (VOPFUNC
)devfs_close
}, /* close */
1678 { &vnop_getattr_desc
, (VOPFUNC
)devfs_getattr
}, /* getattr */
1679 { &vnop_setattr_desc
, (VOPFUNC
)devfs_setattr
}, /* setattr */
1680 { &vnop_revoke_desc
, (VOPFUNC
)err_revoke
}, /* revoke */
1681 { &vnop_fsync_desc
, (VOPFUNC
)nop_fsync
}, /* fsync */
1682 { &vnop_readdir_desc
, (VOPFUNC
)devfs_devfd_readdir
}, /* readdir */
1683 { &vnop_inactive_desc
, (VOPFUNC
)devfs_inactive
}, /* inactive */
1684 { &vnop_reclaim_desc
, (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1685 { &vnop_pathconf_desc
, (VOPFUNC
)devs_vnop_pathconf
}, /* pathconf */
1687 { &vnop_setlabel_desc
, (VOPFUNC
)devfs_setlabel
}, /* setlabel */
1689 { (struct vnodeop_desc
*)NULL
, (int(*)(void *))NULL
}
1691 struct vnodeopv_desc devfs_devfd_vnodeop_opv_desc
=
1692 { &devfs_devfd_vnodeop_p
, devfs_devfd_vnodeop_entries
};