2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright 1997,1998 Julian Elischer. All rights reserved.
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions are
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright notice,
32 * this list of conditions and the following disclaimer in the documentation
33 * and/or other materials provided with the distribution.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
36 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
37 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
39 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
42 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * Clark Warner (warner_c@apple.com) Tue Feb 10 2000
53 * - Added err_copyfile to the vnode operations table
54 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
55 * - instead of duplicating specfs here, created a vnode-ops table
56 * that redirects most operations to specfs (as is done with ufs);
57 * - removed routines that made no sense
58 * - cleaned up reclaim: replaced devfs_vntodn() with a macro VTODN()
59 * - cleaned up symlink, link locking
60 * - added the devfs_lock to protect devfs data structures against
61 * driver's calling devfs_add_devswf()/etc.
62 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
63 * - free the devfs devnode in devfs_inactive(), not just in devfs_reclaim()
64 * to free up kernel memory as soon as it's available
65 * - got rid of devfsspec_{read, write}
66 * Dieter Siegmund (dieter@apple.com) Fri Sep 17 09:58:38 PDT 1999
67 * - update the mod/access times
70 #include <sys/param.h>
71 #include <sys/systm.h>
73 #include <sys/namei.h>
74 #include <sys/kernel.h>
75 #include <sys/fcntl.h>
77 #include <sys/disklabel.h>
80 #include <sys/mount.h>
83 #include <sys/vnode.h>
84 #include <miscfs/specfs/specdev.h>
85 #include <sys/dirent.h>
86 #include <sys/vmmeter.h>
89 #include "devfsdefs.h"
92 * Convert a component of a pathname into a pointer to a locked node.
93 * This is a very central and rather complicated routine.
94 * If the file system is not maintained in a strict tree hierarchy,
95 * this can result in a deadlock situation (see comments in code below).
97 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
98 * whether the name is to be looked up, created, renamed, or deleted.
99 * When CREATE, RENAME, or DELETE is specified, information usable in
100 * creating, renaming, or deleting a directory entry may be calculated.
101 * If flag has LOCKPARENT or'ed into it and the target of the pathname
102 * exists, lookup returns both the target and its parent directory locked.
103 * When creating or renaming and LOCKPARENT is specified, the target may
104 * not be ".". When deleting and LOCKPARENT is specified, the target may
105 * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
106 * instead of two DNUNLOCKs.
108 * Overall outline of devfs_lookup:
110 * check accessibility of directory
111 * null terminate the component (lookup leaves the whole string alone)
112 * look for name in cache, if found, then if at end of path
113 * and deleting or creating, drop it, else return name
114 * search for name in directory, to found or notfound
116 * if creating, return locked directory,
119 * if at end of path and deleting, return information to allow delete
120 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
121 * node and return info to allow rewrite
122 * if not at end, add name to cache; if at end and neither creating
123 * nor deleting, add name to cache
124 * On return to lookup, remove the null termination we put in at the start.
126 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
129 devfs_lookup(struct vop_lookup_args
*ap
)
130 /*struct vop_lookup_args {
131 struct vnode * a_dvp; directory vnode ptr
132 struct vnode ** a_vpp; where to put the result
133 struct componentname * a_cnp; the name we want
136 struct componentname
*cnp
= ap
->a_cnp
;
137 struct vnode
*dir_vnode
= ap
->a_dvp
;
138 struct vnode
**result_vnode
= ap
->a_vpp
;
139 devnode_t
* dir_node
; /* the directory we are searching */
140 devnode_t
* node
= NULL
; /* the node we are searching for */
141 devdirent_t
* nodename
;
142 int flags
= cnp
->cn_flags
;
143 int op
= cnp
->cn_nameiop
; /* LOOKUP, CREATE, RENAME, or DELETE */
144 int lockparent
= flags
& LOCKPARENT
;
145 int wantparent
= flags
& (LOCKPARENT
|WANTPARENT
);
147 struct proc
*p
= cnp
->cn_proc
;
148 char heldchar
; /* the char at the end of the name componet */
150 *result_vnode
= NULL
; /* safe not sorry */ /*XXX*/
152 if (dir_vnode
->v_usecount
== 0)
153 printf("devfs_lookup: dir had no refs ");
154 dir_node
= VTODN(dir_vnode
);
157 * Check accessiblity of directory.
159 if (dir_node
->dn_type
!= DEV_DIR
) {
163 if ((error
= VOP_ACCESS(dir_vnode
, VEXEC
, cnp
->cn_cred
, p
)) != 0) {
167 /* temporarily terminate string component */
168 heldchar
= cnp
->cn_nameptr
[cnp
->cn_namelen
];
169 cnp
->cn_nameptr
[cnp
->cn_namelen
] = '\0';
171 nodename
= dev_findname(dir_node
,cnp
->cn_nameptr
);
174 node
= nodename
->de_dnp
;
175 node
->dn_last_lookup
= nodename
; /* for unlink */
176 /* Do potential vnode allocation here inside the lock
177 * to make sure that our device node has a non-NULL dn_vn
178 * associated with it. The device node might otherwise
179 * get deleted out from under us (see devfs_dn_free()).
181 error
= devfs_dntovn(node
, result_vnode
, p
);
184 /* restore saved character */
185 cnp
->cn_nameptr
[cnp
->cn_namelen
] = heldchar
;
190 if (!nodename
) { /* no entry */
191 /* If it doesn't exist and we're not the last component,
192 * or we're at the last component, but we're not creating
193 * or renaming, return ENOENT.
195 if (!(flags
& ISLASTCN
) || !(op
== CREATE
|| op
== RENAME
)) {
199 * Access for write is interpreted as allowing
200 * creation of files in the directory.
202 if ((error
= VOP_ACCESS(dir_vnode
, VWRITE
,
203 cnp
->cn_cred
, p
)) != 0)
208 * We return with the directory locked, so that
209 * the parameters we set up above will still be
210 * valid if we actually decide to add a new entry.
211 * We return ni_vp == NULL to indicate that the entry
212 * does not currently exist; we leave a pointer to
213 * the (locked) directory vnode in namei_data->ni_dvp.
214 * The pathname buffer is saved so that the name
215 * can be obtained later.
217 * NB - if the directory is unlocked, then this
218 * information cannot be used.
220 cnp
->cn_flags
|= SAVENAME
;
222 VOP_UNLOCK(dir_vnode
, 0, p
);
223 return (EJUSTRETURN
);
227 * If deleting, and at end of pathname, return
228 * parameters which can be used to remove file.
229 * If the wantparent flag isn't set, we return only
230 * the directory (in namei_data->ni_dvp), otherwise we go
231 * on and lock the node, being careful with ".".
233 if (op
== DELETE
&& (flags
& ISLASTCN
)) {
235 * Write access to directory required to delete files.
237 if ((error
= VOP_ACCESS(dir_vnode
, VWRITE
,
238 cnp
->cn_cred
, p
)) != 0)
241 * we are trying to delete '.'. What does this mean? XXX
243 if (dir_node
== node
) {
245 *result_vnode
= dir_vnode
;
250 * If directory is "sticky", then user must own
251 * the directory, or the file in it, else she
252 * may not delete it (unless she's root). This
253 * implements append-only directories.
255 if ((dir_node
->mode
& ISVTX
) &&
256 cnp
->cn_cred
->cr_uid
!= 0 &&
257 cnp
->cn_cred
->cr_uid
!= dir_node
->uid
&&
258 cnp
->cn_cred
->cr_uid
!= node
->uid
) {
259 VOP_UNLOCK(*result_vnode
, 0, p
);
264 VOP_UNLOCK(dir_vnode
, 0, p
);
269 * If rewriting (RENAME), return the vnode and the
270 * information required to rewrite the present directory
271 * Must get node of directory entry to verify it's a
272 * regular file, or empty directory.
274 if (op
== RENAME
&& wantparent
&& (flags
& ISLASTCN
)) {
276 * Are we allowed to change the holding directory?
278 if ((error
= VOP_ACCESS(dir_vnode
, VWRITE
,
279 cnp
->cn_cred
, p
)) != 0)
282 * Careful about locking second node.
283 * This can only occur if the target is ".".
285 if (dir_node
== node
)
287 /* hmm save the 'from' name (we need to delete it) */
288 cnp
->cn_flags
|= SAVENAME
;
290 VOP_UNLOCK(dir_vnode
, 0, p
);
295 * Step through the translation in the name. We do not unlock the
296 * directory because we may need it again if a symbolic link
297 * is relative to the current directory. Instead we save it
298 * unlocked as "saved_dir_node" XXX. We must get the target
299 * node before unlocking
300 * the directory to insure that the node will not be removed
301 * before we get it. We prevent deadlock by always fetching
302 * nodes from the root, moving down the directory tree. Thus
303 * when following backward pointers ".." we must unlock the
304 * parent directory before getting the requested directory.
305 * There is a potential race condition here if both the current
306 * and parent directories are removed before the lock for the
307 * node associated with ".." returns. We hope that this occurs
308 * infrequently since we cannot avoid this race condition without
309 * implementing a sophisticated deadlock detection algorithm.
310 * Note also that this simple deadlock detection scheme will not
311 * work if the file system has any hard links other than ".."
312 * that point backwards in the directory structure.
314 if (flags
& ISDOTDOT
) {
315 VOP_UNLOCK(dir_vnode
, 0, p
); /* race to get the node */
316 if (lockparent
&& (flags
& ISLASTCN
))
317 vn_lock(dir_vnode
, LK_EXCLUSIVE
| LK_RETRY
, p
);
318 } else if (dir_node
== node
) {
321 * this next statement is wrong: we already did a vget in
322 * devfs_dntovn(); DWS 4/16/1999
324 VREF(dir_vnode
); /* we want ourself, ie "." */
326 *result_vnode
= dir_vnode
;
328 if (!lockparent
|| (flags
& ISLASTCN
))
329 VOP_UNLOCK(dir_vnode
, 0, p
);
336 devfs_access(struct vop_access_args
*ap
)
337 /*struct vop_access_args {
340 struct ucred *a_cred;
345 * mode is filled with a combination of VREAD, VWRITE,
346 * and/or VEXEC bits turned on. In an octal number these
349 struct vnode
*vp
= ap
->a_vp
;
350 int mode
= ap
->a_mode
;
351 struct ucred
*cred
= ap
->a_cred
;
352 devnode_t
* file_node
;
355 struct proc
*p
= ap
->a_p
;
357 file_node
= VTODN(vp
);
359 * if we are not running as a process, we are in the
360 * kernel and we DO have permission
366 * Access check is based on only one of owner, group, public.
367 * If not owner, then check group. If not a member of the
368 * group, then check public access.
370 if (cred
->cr_uid
!= file_node
->dn_uid
)
372 /* failing that.. try groups */
374 gp
= cred
->cr_groups
;
375 for (i
= 0; i
< cred
->cr_ngroups
; i
++, gp
++)
377 if (file_node
->dn_gid
== *gp
)
382 /* failing that.. try general access */
387 if ((file_node
->dn_mode
& mode
) == mode
)
390 * Root gets to do anything.
391 * but only use suser prives as a last resort
392 * (Use of super powers is recorded in ap->a_p->p_acflag)
394 if( suser(cred
, &ap
->a_p
->p_acflag
) == 0) /* XXX what if no proc? */
400 devfs_getattr(struct vop_getattr_args
*ap
)
401 /*struct vop_getattr_args {
404 struct ucred *a_cred;
408 struct vnode
*vp
= ap
->a_vp
;
409 struct vattr
*vap
= ap
->a_vap
;
410 devnode_t
* file_node
;
413 file_node
= VTODN(vp
);
415 dn_times(file_node
, tv
, tv
);
416 vap
->va_rdev
= 0;/* default value only */
417 vap
->va_mode
= file_node
->dn_mode
;
418 switch (file_node
->dn_type
)
421 vap
->va_rdev
= (dev_t
)file_node
->dn_dvm
;
422 vap
->va_mode
|= (S_IFDIR
);
425 vap
->va_rdev
= file_node
->dn_typeinfo
.dev
;
426 vap
->va_mode
|= (S_IFCHR
);
429 vap
->va_rdev
= file_node
->dn_typeinfo
.dev
;
430 vap
->va_mode
|= (S_IFBLK
);
433 vap
->va_mode
|= (S_IFLNK
);
436 vap
->va_type
= vp
->v_type
;
437 vap
->va_nlink
= file_node
->dn_links
;
438 vap
->va_uid
= file_node
->dn_uid
;
439 vap
->va_gid
= file_node
->dn_gid
;
440 vap
->va_fsid
= (int32_t)(void *)file_node
->dn_dvm
;
441 vap
->va_fileid
= (int32_t)(void *)file_node
;
442 vap
->va_size
= file_node
->dn_len
; /* now a u_quad_t */
443 /* this doesn't belong here */
444 if (vp
->v_type
== VBLK
)
445 vap
->va_blocksize
= BLKDEV_IOSIZE
;
446 else if (vp
->v_type
== VCHR
)
447 vap
->va_blocksize
= MAXPHYSIO
;
449 vap
->va_blocksize
= vp
->v_mount
->mnt_stat
.f_iosize
;
450 /* if the time is bogus, set it to the boot time */
451 if (file_node
->dn_ctime
.tv_sec
== 0)
452 file_node
->dn_ctime
.tv_sec
= boottime
.tv_sec
;
453 if (file_node
->dn_mtime
.tv_sec
== 0)
454 file_node
->dn_mtime
.tv_sec
= boottime
.tv_sec
;
455 if (file_node
->dn_atime
.tv_sec
== 0)
456 file_node
->dn_atime
.tv_sec
= boottime
.tv_sec
;
457 vap
->va_ctime
= file_node
->dn_ctime
;
458 vap
->va_mtime
= file_node
->dn_mtime
;
459 vap
->va_atime
= file_node
->dn_atime
;
462 vap
->va_bytes
= file_node
->dn_len
; /* u_quad_t */
463 vap
->va_filerev
= 0; /* XXX */ /* u_quad_t */
464 vap
->va_vaflags
= 0; /* XXX */
469 devfs_setattr(struct vop_setattr_args
*ap
)
470 /*struct vop_setattr_args {
473 struct ucred *a_cred;
477 struct vnode
*vp
= ap
->a_vp
;
478 struct vattr
*vap
= ap
->a_vap
;
479 struct ucred
*cred
= ap
->a_cred
;
480 struct proc
*p
= ap
->a_p
;
484 devnode_t
* file_node
;
485 struct timeval atimeval
, mtimeval
;
487 if (vap
->va_flags
!= VNOVAL
) /* XXX needs to be implemented */
490 file_node
= VTODN(vp
);
492 if ((vap
->va_type
!= VNON
) ||
493 (vap
->va_nlink
!= VNOVAL
) ||
494 (vap
->va_fsid
!= VNOVAL
) ||
495 (vap
->va_fileid
!= VNOVAL
) ||
496 (vap
->va_blocksize
!= VNOVAL
) ||
497 (vap
->va_rdev
!= VNOVAL
) ||
498 (vap
->va_bytes
!= VNOVAL
) ||
499 (vap
->va_gen
!= VNOVAL
))
505 * Go through the fields and update iff not VNOVAL.
507 if (vap
->va_atime
.tv_sec
!= VNOVAL
|| vap
->va_mtime
.tv_sec
!= VNOVAL
) {
508 if (cred
->cr_uid
!= file_node
->dn_uid
&&
509 (error
= suser(cred
, &p
->p_acflag
)) &&
510 ((vap
->va_vaflags
& VA_UTIMES_NULL
) == 0 ||
511 (error
= VOP_ACCESS(vp
, VWRITE
, cred
, p
))))
513 if (vap
->va_atime
.tv_sec
!= VNOVAL
)
514 file_node
->dn_flags
|= DN_ACCESS
;
515 if (vap
->va_mtime
.tv_sec
!= VNOVAL
)
516 file_node
->dn_flags
|= DN_CHANGE
| DN_UPDATE
;
517 atimeval
.tv_sec
= vap
->va_atime
.tv_sec
;
518 atimeval
.tv_usec
= vap
->va_atime
.tv_nsec
/ 1000;
519 mtimeval
.tv_sec
= vap
->va_mtime
.tv_sec
;
520 mtimeval
.tv_usec
= vap
->va_mtime
.tv_nsec
/ 1000;
521 if (error
= VOP_UPDATE(vp
, &atimeval
, &mtimeval
, 1))
526 * Change the permissions.. must be root or owner to do this.
528 if (vap
->va_mode
!= (u_short
)VNOVAL
) {
529 if ((cred
->cr_uid
!= file_node
->dn_uid
)
530 && (error
= suser(cred
, &p
->p_acflag
)))
532 file_node
->dn_mode
&= ~07777;
533 file_node
->dn_mode
|= vap
->va_mode
& 07777;
537 * Change the owner.. must be root to do this.
539 if (vap
->va_uid
!= (uid_t
)VNOVAL
) {
540 if (error
= suser(cred
, &p
->p_acflag
))
542 file_node
->dn_uid
= vap
->va_uid
;
546 * Change the group.. must be root or owner to do this.
547 * If we are the owner, we must be in the target group too.
548 * don't use suser() unless you have to as it reports
549 * whether you needed suser powers or not.
551 if (vap
->va_gid
!= (gid_t
)VNOVAL
) {
552 if (cred
->cr_uid
== file_node
->dn_uid
){
553 gp
= cred
->cr_groups
;
554 for (i
= 0; i
< cred
->cr_ngroups
; i
++, gp
++) {
555 if (vap
->va_gid
== *gp
)
560 * we can't do it with normal privs,
561 * do we have an ace up our sleeve?
563 if (error
= suser(cred
, &p
->p_acflag
))
566 file_node
->dn_gid
= vap
->va_gid
;
570 * Copied from somewhere else
571 * but only kept as a marker and reminder of the fact that
572 * flags should be handled some day
574 if (vap
->va_flags
!= VNOVAL
) {
575 if (error
= suser(cred
, &p
->p_acflag
))
577 if (cred
->cr_uid
== 0)
587 devfs_read(struct vop_read_args
*ap
)
588 /*struct vop_read_args {
592 struct ucred *a_cred;
595 devnode_t
* dn_p
= VTODN(ap
->a_vp
);
597 switch (ap
->a_vp
->v_type
) {
599 dn_p
->dn_flags
|= DN_ACCESS
;
600 return VOP_READDIR(ap
->a_vp
, ap
->a_uio
, ap
->a_cred
,
604 printf("devfs_read(): bad file type %d", ap
->a_vp
->v_type
);
609 return (0); /* not reached */
614 struct vop_close_args
/* {
617 struct ucred *a_cred;
621 struct vnode
* vp
= ap
->a_vp
;
622 register devnode_t
* dnp
= VTODN(vp
);
624 simple_lock(&vp
->v_interlock
);
625 if (vp
->v_usecount
> 1)
626 dn_times(dnp
, time
, time
);
627 simple_unlock(&vp
->v_interlock
);
633 struct vop_close_args
/* {
636 struct ucred *a_cred;
640 struct vnode
* vp
= ap
->a_vp
;
641 register devnode_t
* dnp
= VTODN(vp
);
643 simple_lock(&vp
->v_interlock
);
644 if (vp
->v_usecount
> 1)
645 dn_times(dnp
, time
, time
);
646 simple_unlock(&vp
->v_interlock
);
647 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
651 devfsspec_read(struct vop_read_args
*ap
)
652 /*struct vop_read_args {
656 struct ucred *a_cred;
659 VTODN(ap
->a_vp
)->dn_flags
|= DN_ACCESS
;
660 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
664 devfsspec_write(struct vop_write_args
*ap
)
665 /*struct vop_write_args {
669 struct ucred *a_cred;
672 VTODN(ap
->a_vp
)->dn_flags
|= DN_CHANGE
| DN_UPDATE
;
673 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
677 * Write data to a file or directory.
680 devfs_write(struct vop_write_args
*ap
)
681 /*struct vop_write_args {
685 struct ucred *a_cred;
688 switch (ap
->a_vp
->v_type
) {
692 printf("devfs_write(): bad file type %d", ap
->a_vp
->v_type
);
695 return 0; /* not reached */
699 devfs_remove(struct vop_remove_args
*ap
)
700 /*struct vop_remove_args {
703 struct componentname *a_cnp;
706 struct vnode
*vp
= ap
->a_vp
;
707 struct vnode
*dvp
= ap
->a_dvp
;
708 struct componentname
*cnp
= ap
->a_cnp
;
712 int doingdirectory
= 0;
714 uid_t ouruid
= cnp
->cn_cred
->cr_uid
;
715 struct proc
*p
= cnp
->cn_proc
;
718 * Lock our directories and get our name pointers
719 * assume that the names are null terminated as they
720 * are the end of the path. Get pointers to all our
726 * Assuming we are atomic, dev_lookup left this for us
728 tnp
= tp
->dn_last_lookup
;
731 * Check we are doing legal things WRT the new flags
733 if ((tp
->dn_flags
& (IMMUTABLE
| APPEND
))
734 || (tdp
->dn_flags
& APPEND
) /*XXX eh?*/ ) {
740 * Make sure that we don't try do something stupid
742 if ((tp
->dn_type
) == DEV_DIR
) {
744 * Avoid ".", "..", and aliases of "." for obvious reasons.
746 if ( (cnp
->cn_namelen
== 1 && cnp
->cn_nameptr
[0] == '.')
747 || (cnp
->cn_flags
&ISDOTDOT
) ) {
754 /***********************************
755 * Start actually doing things.... *
756 ***********************************/
757 tdp
->dn_flags
|= DN_CHANGE
| DN_UPDATE
;
760 * own the parent directory, or the destination of the rename,
761 * otherwise the destination may not be changed (except by
762 * root). This implements append-only directories.
763 * XXX shoudn't this be in generic code?
765 if ((tdp
->dn_mode
& S_ISTXT
)
767 && ouruid
!= tdp
->dn_uid
768 && ouruid
!= tp
->dn_uid
) {
773 * Target must be empty if a directory and have no links
774 * to it. Also, ensure source and target are compatible
775 * (both directories, or both not directories).
777 if (( doingdirectory
) && (tp
->dn_links
> 2)) {
796 devfs_link(struct vop_link_args
*ap
)
797 /*struct vop_link_args {
798 struct vnode *a_tdvp;
800 struct componentname *a_cnp;
803 struct vnode
*vp
= ap
->a_vp
;
804 struct vnode
*tdvp
= ap
->a_tdvp
;
805 struct componentname
*cnp
= ap
->a_cnp
;
806 struct proc
*p
= cnp
->cn_proc
;
814 * First catch an arbitrary restriction for this FS
816 if (cnp
->cn_namelen
> DEVMAXNAMESIZE
) {
817 error
= ENAMETOOLONG
;
822 * Lock our directories and get our name pointers
823 * assume that the names are null terminated as they
824 * are the end of the path. Get pointers to all our
830 if (tdvp
->v_mount
!= vp
->v_mount
) {
832 VOP_ABORTOP(tdvp
, cnp
);
835 if (tdvp
!= vp
&& (error
= vn_lock(vp
, LK_EXCLUSIVE
, p
))) {
836 VOP_ABORTOP(tdvp
, cnp
);
841 * Check we are doing legal things WRT the new flags
843 if (fp
->dn_flags
& (IMMUTABLE
| APPEND
)) {
844 VOP_ABORTOP(tdvp
, cnp
);
849 /***********************************
850 * Start actually doing things.... *
851 ***********************************/
852 fp
->dn_flags
|= DN_CHANGE
;
854 error
= VOP_UPDATE(vp
, &tv
, &tv
, 1);
857 error
= dev_add_name(cnp
->cn_nameptr
, tdp
, NULL
, fp
, &tnp
);
862 VOP_UNLOCK(vp
, 0, p
);
869 * Check if source directory is in the path of the target directory.
870 * Target is supplied locked, source is unlocked.
871 * The target is always vput before returning.
874 devfs_checkpath(source
, target
)
875 devnode_t
*source
, *target
;
891 } while ((tmp
= tmp
->dn_typeinfo
.Dir
.parent
) != ntmp
);
899 * Rename system call. Seems overly complicated to me...
900 * rename("foo", "bar");
903 * link("foo", "bar");
905 * but ``atomically''.
907 * When the target exists, both the directory
908 * and target vnodes are locked.
909 * the source and source-parent vnodes are referenced
912 * Basic algorithm is:
914 * 1) Bump link count on source while we're linking it to the
915 * target. This also ensure the inode won't be deleted out
916 * from underneath us while we work (it may be truncated by
917 * a concurrent `trunc' or `open' for creation).
918 * 2) Link source to destination. If destination already exists,
920 * 3) Unlink source reference to node if still around. If a
921 * directory was moved and the parent of the destination
922 * is different from the source, patch the ".." entry in the
926 devfs_rename(struct vop_rename_args
*ap
)
927 /*struct vop_rename_args {
928 struct vnode *a_fdvp;
930 struct componentname *a_fcnp;
931 struct vnode *a_tdvp;
933 struct componentname *a_tcnp;
936 struct vnode
*tvp
= ap
->a_tvp
;
937 struct vnode
*tdvp
= ap
->a_tdvp
;
938 struct vnode
*fvp
= ap
->a_fvp
;
939 struct vnode
*fdvp
= ap
->a_fdvp
;
940 struct componentname
*tcnp
= ap
->a_tcnp
;
941 struct componentname
*fcnp
= ap
->a_fcnp
;
942 struct proc
*p
= fcnp
->cn_proc
;
943 devnode_t
*fp
, *fdp
, *tp
, *tdp
;
944 devdirent_t
*fnp
,*tnp
;
945 int doingdirectory
= 0;
950 * First catch an arbitrary restriction for this FS
952 if(tcnp
->cn_namelen
> DEVMAXNAMESIZE
) {
953 error
= ENAMETOOLONG
;
958 * Lock our directories and get our name pointers
959 * assume that the names are null terminated as they
960 * are the end of the path. Get pointers to all our
966 fnp
= fp
->dn_last_lookup
;
971 tnp
= tp
->dn_last_lookup
;
975 * trying to move it out of devfs?
976 * if we move a dir across mnt points. we need to fix all
977 * the mountpoint pointers! XXX
978 * so for now keep dirs within the same mount
980 if ((fvp
->v_mount
!= tdvp
->v_mount
) ||
981 (tvp
&& (fvp
->v_mount
!= tvp
->v_mount
))) {
984 VOP_ABORTOP(tdvp
, tcnp
);
985 if (tdvp
== tvp
) /* eh? */
991 VOP_ABORTOP(fdvp
, fcnp
); /* XXX, why not in NFS? */
998 * Check we are doing legal things WRT the new flags
1000 if ((tp
&& (tp
->dn_flags
& (IMMUTABLE
| APPEND
)))
1001 || (fp
->dn_flags
& (IMMUTABLE
| APPEND
))
1002 || (fdp
->dn_flags
& APPEND
)) {
1008 * Make sure that we don't try do something stupid
1010 if ((fp
->dn_type
) == DEV_DIR
) {
1012 * Avoid ".", "..", and aliases of "." for obvious reasons.
1014 if ((fcnp
->cn_namelen
== 1 && fcnp
->cn_nameptr
[0] == '.')
1015 || (fcnp
->cn_flags
&ISDOTDOT
)
1016 || (tcnp
->cn_namelen
== 1 && tcnp
->cn_nameptr
[0] == '.')
1017 || (tcnp
->cn_flags
&ISDOTDOT
)
1026 * If ".." must be changed (ie the directory gets a new
1027 * parent) then the source directory must not be in the
1028 * directory hierarchy above the target, as this would
1029 * orphan everything below the source directory. Also
1030 * the user must have write permission in the source so
1031 * as to be able to change "..".
1033 if (doingdirectory
&& (tdp
!= fdp
)) {
1034 devnode_t
* tmp
, *ntmp
;
1035 error
= VOP_ACCESS(fvp
, VWRITE
, tcnp
->cn_cred
, tcnp
->cn_proc
);
1039 /* XXX unlock stuff here probably */
1044 } while ((tmp
= tmp
->dn_typeinfo
.Dir
.parent
) != ntmp
);
1047 /***********************************
1048 * Start actually doing things.... *
1049 ***********************************/
1050 fp
->dn_flags
|= DN_CHANGE
;
1052 if (error
= VOP_UPDATE(fvp
, &tv
, &tv
, 1)) {
1053 VOP_UNLOCK(fvp
, 0, p
);
1057 * Check if just deleting a link name.
1060 if (fvp
->v_type
== VDIR
) {
1065 /* Release destination completely. */
1066 VOP_ABORTOP(tdvp
, tcnp
);
1070 /* Delete source. */
1071 VOP_ABORTOP(fdvp
, fcnp
); /*XXX*/
1081 * 1) Bump link count while we're moving stuff
1082 * around. If we crash somewhere before
1083 * completing our work, too bad :)
1087 * If the target exists zap it (unless it's a non-empty directory)
1088 * We could do that as well but won't
1091 int ouruid
= tcnp
->cn_cred
->cr_uid
;
1093 * If the parent directory is "sticky", then the user must
1094 * own the parent directory, or the destination of the rename,
1095 * otherwise the destination may not be changed (except by
1096 * root). This implements append-only directories.
1097 * XXX shoudn't this be in generic code?
1099 if ((tdp
->dn_mode
& S_ISTXT
)
1101 && ouruid
!= tdp
->dn_uid
1102 && ouruid
!= tp
->dn_uid
) {
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.. */
1122 fp
->dn_links
--; /* we added one earlier*/
1135 if (vn_lock(fvp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1136 fp
->dn_links
--; /* we added one earlier*/
1144 devfs_symlink(struct vop_symlink_args
*ap
)
1145 /*struct vop_symlink_args {
1146 struct vnode *a_dvp;
1147 struct vnode **a_vpp;
1148 struct componentname *a_cnp;
1149 struct vattr *a_vap;
1153 struct componentname
* cnp
= ap
->a_cnp
;
1154 struct vnode
*vp
= NULL
;
1157 devnode_type_t typeinfo
;
1160 struct vattr
* vap
= ap
->a_vap
;
1161 struct vnode
* * vpp
= ap
->a_vpp
;
1162 struct proc
*p
= cnp
->cn_proc
;
1165 dir_p
= VTODN(ap
->a_dvp
);
1166 typeinfo
.Slnk
.name
= ap
->a_target
;
1167 typeinfo
.Slnk
.namelen
= strlen(ap
->a_target
);
1169 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
, DEV_SLNK
,
1170 &typeinfo
, NULL
, NULL
, &nm_p
);
1176 dev_p
= nm_p
->de_dnp
;
1177 dev_p
->dn_uid
= dir_p
->dn_uid
;
1178 dev_p
->dn_gid
= dir_p
->dn_gid
;
1179 dev_p
->dn_mode
= vap
->va_mode
;
1180 dn_copy_times(dev_p
, dir_p
);
1181 error
= devfs_dntovn(dev_p
, vpp
, p
);
1187 if ((cnp
->cn_flags
& SAVESTART
) == 0)
1188 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
1199 struct vop_mknod_args
/* {
1200 struct vnode *a_dvp;
1201 struct vnode **a_vpp;
1202 struct componentname *a_cnp;
1203 struct vattr *a_vap;
1206 struct componentname
* cnp
= ap
->a_cnp
;
1208 devdirent_t
* devent
;
1209 devnode_t
* dir_p
; /* devnode for parent directory */
1210 struct vnode
* dvp
= ap
->a_dvp
;
1212 devnode_type_t typeinfo
;
1213 struct vattr
* vap
= ap
->a_vap
;
1214 struct vnode
** vpp
= ap
->a_vpp
;
1215 struct proc
* p
= cnp
->cn_proc
;
1218 if (!vap
->va_type
== VBLK
&& !vap
->va_type
== VCHR
) {
1219 error
= EINVAL
; /* only support mknod of special files */
1223 typeinfo
.dev
= vap
->va_rdev
;
1225 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
,
1226 (vap
->va_type
== VBLK
) ? DEV_BDEV
: DEV_CDEV
,
1227 &typeinfo
, NULL
, NULL
, &devent
);
1232 dev_p
= devent
->de_dnp
;
1233 error
= devfs_dntovn(dev_p
, vpp
, p
);
1236 dev_p
->dn_uid
= cnp
->cn_cred
->cr_uid
;
1237 dev_p
->dn_gid
= dir_p
->dn_gid
;
1238 dev_p
->dn_mode
= vap
->va_mode
;
1244 if ((cnp
->cn_flags
& SAVESTART
) == 0)
1245 FREE_ZONE(cnp
->cn_pnbuf
, cnp
->cn_pnlen
, M_NAMEI
);
1251 * Vnode op for readdir
1254 devfs_readdir(struct vop_readdir_args
*ap
)
1255 /*struct vop_readdir_args {
1258 struct ucred *a_cred;
1264 struct vnode
*vp
= ap
->a_vp
;
1265 struct uio
*uio
= ap
->a_uio
;
1266 struct dirent dirent
;
1267 devnode_t
* dir_node
;
1268 devdirent_t
* name_node
;
1274 struct proc
* p
= uio
->uio_procp
;
1276 /* set up refs to dir */
1277 dir_node
= VTODN(vp
);
1278 if(dir_node
->dn_type
!= DEV_DIR
)
1282 startpos
= uio
->uio_offset
;
1284 name_node
= dir_node
->dn_typeinfo
.Dir
.dirlist
;
1286 dir_node
->dn_flags
|= DN_ACCESS
;
1288 while ((name_node
|| (nodenumber
< 2)) && (uio
->uio_resid
> 0))
1293 dirent
.d_fileno
= (int32_t)(void *)dir_node
;
1295 dirent
.d_namlen
= 1;
1296 dirent
.d_type
= DT_DIR
;
1299 if(dir_node
->dn_typeinfo
.Dir
.parent
)
1301 = (int32_t)dir_node
->dn_typeinfo
.Dir
.parent
;
1303 dirent
.d_fileno
= (u_int32_t
)dir_node
;
1305 dirent
.d_namlen
= 2;
1306 dirent
.d_type
= DT_DIR
;
1309 dirent
.d_fileno
= (int32_t)(void *)name_node
->de_dnp
;
1310 dirent
.d_namlen
= strlen(name_node
->de_name
);
1311 name
= name_node
->de_name
;
1312 switch(name_node
->de_dnp
->dn_type
) {
1314 dirent
.d_type
= DT_BLK
;
1317 dirent
.d_type
= DT_CHR
;
1320 dirent
.d_type
= DT_DIR
;
1323 dirent
.d_type
= DT_LNK
;
1326 dirent
.d_type
= DT_UNKNOWN
;
1329 #define GENERIC_DIRSIZ(dp) \
1330 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1332 reclen
= dirent
.d_reclen
= GENERIC_DIRSIZ(&dirent
);
1334 if(pos
>= startpos
) /* made it to the offset yet? */
1336 if (uio
->uio_resid
< reclen
) /* will it fit? */
1338 strcpy( dirent
.d_name
,name
);
1339 if ((error
= uiomove ((caddr_t
)&dirent
,
1340 dirent
.d_reclen
, uio
)) != 0)
1344 if((nodenumber
>1) && name_node
)
1345 name_node
= name_node
->de_next
;
1349 uio
->uio_offset
= pos
;
1358 devfs_readlink(struct vop_readlink_args
*ap
)
1359 /*struct vop_readlink_args {
1362 struct ucred *a_cred;
1365 struct vnode
*vp
= ap
->a_vp
;
1366 struct uio
*uio
= ap
->a_uio
;
1367 devnode_t
* lnk_node
;
1370 /* set up refs to dir */
1371 lnk_node
= VTODN(vp
);
1372 if(lnk_node
->dn_type
!= DEV_SLNK
)
1374 if ((error
= VOP_ACCESS(vp
, VREAD
, ap
->a_cred
, NULL
)) != 0) { /* XXX */
1377 error
= uiomove(lnk_node
->dn_typeinfo
.Slnk
.name
,
1378 lnk_node
->dn_typeinfo
.Slnk
.namelen
, uio
);
1383 devfs_abortop(struct vop_abortop_args
*ap
)
1384 /*struct vop_abortop_args {
1385 struct vnode *a_dvp;
1386 struct componentname *a_cnp;
1389 if ((ap
->a_cnp
->cn_flags
& (HASBUF
| SAVESTART
)) == HASBUF
) {
1390 FREE_ZONE(ap
->a_cnp
->cn_pnbuf
, ap
->a_cnp
->cn_pnlen
, M_NAMEI
);
1397 devfs_reclaim(struct vop_reclaim_args
*ap
)
1398 /*struct vop_reclaim_args {
1402 struct vnode
* vp
= ap
->a_vp
;
1403 devnode_t
* dnp
= VTODN(vp
);
1407 * do the same as devfs_inactive in case it is not called
1408 * before us (can that ever happen?)
1412 if (dnp
->dn_delete
) {
1420 * Print out the contents of a /devfs vnode.
1423 devfs_print(struct vop_print_args
*ap
)
1424 /*struct vop_print_args {
1432 /**************************************************************************\
1434 \**************************************************************************/
1438 * struct vop_inactive_args {
1439 * struct vnode *a_vp;
1445 devfs_inactive(struct vop_inactive_args
*ap
)
1447 struct vnode
* vp
= ap
->a_vp
;
1448 devnode_t
* dnp
= VTODN(vp
);
1453 if (dnp
->dn_delete
) {
1457 VOP_UNLOCK(vp
, 0, ap
->a_p
);
1463 struct vop_update_args
/* {
1465 struct timeval *a_access;
1466 struct timeval *a_modify;
1470 register struct fs
*fs
;
1474 ip
= VTODN(ap
->a_vp
);
1475 if (ap
->a_vp
->v_mount
->mnt_flag
& MNT_RDONLY
) {
1477 ~(DN_ACCESS
| DN_CHANGE
| DN_MODIFIED
| DN_UPDATE
);
1481 (DN_ACCESS
| DN_CHANGE
| DN_MODIFIED
| DN_UPDATE
)) == 0)
1483 dn_times(ip
, time
, time
);
1487 #define VOPFUNC int (*)(void *)
1489 /* The following ops are used by directories and symlinks */
1490 int (**devfs_vnodeop_p
)(void *);
1491 static struct vnodeopv_entry_desc devfs_vnodeop_entries
[] = {
1492 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
1493 { &vop_lookup_desc
, (VOPFUNC
)devfs_lookup
}, /* lookup */
1494 { &vop_create_desc
, (VOPFUNC
)err_create
}, /* create */
1495 { &vop_whiteout_desc
, (VOPFUNC
)err_whiteout
}, /* whiteout */
1496 { &vop_mknod_desc
, (VOPFUNC
)devfs_mknod
}, /* mknod */
1497 { &vop_open_desc
, (VOPFUNC
)nop_open
}, /* open */
1498 { &vop_close_desc
, (VOPFUNC
)devfs_close
}, /* close */
1499 { &vop_access_desc
, (VOPFUNC
)devfs_access
}, /* access */
1500 { &vop_getattr_desc
, (VOPFUNC
)devfs_getattr
}, /* getattr */
1501 { &vop_setattr_desc
, (VOPFUNC
)devfs_setattr
}, /* setattr */
1502 { &vop_read_desc
, (VOPFUNC
)devfs_read
}, /* read */
1503 { &vop_write_desc
, (VOPFUNC
)devfs_write
}, /* write */
1504 { &vop_lease_desc
, (VOPFUNC
)nop_lease
}, /* lease */
1505 { &vop_ioctl_desc
, (VOPFUNC
)err_ioctl
}, /* ioctl */
1506 { &vop_select_desc
, (VOPFUNC
)err_select
}, /* select */
1507 { &vop_revoke_desc
, (VOPFUNC
)err_revoke
}, /* revoke */
1508 { &vop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
1509 { &vop_fsync_desc
, (VOPFUNC
)nop_fsync
}, /* fsync */
1510 { &vop_seek_desc
, (VOPFUNC
)err_seek
}, /* seek */
1511 { &vop_remove_desc
, (VOPFUNC
)devfs_remove
}, /* remove */
1512 { &vop_link_desc
, (VOPFUNC
)devfs_link
}, /* link */
1513 { &vop_rename_desc
, (VOPFUNC
)devfs_rename
}, /* rename */
1514 { &vop_mkdir_desc
, (VOPFUNC
)err_mkdir
}, /* mkdir */
1515 { &vop_rmdir_desc
, (VOPFUNC
)err_rmdir
}, /* rmdir */
1516 { &vop_symlink_desc
, (VOPFUNC
)devfs_symlink
}, /* symlink */
1517 { &vop_readdir_desc
, (VOPFUNC
)devfs_readdir
}, /* readdir */
1518 { &vop_readlink_desc
, (VOPFUNC
)devfs_readlink
}, /* readlink */
1519 { &vop_abortop_desc
, (VOPFUNC
)devfs_abortop
}, /* abortop */
1520 { &vop_inactive_desc
, (VOPFUNC
)devfs_inactive
}, /* inactive */
1521 { &vop_reclaim_desc
, (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1522 { &vop_lock_desc
, (VOPFUNC
)nop_lock
}, /* lock */
1523 { &vop_unlock_desc
, (VOPFUNC
)nop_unlock
}, /* unlock */
1524 { &vop_bmap_desc
, (VOPFUNC
)err_bmap
}, /* bmap */
1525 { &vop_strategy_desc
, (VOPFUNC
)err_strategy
}, /* strategy */
1526 { &vop_print_desc
, (VOPFUNC
)err_print
}, /* print */
1527 { &vop_islocked_desc
, (VOPFUNC
)nop_islocked
}, /* islocked */
1528 { &vop_pathconf_desc
, (VOPFUNC
)err_pathconf
}, /* pathconf */
1529 { &vop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
1530 { &vop_blkatoff_desc
, (VOPFUNC
)err_blkatoff
}, /* blkatoff */
1531 { &vop_valloc_desc
, (VOPFUNC
)err_valloc
}, /* valloc */
1532 { &vop_reallocblks_desc
, (VOPFUNC
)err_reallocblks
}, /* reallocblks */
1533 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
1534 { &vop_truncate_desc
, (VOPFUNC
)err_truncate
}, /* truncate */
1535 { &vop_update_desc
, (VOPFUNC
)devfs_update
}, /* update */
1536 { &vop_bwrite_desc
, (VOPFUNC
)err_bwrite
},
1537 { &vop_pagein_desc
, (VOPFUNC
)err_pagein
}, /* Pagein */
1538 { &vop_pageout_desc
, (VOPFUNC
)err_pageout
}, /* Pageout */
1539 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1540 { &vop_blktooff_desc
, (VOPFUNC
)err_blktooff
}, /* blktooff */
1541 { &vop_offtoblk_desc
, (VOPFUNC
)err_offtoblk
}, /* offtoblk */
1542 { &vop_cmap_desc
, (VOPFUNC
)err_cmap
}, /* cmap */
1543 { (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
1545 struct vnodeopv_desc devfs_vnodeop_opv_desc
=
1546 { &devfs_vnodeop_p
, devfs_vnodeop_entries
};
1548 /* The following ops are used by the device nodes */
1549 int (**devfs_spec_vnodeop_p
)(void *);
1550 static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries
[] = {
1551 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
1552 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
1553 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
1554 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
1555 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
1556 { &vop_close_desc
, (VOPFUNC
)devfsspec_close
}, /* close */
1557 { &vop_access_desc
, (VOPFUNC
)devfs_access
}, /* access */
1558 { &vop_getattr_desc
, (VOPFUNC
)devfs_getattr
}, /* getattr */
1559 { &vop_setattr_desc
, (VOPFUNC
)devfs_setattr
}, /* setattr */
1560 { &vop_read_desc
, (VOPFUNC
)devfsspec_read
}, /* read */
1561 { &vop_write_desc
, (VOPFUNC
)devfsspec_write
}, /* write */
1562 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
1563 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
1564 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
1565 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
1566 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
1567 { &vop_fsync_desc
, (VOPFUNC
)spec_fsync
}, /* fsync */
1568 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
1569 { &vop_remove_desc
, (VOPFUNC
)devfs_remove
}, /* remove */
1570 { &vop_link_desc
, (VOPFUNC
)devfs_link
}, /* link */
1571 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
1572 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
1573 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
1574 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
1575 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
1576 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
1577 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
1578 { &vop_inactive_desc
, (VOPFUNC
)devfs_inactive
}, /* inactive */
1579 { &vop_reclaim_desc
, (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1580 { &vop_lock_desc
, (VOPFUNC
)nop_lock
}, /* lock */
1581 { &vop_unlock_desc
, (VOPFUNC
)nop_unlock
}, /* unlock */
1582 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
1583 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
1584 { &vop_print_desc
, (VOPFUNC
)devfs_print
}, /* print */
1585 { &vop_islocked_desc
, (VOPFUNC
)nop_islocked
}, /* islocked */
1586 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
1587 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
1588 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
1589 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
1590 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
1591 { &vop_vfree_desc
, (VOPFUNC
)nop_vfree
}, /* vfree */
1592 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
1593 { &vop_update_desc
, (VOPFUNC
)devfs_update
}, /* update */
1594 { &vop_bwrite_desc
, (VOPFUNC
)vn_bwrite
},
1595 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
1596 { &vop_pagein_desc
, (VOPFUNC
)err_pagein
}, /* Pagein */
1597 { &vop_pageout_desc
, (VOPFUNC
)err_pageout
}, /* Pageout */
1598 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1599 { &vop_blktooff_desc
, (VOPFUNC
)spec_blktooff
}, /* blktooff */
1600 { &vop_blktooff_desc
, (VOPFUNC
)spec_offtoblk
}, /* blkofftoblk */
1601 { &vop_cmap_desc
, (VOPFUNC
)spec_cmap
}, /* cmap */
1602 { (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
1604 struct vnodeopv_desc devfs_spec_vnodeop_opv_desc
=
1605 { &devfs_spec_vnodeop_p
, devfs_spec_vnodeop_entries
};