2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright 1997,1998 Julian Elischer. All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions are
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright notice,
35 * this list of conditions and the following disclaimer in the documentation
36 * and/or other materials provided with the distribution.
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
39 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
40 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
42 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
43 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
44 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
45 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
46 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
47 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * Clark Warner (warner_c@apple.com) Tue Feb 10 2000
56 * - Added err_copyfile to the vnode operations table
57 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
58 * - instead of duplicating specfs here, created a vnode-ops table
59 * that redirects most operations to specfs (as is done with ufs);
60 * - removed routines that made no sense
61 * - cleaned up reclaim: replaced devfs_vntodn() with a macro VTODN()
62 * - cleaned up symlink, link locking
63 * - added the devfs_lock to protect devfs data structures against
64 * driver's calling devfs_add_devswf()/etc.
65 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
66 * - free the devfs devnode in devfs_inactive(), not just in devfs_reclaim()
67 * to free up kernel memory as soon as it's available
68 * - got rid of devfsspec_{read, write}
69 * Dieter Siegmund (dieter@apple.com) Fri Sep 17 09:58:38 PDT 1999
70 * - update the mod/access times
73 #include <sys/param.h>
74 #include <sys/systm.h>
76 #include <sys/namei.h>
77 #include <sys/kernel.h>
78 #include <sys/fcntl.h>
80 #include <sys/disklabel.h>
83 #include <sys/mount.h>
86 #include <sys/vnode.h>
87 #include <miscfs/specfs/specdev.h>
88 #include <sys/dirent.h>
89 #include <sys/vmmeter.h>
92 #include "devfsdefs.h"
95 * Convert a component of a pathname into a pointer to a locked node.
96 * This is a very central and rather complicated routine.
97 * If the file system is not maintained in a strict tree hierarchy,
98 * this can result in a deadlock situation (see comments in code below).
100 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
101 * whether the name is to be looked up, created, renamed, or deleted.
102 * When CREATE, RENAME, or DELETE is specified, information usable in
103 * creating, renaming, or deleting a directory entry may be calculated.
104 * If flag has LOCKPARENT or'ed into it and the target of the pathname
105 * exists, lookup returns both the target and its parent directory locked.
106 * When creating or renaming and LOCKPARENT is specified, the target may
107 * not be ".". When deleting and LOCKPARENT is specified, the target may
108 * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
109 * instead of two DNUNLOCKs.
111 * Overall outline of devfs_lookup:
113 * check accessibility of directory
114 * null terminate the component (lookup leaves the whole string alone)
115 * look for name in cache, if found, then if at end of path
116 * and deleting or creating, drop it, else return name
117 * search for name in directory, to found or notfound
119 * if creating, return locked directory,
122 * if at end of path and deleting, return information to allow delete
123 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
124 * node and return info to allow rewrite
125 * if not at end, add name to cache; if at end and neither creating
126 * nor deleting, add name to cache
127 * On return to lookup, remove the null termination we put in at the start.
129 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
132 devfs_lookup(struct vop_lookup_args
*ap
)
133 /*struct vop_lookup_args {
134 struct vnode * a_dvp; directory vnode ptr
135 struct vnode ** a_vpp; where to put the result
136 struct componentname * a_cnp; the name we want
139 struct componentname
*cnp
= ap
->a_cnp
;
140 struct vnode
*dir_vnode
= ap
->a_dvp
;
141 struct vnode
**result_vnode
= ap
->a_vpp
;
142 devnode_t
* dir_node
; /* the directory we are searching */
143 devnode_t
* node
= NULL
; /* the node we are searching for */
144 devdirent_t
* nodename
;
145 int flags
= cnp
->cn_flags
;
146 int op
= cnp
->cn_nameiop
; /* LOOKUP, CREATE, RENAME, or DELETE */
147 int lockparent
= flags
& LOCKPARENT
;
148 int wantparent
= flags
& (LOCKPARENT
|WANTPARENT
);
150 struct proc
*p
= cnp
->cn_proc
;
151 char heldchar
; /* the char at the end of the name componet */
153 *result_vnode
= NULL
; /* safe not sorry */ /*XXX*/
155 if (dir_vnode
->v_usecount
== 0)
156 printf("devfs_lookup: dir had no refs ");
157 dir_node
= VTODN(dir_vnode
);
160 * Check accessiblity of directory.
162 if (dir_node
->dn_type
!= DEV_DIR
) {
166 if ((error
= VOP_ACCESS(dir_vnode
, VEXEC
, cnp
->cn_cred
, p
)) != 0) {
170 /* temporarily terminate string component */
171 heldchar
= cnp
->cn_nameptr
[cnp
->cn_namelen
];
172 cnp
->cn_nameptr
[cnp
->cn_namelen
] = '\0';
174 nodename
= dev_findname(dir_node
,cnp
->cn_nameptr
);
177 node
= nodename
->de_dnp
;
178 node
->dn_last_lookup
= nodename
; /* for unlink */
179 /* Do potential vnode allocation here inside the lock
180 * to make sure that our device node has a non-NULL dn_vn
181 * associated with it. The device node might otherwise
182 * get deleted out from under us (see devfs_dn_free()).
184 error
= devfs_dntovn(node
, result_vnode
, p
);
187 /* restore saved character */
188 cnp
->cn_nameptr
[cnp
->cn_namelen
] = heldchar
;
193 if (!nodename
) { /* no entry */
194 /* If it doesn't exist and we're not the last component,
195 * or we're at the last component, but we're not creating
196 * or renaming, return ENOENT.
198 if (!(flags
& ISLASTCN
) || !(op
== CREATE
|| op
== RENAME
)) {
202 * Access for write is interpreted as allowing
203 * creation of files in the directory.
205 if ((error
= VOP_ACCESS(dir_vnode
, VWRITE
,
206 cnp
->cn_cred
, p
)) != 0)
211 * We return with the directory locked, so that
212 * the parameters we set up above will still be
213 * valid if we actually decide to add a new entry.
214 * We return ni_vp == NULL to indicate that the entry
215 * does not currently exist; we leave a pointer to
216 * the (locked) directory vnode in namei_data->ni_dvp.
217 * The pathname buffer is saved so that the name
218 * can be obtained later.
220 * NB - if the directory is unlocked, then this
221 * information cannot be used.
223 cnp
->cn_flags
|= SAVENAME
;
225 VOP_UNLOCK(dir_vnode
, 0, p
);
226 return (EJUSTRETURN
);
230 * If deleting, and at end of pathname, return
231 * parameters which can be used to remove file.
232 * If the wantparent flag isn't set, we return only
233 * the directory (in namei_data->ni_dvp), otherwise we go
234 * on and lock the node, being careful with ".".
236 if (op
== DELETE
&& (flags
& ISLASTCN
)) {
238 * Write access to directory required to delete files.
240 if ((error
= VOP_ACCESS(dir_vnode
, VWRITE
,
241 cnp
->cn_cred
, p
)) != 0)
244 * we are trying to delete '.'. What does this mean? XXX
246 if (dir_node
== node
) {
248 *result_vnode
= dir_vnode
;
253 * If directory is "sticky", then user must own
254 * the directory, or the file in it, else she
255 * may not delete it (unless she's root). This
256 * implements append-only directories.
258 if ((dir_node
->mode
& ISVTX
) &&
259 cnp
->cn_cred
->cr_uid
!= 0 &&
260 cnp
->cn_cred
->cr_uid
!= dir_node
->uid
&&
261 cnp
->cn_cred
->cr_uid
!= node
->uid
) {
262 VOP_UNLOCK(*result_vnode
, 0, p
);
267 VOP_UNLOCK(dir_vnode
, 0, p
);
272 * If rewriting (RENAME), return the vnode and the
273 * information required to rewrite the present directory
274 * Must get node of directory entry to verify it's a
275 * regular file, or empty directory.
277 if (op
== RENAME
&& wantparent
&& (flags
& ISLASTCN
)) {
279 * Are we allowed to change the holding directory?
281 if ((error
= VOP_ACCESS(dir_vnode
, VWRITE
,
282 cnp
->cn_cred
, p
)) != 0)
285 * Careful about locking second node.
286 * This can only occur if the target is ".".
288 if (dir_node
== node
)
290 /* hmm save the 'from' name (we need to delete it) */
291 cnp
->cn_flags
|= SAVENAME
;
293 VOP_UNLOCK(dir_vnode
, 0, p
);
298 * Step through the translation in the name. We do not unlock the
299 * directory because we may need it again if a symbolic link
300 * is relative to the current directory. Instead we save it
301 * unlocked as "saved_dir_node" XXX. We must get the target
302 * node before unlocking
303 * the directory to insure that the node will not be removed
304 * before we get it. We prevent deadlock by always fetching
305 * nodes from the root, moving down the directory tree. Thus
306 * when following backward pointers ".." we must unlock the
307 * parent directory before getting the requested directory.
308 * There is a potential race condition here if both the current
309 * and parent directories are removed before the lock for the
310 * node associated with ".." returns. We hope that this occurs
311 * infrequently since we cannot avoid this race condition without
312 * implementing a sophisticated deadlock detection algorithm.
313 * Note also that this simple deadlock detection scheme will not
314 * work if the file system has any hard links other than ".."
315 * that point backwards in the directory structure.
317 if (flags
& ISDOTDOT
) {
318 VOP_UNLOCK(dir_vnode
, 0, p
); /* race to get the node */
319 if (lockparent
&& (flags
& ISLASTCN
))
320 vn_lock(dir_vnode
, LK_EXCLUSIVE
| LK_RETRY
, p
);
321 } else if (dir_node
== node
) {
324 * this next statement is wrong: we already did a vget in
325 * devfs_dntovn(); DWS 4/16/1999
327 VREF(dir_vnode
); /* we want ourself, ie "." */
329 *result_vnode
= dir_vnode
;
331 if (!lockparent
|| (flags
& ISLASTCN
))
332 VOP_UNLOCK(dir_vnode
, 0, p
);
339 devfs_access(struct vop_access_args
*ap
)
340 /*struct vop_access_args {
343 struct ucred *a_cred;
348 * mode is filled with a combination of VREAD, VWRITE,
349 * and/or VEXEC bits turned on. In an octal number these
352 struct vnode
*vp
= ap
->a_vp
;
353 int mode
= ap
->a_mode
;
354 struct ucred
*cred
= ap
->a_cred
;
355 devnode_t
* file_node
;
358 struct proc
*p
= ap
->a_p
;
360 file_node
= VTODN(vp
);
362 * if we are not running as a process, we are in the
363 * kernel and we DO have permission
369 * Access check is based on only one of owner, group, public.
370 * If not owner, then check group. If not a member of the
371 * group, then check public access.
373 if (cred
->cr_uid
!= file_node
->dn_uid
)
375 /* failing that.. try groups */
377 gp
= cred
->cr_groups
;
378 for (i
= 0; i
< cred
->cr_ngroups
; i
++, gp
++)
380 if (file_node
->dn_gid
== *gp
)
385 /* failing that.. try general access */
390 if ((file_node
->dn_mode
& mode
) == mode
)
393 * Root gets to do anything.
394 * but only use suser prives as a last resort
395 * (Use of super powers is recorded in ap->a_p->p_acflag)
397 if( suser(cred
, &ap
->a_p
->p_acflag
) == 0) /* XXX what if no proc? */
403 devfs_getattr(struct vop_getattr_args
*ap
)
404 /*struct vop_getattr_args {
407 struct ucred *a_cred;
411 struct vnode
*vp
= ap
->a_vp
;
412 struct vattr
*vap
= ap
->a_vap
;
413 devnode_t
* file_node
;
416 file_node
= VTODN(vp
);
418 dn_times(file_node
, tv
, tv
);
419 vap
->va_rdev
= 0;/* default value only */
420 vap
->va_mode
= file_node
->dn_mode
;
421 switch (file_node
->dn_type
)
424 vap
->va_rdev
= (dev_t
)file_node
->dn_dvm
;
425 vap
->va_mode
|= (S_IFDIR
);
428 vap
->va_rdev
= file_node
->dn_typeinfo
.dev
;
429 vap
->va_mode
|= (S_IFCHR
);
432 vap
->va_rdev
= file_node
->dn_typeinfo
.dev
;
433 vap
->va_mode
|= (S_IFBLK
);
436 vap
->va_mode
|= (S_IFLNK
);
439 vap
->va_type
= vp
->v_type
;
440 vap
->va_nlink
= file_node
->dn_links
;
441 vap
->va_uid
= file_node
->dn_uid
;
442 vap
->va_gid
= file_node
->dn_gid
;
443 vap
->va_fsid
= (int32_t)(void *)file_node
->dn_dvm
;
444 vap
->va_fileid
= (int32_t)(void *)file_node
;
445 vap
->va_size
= file_node
->dn_len
; /* now a u_quad_t */
446 /* this doesn't belong here */
447 if (vp
->v_type
== VBLK
)
448 vap
->va_blocksize
= BLKDEV_IOSIZE
;
449 else if (vp
->v_type
== VCHR
)
450 vap
->va_blocksize
= MAXPHYSIO
;
452 vap
->va_blocksize
= vp
->v_mount
->mnt_stat
.f_iosize
;
453 /* if the time is bogus, set it to the boot time */
454 if (file_node
->dn_ctime
.tv_sec
== 0)
455 file_node
->dn_ctime
.tv_sec
= boottime
.tv_sec
;
456 if (file_node
->dn_mtime
.tv_sec
== 0)
457 file_node
->dn_mtime
.tv_sec
= boottime
.tv_sec
;
458 if (file_node
->dn_atime
.tv_sec
== 0)
459 file_node
->dn_atime
.tv_sec
= boottime
.tv_sec
;
460 vap
->va_ctime
= file_node
->dn_ctime
;
461 vap
->va_mtime
= file_node
->dn_mtime
;
462 vap
->va_atime
= file_node
->dn_atime
;
465 vap
->va_bytes
= file_node
->dn_len
; /* u_quad_t */
466 vap
->va_filerev
= 0; /* XXX */ /* u_quad_t */
467 vap
->va_vaflags
= 0; /* XXX */
472 devfs_setattr(struct vop_setattr_args
*ap
)
473 /*struct vop_setattr_args {
476 struct ucred *a_cred;
480 struct vnode
*vp
= ap
->a_vp
;
481 struct vattr
*vap
= ap
->a_vap
;
482 struct ucred
*cred
= ap
->a_cred
;
483 struct proc
*p
= ap
->a_p
;
487 devnode_t
* file_node
;
488 struct timeval atimeval
, mtimeval
;
490 if (vap
->va_flags
!= VNOVAL
) /* XXX needs to be implemented */
493 file_node
= VTODN(vp
);
495 if ((vap
->va_type
!= VNON
) ||
496 (vap
->va_nlink
!= VNOVAL
) ||
497 (vap
->va_fsid
!= VNOVAL
) ||
498 (vap
->va_fileid
!= VNOVAL
) ||
499 (vap
->va_blocksize
!= VNOVAL
) ||
500 (vap
->va_rdev
!= VNOVAL
) ||
501 (vap
->va_bytes
!= VNOVAL
) ||
502 (vap
->va_gen
!= VNOVAL
))
508 * Go through the fields and update iff not VNOVAL.
510 if (vap
->va_atime
.tv_sec
!= VNOVAL
|| vap
->va_mtime
.tv_sec
!= VNOVAL
) {
511 if (cred
->cr_uid
!= file_node
->dn_uid
&&
512 (error
= suser(cred
, &p
->p_acflag
)) &&
513 ((vap
->va_vaflags
& VA_UTIMES_NULL
) == 0 ||
514 (error
= VOP_ACCESS(vp
, VWRITE
, cred
, p
))))
516 if (vap
->va_atime
.tv_sec
!= VNOVAL
)
517 file_node
->dn_flags
|= DN_ACCESS
;
518 if (vap
->va_mtime
.tv_sec
!= VNOVAL
)
519 file_node
->dn_flags
|= DN_CHANGE
| DN_UPDATE
;
520 atimeval
.tv_sec
= vap
->va_atime
.tv_sec
;
521 atimeval
.tv_usec
= vap
->va_atime
.tv_nsec
/ 1000;
522 mtimeval
.tv_sec
= vap
->va_mtime
.tv_sec
;
523 mtimeval
.tv_usec
= vap
->va_mtime
.tv_nsec
/ 1000;
524 if (error
= VOP_UPDATE(vp
, &atimeval
, &mtimeval
, 1))
529 * Change the permissions.. must be root or owner to do this.
531 if (vap
->va_mode
!= (u_short
)VNOVAL
) {
532 if ((cred
->cr_uid
!= file_node
->dn_uid
)
533 && (error
= suser(cred
, &p
->p_acflag
)))
535 file_node
->dn_mode
&= ~07777;
536 file_node
->dn_mode
|= vap
->va_mode
& 07777;
540 * Change the owner.. must be root to do this.
542 if (vap
->va_uid
!= (uid_t
)VNOVAL
) {
543 if (error
= suser(cred
, &p
->p_acflag
))
545 file_node
->dn_uid
= vap
->va_uid
;
549 * Change the group.. must be root or owner to do this.
550 * If we are the owner, we must be in the target group too.
551 * don't use suser() unless you have to as it reports
552 * whether you needed suser powers or not.
554 if (vap
->va_gid
!= (gid_t
)VNOVAL
) {
555 if (cred
->cr_uid
== file_node
->dn_uid
){
556 gp
= cred
->cr_groups
;
557 for (i
= 0; i
< cred
->cr_ngroups
; i
++, gp
++) {
558 if (vap
->va_gid
== *gp
)
563 * we can't do it with normal privs,
564 * do we have an ace up our sleeve?
566 if (error
= suser(cred
, &p
->p_acflag
))
569 file_node
->dn_gid
= vap
->va_gid
;
573 * Copied from somewhere else
574 * but only kept as a marker and reminder of the fact that
575 * flags should be handled some day
577 if (vap
->va_flags
!= VNOVAL
) {
578 if (error
= suser(cred
, &p
->p_acflag
))
580 if (cred
->cr_uid
== 0)
590 devfs_read(struct vop_read_args
*ap
)
591 /*struct vop_read_args {
595 struct ucred *a_cred;
598 devnode_t
* dn_p
= VTODN(ap
->a_vp
);
600 switch (ap
->a_vp
->v_type
) {
602 dn_p
->dn_flags
|= DN_ACCESS
;
603 return VOP_READDIR(ap
->a_vp
, ap
->a_uio
, ap
->a_cred
,
607 printf("devfs_read(): bad file type %d", ap
->a_vp
->v_type
);
612 return (0); /* not reached */
617 struct vop_close_args
/* {
620 struct ucred *a_cred;
624 struct vnode
* vp
= ap
->a_vp
;
625 register devnode_t
* dnp
= VTODN(vp
);
627 simple_lock(&vp
->v_interlock
);
628 if (vp
->v_usecount
> 1)
629 dn_times(dnp
, time
, time
);
630 simple_unlock(&vp
->v_interlock
);
636 struct vop_close_args
/* {
639 struct ucred *a_cred;
643 struct vnode
* vp
= ap
->a_vp
;
644 register devnode_t
* dnp
= VTODN(vp
);
646 simple_lock(&vp
->v_interlock
);
647 if (vp
->v_usecount
> 1)
648 dn_times(dnp
, time
, time
);
649 simple_unlock(&vp
->v_interlock
);
650 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_close
), ap
));
654 devfsspec_read(struct vop_read_args
*ap
)
655 /*struct vop_read_args {
659 struct ucred *a_cred;
662 VTODN(ap
->a_vp
)->dn_flags
|= DN_ACCESS
;
663 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_read
), ap
));
667 devfsspec_write(struct vop_write_args
*ap
)
668 /*struct vop_write_args {
672 struct ucred *a_cred;
675 VTODN(ap
->a_vp
)->dn_flags
|= DN_CHANGE
| DN_UPDATE
;
676 return (VOCALL (spec_vnodeop_p
, VOFFSET(vop_write
), ap
));
680 * Write data to a file or directory.
683 devfs_write(struct vop_write_args
*ap
)
684 /*struct vop_write_args {
688 struct ucred *a_cred;
691 switch (ap
->a_vp
->v_type
) {
695 printf("devfs_write(): bad file type %d", ap
->a_vp
->v_type
);
698 return 0; /* not reached */
702 devfs_remove(struct vop_remove_args
*ap
)
703 /*struct vop_remove_args {
706 struct componentname *a_cnp;
709 struct vnode
*vp
= ap
->a_vp
;
710 struct vnode
*dvp
= ap
->a_dvp
;
711 struct componentname
*cnp
= ap
->a_cnp
;
715 int doingdirectory
= 0;
717 uid_t ouruid
= cnp
->cn_cred
->cr_uid
;
718 struct proc
*p
= cnp
->cn_proc
;
721 * Lock our directories and get our name pointers
722 * assume that the names are null terminated as they
723 * are the end of the path. Get pointers to all our
729 * Assuming we are atomic, dev_lookup left this for us
731 tnp
= tp
->dn_last_lookup
;
734 * Check we are doing legal things WRT the new flags
736 if ((tp
->dn_flags
& (IMMUTABLE
| APPEND
))
737 || (tdp
->dn_flags
& APPEND
) /*XXX eh?*/ ) {
743 * Make sure that we don't try do something stupid
745 if ((tp
->dn_type
) == DEV_DIR
) {
747 * Avoid ".", "..", and aliases of "." for obvious reasons.
749 if ( (cnp
->cn_namelen
== 1 && cnp
->cn_nameptr
[0] == '.')
750 || (cnp
->cn_flags
&ISDOTDOT
) ) {
757 /***********************************
758 * Start actually doing things.... *
759 ***********************************/
760 tdp
->dn_flags
|= DN_CHANGE
| DN_UPDATE
;
763 * own the parent directory, or the destination of the rename,
764 * otherwise the destination may not be changed (except by
765 * root). This implements append-only directories.
766 * XXX shoudn't this be in generic code?
768 if ((tdp
->dn_mode
& S_ISTXT
)
770 && ouruid
!= tdp
->dn_uid
771 && ouruid
!= tp
->dn_uid
) {
776 * Target must be empty if a directory and have no links
777 * to it. Also, ensure source and target are compatible
778 * (both directories, or both not directories).
780 if (( doingdirectory
) && (tp
->dn_links
> 2)) {
799 devfs_link(struct vop_link_args
*ap
)
800 /*struct vop_link_args {
801 struct vnode *a_tdvp;
803 struct componentname *a_cnp;
806 struct vnode
*vp
= ap
->a_vp
;
807 struct vnode
*tdvp
= ap
->a_tdvp
;
808 struct componentname
*cnp
= ap
->a_cnp
;
809 struct proc
*p
= cnp
->cn_proc
;
817 * First catch an arbitrary restriction for this FS
819 if (cnp
->cn_namelen
> DEVMAXNAMESIZE
) {
820 error
= ENAMETOOLONG
;
825 * Lock our directories and get our name pointers
826 * assume that the names are null terminated as they
827 * are the end of the path. Get pointers to all our
833 if (tdvp
->v_mount
!= vp
->v_mount
) {
835 VOP_ABORTOP(tdvp
, cnp
);
838 if (tdvp
!= vp
&& (error
= vn_lock(vp
, LK_EXCLUSIVE
, p
))) {
839 VOP_ABORTOP(tdvp
, cnp
);
844 * Check we are doing legal things WRT the new flags
846 if (fp
->dn_flags
& (IMMUTABLE
| APPEND
)) {
847 VOP_ABORTOP(tdvp
, cnp
);
852 /***********************************
853 * Start actually doing things.... *
854 ***********************************/
855 fp
->dn_flags
|= DN_CHANGE
;
857 error
= VOP_UPDATE(vp
, &tv
, &tv
, 1);
860 error
= dev_add_name(cnp
->cn_nameptr
, tdp
, NULL
, fp
, &tnp
);
865 VOP_UNLOCK(vp
, 0, p
);
872 * Check if source directory is in the path of the target directory.
873 * Target is supplied locked, source is unlocked.
874 * The target is always vput before returning.
877 devfs_checkpath(source
, target
)
878 devnode_t
*source
, *target
;
894 } while ((tmp
= tmp
->dn_typeinfo
.Dir
.parent
) != ntmp
);
902 * Rename system call. Seems overly complicated to me...
903 * rename("foo", "bar");
906 * link("foo", "bar");
908 * but ``atomically''.
910 * When the target exists, both the directory
911 * and target vnodes are locked.
912 * the source and source-parent vnodes are referenced
915 * Basic algorithm is:
917 * 1) Bump link count on source while we're linking it to the
918 * target. This also ensure the inode won't be deleted out
919 * from underneath us while we work (it may be truncated by
920 * a concurrent `trunc' or `open' for creation).
921 * 2) Link source to destination. If destination already exists,
923 * 3) Unlink source reference to node if still around. If a
924 * directory was moved and the parent of the destination
925 * is different from the source, patch the ".." entry in the
929 devfs_rename(struct vop_rename_args
*ap
)
930 /*struct vop_rename_args {
931 struct vnode *a_fdvp;
933 struct componentname *a_fcnp;
934 struct vnode *a_tdvp;
936 struct componentname *a_tcnp;
939 struct vnode
*tvp
= ap
->a_tvp
;
940 struct vnode
*tdvp
= ap
->a_tdvp
;
941 struct vnode
*fvp
= ap
->a_fvp
;
942 struct vnode
*fdvp
= ap
->a_fdvp
;
943 struct componentname
*tcnp
= ap
->a_tcnp
;
944 struct componentname
*fcnp
= ap
->a_fcnp
;
945 struct proc
*p
= fcnp
->cn_proc
;
946 devnode_t
*fp
, *fdp
, *tp
, *tdp
;
947 devdirent_t
*fnp
,*tnp
;
948 int doingdirectory
= 0;
953 * First catch an arbitrary restriction for this FS
955 if(tcnp
->cn_namelen
> DEVMAXNAMESIZE
) {
956 error
= ENAMETOOLONG
;
961 * Lock our directories and get our name pointers
962 * assume that the names are null terminated as they
963 * are the end of the path. Get pointers to all our
969 fnp
= fp
->dn_last_lookup
;
974 tnp
= tp
->dn_last_lookup
;
978 * trying to move it out of devfs?
979 * if we move a dir across mnt points. we need to fix all
980 * the mountpoint pointers! XXX
981 * so for now keep dirs within the same mount
983 if ((fvp
->v_mount
!= tdvp
->v_mount
) ||
984 (tvp
&& (fvp
->v_mount
!= tvp
->v_mount
))) {
987 VOP_ABORTOP(tdvp
, tcnp
);
988 if (tdvp
== tvp
) /* eh? */
994 VOP_ABORTOP(fdvp
, fcnp
); /* XXX, why not in NFS? */
1001 * Check we are doing legal things WRT the new flags
1003 if ((tp
&& (tp
->dn_flags
& (IMMUTABLE
| APPEND
)))
1004 || (fp
->dn_flags
& (IMMUTABLE
| APPEND
))
1005 || (fdp
->dn_flags
& APPEND
)) {
1011 * Make sure that we don't try do something stupid
1013 if ((fp
->dn_type
) == DEV_DIR
) {
1015 * Avoid ".", "..", and aliases of "." for obvious reasons.
1017 if ((fcnp
->cn_namelen
== 1 && fcnp
->cn_nameptr
[0] == '.')
1018 || (fcnp
->cn_flags
&ISDOTDOT
)
1019 || (tcnp
->cn_namelen
== 1 && tcnp
->cn_nameptr
[0] == '.')
1020 || (tcnp
->cn_flags
&ISDOTDOT
)
1029 * If ".." must be changed (ie the directory gets a new
1030 * parent) then the source directory must not be in the
1031 * directory hierarchy above the target, as this would
1032 * orphan everything below the source directory. Also
1033 * the user must have write permission in the source so
1034 * as to be able to change "..".
1036 if (doingdirectory
&& (tdp
!= fdp
)) {
1037 devnode_t
* tmp
, *ntmp
;
1038 error
= VOP_ACCESS(fvp
, VWRITE
, tcnp
->cn_cred
, tcnp
->cn_proc
);
1042 /* XXX unlock stuff here probably */
1047 } while ((tmp
= tmp
->dn_typeinfo
.Dir
.parent
) != ntmp
);
1050 /***********************************
1051 * Start actually doing things.... *
1052 ***********************************/
1053 fp
->dn_flags
|= DN_CHANGE
;
1055 if (error
= VOP_UPDATE(fvp
, &tv
, &tv
, 1)) {
1056 VOP_UNLOCK(fvp
, 0, p
);
1060 * Check if just deleting a link name.
1063 if (fvp
->v_type
== VDIR
) {
1068 /* Release destination completely. */
1069 VOP_ABORTOP(tdvp
, tcnp
);
1073 /* Delete source. */
1074 VOP_ABORTOP(fdvp
, fcnp
); /*XXX*/
1084 * 1) Bump link count while we're moving stuff
1085 * around. If we crash somewhere before
1086 * completing our work, too bad :)
1090 * If the target exists zap it (unless it's a non-empty directory)
1091 * We could do that as well but won't
1094 int ouruid
= tcnp
->cn_cred
->cr_uid
;
1096 * If the parent directory is "sticky", then the user must
1097 * own the parent directory, or the destination of the rename,
1098 * otherwise the destination may not be changed (except by
1099 * root). This implements append-only directories.
1100 * XXX shoudn't this be in generic code?
1102 if ((tdp
->dn_mode
& S_ISTXT
)
1104 && ouruid
!= tdp
->dn_uid
1105 && ouruid
!= tp
->dn_uid
) {
1110 * Target must be empty if a directory and have no links
1111 * to it. Also, ensure source and target are compatible
1112 * (both directories, or both not directories).
1114 if (( doingdirectory
) && (tp
->dn_links
> 2)) {
1121 dev_add_name(tcnp
->cn_nameptr
,tdp
,NULL
,fp
,&tnp
);
1123 fp
->dn_links
--; /* one less link to it.. */
1125 fp
->dn_links
--; /* we added one earlier*/
1138 if (vn_lock(fvp
, LK_EXCLUSIVE
| LK_RETRY
, p
) == 0) {
1139 fp
->dn_links
--; /* we added one earlier*/
1147 devfs_symlink(struct vop_symlink_args
*ap
)
1148 /*struct vop_symlink_args {
1149 struct vnode *a_dvp;
1150 struct vnode **a_vpp;
1151 struct componentname *a_cnp;
1152 struct vattr *a_vap;
1156 struct componentname
* cnp
= ap
->a_cnp
;
1157 struct vnode
*vp
= NULL
;
1160 devnode_type_t typeinfo
;
1163 struct vattr
* vap
= ap
->a_vap
;
1164 struct vnode
* * vpp
= ap
->a_vpp
;
1165 struct proc
*p
= cnp
->cn_proc
;
1168 dir_p
= VTODN(ap
->a_dvp
);
1169 typeinfo
.Slnk
.name
= ap
->a_target
;
1170 typeinfo
.Slnk
.namelen
= strlen(ap
->a_target
);
1172 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
, DEV_SLNK
,
1173 &typeinfo
, NULL
, NULL
, &nm_p
);
1179 dev_p
= nm_p
->de_dnp
;
1180 dev_p
->dn_uid
= dir_p
->dn_uid
;
1181 dev_p
->dn_gid
= dir_p
->dn_gid
;
1182 dev_p
->dn_mode
= vap
->va_mode
;
1183 dn_copy_times(dev_p
, dir_p
);
1184 error
= devfs_dntovn(dev_p
, vpp
, p
);
1190 if ((cnp
->cn_flags
& SAVESTART
) == 0) {
1191 char *tmp
= cnp
->cn_pnbuf
;
1192 cnp
->cn_pnbuf
= NULL
;
1193 cnp
->cn_flags
&= ~HASBUF
;
1194 FREE_ZONE(tmp
, cnp
->cn_pnlen
, M_NAMEI
);
1206 struct vop_mknod_args
/* {
1207 struct vnode *a_dvp;
1208 struct vnode **a_vpp;
1209 struct componentname *a_cnp;
1210 struct vattr *a_vap;
1213 struct componentname
* cnp
= ap
->a_cnp
;
1215 devdirent_t
* devent
;
1216 devnode_t
* dir_p
; /* devnode for parent directory */
1217 struct vnode
* dvp
= ap
->a_dvp
;
1219 devnode_type_t typeinfo
;
1220 struct vattr
* vap
= ap
->a_vap
;
1221 struct vnode
** vpp
= ap
->a_vpp
;
1222 struct proc
* p
= cnp
->cn_proc
;
1225 if (!vap
->va_type
== VBLK
&& !vap
->va_type
== VCHR
) {
1226 error
= EINVAL
; /* only support mknod of special files */
1230 typeinfo
.dev
= vap
->va_rdev
;
1232 error
= dev_add_entry(cnp
->cn_nameptr
, dir_p
,
1233 (vap
->va_type
== VBLK
) ? DEV_BDEV
: DEV_CDEV
,
1234 &typeinfo
, NULL
, NULL
, &devent
);
1239 dev_p
= devent
->de_dnp
;
1240 error
= devfs_dntovn(dev_p
, vpp
, p
);
1243 dev_p
->dn_uid
= cnp
->cn_cred
->cr_uid
;
1244 dev_p
->dn_gid
= dir_p
->dn_gid
;
1245 dev_p
->dn_mode
= vap
->va_mode
;
1251 if ((cnp
->cn_flags
& SAVESTART
) == 0) {
1252 char *tmp
= cnp
->cn_pnbuf
;
1253 cnp
->cn_pnbuf
= NULL
;
1254 cnp
->cn_flags
&= ~HASBUF
;
1255 FREE_ZONE(tmp
, cnp
->cn_pnlen
, M_NAMEI
);
1262 * Vnode op for readdir
1265 devfs_readdir(struct vop_readdir_args
*ap
)
1266 /*struct vop_readdir_args {
1269 struct ucred *a_cred;
1275 struct vnode
*vp
= ap
->a_vp
;
1276 struct uio
*uio
= ap
->a_uio
;
1277 struct dirent dirent
;
1278 devnode_t
* dir_node
;
1279 devdirent_t
* name_node
;
1285 struct proc
* p
= uio
->uio_procp
;
1287 /* set up refs to dir */
1288 dir_node
= VTODN(vp
);
1289 if(dir_node
->dn_type
!= DEV_DIR
)
1293 startpos
= uio
->uio_offset
;
1295 name_node
= dir_node
->dn_typeinfo
.Dir
.dirlist
;
1297 dir_node
->dn_flags
|= DN_ACCESS
;
1299 while ((name_node
|| (nodenumber
< 2)) && (uio
->uio_resid
> 0))
1304 dirent
.d_fileno
= (int32_t)(void *)dir_node
;
1306 dirent
.d_namlen
= 1;
1307 dirent
.d_type
= DT_DIR
;
1310 if(dir_node
->dn_typeinfo
.Dir
.parent
)
1312 = (int32_t)dir_node
->dn_typeinfo
.Dir
.parent
;
1314 dirent
.d_fileno
= (u_int32_t
)dir_node
;
1316 dirent
.d_namlen
= 2;
1317 dirent
.d_type
= DT_DIR
;
1320 dirent
.d_fileno
= (int32_t)(void *)name_node
->de_dnp
;
1321 dirent
.d_namlen
= strlen(name_node
->de_name
);
1322 name
= name_node
->de_name
;
1323 switch(name_node
->de_dnp
->dn_type
) {
1325 dirent
.d_type
= DT_BLK
;
1328 dirent
.d_type
= DT_CHR
;
1331 dirent
.d_type
= DT_DIR
;
1334 dirent
.d_type
= DT_LNK
;
1337 dirent
.d_type
= DT_UNKNOWN
;
1340 #define GENERIC_DIRSIZ(dp) \
1341 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1343 reclen
= dirent
.d_reclen
= GENERIC_DIRSIZ(&dirent
);
1345 if(pos
>= startpos
) /* made it to the offset yet? */
1347 if (uio
->uio_resid
< reclen
) /* will it fit? */
1349 strcpy( dirent
.d_name
,name
);
1350 if ((error
= uiomove ((caddr_t
)&dirent
,
1351 dirent
.d_reclen
, uio
)) != 0)
1355 if((nodenumber
>1) && name_node
)
1356 name_node
= name_node
->de_next
;
1360 uio
->uio_offset
= pos
;
1369 devfs_readlink(struct vop_readlink_args
*ap
)
1370 /*struct vop_readlink_args {
1373 struct ucred *a_cred;
1376 struct vnode
*vp
= ap
->a_vp
;
1377 struct uio
*uio
= ap
->a_uio
;
1378 devnode_t
* lnk_node
;
1381 /* set up refs to dir */
1382 lnk_node
= VTODN(vp
);
1383 if(lnk_node
->dn_type
!= DEV_SLNK
)
1385 if ((error
= VOP_ACCESS(vp
, VREAD
, ap
->a_cred
, NULL
)) != 0) { /* XXX */
1388 error
= uiomove(lnk_node
->dn_typeinfo
.Slnk
.name
,
1389 lnk_node
->dn_typeinfo
.Slnk
.namelen
, uio
);
1394 devfs_reclaim(struct vop_reclaim_args
*ap
)
1395 /*struct vop_reclaim_args {
1399 struct vnode
* vp
= ap
->a_vp
;
1400 devnode_t
* dnp
= VTODN(vp
);
1404 * do the same as devfs_inactive in case it is not called
1405 * before us (can that ever happen?)
1409 if (dnp
->dn_delete
) {
1417 * Print out the contents of a /devfs vnode.
1420 devfs_print(struct vop_print_args
*ap
)
1421 /*struct vop_print_args {
1429 /**************************************************************************\
1431 \**************************************************************************/
1435 * struct vop_inactive_args {
1436 * struct vnode *a_vp;
1442 devfs_inactive(struct vop_inactive_args
*ap
)
1444 struct vnode
* vp
= ap
->a_vp
;
1445 devnode_t
* dnp
= VTODN(vp
);
1450 if (dnp
->dn_delete
) {
1454 VOP_UNLOCK(vp
, 0, ap
->a_p
);
1460 struct vop_update_args
/* {
1462 struct timeval *a_access;
1463 struct timeval *a_modify;
1467 register struct fs
*fs
;
1471 ip
= VTODN(ap
->a_vp
);
1472 if (ap
->a_vp
->v_mount
->mnt_flag
& MNT_RDONLY
) {
1474 ~(DN_ACCESS
| DN_CHANGE
| DN_MODIFIED
| DN_UPDATE
);
1478 (DN_ACCESS
| DN_CHANGE
| DN_MODIFIED
| DN_UPDATE
)) == 0)
1480 dn_times(ip
, time
, time
);
1484 #define VOPFUNC int (*)(void *)
1486 /* The following ops are used by directories and symlinks */
1487 int (**devfs_vnodeop_p
)(void *);
1488 static struct vnodeopv_entry_desc devfs_vnodeop_entries
[] = {
1489 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
1490 { &vop_lookup_desc
, (VOPFUNC
)devfs_lookup
}, /* lookup */
1491 { &vop_create_desc
, (VOPFUNC
)err_create
}, /* create */
1492 { &vop_whiteout_desc
, (VOPFUNC
)err_whiteout
}, /* whiteout */
1493 { &vop_mknod_desc
, (VOPFUNC
)devfs_mknod
}, /* mknod */
1494 { &vop_open_desc
, (VOPFUNC
)nop_open
}, /* open */
1495 { &vop_close_desc
, (VOPFUNC
)devfs_close
}, /* close */
1496 { &vop_access_desc
, (VOPFUNC
)devfs_access
}, /* access */
1497 { &vop_getattr_desc
, (VOPFUNC
)devfs_getattr
}, /* getattr */
1498 { &vop_setattr_desc
, (VOPFUNC
)devfs_setattr
}, /* setattr */
1499 { &vop_read_desc
, (VOPFUNC
)devfs_read
}, /* read */
1500 { &vop_write_desc
, (VOPFUNC
)devfs_write
}, /* write */
1501 { &vop_lease_desc
, (VOPFUNC
)nop_lease
}, /* lease */
1502 { &vop_ioctl_desc
, (VOPFUNC
)err_ioctl
}, /* ioctl */
1503 { &vop_select_desc
, (VOPFUNC
)err_select
}, /* select */
1504 { &vop_revoke_desc
, (VOPFUNC
)err_revoke
}, /* revoke */
1505 { &vop_mmap_desc
, (VOPFUNC
)err_mmap
}, /* mmap */
1506 { &vop_fsync_desc
, (VOPFUNC
)nop_fsync
}, /* fsync */
1507 { &vop_seek_desc
, (VOPFUNC
)err_seek
}, /* seek */
1508 { &vop_remove_desc
, (VOPFUNC
)devfs_remove
}, /* remove */
1509 { &vop_link_desc
, (VOPFUNC
)devfs_link
}, /* link */
1510 { &vop_rename_desc
, (VOPFUNC
)devfs_rename
}, /* rename */
1511 { &vop_mkdir_desc
, (VOPFUNC
)err_mkdir
}, /* mkdir */
1512 { &vop_rmdir_desc
, (VOPFUNC
)err_rmdir
}, /* rmdir */
1513 { &vop_symlink_desc
, (VOPFUNC
)devfs_symlink
}, /* symlink */
1514 { &vop_readdir_desc
, (VOPFUNC
)devfs_readdir
}, /* readdir */
1515 { &vop_readlink_desc
, (VOPFUNC
)devfs_readlink
}, /* readlink */
1516 { &vop_abortop_desc
, (VOPFUNC
)nop_abortop
}, /* abortop */
1517 { &vop_inactive_desc
, (VOPFUNC
)devfs_inactive
}, /* inactive */
1518 { &vop_reclaim_desc
, (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1519 { &vop_lock_desc
, (VOPFUNC
)nop_lock
}, /* lock */
1520 { &vop_unlock_desc
, (VOPFUNC
)nop_unlock
}, /* unlock */
1521 { &vop_bmap_desc
, (VOPFUNC
)err_bmap
}, /* bmap */
1522 { &vop_strategy_desc
, (VOPFUNC
)err_strategy
}, /* strategy */
1523 { &vop_print_desc
, (VOPFUNC
)err_print
}, /* print */
1524 { &vop_islocked_desc
, (VOPFUNC
)nop_islocked
}, /* islocked */
1525 { &vop_pathconf_desc
, (VOPFUNC
)err_pathconf
}, /* pathconf */
1526 { &vop_advlock_desc
, (VOPFUNC
)err_advlock
}, /* advlock */
1527 { &vop_blkatoff_desc
, (VOPFUNC
)err_blkatoff
}, /* blkatoff */
1528 { &vop_valloc_desc
, (VOPFUNC
)err_valloc
}, /* valloc */
1529 { &vop_reallocblks_desc
, (VOPFUNC
)err_reallocblks
}, /* reallocblks */
1530 { &vop_vfree_desc
, (VOPFUNC
)err_vfree
}, /* vfree */
1531 { &vop_truncate_desc
, (VOPFUNC
)err_truncate
}, /* truncate */
1532 { &vop_update_desc
, (VOPFUNC
)devfs_update
}, /* update */
1533 { &vop_bwrite_desc
, (VOPFUNC
)err_bwrite
},
1534 { &vop_pagein_desc
, (VOPFUNC
)err_pagein
}, /* Pagein */
1535 { &vop_pageout_desc
, (VOPFUNC
)err_pageout
}, /* Pageout */
1536 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1537 { &vop_blktooff_desc
, (VOPFUNC
)err_blktooff
}, /* blktooff */
1538 { &vop_offtoblk_desc
, (VOPFUNC
)err_offtoblk
}, /* offtoblk */
1539 { &vop_cmap_desc
, (VOPFUNC
)err_cmap
}, /* cmap */
1540 { (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
1542 struct vnodeopv_desc devfs_vnodeop_opv_desc
=
1543 { &devfs_vnodeop_p
, devfs_vnodeop_entries
};
1545 /* The following ops are used by the device nodes */
1546 int (**devfs_spec_vnodeop_p
)(void *);
1547 static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries
[] = {
1548 { &vop_default_desc
, (VOPFUNC
)vn_default_error
},
1549 { &vop_lookup_desc
, (VOPFUNC
)spec_lookup
}, /* lookup */
1550 { &vop_create_desc
, (VOPFUNC
)spec_create
}, /* create */
1551 { &vop_mknod_desc
, (VOPFUNC
)spec_mknod
}, /* mknod */
1552 { &vop_open_desc
, (VOPFUNC
)spec_open
}, /* open */
1553 { &vop_close_desc
, (VOPFUNC
)devfsspec_close
}, /* close */
1554 { &vop_access_desc
, (VOPFUNC
)devfs_access
}, /* access */
1555 { &vop_getattr_desc
, (VOPFUNC
)devfs_getattr
}, /* getattr */
1556 { &vop_setattr_desc
, (VOPFUNC
)devfs_setattr
}, /* setattr */
1557 { &vop_read_desc
, (VOPFUNC
)devfsspec_read
}, /* read */
1558 { &vop_write_desc
, (VOPFUNC
)devfsspec_write
}, /* write */
1559 { &vop_lease_desc
, (VOPFUNC
)spec_lease_check
}, /* lease */
1560 { &vop_ioctl_desc
, (VOPFUNC
)spec_ioctl
}, /* ioctl */
1561 { &vop_select_desc
, (VOPFUNC
)spec_select
}, /* select */
1562 { &vop_revoke_desc
, (VOPFUNC
)spec_revoke
}, /* revoke */
1563 { &vop_mmap_desc
, (VOPFUNC
)spec_mmap
}, /* mmap */
1564 { &vop_fsync_desc
, (VOPFUNC
)spec_fsync
}, /* fsync */
1565 { &vop_seek_desc
, (VOPFUNC
)spec_seek
}, /* seek */
1566 { &vop_remove_desc
, (VOPFUNC
)devfs_remove
}, /* remove */
1567 { &vop_link_desc
, (VOPFUNC
)devfs_link
}, /* link */
1568 { &vop_rename_desc
, (VOPFUNC
)spec_rename
}, /* rename */
1569 { &vop_mkdir_desc
, (VOPFUNC
)spec_mkdir
}, /* mkdir */
1570 { &vop_rmdir_desc
, (VOPFUNC
)spec_rmdir
}, /* rmdir */
1571 { &vop_symlink_desc
, (VOPFUNC
)spec_symlink
}, /* symlink */
1572 { &vop_readdir_desc
, (VOPFUNC
)spec_readdir
}, /* readdir */
1573 { &vop_readlink_desc
, (VOPFUNC
)spec_readlink
}, /* readlink */
1574 { &vop_abortop_desc
, (VOPFUNC
)spec_abortop
}, /* abortop */
1575 { &vop_inactive_desc
, (VOPFUNC
)devfs_inactive
}, /* inactive */
1576 { &vop_reclaim_desc
, (VOPFUNC
)devfs_reclaim
}, /* reclaim */
1577 { &vop_lock_desc
, (VOPFUNC
)nop_lock
}, /* lock */
1578 { &vop_unlock_desc
, (VOPFUNC
)nop_unlock
}, /* unlock */
1579 { &vop_bmap_desc
, (VOPFUNC
)spec_bmap
}, /* bmap */
1580 { &vop_strategy_desc
, (VOPFUNC
)spec_strategy
}, /* strategy */
1581 { &vop_print_desc
, (VOPFUNC
)devfs_print
}, /* print */
1582 { &vop_islocked_desc
, (VOPFUNC
)nop_islocked
}, /* islocked */
1583 { &vop_pathconf_desc
, (VOPFUNC
)spec_pathconf
}, /* pathconf */
1584 { &vop_advlock_desc
, (VOPFUNC
)spec_advlock
}, /* advlock */
1585 { &vop_blkatoff_desc
, (VOPFUNC
)spec_blkatoff
}, /* blkatoff */
1586 { &vop_valloc_desc
, (VOPFUNC
)spec_valloc
}, /* valloc */
1587 { &vop_reallocblks_desc
, (VOPFUNC
)spec_reallocblks
}, /* reallocblks */
1588 { &vop_vfree_desc
, (VOPFUNC
)nop_vfree
}, /* vfree */
1589 { &vop_truncate_desc
, (VOPFUNC
)spec_truncate
}, /* truncate */
1590 { &vop_update_desc
, (VOPFUNC
)devfs_update
}, /* update */
1591 { &vop_bwrite_desc
, (VOPFUNC
)vn_bwrite
},
1592 { &vop_devblocksize_desc
, (VOPFUNC
)spec_devblocksize
}, /* devblocksize */
1593 { &vop_pagein_desc
, (VOPFUNC
)err_pagein
}, /* Pagein */
1594 { &vop_pageout_desc
, (VOPFUNC
)err_pageout
}, /* Pageout */
1595 { &vop_copyfile_desc
, (VOPFUNC
)err_copyfile
}, /* Copyfile */
1596 { &vop_blktooff_desc
, (VOPFUNC
)spec_blktooff
}, /* blktooff */
1597 { &vop_blktooff_desc
, (VOPFUNC
)spec_offtoblk
}, /* blkofftoblk */
1598 { &vop_cmap_desc
, (VOPFUNC
)spec_cmap
}, /* cmap */
1599 { (struct vnodeop_desc
*)NULL
, (int(*)())NULL
}
1601 struct vnodeopv_desc devfs_spec_vnodeop_opv_desc
=
1602 { &devfs_spec_vnodeop_p
, devfs_spec_vnodeop_entries
};