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