]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_vnops.c
bf7f11047df37398227a43f7366a56b893077e6e
[apple/xnu.git] / bsd / miscfs / devfs / devfs_vnops.c
1 /*
2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
29 */
30 /*
31 * Copyright 1997,1998 Julian Elischer. All rights reserved.
32 * julian@freebsd.org
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions are
36 * met:
37 * 1. Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright notice,
40 * this list of conditions and the following disclaimer in the documentation
41 * and/or other materials provided with the distribution.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
44 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
45 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
46 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
47 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
49 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
50 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 *
55 * devfs_vnops.c
56 */
57
58 /*
59 * HISTORY
60 * Clark Warner (warner_c@apple.com) Tue Feb 10 2000
61 * - Added err_copyfile to the vnode operations table
62 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
63 * - instead of duplicating specfs here, created a vnode-ops table
64 * that redirects most operations to specfs (as is done with ufs);
65 * - removed routines that made no sense
66 * - cleaned up reclaim: replaced devfs_vntodn() with a macro VTODN()
67 * - cleaned up symlink, link locking
68 * - added the devfs_lock to protect devfs data structures against
69 * driver's calling devfs_add_devswf()/etc.
70 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
71 * - free the devfs devnode in devfs_inactive(), not just in devfs_reclaim()
72 * to free up kernel memory as soon as it's available
73 * - got rid of devfsspec_{read, write}
74 * Dieter Siegmund (dieter@apple.com) Fri Sep 17 09:58:38 PDT 1999
75 * - update the mod/access times
76 */
77
78 #include <sys/param.h>
79 #include <sys/systm.h>
80 #include <sys/namei.h>
81 #include <sys/kernel.h>
82 #include <sys/fcntl.h>
83 #include <sys/conf.h>
84 #include <sys/disklabel.h>
85 #include <sys/lock.h>
86 #include <sys/stat.h>
87 #include <sys/mount_internal.h>
88 #include <sys/proc.h>
89 #include <sys/kauth.h>
90 #include <sys/time.h>
91 #include <sys/vnode_internal.h>
92 #include <miscfs/specfs/specdev.h>
93 #include <sys/dirent.h>
94 #include <sys/vmmeter.h>
95 #include <sys/vm.h>
96 #include <sys/uio_internal.h>
97
98 #include "devfsdefs.h"
99
100 static int devfs_update(struct vnode *vp, struct timeval *access,
101 struct timeval *modify);
102
103
104 /*
105 * Convert a component of a pathname into a pointer to a locked node.
106 * This is a very central and rather complicated routine.
107 * If the file system is not maintained in a strict tree hierarchy,
108 * this can result in a deadlock situation (see comments in code below).
109 *
110 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
111 * whether the name is to be looked up, created, renamed, or deleted.
112 * When CREATE, RENAME, or DELETE is specified, information usable in
113 * creating, renaming, or deleting a directory entry may be calculated.
114 * If flag has LOCKPARENT or'ed into it and the target of the pathname
115 * exists, lookup returns both the target and its parent directory locked.
116 * When creating or renaming and LOCKPARENT is specified, the target may
117 * not be ".". When deleting and LOCKPARENT is specified, the target may
118 * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
119 * instead of two DNUNLOCKs.
120 *
121 * Overall outline of devfs_lookup:
122 *
123 * check accessibility of directory
124 * null terminate the component (lookup leaves the whole string alone)
125 * look for name in cache, if found, then if at end of path
126 * and deleting or creating, drop it, else return name
127 * search for name in directory, to found or notfound
128 * notfound:
129 * if creating, return locked directory,
130 * else return error
131 * found:
132 * if at end of path and deleting, return information to allow delete
133 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
134 * node and return info to allow rewrite
135 * if not at end, add name to cache; if at end and neither creating
136 * nor deleting, add name to cache
137 * On return to lookup, remove the null termination we put in at the start.
138 *
139 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
140 */
141 static int
142 devfs_lookup(struct vnop_lookup_args *ap)
143 /*struct vnop_lookup_args {
144 struct vnode * a_dvp; directory vnode ptr
145 struct vnode ** a_vpp; where to put the result
146 struct componentname * a_cnp; the name we want
147 vfs_context_t a_context;
148 };*/
149 {
150 struct componentname *cnp = ap->a_cnp;
151 vfs_context_t ctx = cnp->cn_context;
152 struct proc *p = vfs_context_proc(ctx);
153 struct vnode *dir_vnode = ap->a_dvp;
154 struct vnode **result_vnode = ap->a_vpp;
155 devnode_t * dir_node; /* the directory we are searching */
156 devnode_t * node = NULL; /* the node we are searching for */
157 devdirent_t * nodename;
158 int flags = cnp->cn_flags;
159 int op = cnp->cn_nameiop; /* LOOKUP, CREATE, RENAME, or DELETE */
160 int wantparent = flags & (LOCKPARENT|WANTPARENT);
161 int error = 0;
162 char heldchar; /* the char at the end of the name componet */
163
164 retry:
165
166 *result_vnode = NULL; /* safe not sorry */ /*XXX*/
167
168 //if (dir_vnode->v_usecount == 0)
169 //printf("devfs_lookup: dir had no refs ");
170 dir_node = VTODN(dir_vnode);
171
172 /*
173 * Make sure that our node is a directory as well.
174 */
175 if (dir_node->dn_type != DEV_DIR) {
176 return (ENOTDIR);
177 }
178
179 DEVFS_LOCK();
180 /*
181 * temporarily terminate string component
182 */
183 heldchar = cnp->cn_nameptr[cnp->cn_namelen];
184 cnp->cn_nameptr[cnp->cn_namelen] = '\0';
185
186 nodename = dev_findname(dir_node, cnp->cn_nameptr);
187 /*
188 * restore saved character
189 */
190 cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
191
192 if (nodename) {
193 /* entry exists */
194 node = nodename->de_dnp;
195
196 /* Do potential vnode allocation here inside the lock
197 * to make sure that our device node has a non-NULL dn_vn
198 * associated with it. The device node might otherwise
199 * get deleted out from under us (see devfs_dn_free()).
200 */
201 error = devfs_dntovn(node, result_vnode, p);
202 }
203 DEVFS_UNLOCK();
204
205 if (error) {
206 if (error == EAGAIN)
207 goto retry;
208 return error;
209 }
210 if (!nodename) {
211 /*
212 * we haven't called devfs_dntovn if we get here
213 * we have not taken a reference on the node.. no
214 * vnode_put is necessary on these error returns
215 *
216 * If it doesn't exist and we're not the last component,
217 * or we're at the last component, but we're not creating
218 * or renaming, return ENOENT.
219 */
220 if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
221 return ENOENT;
222 }
223 /*
224 * We return with the directory locked, so that
225 * the parameters we set up above will still be
226 * valid if we actually decide to add a new entry.
227 * We return ni_vp == NULL to indicate that the entry
228 * does not currently exist; we leave a pointer to
229 * the (locked) directory vnode in namei_data->ni_dvp.
230 *
231 * NB - if the directory is unlocked, then this
232 * information cannot be used.
233 */
234 return (EJUSTRETURN);
235 }
236 /*
237 * from this point forward, we need to vnode_put the reference
238 * picked up in devfs_dntovn if we decide to return an error
239 */
240
241 /*
242 * If deleting, and at end of pathname, return
243 * parameters which can be used to remove file.
244 * If the wantparent flag isn't set, we return only
245 * the directory (in namei_data->ni_dvp), otherwise we go
246 * on and lock the node, being careful with ".".
247 */
248 if (op == DELETE && (flags & ISLASTCN)) {
249
250 /*
251 * we are trying to delete '.'. What does this mean? XXX
252 */
253 if (dir_node == node) {
254 if (*result_vnode) {
255 vnode_put(*result_vnode);
256 *result_vnode = NULL;
257 }
258 if ( ((error = vnode_get(dir_vnode)) == 0) ) {
259 *result_vnode = dir_vnode;
260 }
261 return (error);
262 }
263 return (0);
264 }
265
266 /*
267 * If rewriting (RENAME), return the vnode and the
268 * information required to rewrite the present directory
269 * Must get node of directory entry to verify it's a
270 * regular file, or empty directory.
271 */
272 if (op == RENAME && wantparent && (flags & ISLASTCN)) {
273
274 /*
275 * Careful about locking second node.
276 * This can only occur if the target is ".".
277 */
278 if (dir_node == node) {
279 error = EISDIR;
280 goto drop_ref;
281 }
282 return (0);
283 }
284
285 /*
286 * Step through the translation in the name. We do not unlock the
287 * directory because we may need it again if a symbolic link
288 * is relative to the current directory. Instead we save it
289 * unlocked as "saved_dir_node" XXX. We must get the target
290 * node before unlocking
291 * the directory to insure that the node will not be removed
292 * before we get it. We prevent deadlock by always fetching
293 * nodes from the root, moving down the directory tree. Thus
294 * when following backward pointers ".." we must unlock the
295 * parent directory before getting the requested directory.
296 * There is a potential race condition here if both the current
297 * and parent directories are removed before the lock for the
298 * node associated with ".." returns. We hope that this occurs
299 * infrequently since we cannot avoid this race condition without
300 * implementing a sophisticated deadlock detection algorithm.
301 * Note also that this simple deadlock detection scheme will not
302 * work if the file system has any hard links other than ".."
303 * that point backwards in the directory structure.
304 */
305 if ((flags & ISDOTDOT) == 0 && dir_node == node) {
306 if (*result_vnode) {
307 vnode_put(*result_vnode);
308 *result_vnode = NULL;
309 }
310 if ( (error = vnode_get(dir_vnode)) ) {
311 return (error);
312 }
313 *result_vnode = dir_vnode;
314 }
315 return (0);
316
317 drop_ref:
318 if (*result_vnode) {
319 vnode_put(*result_vnode);
320 *result_vnode = NULL;
321 }
322 return (error);
323 }
324
325 static int
326 devfs_getattr(struct vnop_getattr_args *ap)
327 /*struct vnop_getattr_args {
328 struct vnode *a_vp;
329 struct vnode_attr *a_vap;
330 kauth_cred_t a_cred;
331 struct proc *a_p;
332 } */
333 {
334 struct vnode *vp = ap->a_vp;
335 struct vnode_attr *vap = ap->a_vap;
336 devnode_t * file_node;
337 struct timeval now;
338
339 file_node = VTODN(vp);
340
341 DEVFS_LOCK();
342
343 microtime(&now);
344 dn_times(file_node, &now, &now, &now);
345
346 VATTR_RETURN(vap, va_mode, file_node->dn_mode);
347
348 switch (file_node->dn_type)
349 {
350 case DEV_DIR:
351 VATTR_RETURN(vap, va_rdev, (dev_t)file_node->dn_dvm);
352 vap->va_mode |= (S_IFDIR);
353 break;
354 case DEV_CDEV:
355 VATTR_RETURN(vap, va_rdev, file_node->dn_typeinfo.dev);
356 vap->va_mode |= (S_IFCHR);
357 break;
358 case DEV_BDEV:
359 VATTR_RETURN(vap, va_rdev, file_node->dn_typeinfo.dev);
360 vap->va_mode |= (S_IFBLK);
361 break;
362 case DEV_SLNK:
363 VATTR_RETURN(vap, va_rdev, 0);
364 vap->va_mode |= (S_IFLNK);
365 break;
366 default:
367 VATTR_RETURN(vap, va_rdev, 0); /* default value only */
368 }
369 VATTR_RETURN(vap, va_type, vp->v_type);
370 VATTR_RETURN(vap, va_nlink, file_node->dn_links);
371 VATTR_RETURN(vap, va_uid, file_node->dn_uid);
372 VATTR_RETURN(vap, va_gid, file_node->dn_gid);
373 VATTR_RETURN(vap, va_fsid, (uintptr_t)file_node->dn_dvm);
374 VATTR_RETURN(vap, va_fileid, (uintptr_t)file_node);
375 VATTR_RETURN(vap, va_data_size, file_node->dn_len);
376
377 /* return an override block size (advisory) */
378 if (vp->v_type == VBLK)
379 VATTR_RETURN(vap, va_iosize, BLKDEV_IOSIZE);
380 else if (vp->v_type == VCHR)
381 VATTR_RETURN(vap, va_iosize, MAXPHYSIO);
382 else
383 VATTR_RETURN(vap, va_iosize, vp->v_mount->mnt_vfsstat.f_iosize);
384 /* if the time is bogus, set it to the boot time */
385 if (file_node->dn_ctime.tv_sec == 0) {
386 file_node->dn_ctime.tv_sec = boottime_sec();
387 file_node->dn_ctime.tv_nsec = 0;
388 }
389 if (file_node->dn_mtime.tv_sec == 0)
390 file_node->dn_mtime = file_node->dn_ctime;
391 if (file_node->dn_atime.tv_sec == 0)
392 file_node->dn_atime = file_node->dn_ctime;
393 VATTR_RETURN(vap, va_change_time, file_node->dn_ctime);
394 VATTR_RETURN(vap, va_modify_time, file_node->dn_mtime);
395 VATTR_RETURN(vap, va_access_time, file_node->dn_atime);
396 VATTR_RETURN(vap, va_gen, 0);
397 VATTR_RETURN(vap, va_flags, 0);
398 VATTR_RETURN(vap, va_filerev, 0);
399 VATTR_RETURN(vap, va_acl, NULL);
400
401 DEVFS_UNLOCK();
402
403 return 0;
404 }
405
406 static int
407 devfs_setattr(struct vnop_setattr_args *ap)
408 /*struct vnop_setattr_args {
409 struct vnode *a_vp;
410 struct vnode_attr *a_vap;
411 vfs_context_t a_context;
412 } */
413 {
414 struct vnode *vp = ap->a_vp;
415 struct vnode_attr *vap = ap->a_vap;
416 kauth_cred_t cred = vfs_context_ucred(ap->a_context);
417 struct proc *p = vfs_context_proc(ap->a_context);
418 int error = 0;
419 devnode_t * file_node;
420 struct timeval atimeval, mtimeval;
421
422 file_node = VTODN(vp);
423
424 DEVFS_LOCK();
425 /*
426 * Go through the fields and update if set.
427 */
428 if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) {
429
430
431 if (VATTR_IS_ACTIVE(vap, va_access_time))
432 file_node->dn_access = 1;
433 if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
434 file_node->dn_change = 1;
435 file_node->dn_update = 1;
436 }
437 atimeval.tv_sec = vap->va_access_time.tv_sec;
438 atimeval.tv_usec = vap->va_access_time.tv_nsec / 1000;
439 mtimeval.tv_sec = vap->va_modify_time.tv_sec;
440 mtimeval.tv_usec = vap->va_modify_time.tv_nsec / 1000;
441
442 if ( (error = devfs_update(vp, &atimeval, &mtimeval)) )
443 goto exit;
444 }
445 VATTR_SET_SUPPORTED(vap, va_access_time);
446 VATTR_SET_SUPPORTED(vap, va_change_time);
447
448 /*
449 * Change the permissions.
450 */
451 if (VATTR_IS_ACTIVE(vap, va_mode)) {
452 file_node->dn_mode &= ~07777;
453 file_node->dn_mode |= vap->va_mode & 07777;
454 }
455 VATTR_SET_SUPPORTED(vap, va_mode);
456
457 /*
458 * Change the owner.
459 */
460 if (VATTR_IS_ACTIVE(vap, va_uid))
461 file_node->dn_uid = vap->va_uid;
462 VATTR_SET_SUPPORTED(vap, va_uid);
463
464 /*
465 * Change the group.
466 */
467 if (VATTR_IS_ACTIVE(vap, va_gid))
468 file_node->dn_gid = vap->va_gid;
469 VATTR_SET_SUPPORTED(vap, va_gid);
470 exit:
471 DEVFS_UNLOCK();
472
473 return error;
474 }
475
476 static int
477 devfs_read(struct vnop_read_args *ap)
478 /* struct vnop_read_args {
479 struct vnode *a_vp;
480 struct uio *a_uio;
481 int a_ioflag;
482 vfs_context_t a_context;
483 } */
484 {
485 devnode_t * dn_p = VTODN(ap->a_vp);
486
487 switch (ap->a_vp->v_type) {
488 case VDIR: {
489 dn_p->dn_access = 1;
490
491 return VNOP_READDIR(ap->a_vp, ap->a_uio, 0, NULL, NULL, ap->a_context);
492 }
493 default: {
494 printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
495 return(EINVAL);
496 break;
497 }
498 }
499 return (0); /* not reached */
500 }
501
502 static int
503 devfs_close(struct vnop_close_args *ap)
504 /* struct vnop_close_args {
505 struct vnode *a_vp;
506 int a_fflag;
507 vfs_context_t a_context;
508 } */
509 {
510 struct vnode * vp = ap->a_vp;
511 register devnode_t * dnp = VTODN(vp);
512 struct timeval now;
513
514 if (vnode_isinuse(vp, 1)) {
515 DEVFS_LOCK();
516 microtime(&now);
517 dn_times(dnp, &now, &now, &now);
518 DEVFS_UNLOCK();
519 }
520 return (0);
521 }
522
523 static int
524 devfsspec_close(struct vnop_close_args *ap)
525 /* struct vnop_close_args {
526 struct vnode *a_vp;
527 int a_fflag;
528 vfs_context_t a_context;
529 } */
530 {
531 struct vnode * vp = ap->a_vp;
532 register devnode_t * dnp = VTODN(vp);
533 struct timeval now;
534
535 if (vnode_isinuse(vp, 1)) {
536 DEVFS_LOCK();
537 microtime(&now);
538 dn_times(dnp, &now, &now, &now);
539 DEVFS_UNLOCK();
540 }
541 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
542 }
543
544 static int
545 devfsspec_read(struct vnop_read_args *ap)
546 /* struct vnop_read_args {
547 struct vnode *a_vp;
548 struct uio *a_uio;
549 int a_ioflag;
550 kauth_cred_t a_cred;
551 } */
552 {
553 register devnode_t * dnp = VTODN(ap->a_vp);
554
555 dnp->dn_access = 1;
556
557 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
558 }
559
560 static int
561 devfsspec_write(struct vnop_write_args *ap)
562 /* struct vnop_write_args {
563 struct vnode *a_vp;
564 struct uio *a_uio;
565 int a_ioflag;
566 vfs_context_t a_context;
567 } */
568 {
569 register devnode_t * dnp = VTODN(ap->a_vp);
570
571 dnp->dn_change = 1;
572 dnp->dn_update = 1;
573
574 return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
575 }
576
577 /*
578 * Write data to a file or directory.
579 */
580 static int
581 devfs_write(struct vnop_write_args *ap)
582 /* struct vnop_write_args {
583 struct vnode *a_vp;
584 struct uio *a_uio;
585 int a_ioflag;
586 kauth_cred_t a_cred;
587 } */
588 {
589 switch (ap->a_vp->v_type) {
590 case VDIR:
591 return(EISDIR);
592 default:
593 printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
594 return (EINVAL);
595 }
596 return 0; /* not reached */
597 }
598
599 static int
600 devfs_remove(struct vnop_remove_args *ap)
601 /* struct vnop_remove_args {
602 struct vnode *a_dvp;
603 struct vnode *a_vp;
604 struct componentname *a_cnp;
605 } */
606 {
607 struct vnode *vp = ap->a_vp;
608 struct vnode *dvp = ap->a_dvp;
609 struct componentname *cnp = ap->a_cnp;
610 vfs_context_t ctx = cnp->cn_context;
611 devnode_t * tp;
612 devnode_t * tdp;
613 devdirent_t * tnp;
614 int doingdirectory = 0;
615 int error = 0;
616 uid_t ouruid = kauth_cred_getuid(vfs_context_ucred(ctx));
617
618 /*
619 * assume that the name is null terminated as they
620 * are the end of the path. Get pointers to all our
621 * devfs structures.
622 */
623 tp = VTODN(vp);
624 tdp = VTODN(dvp);
625
626 DEVFS_LOCK();
627
628 tnp = dev_findname(tdp, cnp->cn_nameptr);
629
630 if (tnp == NULL) {
631 error = ENOENT;
632 goto abort;
633 }
634
635 /*
636 * Make sure that we don't try do something stupid
637 */
638 if ((tp->dn_type) == DEV_DIR) {
639 /*
640 * Avoid ".", "..", and aliases of "." for obvious reasons.
641 */
642 if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
643 || (cnp->cn_flags&ISDOTDOT) ) {
644 error = EINVAL;
645 goto abort;
646 }
647 doingdirectory++;
648 }
649
650 /***********************************
651 * Start actually doing things.... *
652 ***********************************/
653 tdp->dn_change = 1;
654 tdp->dn_update = 1;
655
656 /*
657 * Target must be empty if a directory and have no links
658 * to it. Also, ensure source and target are compatible
659 * (both directories, or both not directories).
660 */
661 if (( doingdirectory) && (tp->dn_links > 2)) {
662 error = ENOTEMPTY;
663 goto abort;
664 }
665 dev_free_name(tnp);
666 abort:
667 DEVFS_UNLOCK();
668
669 return (error);
670 }
671
672 /*
673 */
674 static int
675 devfs_link(struct vnop_link_args *ap)
676 /*struct vnop_link_args {
677 struct vnode *a_tdvp;
678 struct vnode *a_vp;
679 struct componentname *a_cnp;
680 vfs_context_t a_context;
681 } */
682 {
683 struct vnode *vp = ap->a_vp;
684 struct vnode *tdvp = ap->a_tdvp;
685 struct componentname *cnp = ap->a_cnp;
686 devnode_t * fp;
687 devnode_t * tdp;
688 devdirent_t * tnp;
689 int error = 0;
690 struct timeval now;
691
692 /*
693 * First catch an arbitrary restriction for this FS
694 */
695 if (cnp->cn_namelen > DEVMAXNAMESIZE) {
696 error = ENAMETOOLONG;
697 goto out1;
698 }
699
700 /*
701 * Lock our directories and get our name pointers
702 * assume that the names are null terminated as they
703 * are the end of the path. Get pointers to all our
704 * devfs structures.
705 */
706 tdp = VTODN(tdvp);
707 fp = VTODN(vp);
708
709 if (tdvp->v_mount != vp->v_mount) {
710 return (EXDEV);
711 }
712 DEVFS_LOCK();
713
714
715 /***********************************
716 * Start actually doing things.... *
717 ***********************************/
718 fp->dn_change = 1;
719
720 microtime(&now);
721 error = devfs_update(vp, &now, &now);
722
723 if (!error) {
724 error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
725 }
726 out1:
727 DEVFS_UNLOCK();
728
729 return (error);
730 }
731
732 /*
733 * Rename system call. Seems overly complicated to me...
734 * rename("foo", "bar");
735 * is essentially
736 * unlink("bar");
737 * link("foo", "bar");
738 * unlink("foo");
739 * but ``atomically''.
740 *
741 * When the target exists, both the directory
742 * and target vnodes are locked.
743 * the source and source-parent vnodes are referenced
744 *
745 *
746 * Basic algorithm is:
747 *
748 * 1) Bump link count on source while we're linking it to the
749 * target. This also ensure the inode won't be deleted out
750 * from underneath us while we work (it may be truncated by
751 * a concurrent `trunc' or `open' for creation).
752 * 2) Link source to destination. If destination already exists,
753 * delete it first.
754 * 3) Unlink source reference to node if still around. If a
755 * directory was moved and the parent of the destination
756 * is different from the source, patch the ".." entry in the
757 * directory.
758 */
759 static int
760 devfs_rename(struct vnop_rename_args *ap)
761 /*struct vnop_rename_args {
762 struct vnode *a_fdvp;
763 struct vnode *a_fvp;
764 struct componentname *a_fcnp;
765 struct vnode *a_tdvp;
766 struct vnode *a_tvp;
767 struct componentname *a_tcnp;
768 vfs_context_t a_context;
769 } */
770 {
771 struct vnode *tvp = ap->a_tvp;
772 struct vnode *tdvp = ap->a_tdvp;
773 struct vnode *fvp = ap->a_fvp;
774 struct vnode *fdvp = ap->a_fdvp;
775 struct componentname *tcnp = ap->a_tcnp;
776 struct componentname *fcnp = ap->a_fcnp;
777 devnode_t *fp, *fdp, *tp, *tdp;
778 devdirent_t *fnp,*tnp;
779 int doingdirectory = 0;
780 int error = 0;
781 struct timeval now;
782
783 DEVFS_LOCK();
784 /*
785 * First catch an arbitrary restriction for this FS
786 */
787 if (tcnp->cn_namelen > DEVMAXNAMESIZE) {
788 error = ENAMETOOLONG;
789 goto out;
790 }
791
792 /*
793 * assume that the names are null terminated as they
794 * are the end of the path. Get pointers to all our
795 * devfs structures.
796 */
797 tdp = VTODN(tdvp);
798 fdp = VTODN(fdvp);
799 fp = VTODN(fvp);
800
801 fnp = dev_findname(fdp, fcnp->cn_nameptr);
802
803 if (fnp == NULL) {
804 error = ENOENT;
805 goto out;
806 }
807 tp = NULL;
808 tnp = NULL;
809
810 if (tvp) {
811 tnp = dev_findname(tdp, tcnp->cn_nameptr);
812
813 if (tnp == NULL) {
814 error = ENOENT;
815 goto out;
816 }
817 tp = VTODN(tvp);
818 }
819
820 /*
821 * Make sure that we don't try do something stupid
822 */
823 if ((fp->dn_type) == DEV_DIR) {
824 /*
825 * Avoid ".", "..", and aliases of "." for obvious reasons.
826 */
827 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
828 || (fcnp->cn_flags&ISDOTDOT)
829 || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')
830 || (tcnp->cn_flags&ISDOTDOT)
831 || (tdp == fp )) {
832 error = EINVAL;
833 goto out;
834 }
835 doingdirectory++;
836 }
837
838 /*
839 * If ".." must be changed (ie the directory gets a new
840 * parent) then the source directory must not be in the
841 * directory hierarchy above the target, as this would
842 * orphan everything below the source directory. Also
843 * the user must have write permission in the source so
844 * as to be able to change "..".
845 */
846 if (doingdirectory && (tdp != fdp)) {
847 devnode_t * tmp, *ntmp;
848 tmp = tdp;
849 do {
850 if(tmp == fp) {
851 /* XXX unlock stuff here probably */
852 error = EINVAL;
853 goto out;
854 }
855 ntmp = tmp;
856 } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
857 }
858
859 /***********************************
860 * Start actually doing things.... *
861 ***********************************/
862 fp->dn_change = 1;
863 microtime(&now);
864
865 if ( (error = devfs_update(fvp, &now, &now)) ) {
866 goto out;
867 }
868 /*
869 * Check if just deleting a link name.
870 */
871 if (fvp == tvp) {
872 if (fvp->v_type == VDIR) {
873 error = EINVAL;
874 goto out;
875 }
876 /* Release destination completely. */
877 dev_free_name(fnp);
878
879 DEVFS_UNLOCK();
880 return 0;
881 }
882 /*
883 * 1) Bump link count while we're moving stuff
884 * around. If we crash somewhere before
885 * completing our work, too bad :)
886 */
887 fp->dn_links++;
888 /*
889 * If the target exists zap it (unless it's a non-empty directory)
890 * We could do that as well but won't
891 */
892 if (tp) {
893 int ouruid = kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context));
894 /*
895 * Target must be empty if a directory and have no links
896 * to it. Also, ensure source and target are compatible
897 * (both directories, or both not directories).
898 */
899 if (( doingdirectory) && (tp->dn_links > 2)) {
900 error = ENOTEMPTY;
901 goto bad;
902 }
903 dev_free_name(tnp);
904 tp = NULL;
905 }
906 dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp);
907 fnp->de_dnp = NULL;
908 fp->dn_links--; /* one less link to it.. */
909
910 dev_free_name(fnp);
911 bad:
912 fp->dn_links--; /* we added one earlier*/
913 out:
914 DEVFS_UNLOCK();
915 return (error);
916 }
917
918 static int
919 devfs_symlink(struct vnop_symlink_args *ap)
920 /*struct vnop_symlink_args {
921 struct vnode *a_dvp;
922 struct vnode **a_vpp;
923 struct componentname *a_cnp;
924 struct vnode_attr *a_vap;
925 char *a_target;
926 vfs_context_t a_context;
927 } */
928 {
929 struct componentname * cnp = ap->a_cnp;
930 vfs_context_t ctx = cnp->cn_context;
931 struct proc *p = vfs_context_proc(ctx);
932 int error = 0;
933 devnode_t * dir_p;
934 devnode_type_t typeinfo;
935 devdirent_t * nm_p;
936 devnode_t * dev_p;
937 struct vnode_attr * vap = ap->a_vap;
938 struct vnode * * vpp = ap->a_vpp;
939
940 dir_p = VTODN(ap->a_dvp);
941 typeinfo.Slnk.name = ap->a_target;
942 typeinfo.Slnk.namelen = strlen(ap->a_target);
943
944 DEVFS_LOCK();
945 error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK,
946 &typeinfo, NULL, NULL, &nm_p);
947 if (error) {
948 goto failure;
949 }
950 dev_p = nm_p->de_dnp;
951 dev_p->dn_uid = dir_p->dn_uid;
952 dev_p->dn_gid = dir_p->dn_gid;
953 dev_p->dn_mode = vap->va_mode;
954 dn_copy_times(dev_p, dir_p);
955
956 error = devfs_dntovn(dev_p, vpp, p);
957 failure:
958 DEVFS_UNLOCK();
959
960 return error;
961 }
962
963 /*
964 * Mknod vnode call
965 */
966 static int
967 devfs_mknod(struct vnop_mknod_args *ap)
968 /* struct vnop_mknod_args {
969 struct vnode *a_dvp;
970 struct vnode **a_vpp;
971 struct componentname *a_cnp;
972 struct vnode_attr *a_vap;
973 vfs_context_t a_context;
974 } */
975 {
976 struct componentname * cnp = ap->a_cnp;
977 vfs_context_t ctx = cnp->cn_context;
978 struct proc *p = vfs_context_proc(ctx);
979 devnode_t * dev_p;
980 devdirent_t * devent;
981 devnode_t * dir_p; /* devnode for parent directory */
982 struct vnode * dvp = ap->a_dvp;
983 int error = 0;
984 devnode_type_t typeinfo;
985 struct vnode_attr * vap = ap->a_vap;
986 struct vnode ** vpp = ap->a_vpp;
987
988 *vpp = NULL;
989 if (!(vap->va_type == VBLK) && !(vap->va_type == VCHR)) {
990 return (EINVAL); /* only support mknod of special files */
991 }
992 dir_p = VTODN(dvp);
993 typeinfo.dev = vap->va_rdev;
994
995 DEVFS_LOCK();
996 error = dev_add_entry(cnp->cn_nameptr, dir_p,
997 (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
998 &typeinfo, NULL, NULL, &devent);
999 if (error) {
1000 goto failure;
1001 }
1002 dev_p = devent->de_dnp;
1003 error = devfs_dntovn(dev_p, vpp, p);
1004 if (error)
1005 goto failure;
1006 dev_p->dn_uid = vap->va_uid;
1007 dev_p->dn_gid = vap->va_gid;
1008 dev_p->dn_mode = vap->va_mode;
1009 VATTR_SET_SUPPORTED(vap, va_uid);
1010 VATTR_SET_SUPPORTED(vap, va_gid);
1011 VATTR_SET_SUPPORTED(vap, va_mode);
1012 failure:
1013 DEVFS_UNLOCK();
1014
1015 return (error);
1016 }
1017
1018 /*
1019 * Vnode op for readdir
1020 */
1021 static int
1022 devfs_readdir(struct vnop_readdir_args *ap)
1023 /*struct vnop_readdir_args {
1024 struct vnode *a_vp;
1025 struct uio *a_uio;
1026 int a_flags;
1027 int *a_eofflag;
1028 int *a_numdirent;
1029 vfs_context_t a_context;
1030 } */
1031 {
1032 struct vnode *vp = ap->a_vp;
1033 struct uio *uio = ap->a_uio;
1034 struct dirent dirent;
1035 devnode_t * dir_node;
1036 devdirent_t * name_node;
1037 char *name;
1038 int error = 0;
1039 int reclen;
1040 int nodenumber;
1041 int startpos,pos;
1042
1043 if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
1044 return (EINVAL);
1045
1046 /* set up refs to dir */
1047 dir_node = VTODN(vp);
1048 if (dir_node->dn_type != DEV_DIR)
1049 return(ENOTDIR);
1050 pos = 0;
1051 startpos = uio->uio_offset;
1052
1053 DEVFS_LOCK();
1054
1055 name_node = dir_node->dn_typeinfo.Dir.dirlist;
1056 nodenumber = 0;
1057
1058 dir_node->dn_access = 1;
1059
1060 while ((name_node || (nodenumber < 2)) && (uio_resid(uio) > 0))
1061 {
1062 switch(nodenumber)
1063 {
1064 case 0:
1065 dirent.d_fileno = (int32_t)(void *)dir_node;
1066 name = ".";
1067 dirent.d_namlen = 1;
1068 dirent.d_type = DT_DIR;
1069 break;
1070 case 1:
1071 if(dir_node->dn_typeinfo.Dir.parent)
1072 dirent.d_fileno
1073 = (int32_t)dir_node->dn_typeinfo.Dir.parent;
1074 else
1075 dirent.d_fileno = (u_int32_t)dir_node;
1076 name = "..";
1077 dirent.d_namlen = 2;
1078 dirent.d_type = DT_DIR;
1079 break;
1080 default:
1081 dirent.d_fileno = (int32_t)(void *)name_node->de_dnp;
1082 dirent.d_namlen = strlen(name_node->de_name);
1083 name = name_node->de_name;
1084 switch(name_node->de_dnp->dn_type) {
1085 case DEV_BDEV:
1086 dirent.d_type = DT_BLK;
1087 break;
1088 case DEV_CDEV:
1089 dirent.d_type = DT_CHR;
1090 break;
1091 case DEV_DIR:
1092 dirent.d_type = DT_DIR;
1093 break;
1094 case DEV_SLNK:
1095 dirent.d_type = DT_LNK;
1096 break;
1097 default:
1098 dirent.d_type = DT_UNKNOWN;
1099 }
1100 }
1101 #define GENERIC_DIRSIZ(dp) \
1102 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1103
1104 reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
1105
1106 if(pos >= startpos) /* made it to the offset yet? */
1107 {
1108 if (uio_resid(uio) < reclen) /* will it fit? */
1109 break;
1110 strcpy( dirent.d_name,name);
1111 if ((error = uiomove ((caddr_t)&dirent,
1112 dirent.d_reclen, uio)) != 0)
1113 break;
1114 }
1115 pos += reclen;
1116 if((nodenumber >1) && name_node)
1117 name_node = name_node->de_next;
1118 nodenumber++;
1119 }
1120 DEVFS_UNLOCK();
1121 uio->uio_offset = pos;
1122
1123 return (error);
1124 }
1125
1126
1127 /*
1128 */
1129 static int
1130 devfs_readlink(struct vnop_readlink_args *ap)
1131 /*struct vnop_readlink_args {
1132 struct vnode *a_vp;
1133 struct uio *a_uio;
1134 vfs_context_t a_context;
1135 } */
1136 {
1137 struct vnode *vp = ap->a_vp;
1138 struct uio *uio = ap->a_uio;
1139 devnode_t * lnk_node;
1140 int error = 0;
1141
1142 /* set up refs to dir */
1143 lnk_node = VTODN(vp);
1144
1145 if (lnk_node->dn_type != DEV_SLNK) {
1146 error = EINVAL;
1147 goto out;
1148 }
1149 error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
1150 lnk_node->dn_typeinfo.Slnk.namelen, uio);
1151 out:
1152 return error;
1153 }
1154
1155 static int
1156 devfs_reclaim(struct vnop_reclaim_args *ap)
1157 /*struct vnop_reclaim_args {
1158 struct vnode *a_vp;
1159 } */
1160 {
1161 struct vnode * vp = ap->a_vp;
1162 devnode_t * dnp = VTODN(vp);
1163
1164 DEVFS_LOCK();
1165
1166 if (dnp) {
1167 /*
1168 * do the same as devfs_inactive in case it is not called
1169 * before us (can that ever happen?)
1170 */
1171 dnp->dn_vn = NULL;
1172 vp->v_data = NULL;
1173
1174 if (dnp->dn_delete) {
1175 devnode_free(dnp);
1176 }
1177 }
1178 DEVFS_UNLOCK();
1179
1180 return(0);
1181 }
1182
1183
1184 /*
1185 * Get configurable pathname variables.
1186 */
1187 static int
1188 devs_vnop_pathconf(
1189 struct vnop_pathconf_args /* {
1190 struct vnode *a_vp;
1191 int a_name;
1192 int *a_retval;
1193 vfs_context_t a_context;
1194 } */ *ap)
1195 {
1196 switch (ap->a_name) {
1197 case _PC_LINK_MAX:
1198 /* arbitrary limit matching HFS; devfs has no hard limit */
1199 *ap->a_retval = 32767;
1200 break;
1201 case _PC_NAME_MAX:
1202 *ap->a_retval = DEVMAXNAMESIZE - 1; /* includes NUL */
1203 break;
1204 case _PC_PATH_MAX:
1205 *ap->a_retval = DEVMAXPATHSIZE - 1; /* XXX nonconformant */
1206 break;
1207 case _PC_CHOWN_RESTRICTED:
1208 *ap->a_retval = 1;
1209 break;
1210 case _PC_NO_TRUNC:
1211 *ap->a_retval = 0;
1212 break;
1213 case _PC_CASE_SENSITIVE:
1214 *ap->a_retval = 1;
1215 break;
1216 case _PC_CASE_PRESERVING:
1217 *ap->a_retval = 1;
1218 break;
1219 default:
1220 return (EINVAL);
1221 }
1222
1223 return (0);
1224 }
1225
1226
1227
1228 /**************************************************************************\
1229 * pseudo ops *
1230 \**************************************************************************/
1231
1232 /*
1233 *
1234 * struct vnop_inactive_args {
1235 * struct vnode *a_vp;
1236 * vfs_context_t a_context;
1237 * }
1238 */
1239
1240 static int
1241 devfs_inactive(__unused struct vnop_inactive_args *ap)
1242 {
1243 return (0);
1244 }
1245
1246 /*
1247 * called with DEVFS_LOCK held
1248 */
1249 static int
1250 devfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify)
1251 {
1252 devnode_t * ip;
1253 struct timeval now;
1254
1255 ip = VTODN(vp);
1256 if (vp->v_mount->mnt_flag & MNT_RDONLY) {
1257 ip->dn_access = 0;
1258 ip->dn_change = 0;
1259 ip->dn_update = 0;
1260
1261 return (0);
1262 }
1263 microtime(&now);
1264 dn_times(ip, access, modify, &now);
1265
1266 return (0);
1267 }
1268
1269 #define VOPFUNC int (*)(void *)
1270
1271 /* The following ops are used by directories and symlinks */
1272 int (**devfs_vnodeop_p)(void *);
1273 static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
1274 { &vnop_default_desc, (VOPFUNC)vn_default_error },
1275 { &vnop_lookup_desc, (VOPFUNC)devfs_lookup }, /* lookup */
1276 { &vnop_create_desc, (VOPFUNC)err_create }, /* create */
1277 { &vnop_whiteout_desc, (VOPFUNC)err_whiteout }, /* whiteout */
1278 { &vnop_mknod_desc, (VOPFUNC)devfs_mknod }, /* mknod */
1279 { &vnop_open_desc, (VOPFUNC)nop_open }, /* open */
1280 { &vnop_close_desc, (VOPFUNC)devfs_close }, /* close */
1281 { &vnop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */
1282 { &vnop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */
1283 { &vnop_read_desc, (VOPFUNC)devfs_read }, /* read */
1284 { &vnop_write_desc, (VOPFUNC)devfs_write }, /* write */
1285 { &vnop_ioctl_desc, (VOPFUNC)err_ioctl }, /* ioctl */
1286 { &vnop_select_desc, (VOPFUNC)err_select }, /* select */
1287 { &vnop_revoke_desc, (VOPFUNC)err_revoke }, /* revoke */
1288 { &vnop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
1289 { &vnop_fsync_desc, (VOPFUNC)nop_fsync }, /* fsync */
1290 { &vnop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */
1291 { &vnop_link_desc, (VOPFUNC)devfs_link }, /* link */
1292 { &vnop_rename_desc, (VOPFUNC)devfs_rename }, /* rename */
1293 { &vnop_mkdir_desc, (VOPFUNC)err_mkdir }, /* mkdir */
1294 { &vnop_rmdir_desc, (VOPFUNC)err_rmdir }, /* rmdir */
1295 { &vnop_symlink_desc, (VOPFUNC)devfs_symlink }, /* symlink */
1296 { &vnop_readdir_desc, (VOPFUNC)devfs_readdir }, /* readdir */
1297 { &vnop_readlink_desc, (VOPFUNC)devfs_readlink }, /* readlink */
1298 { &vnop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */
1299 { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */
1300 { &vnop_strategy_desc, (VOPFUNC)err_strategy }, /* strategy */
1301 { &vnop_pathconf_desc, (VOPFUNC)devs_vnop_pathconf }, /* pathconf */
1302 { &vnop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
1303 { &vnop_bwrite_desc, (VOPFUNC)err_bwrite },
1304 { &vnop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */
1305 { &vnop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */
1306 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
1307 { &vnop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff */
1308 { &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */
1309 { &vnop_blockmap_desc, (VOPFUNC)err_blockmap }, /* blockmap */
1310 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1311 };
1312 struct vnodeopv_desc devfs_vnodeop_opv_desc =
1313 { &devfs_vnodeop_p, devfs_vnodeop_entries };
1314
1315 /* The following ops are used by the device nodes */
1316 int (**devfs_spec_vnodeop_p)(void *);
1317 static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
1318 { &vnop_default_desc, (VOPFUNC)vn_default_error },
1319 { &vnop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
1320 { &vnop_create_desc, (VOPFUNC)spec_create }, /* create */
1321 { &vnop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
1322 { &vnop_open_desc, (VOPFUNC)spec_open }, /* open */
1323 { &vnop_close_desc, (VOPFUNC)devfsspec_close }, /* close */
1324 { &vnop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */
1325 { &vnop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */
1326 { &vnop_read_desc, (VOPFUNC)devfsspec_read }, /* read */
1327 { &vnop_write_desc, (VOPFUNC)devfsspec_write }, /* write */
1328 { &vnop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
1329 { &vnop_select_desc, (VOPFUNC)spec_select }, /* select */
1330 { &vnop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
1331 { &vnop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
1332 { &vnop_fsync_desc, (VOPFUNC)spec_fsync }, /* fsync */
1333 { &vnop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */
1334 { &vnop_link_desc, (VOPFUNC)devfs_link }, /* link */
1335 { &vnop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
1336 { &vnop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
1337 { &vnop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
1338 { &vnop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
1339 { &vnop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
1340 { &vnop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
1341 { &vnop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */
1342 { &vnop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */
1343 { &vnop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
1344 { &vnop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
1345 { &vnop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */
1346 { &vnop_bwrite_desc, (VOPFUNC)vn_bwrite },
1347 { &vnop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
1348 { &vnop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */
1349 { &vnop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */
1350 { &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
1351 { &vnop_blktooff_desc, (VOPFUNC)spec_blktooff }, /* blktooff */
1352 { &vnop_blktooff_desc, (VOPFUNC)spec_offtoblk }, /* blkofftoblk */
1353 { &vnop_blockmap_desc, (VOPFUNC)spec_blockmap }, /* blockmap */
1354 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1355 };
1356 struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
1357 { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };
1358