2 * Copyright (c) 2000-2019 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
)
126 lck_mtx_assert(&devfs_attr_mutex
, LCK_MTX_ASSERT_OWNED
);
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;
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;
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;
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;
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;
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;
160 dn_mark_for_delayed_times_update(devnode_t
*dnp
, uint32_t just_changed_flags
)
162 if (just_changed_flags
& DEVFS_UPDATE_CHANGE
) {
165 if (just_changed_flags
& DEVFS_UPDATE_ACCESS
) {
168 if (just_changed_flags
& DEVFS_UPDATE_MOD
) {
174 * Update times based on pending updates and optionally a set of new changes.
177 dn_times_now(devnode_t
* dnp
, uint32_t just_changed_flags
)
181 DEVFS_ATTR_LOCK_SPIN();
183 dn_times_locked(dnp
, &now
, &now
, &now
, just_changed_flags
);
188 * Critical devfs devices cannot be renamed or removed.
189 * However, links to them may be moved/unlinked. So we block
190 * remove/rename on a per-name basis, rather than per-node.
193 devfs_is_name_protected(struct vnode
*dvp
, const char *name
)
196 * Only names in root are protected. E.g. /dev/null is protected,
197 * but /dev/foo/null isn't.
199 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)) {
216 * Convert a component of a pathname into a pointer to a locked node.
217 * This is a very central and rather complicated routine.
218 * If the file system is not maintained in a strict tree hierarchy,
219 * this can result in a deadlock situation (see comments in code below).
221 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
222 * whether the name is to be looked up, created, renamed, or deleted.
223 * When CREATE, RENAME, or DELETE is specified, information usable in
224 * creating, renaming, or deleting a directory entry may be calculated.
225 * If flag has LOCKPARENT or'ed into it and the target of the pathname
226 * exists, lookup returns both the target and its parent directory locked.
227 * When creating or renaming and LOCKPARENT is specified, the target may
228 * not be ".". When deleting and LOCKPARENT is specified, the target may
229 * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
230 * instead of two DNUNLOCKs.
232 * Overall outline of devfs_lookup:
234 * check accessibility of directory
235 * null terminate the component (lookup leaves the whole string alone)
236 * look for name in cache, if found, then if at end of path
237 * and deleting or creating, drop it, else return name
238 * search for name in directory, to found or notfound
240 * if creating, return locked directory,
243 * if at end of path and deleting, return information to allow delete
244 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
245 * node and return info to allow rewrite
246 * if not at end, add name to cache; if at end and neither creating
247 * nor deleting, add name to cache
248 * On return to lookup, remove the null termination we put in at the start.
250 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
253 devfs_lookup(struct vnop_lookup_args
*ap
)
254 /*struct vnop_lookup_args {
255 * struct vnode * a_dvp; directory vnode ptr
256 * struct vnode ** a_vpp; where to put the result
257 * struct componentname * a_cnp; the name we want
258 * vfs_context_t a_context;
261 struct componentname
*cnp
= ap
->a_cnp
;
262 vfs_context_t ctx
= cnp
->cn_context
;
263 struct proc
*p
= vfs_context_proc(ctx
);
264 struct vnode
*dir_vnode
= ap
->a_dvp
;
265 struct vnode
**result_vnode
= ap
->a_vpp
;
266 devnode_t
* dir_node
; /* the directory we are searching */
267 devnode_t
* node
= NULL
; /* the node we are searching for */
268 devdirent_t
* nodename
;
269 int flags
= cnp
->cn_flags
;
270 int op
= cnp
->cn_nameiop
; /* LOOKUP, CREATE, RENAME, or DELETE */
271 int wantparent
= flags
& (LOCKPARENT
| WANTPARENT
);
273 char heldchar
; /* the char at the end of the name componet */
277 *result_vnode
= NULL
; /* safe not sorry */ /*XXX*/
279 /* okay to look at directory vnodes ourside devfs lock as they are not aliased */
280 dir_node
= VTODN(dir_vnode
);
283 * Make sure that our node is a directory as well.
285 if (dir_node
->dn_type
!= DEV_DIR
) {
291 * temporarily terminate string component
293 heldchar
= cnp
->cn_nameptr
[cnp
->cn_namelen
];
294 cnp
->cn_nameptr
[cnp
->cn_namelen
] = '\0';
296 nodename
= dev_findname(dir_node
, cnp
->cn_nameptr
);
298 * restore saved character
300 cnp
->cn_nameptr
[cnp
->cn_namelen
] = heldchar
;
304 node
= nodename
->de_dnp
;
306 /* Do potential vnode allocation here inside the lock
307 * to make sure that our device node has a non-NULL dn_vn
308 * associated with it. The device node might otherwise
309 * get deleted out from under us (see devfs_dn_free()).
311 error
= devfs_dntovn(node
, result_vnode
, p
);
316 if (error
== EAGAIN
) {
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.
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
)) {
361 * we are trying to delete '.'. What does this mean? XXX
363 if (dir_node
== node
) {
365 vnode_put(*result_vnode
);
366 *result_vnode
= NULL
;
368 if (((error
= vnode_get(dir_vnode
)) == 0)) {
369 *result_vnode
= dir_vnode
;
377 * If rewriting (RENAME), return the vnode and the
378 * information required to rewrite the present directory
379 * Must get node of directory entry to verify it's a
380 * regular file, or empty directory.
382 if (op
== RENAME
&& wantparent
&& (flags
& ISLASTCN
)) {
384 * Careful about locking second node.
385 * This can only occur if the target is ".".
387 if (dir_node
== node
) {
395 * Step through the translation in the name. We do not unlock the
396 * directory because we may need it again if a symbolic link
397 * is relative to the current directory. Instead we save it
398 * unlocked as "saved_dir_node" XXX. We must get the target
399 * node before unlocking
400 * the directory to insure that the node will not be removed
401 * before we get it. We prevent deadlock by always fetching
402 * nodes from the root, moving down the directory tree. Thus
403 * when following backward pointers ".." we must unlock the
404 * parent directory before getting the requested directory.
405 * There is a potential race condition here if both the current
406 * and parent directories are removed before the lock for the
407 * node associated with ".." returns. We hope that this occurs
408 * infrequently since we cannot avoid this race condition without
409 * implementing a sophisticated deadlock detection algorithm.
410 * Note also that this simple deadlock detection scheme will not
411 * work if the file system has any hard links other than ".."
412 * that point backwards in the directory structure.
414 if ((flags
& ISDOTDOT
) == 0 && dir_node
== node
) {
416 vnode_put(*result_vnode
);
417 *result_vnode
= NULL
;
419 if ((error
= vnode_get(dir_vnode
))) {
422 *result_vnode
= dir_vnode
;
428 vnode_put(*result_vnode
);
429 *result_vnode
= NULL
;
435 devfs_getattr(struct vnop_getattr_args
*ap
)
436 /*struct vnop_getattr_args {
437 * struct vnode *a_vp;
438 * struct vnode_attr *a_vap;
439 * kauth_cred_t a_cred;
443 struct vnode
*vp
= ap
->a_vp
;
444 struct vnode_attr
*vap
= ap
->a_vap
;
445 devnode_t
* file_node
;
450 file_node
= VTODN(vp
);
452 VATTR_RETURN(vap
, va_mode
, file_node
->dn_mode
);
455 * Note: for DEV_CDEV and DEV_BDEV, we return the device from
456 * the vp, not the file_node; if we getting information on a
457 * cloning device, we want the cloned information, not the template.
459 switch (file_node
->dn_type
) {
462 case DEV_DEVFD
: /* Like a directory */
464 VATTR_RETURN(vap
, va_rdev
, 0);
465 vap
->va_mode
|= (S_IFDIR
);
468 VATTR_RETURN(vap
, va_rdev
, vp
->v_rdev
);
469 vap
->va_mode
|= (S_IFCHR
);
472 VATTR_RETURN(vap
, va_rdev
, vp
->v_rdev
);
473 vap
->va_mode
|= (S_IFBLK
);
476 VATTR_RETURN(vap
, va_rdev
, 0);
477 vap
->va_mode
|= (S_IFLNK
);
480 VATTR_RETURN(vap
, va_rdev
, 0); /* default value only */
482 VATTR_RETURN(vap
, va_type
, vp
->v_type
);
483 VATTR_RETURN(vap
, va_nlink
, file_node
->dn_links
);
484 VATTR_RETURN(vap
, va_uid
, file_node
->dn_uid
);
485 VATTR_RETURN(vap
, va_gid
, file_node
->dn_gid
);
486 VATTR_RETURN(vap
, va_fsid
, (uintptr_t)file_node
->dn_dvm
);
487 VATTR_RETURN(vap
, va_fileid
, (uintptr_t)file_node
->dn_ino
);
488 VATTR_RETURN(vap
, va_data_size
, file_node
->dn_len
);
490 /* return an override block size (advisory) */
491 if (vp
->v_type
== VBLK
) {
492 VATTR_RETURN(vap
, va_iosize
, BLKDEV_IOSIZE
);
493 } else if (vp
->v_type
== VCHR
) {
494 VATTR_RETURN(vap
, va_iosize
, MAXPHYSIO
);
496 VATTR_RETURN(vap
, va_iosize
, vp
->v_mount
->mnt_vfsstat
.f_iosize
);
500 DEVFS_ATTR_LOCK_SPIN();
503 dn_times_locked(file_node
, &now
, &now
, &now
, 0);
505 /* if the time is bogus, set it to the boot time */
506 if (file_node
->dn_ctime
.tv_sec
== 0) {
507 file_node
->dn_ctime
.tv_sec
= boottime_sec();
508 file_node
->dn_ctime
.tv_nsec
= 0;
510 if (file_node
->dn_mtime
.tv_sec
== 0) {
511 file_node
->dn_mtime
= file_node
->dn_ctime
;
513 if (file_node
->dn_atime
.tv_sec
== 0) {
514 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 {
541 * struct vnode *a_vp;
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
)) {
559 if (VATTR_IS_ACTIVE(vap
, va_access_time
)) {
560 file_node
->dn_access
= 1;
562 if (VATTR_IS_ACTIVE(vap
, va_modify_time
)) {
563 file_node
->dn_change
= 1;
564 file_node
->dn_update
= 1;
566 atimeval
.tv_sec
= vap
->va_access_time
.tv_sec
;
567 atimeval
.tv_usec
= vap
->va_access_time
.tv_nsec
/ 1000;
568 mtimeval
.tv_sec
= vap
->va_modify_time
.tv_sec
;
569 mtimeval
.tv_usec
= vap
->va_modify_time
.tv_nsec
/ 1000;
571 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
;
593 VATTR_SET_SUPPORTED(vap
, va_uid
);
598 if (VATTR_IS_ACTIVE(vap
, va_gid
)) {
599 file_node
->dn_gid
= vap
->va_gid
;
601 VATTR_SET_SUPPORTED(vap
, va_gid
);
610 devfs_setlabel(struct vnop_setlabel_args
*ap
)
611 /* struct vnop_setlabel_args {
612 * struct vnodeop_desc *a_desc;
613 * struct vnode *a_vp;
614 * struct label *a_vl;
615 * vfs_context_t a_context;
624 mac_vnode_label_update(ap
->a_context
, vp
, ap
->a_vl
);
625 mac_devfs_label_update(vp
->v_mount
, de
, vp
);
632 devfs_read(struct vnop_read_args
*ap
)
633 /* struct vnop_read_args {
634 * struct vnode *a_vp;
637 * vfs_context_t a_context;
640 devnode_t
* dn_p
= VTODN(ap
->a_vp
);
642 switch (ap
->a_vp
->v_type
) {
646 return VNOP_READDIR(ap
->a_vp
, ap
->a_uio
, 0, NULL
, NULL
, ap
->a_context
);
649 printf("devfs_read(): bad file type %d", ap
->a_vp
->v_type
);
656 devfs_close(struct vnop_close_args
*ap
)
657 /* struct vnop_close_args {
658 * struct vnode *a_vp;
660 * vfs_context_t a_context;
663 struct vnode
* vp
= ap
->a_vp
;
666 if (vnode_isinuse(vp
, 1)) {
670 dn_times_now(dnp
, 0);
678 devfsspec_close(struct vnop_close_args
*ap
)
679 /* struct vnop_close_args {
680 * struct vnode *a_vp;
682 * vfs_context_t a_context;
685 struct vnode
* vp
= ap
->a_vp
;
688 if (vnode_isinuse(vp
, 0)) {
692 dn_times_now(dnp
, 0);
697 return VOCALL(spec_vnodeop_p
, VOFFSET(vnop_close
), ap
);
701 devfs_update_needed(long now_s
, long last_s
)
703 if (now_s
> last_s
) {
704 if (now_s
- last_s
>= DEVFS_LAZY_UPDATE_SECONDS
) {
713 * Given a set of time updates required [to happen at some point], check
714 * either make those changes (and resolve other pending updates) or mark
715 * the devnode for a subsequent update.
718 devfs_consider_time_update(devnode_t
*dnp
, uint32_t just_changed_flags
)
726 if (dnp
->dn_change
|| (just_changed_flags
& DEVFS_UPDATE_CHANGE
)) {
727 if (devfs_update_needed(now_s
, dnp
->dn_ctime
.tv_sec
)) {
728 dn_times_now(dnp
, just_changed_flags
);
732 if (dnp
->dn_access
|| (just_changed_flags
& DEVFS_UPDATE_ACCESS
)) {
733 if (devfs_update_needed(now_s
, dnp
->dn_atime
.tv_sec
)) {
734 dn_times_now(dnp
, just_changed_flags
);
738 if (dnp
->dn_update
|| (just_changed_flags
& DEVFS_UPDATE_MOD
)) {
739 if (devfs_update_needed(now_s
, dnp
->dn_mtime
.tv_sec
)) {
740 dn_times_now(dnp
, just_changed_flags
);
745 /* Not going to do anything now--mark for later update */
746 dn_mark_for_delayed_times_update(dnp
, just_changed_flags
);
752 devfsspec_read(struct vnop_read_args
*ap
)
753 /* struct vnop_read_args {
754 * struct vnode *a_vp;
757 * kauth_cred_t a_cred;
760 devnode_t
* dnp
= VTODN(ap
->a_vp
);
762 devfs_consider_time_update(dnp
, DEVFS_UPDATE_ACCESS
);
764 return VOCALL(spec_vnodeop_p
, VOFFSET(vnop_read
), ap
);
768 devfsspec_write(struct vnop_write_args
*ap
)
769 /* struct vnop_write_args {
770 * struct vnode *a_vp;
773 * vfs_context_t a_context;
776 devnode_t
* dnp
= VTODN(ap
->a_vp
);
778 devfs_consider_time_update(dnp
, DEVFS_UPDATE_CHANGE
| DEVFS_UPDATE_MOD
);
780 return VOCALL(spec_vnodeop_p
, VOFFSET(vnop_write
), ap
);
784 * Write data to a file or directory.
787 devfs_write(struct vnop_write_args
*ap
)
788 /* struct vnop_write_args {
789 * struct vnode *a_vp;
792 * kauth_cred_t a_cred;
795 switch (ap
->a_vp
->v_type
) {
799 printf("devfs_write(): bad file type %d", ap
->a_vp
->v_type
);
805 * Deviates from UFS naming convention because there is a KPI function
806 * called devfs_remove().
809 devfs_vnop_remove(struct vnop_remove_args
*ap
)
810 /* struct vnop_remove_args {
811 * struct vnode *a_dvp;
812 * struct vnode *a_vp;
813 * struct componentname *a_cnp;
816 struct vnode
*vp
= ap
->a_vp
;
817 struct vnode
*dvp
= ap
->a_dvp
;
818 struct componentname
*cnp
= ap
->a_cnp
;
822 int doingdirectory
= 0;
826 * assume that the name is null terminated as they
827 * are the end of the path. Get pointers to all our
837 tnp
= dev_findname(tdp
, cnp
->cn_nameptr
);
845 * Don't allow removing critical devfs devices
847 if (devfs_is_name_protected(dvp
, cnp
->cn_nameptr
)) {
853 * Make sure that we don't try do something stupid
855 if ((tp
->dn_type
) == DEV_DIR
) {
857 * Avoid ".", "..", and aliases of "." for obvious reasons.
859 if ((cnp
->cn_namelen
== 1 && cnp
->cn_nameptr
[0] == '.')
860 || (cnp
->cn_flags
& ISDOTDOT
)) {
867 /***********************************
868 * Start actually doing things.... *
869 ***********************************/
870 devfs_consider_time_update(tdp
, DEVFS_UPDATE_CHANGE
| DEVFS_UPDATE_MOD
);
873 * Target must be empty if a directory and have no links
874 * to it. Also, ensure source and target are compatible
875 * (both directories, or both not directories).
877 if ((doingdirectory
) && (tp
->dn_links
> 2)) {
891 devfs_link(struct vnop_link_args
*ap
)
892 /*struct vnop_link_args {
893 * struct vnode *a_tdvp;
894 * struct vnode *a_vp;
895 * struct componentname *a_cnp;
896 * vfs_context_t a_context;
899 struct vnode
*vp
= ap
->a_vp
;
900 struct vnode
*tdvp
= ap
->a_tdvp
;
901 struct componentname
*cnp
= ap
->a_cnp
;
908 * First catch an arbitrary restriction for this FS
910 if (cnp
->cn_namelen
> DEVMAXNAMESIZE
) {
911 error
= ENAMETOOLONG
;
916 * Lock our directories and get our name pointers
917 * assume that the names are null terminated as they
918 * are the end of the path. Get pointers to all our
921 /* can lookup dnode safely for tdvp outside of devfs lock as it is not aliased */
924 if (tdvp
->v_mount
!= vp
->v_mount
) {
931 /***********************************
932 * Start actually doing things.... *
933 ***********************************/
934 dn_times_now(fp
, DEVFS_UPDATE_CHANGE
);
937 error
= dev_add_name(cnp
->cn_nameptr
, tdp
, NULL
, fp
, &tnp
);
946 * Rename system call. Seems overly complicated to me...
947 * rename("foo", "bar");
950 * link("foo", "bar");
952 * but ``atomically''.
954 * When the target exists, both the directory
955 * and target vnodes are locked.
956 * the source and source-parent vnodes are referenced
959 * Basic algorithm is:
961 * 1) Bump link count on source while we're linking it to the
962 * target. This also ensure the inode won't be deleted out
963 * from underneath us while we work (it may be truncated by
964 * a concurrent `trunc' or `open' for creation).
965 * 2) Link source to destination. If destination already exists,
967 * 3) Unlink source reference to node if still around. If a
968 * directory was moved and the parent of the destination
969 * is different from the source, patch the ".." entry in the
973 devfs_rename(struct vnop_rename_args
*ap
)
974 /*struct vnop_rename_args {
975 * struct vnode *a_fdvp;
976 * struct vnode *a_fvp;
977 * struct componentname *a_fcnp;
978 * struct vnode *a_tdvp;
979 * struct vnode *a_tvp;
980 * struct componentname *a_tcnp;
981 * vfs_context_t a_context;
984 struct vnode
*tvp
= ap
->a_tvp
;
985 struct vnode
*tdvp
= ap
->a_tdvp
;
986 struct vnode
*fvp
= ap
->a_fvp
;
987 struct vnode
*fdvp
= ap
->a_fdvp
;
988 struct componentname
*tcnp
= ap
->a_tcnp
;
989 struct componentname
*fcnp
= ap
->a_fcnp
;
990 devnode_t
*fp
, *fdp
, *tp
, *tdp
;
991 devdirent_t
*fnp
, *tnp
;
992 int doingdirectory
= 0;
997 * First catch an arbitrary restriction for this FS
999 if (tcnp
->cn_namelen
> DEVMAXNAMESIZE
) {
1000 error
= ENAMETOOLONG
;
1005 * assume that the names are null terminated as they
1006 * are the end of the path. Get pointers to all our
1013 fnp
= dev_findname(fdp
, fcnp
->cn_nameptr
);
1023 tnp
= dev_findname(tdp
, tcnp
->cn_nameptr
);
1033 * Make sure that we don't try do something stupid
1035 if ((fp
->dn_type
) == DEV_DIR
) {
1037 * Avoid ".", "..", and aliases of "." for obvious reasons.
1039 if ((fcnp
->cn_namelen
== 1 && fcnp
->cn_nameptr
[0] == '.')
1040 || (fcnp
->cn_flags
& ISDOTDOT
)
1041 || (tcnp
->cn_namelen
== 1 && tcnp
->cn_nameptr
[0] == '.')
1042 || (tcnp
->cn_flags
& ISDOTDOT
)
1051 * Don't allow renaming critical devfs devices
1053 if (devfs_is_name_protected(fdvp
, fcnp
->cn_nameptr
) ||
1054 devfs_is_name_protected(tdvp
, tcnp
->cn_nameptr
)) {
1060 * If ".." must be changed (ie the directory gets a new
1061 * parent) then the source directory must not be in the
1062 * directory hierarchy above the target, as this would
1063 * orphan everything below the source directory. Also
1064 * the user must have write permission in the source so
1065 * as to be able to change "..".
1067 if (doingdirectory
&& (tdp
!= fdp
)) {
1068 devnode_t
* tmp
, *ntmp
;
1072 /* XXX unlock stuff here probably */
1077 } while ((tmp
= tmp
->dn_typeinfo
.Dir
.parent
) != ntmp
);
1080 /***********************************
1081 * Start actually doing things.... *
1082 ***********************************/
1083 dn_times_now(fp
, DEVFS_UPDATE_CHANGE
);
1086 * Check if just deleting a link name.
1089 if (fvp
->v_type
== VDIR
) {
1093 /* Release destination completely. */
1100 * 1) Bump link count while we're moving stuff
1101 * around. If we crash somewhere before
1102 * completing our work, too bad :)
1106 * If the target exists zap it (unless it's a non-empty directory)
1107 * We could do that as well but won't
1111 * Target must be empty if a directory and have no links
1112 * to it. Also, ensure source and target are compatible
1113 * (both directories, or both not directories).
1115 if ((doingdirectory
) && (tp
->dn_links
> 2)) {
1122 dev_add_name(tcnp
->cn_nameptr
, tdp
, NULL
, fp
, &tnp
);
1124 fp
->dn_links
--; /* one less link to it.. */
1128 fp
->dn_links
--; /* we added one earlier*/
1135 devfs_mkdir(struct vnop_mkdir_args
*ap
)
1136 /*struct vnop_mkdir_args {
1137 * struct vnode *a_dvp;
1138 * struct vnode **a_vpp;
1139 * struct componentname *a_cnp;
1140 * struct vnode_attr *a_vap;
1141 * vfs_context_t a_context;
1144 struct componentname
* cnp
= ap
->a_cnp
;
1145 vfs_context_t ctx
= cnp
->cn_context
;
1146 struct proc
*p
= vfs_context_proc(ctx
);
1151 struct vnode_attr
* vap
= ap
->a_vap
;
1152 struct vnode
* * vpp
= ap
->a_vpp
;
1156 dir_p
= VTODN(ap
->a_dvp
);
1157 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
, DEV_DIR
,
1158 NULL
, NULL
, NULL
, &nm_p
);
1162 dev_p
= nm_p
->de_dnp
;
1163 dev_p
->dn_uid
= dir_p
->dn_uid
;
1164 dev_p
->dn_gid
= dir_p
->dn_gid
;
1165 dev_p
->dn_mode
= vap
->va_mode
;
1166 dn_copy_times(dev_p
, dir_p
);
1168 error
= devfs_dntovn(dev_p
, vpp
, p
);
1176 * An rmdir is a special type of remove, which we already support; we wrap
1177 * and reexpress the arguments to call devfs_remove directly. The only
1178 * different argument is flags, which we do not set, since it's ignored.
1181 devfs_rmdir(struct vnop_rmdir_args
*ap
)
1182 /* struct vnop_rmdir_args {
1183 * struct vnode *a_dvp;
1184 * struct vnode *a_vp;
1185 * struct componentname *a_cnp;
1186 * vfs_context_t a_context;
1189 struct vnop_remove_args ra
;
1191 ra
.a_dvp
= ap
->a_dvp
;
1193 ra
.a_cnp
= ap
->a_cnp
;
1194 ra
.a_flags
= 0; /* XXX */
1195 ra
.a_context
= ap
->a_context
;
1197 return devfs_vnop_remove(&ra
);
1202 devfs_symlink(struct vnop_symlink_args
*ap
)
1203 /*struct vnop_symlink_args {
1204 * struct vnode *a_dvp;
1205 * struct vnode **a_vpp;
1206 * struct componentname *a_cnp;
1207 * struct vnode_attr *a_vap;
1209 * vfs_context_t a_context;
1213 devdirent_t
*newent
;
1216 error
= devfs_make_symlink(VTODN(ap
->a_dvp
), ap
->a_cnp
->cn_nameptr
, ap
->a_vap
->va_mode
, ap
->a_target
, &newent
);
1219 error
= devfs_dntovn(newent
->de_dnp
, ap
->a_vpp
, vfs_context_proc(ap
->a_context
));
1227 /* Called with devfs locked */
1229 devfs_make_symlink(devnode_t
*dir_p
, char *name
, int mode
, char *target
, devdirent_t
**newent
)
1232 devnode_type_t typeinfo
;
1236 typeinfo
.Slnk
.name
= target
;
1237 typeinfo
.Slnk
.namelen
= strlen(target
);
1239 error
= dev_add_entry(name
, dir_p
, DEV_SLNK
,
1240 &typeinfo
, NULL
, NULL
, &nm_p
);
1244 dev_p
= nm_p
->de_dnp
;
1245 dev_p
->dn_uid
= dir_p
->dn_uid
;
1246 dev_p
->dn_gid
= dir_p
->dn_gid
;
1247 dev_p
->dn_mode
= mode
;
1248 dn_copy_times(dev_p
, dir_p
);
1263 devfs_mknod(struct vnop_mknod_args
*ap
)
1264 /* struct vnop_mknod_args {
1265 * struct vnode *a_dvp;
1266 * struct vnode **a_vpp;
1267 * struct componentname *a_cnp;
1268 * struct vnode_attr *a_vap;
1269 * vfs_context_t a_context;
1272 struct componentname
* cnp
= ap
->a_cnp
;
1273 vfs_context_t ctx
= cnp
->cn_context
;
1274 struct proc
*p
= vfs_context_proc(ctx
);
1276 devdirent_t
* devent
;
1277 devnode_t
* dir_p
; /* devnode for parent directory */
1278 struct vnode
* dvp
= ap
->a_dvp
;
1280 devnode_type_t typeinfo
;
1281 struct vnode_attr
* vap
= ap
->a_vap
;
1282 struct vnode
** vpp
= ap
->a_vpp
;
1285 if (!(vap
->va_type
== VBLK
) && !(vap
->va_type
== VCHR
)) {
1286 return EINVAL
; /* only support mknod of special files */
1288 typeinfo
.dev
= vap
->va_rdev
;
1294 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
,
1295 (vap
->va_type
== VBLK
) ? DEV_BDEV
: DEV_CDEV
,
1296 &typeinfo
, NULL
, NULL
, &devent
);
1300 dev_p
= devent
->de_dnp
;
1301 error
= devfs_dntovn(dev_p
, vpp
, p
);
1305 dev_p
->dn_uid
= vap
->va_uid
;
1306 dev_p
->dn_gid
= vap
->va_gid
;
1307 dev_p
->dn_mode
= vap
->va_mode
;
1308 VATTR_SET_SUPPORTED(vap
, va_uid
);
1309 VATTR_SET_SUPPORTED(vap
, va_gid
);
1310 VATTR_SET_SUPPORTED(vap
, va_mode
);
1318 * Vnode op for readdir
1321 devfs_readdir(struct vnop_readdir_args
*ap
)
1322 /*struct vnop_readdir_args {
1323 * struct vnode *a_vp;
1324 * struct uio *a_uio;
1328 * vfs_context_t a_context;
1331 struct vnode
*vp
= ap
->a_vp
;
1332 struct uio
*uio
= ap
->a_uio
;
1333 struct dirent dirent
;
1334 devnode_t
* dir_node
;
1335 devdirent_t
* name_node
;
1342 if (ap
->a_flags
& (VNODE_READDIR_EXTENDED
| VNODE_READDIR_REQSEEKOFF
)) {
1346 /* set up refs to dir */
1347 dir_node
= VTODN(vp
);
1348 if (dir_node
->dn_type
!= DEV_DIR
) {
1352 startpos
= uio
->uio_offset
;
1356 name_node
= dir_node
->dn_typeinfo
.Dir
.dirlist
;
1359 while ((name_node
|| (nodenumber
< 2)) && (uio_resid(uio
) > 0)) {
1360 switch (nodenumber
) {
1362 dirent
.d_fileno
= dir_node
->dn_ino
;
1364 dirent
.d_namlen
= 1;
1365 dirent
.d_type
= DT_DIR
;
1368 if (dir_node
->dn_typeinfo
.Dir
.parent
) {
1369 dirent
.d_fileno
= dir_node
->dn_typeinfo
.Dir
.parent
->dn_ino
;
1371 dirent
.d_fileno
= dir_node
->dn_ino
;
1374 dirent
.d_namlen
= 2;
1375 dirent
.d_type
= DT_DIR
;
1378 dirent
.d_fileno
= name_node
->de_dnp
->dn_ino
;
1379 dirent
.d_namlen
= strlen(name_node
->de_name
);
1380 name
= name_node
->de_name
;
1381 switch (name_node
->de_dnp
->dn_type
) {
1383 dirent
.d_type
= DT_BLK
;
1386 dirent
.d_type
= DT_CHR
;
1389 dirent
.d_type
= DT_DIR
;
1392 dirent
.d_type
= DT_LNK
;
1395 dirent
.d_type
= DT_UNKNOWN
;
1398 #define GENERIC_DIRSIZ(dp) \
1399 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1401 reclen
= dirent
.d_reclen
= GENERIC_DIRSIZ(&dirent
);
1403 if (pos
>= startpos
) { /* made it to the offset yet? */
1404 if (uio_resid(uio
) < reclen
) { /* will it fit? */
1407 strlcpy(dirent
.d_name
, name
, DEVMAXNAMESIZE
);
1408 if ((error
= uiomove((caddr_t
)&dirent
,
1409 dirent
.d_reclen
, uio
)) != 0) {
1414 if ((nodenumber
> 1) && name_node
) {
1415 name_node
= name_node
->de_next
;
1420 uio
->uio_offset
= pos
;
1422 devfs_consider_time_update(dir_node
, DEVFS_UPDATE_ACCESS
);
1431 devfs_readlink(struct vnop_readlink_args
*ap
)
1432 /*struct vnop_readlink_args {
1433 * struct vnode *a_vp;
1434 * struct uio *a_uio;
1435 * vfs_context_t a_context;
1438 struct vnode
*vp
= ap
->a_vp
;
1439 struct uio
*uio
= ap
->a_uio
;
1440 devnode_t
* lnk_node
;
1443 /* set up refs to dir */
1444 lnk_node
= VTODN(vp
);
1446 if (lnk_node
->dn_type
!= DEV_SLNK
) {
1450 error
= uiomove(lnk_node
->dn_typeinfo
.Slnk
.name
,
1451 lnk_node
->dn_typeinfo
.Slnk
.namelen
, uio
);
1457 devfs_reclaim(struct vnop_reclaim_args
*ap
)
1458 /*struct vnop_reclaim_args {
1459 * struct vnode *a_vp;
1462 struct vnode
* vp
= ap
->a_vp
;
1470 /* If this is a cloning device, it didn't have a dn_vn anyway */
1472 vnode_clearfsnode(vp
);
1474 /* This could delete the node, if we are the last vnode */
1475 devfs_rele_node(dnp
);
1484 * Get configurable pathname variables.
1488 struct vnop_pathconf_args
/* {
1489 * struct vnode *a_vp;
1492 * vfs_context_t a_context;
1495 switch (ap
->a_name
) {
1497 /* arbitrary limit matching HFS; devfs has no hard limit */
1498 *ap
->a_retval
= 32767;
1501 *ap
->a_retval
= DEVMAXNAMESIZE
- 1; /* includes NUL */
1504 *ap
->a_retval
= DEVMAXPATHSIZE
- 1; /* XXX nonconformant */
1506 case _PC_CHOWN_RESTRICTED
:
1507 *ap
->a_retval
= 200112; /* _POSIX_CHOWN_RESTRICTED */
1512 case _PC_CASE_SENSITIVE
:
1515 case _PC_CASE_PRESERVING
:
1527 /**************************************************************************\
1529 \**************************************************************************/
1533 * struct vnop_inactive_args {
1534 * struct vnode *a_vp;
1535 * vfs_context_t a_context;
1540 devfs_inactive(__unused
struct vnop_inactive_args
*ap
)
1542 vnode_t vp
= ap
->a_vp
;
1543 devnode_t
*dnp
= VTODN(vp
);
1546 * Cloned vnodes are not linked in anywhere, so they
1547 * can just be recycled.
1549 if (dnp
->dn_clone
!= NULL
) {
1557 * called with DEVFS_LOCK held
1560 devfs_update(struct vnode
*vp
, struct timeval
*access
, struct timeval
*modify
)
1566 if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) {
1574 DEVFS_ATTR_LOCK_SPIN();
1576 dn_times_locked(ip
, access
, modify
, &now
, DEVFS_UPDATE_ACCESS
| DEVFS_UPDATE_MOD
);
1577 DEVFS_ATTR_UNLOCK();
1582 #define VOPFUNC int (*)(void *)
1584 /* The following ops are used by directories and symlinks */
1585 int(**devfs_vnodeop_p
)(void *);
1586 const static struct vnodeopv_entry_desc devfs_vnodeop_entries
[] = {
1587 { .opve_op
= &vnop_default_desc
, .opve_impl
= (VOPFUNC
)vn_default_error
},
1588 { .opve_op
= &vnop_lookup_desc
, .opve_impl
= (VOPFUNC
)devfs_lookup
}, /* lookup */
1589 { .opve_op
= &vnop_create_desc
, .opve_impl
= (VOPFUNC
)err_create
}, /* create */
1590 { .opve_op
= &vnop_whiteout_desc
, .opve_impl
= (VOPFUNC
)err_whiteout
}, /* whiteout */
1591 { .opve_op
= &vnop_mknod_desc
, .opve_impl
= (VOPFUNC
)devfs_mknod
}, /* mknod */
1592 { .opve_op
= &vnop_open_desc
, .opve_impl
= (VOPFUNC
)nop_open
}, /* open */
1593 { .opve_op
= &vnop_close_desc
, .opve_impl
= (VOPFUNC
)devfs_close
}, /* close */
1594 { .opve_op
= &vnop_getattr_desc
, .opve_impl
= (VOPFUNC
)devfs_getattr
}, /* getattr */
1595 { .opve_op
= &vnop_setattr_desc
, .opve_impl
= (VOPFUNC
)devfs_setattr
}, /* setattr */
1596 { .opve_op
= &vnop_read_desc
, .opve_impl
= (VOPFUNC
)devfs_read
}, /* read */
1597 { .opve_op
= &vnop_write_desc
, .opve_impl
= (VOPFUNC
)devfs_write
}, /* write */
1598 { .opve_op
= &vnop_ioctl_desc
, .opve_impl
= (VOPFUNC
)err_ioctl
}, /* ioctl */
1599 { .opve_op
= &vnop_select_desc
, .opve_impl
= (VOPFUNC
)err_select
}, /* select */
1600 { .opve_op
= &vnop_revoke_desc
, .opve_impl
= (VOPFUNC
)err_revoke
}, /* revoke */
1601 { .opve_op
= &vnop_mmap_desc
, .opve_impl
= (VOPFUNC
)err_mmap
}, /* mmap */
1602 { .opve_op
= &vnop_fsync_desc
, .opve_impl
= (VOPFUNC
)nop_fsync
}, /* fsync */
1603 { .opve_op
= &vnop_remove_desc
, .opve_impl
= (VOPFUNC
)devfs_vnop_remove
}, /* remove */
1604 { .opve_op
= &vnop_link_desc
, .opve_impl
= (VOPFUNC
)devfs_link
}, /* link */
1605 { .opve_op
= &vnop_rename_desc
, .opve_impl
= (VOPFUNC
)devfs_rename
}, /* rename */
1606 { .opve_op
= &vnop_mkdir_desc
, .opve_impl
= (VOPFUNC
)devfs_mkdir
}, /* mkdir */
1607 { .opve_op
= &vnop_rmdir_desc
, .opve_impl
= (VOPFUNC
)devfs_rmdir
}, /* rmdir */
1608 { .opve_op
= &vnop_symlink_desc
, .opve_impl
= (VOPFUNC
)devfs_symlink
}, /* symlink */
1609 { .opve_op
= &vnop_readdir_desc
, .opve_impl
= (VOPFUNC
)devfs_readdir
}, /* readdir */
1610 { .opve_op
= &vnop_readlink_desc
, .opve_impl
= (VOPFUNC
)devfs_readlink
}, /* readlink */
1611 { .opve_op
= &vnop_inactive_desc
, .opve_impl
= (VOPFUNC
)devfs_inactive
}, /* inactive */
1612 { .opve_op
= &vnop_reclaim_desc
, .opve_impl
= (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1613 { .opve_op
= &vnop_strategy_desc
, .opve_impl
= (VOPFUNC
)err_strategy
}, /* strategy */
1614 { .opve_op
= &vnop_pathconf_desc
, .opve_impl
= (VOPFUNC
)devs_vnop_pathconf
}, /* pathconf */
1615 { .opve_op
= &vnop_advlock_desc
, .opve_impl
= (VOPFUNC
)err_advlock
}, /* advlock */
1616 { .opve_op
= &vnop_bwrite_desc
, .opve_impl
= (VOPFUNC
)err_bwrite
},
1617 { .opve_op
= &vnop_pagein_desc
, .opve_impl
= (VOPFUNC
)err_pagein
}, /* Pagein */
1618 { .opve_op
= &vnop_pageout_desc
, .opve_impl
= (VOPFUNC
)err_pageout
}, /* Pageout */
1619 { .opve_op
= &vnop_copyfile_desc
, .opve_impl
= (VOPFUNC
)err_copyfile
}, /* Copyfile */
1620 { .opve_op
= &vnop_blktooff_desc
, .opve_impl
= (VOPFUNC
)err_blktooff
}, /* blktooff */
1621 { .opve_op
= &vnop_offtoblk_desc
, .opve_impl
= (VOPFUNC
)err_offtoblk
}, /* offtoblk */
1622 { .opve_op
= &vnop_blockmap_desc
, .opve_impl
= (VOPFUNC
)err_blockmap
}, /* blockmap */
1624 { .opve_op
= &vnop_setlabel_desc
, .opve_impl
= (VOPFUNC
)devfs_setlabel
}, /* setlabel */
1626 { .opve_op
= (struct vnodeop_desc
*)NULL
, .opve_impl
= (int (*)(void *))NULL
}
1628 const struct vnodeopv_desc devfs_vnodeop_opv_desc
=
1629 { .opv_desc_vector_p
= &devfs_vnodeop_p
, .opv_desc_ops
= devfs_vnodeop_entries
};
1631 /* The following ops are used by the device nodes */
1632 int(**devfs_spec_vnodeop_p
)(void *);
1633 const static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries
[] = {
1634 { .opve_op
= &vnop_default_desc
, .opve_impl
= (VOPFUNC
)vn_default_error
},
1635 { .opve_op
= &vnop_lookup_desc
, .opve_impl
= (VOPFUNC
)spec_lookup
}, /* lookup */
1636 { .opve_op
= &vnop_create_desc
, .opve_impl
= (VOPFUNC
)spec_create
}, /* create */
1637 { .opve_op
= &vnop_mknod_desc
, .opve_impl
= (VOPFUNC
)spec_mknod
}, /* mknod */
1638 { .opve_op
= &vnop_open_desc
, .opve_impl
= (VOPFUNC
)spec_open
}, /* open */
1639 { .opve_op
= &vnop_close_desc
, .opve_impl
= (VOPFUNC
)devfsspec_close
}, /* close */
1640 { .opve_op
= &vnop_getattr_desc
, .opve_impl
= (VOPFUNC
)devfs_getattr
}, /* getattr */
1641 { .opve_op
= &vnop_setattr_desc
, .opve_impl
= (VOPFUNC
)devfs_setattr
}, /* setattr */
1642 { .opve_op
= &vnop_read_desc
, .opve_impl
= (VOPFUNC
)devfsspec_read
}, /* read */
1643 { .opve_op
= &vnop_write_desc
, .opve_impl
= (VOPFUNC
)devfsspec_write
}, /* write */
1644 { .opve_op
= &vnop_ioctl_desc
, .opve_impl
= (VOPFUNC
)spec_ioctl
}, /* ioctl */
1645 { .opve_op
= &vnop_select_desc
, .opve_impl
= (VOPFUNC
)spec_select
}, /* select */
1646 { .opve_op
= &vnop_revoke_desc
, .opve_impl
= (VOPFUNC
)spec_revoke
}, /* revoke */
1647 { .opve_op
= &vnop_mmap_desc
, .opve_impl
= (VOPFUNC
)spec_mmap
}, /* mmap */
1648 { .opve_op
= &vnop_fsync_desc
, .opve_impl
= (VOPFUNC
)spec_fsync
}, /* fsync */
1649 { .opve_op
= &vnop_remove_desc
, .opve_impl
= (VOPFUNC
)devfs_vnop_remove
}, /* remove */
1650 { .opve_op
= &vnop_link_desc
, .opve_impl
= (VOPFUNC
)devfs_link
}, /* link */
1651 { .opve_op
= &vnop_rename_desc
, .opve_impl
= (VOPFUNC
)spec_rename
}, /* rename */
1652 { .opve_op
= &vnop_mkdir_desc
, .opve_impl
= (VOPFUNC
)spec_mkdir
}, /* mkdir */
1653 { .opve_op
= &vnop_rmdir_desc
, .opve_impl
= (VOPFUNC
)spec_rmdir
}, /* rmdir */
1654 { .opve_op
= &vnop_symlink_desc
, .opve_impl
= (VOPFUNC
)spec_symlink
}, /* symlink */
1655 { .opve_op
= &vnop_readdir_desc
, .opve_impl
= (VOPFUNC
)spec_readdir
}, /* readdir */
1656 { .opve_op
= &vnop_readlink_desc
, .opve_impl
= (VOPFUNC
)spec_readlink
}, /* readlink */
1657 { .opve_op
= &vnop_inactive_desc
, .opve_impl
= (VOPFUNC
)devfs_inactive
}, /* inactive */
1658 { .opve_op
= &vnop_reclaim_desc
, .opve_impl
= (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1659 { .opve_op
= &vnop_strategy_desc
, .opve_impl
= (VOPFUNC
)spec_strategy
}, /* strategy */
1660 { .opve_op
= &vnop_pathconf_desc
, .opve_impl
= (VOPFUNC
)spec_pathconf
}, /* pathconf */
1661 { .opve_op
= &vnop_advlock_desc
, .opve_impl
= (VOPFUNC
)spec_advlock
}, /* advlock */
1662 { .opve_op
= &vnop_bwrite_desc
, .opve_impl
= (VOPFUNC
)vn_bwrite
},
1663 { .opve_op
= &vnop_pagein_desc
, .opve_impl
= (VOPFUNC
)err_pagein
}, /* Pagein */
1664 { .opve_op
= &vnop_pageout_desc
, .opve_impl
= (VOPFUNC
)err_pageout
}, /* Pageout */
1665 { .opve_op
= &vnop_copyfile_desc
, .opve_impl
= (VOPFUNC
)err_copyfile
}, /* Copyfile */
1666 { .opve_op
= &vnop_blktooff_desc
, .opve_impl
= (VOPFUNC
)spec_blktooff
}, /* blktooff */
1667 { .opve_op
= &vnop_blktooff_desc
, .opve_impl
= (VOPFUNC
)spec_offtoblk
}, /* blkofftoblk */
1668 { .opve_op
= &vnop_blockmap_desc
, .opve_impl
= (VOPFUNC
)spec_blockmap
}, /* blockmap */
1670 { .opve_op
= &vnop_setlabel_desc
, .opve_impl
= (VOPFUNC
)devfs_setlabel
}, /* setlabel */
1672 { .opve_op
= (struct vnodeop_desc
*)NULL
, .opve_impl
= (int (*)(void *))NULL
}
1674 const struct vnodeopv_desc devfs_spec_vnodeop_opv_desc
=
1675 { .opv_desc_vector_p
= &devfs_spec_vnodeop_p
, .opv_desc_ops
= devfs_spec_vnodeop_entries
};
1679 int(**devfs_devfd_vnodeop_p
)(void*);
1680 const static struct vnodeopv_entry_desc devfs_devfd_vnodeop_entries
[] = {
1681 { .opve_op
= &vnop_default_desc
, .opve_impl
= (VOPFUNC
)vn_default_error
},
1682 { .opve_op
= &vnop_lookup_desc
, .opve_impl
= (VOPFUNC
)devfs_devfd_lookup
}, /* lookup */
1683 { .opve_op
= &vnop_open_desc
, .opve_impl
= (VOPFUNC
)nop_open
}, /* open */
1684 { .opve_op
= &vnop_close_desc
, .opve_impl
= (VOPFUNC
)devfs_close
}, /* close */
1685 { .opve_op
= &vnop_getattr_desc
, .opve_impl
= (VOPFUNC
)devfs_getattr
}, /* getattr */
1686 { .opve_op
= &vnop_setattr_desc
, .opve_impl
= (VOPFUNC
)devfs_setattr
}, /* setattr */
1687 { .opve_op
= &vnop_revoke_desc
, .opve_impl
= (VOPFUNC
)err_revoke
}, /* revoke */
1688 { .opve_op
= &vnop_fsync_desc
, .opve_impl
= (VOPFUNC
)nop_fsync
}, /* fsync */
1689 { .opve_op
= &vnop_readdir_desc
, .opve_impl
= (VOPFUNC
)devfs_devfd_readdir
}, /* readdir */
1690 { .opve_op
= &vnop_inactive_desc
, .opve_impl
= (VOPFUNC
)devfs_inactive
}, /* inactive */
1691 { .opve_op
= &vnop_reclaim_desc
, .opve_impl
= (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1692 { .opve_op
= &vnop_pathconf_desc
, .opve_impl
= (VOPFUNC
)devs_vnop_pathconf
}, /* pathconf */
1694 { .opve_op
= &vnop_setlabel_desc
, .opve_impl
= (VOPFUNC
)devfs_setlabel
}, /* setlabel */
1696 { .opve_op
= (struct vnodeop_desc
*)NULL
, .opve_impl
= (int (*)(void *))NULL
}
1698 const struct vnodeopv_desc devfs_devfd_vnodeop_opv_desc
=
1699 { .opv_desc_vector_p
= &devfs_devfd_vnodeop_p
, .opv_desc_ops
= devfs_devfd_vnodeop_entries
};