]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_vnops.c
5db06b9ce2c8b1b249f69d37a532e9eac41fa27f
[apple/xnu.git] / bsd / miscfs / devfs / devfs_vnops.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Copyright 1997,1998 Julian Elischer. All rights reserved.
27 * julian@freebsd.org
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions are
31 * met:
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.
37 *
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
48 * SUCH DAMAGE.
49 *
50 * devfs_vnops.c
51 */
52
53 /*
54 * HISTORY
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
71 */
72
73 #include <sys/param.h>
74 #include <sys/systm.h>
75 #include <sys/buf.h>
76 #include <sys/namei.h>
77 #include <sys/kernel.h>
78 #include <sys/fcntl.h>
79 #include <sys/conf.h>
80 #include <sys/disklabel.h>
81 #include <sys/lock.h>
82 #include <sys/stat.h>
83 #include <sys/mount.h>
84 #include <sys/proc.h>
85 #include <sys/time.h>
86 #include <sys/vnode.h>
87 #include <miscfs/specfs/specdev.h>
88 #include <sys/dirent.h>
89 #include <sys/vmmeter.h>
90 #include <sys/vm.h>
91
92 #include "devfsdefs.h"
93
94 /*
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).
99 *
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.
110 *
111 * Overall outline of devfs_lookup:
112 *
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
118 * notfound:
119 * if creating, return locked directory,
120 * else return error
121 * found:
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.
128 *
129 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
130 */
131 static int
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
137 };*/
138 {
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);
149 int error = 0;
150 struct proc *p = cnp->cn_proc;
151 char heldchar; /* the char at the end of the name componet */
152
153 *result_vnode = NULL; /* safe not sorry */ /*XXX*/
154
155 if (dir_vnode->v_usecount == 0)
156 printf("devfs_lookup: dir had no refs ");
157 dir_node = VTODN(dir_vnode);
158
159 /*
160 * Check accessiblity of directory.
161 */
162 if (dir_node->dn_type != DEV_DIR) {
163 return (ENOTDIR);
164 }
165
166 if ((error = VOP_ACCESS(dir_vnode, VEXEC, cnp->cn_cred, p)) != 0) {
167 return (error);
168 }
169
170 /* temporarily terminate string component */
171 heldchar = cnp->cn_nameptr[cnp->cn_namelen];
172 cnp->cn_nameptr[cnp->cn_namelen] = '\0';
173 DEVFS_LOCK(p);
174 nodename = dev_findname(dir_node,cnp->cn_nameptr);
175 if (nodename) {
176 /* entry exists */
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()).
183 */
184 error = devfs_dntovn(node, result_vnode, p);
185 }
186 DEVFS_UNLOCK(p);
187 /* restore saved character */
188 cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
189
190 if (error)
191 return (error);
192
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.
197 */
198 if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
199 return ENOENT;
200 }
201 /*
202 * Access for write is interpreted as allowing
203 * creation of files in the directory.
204 */
205 if ((error = VOP_ACCESS(dir_vnode, VWRITE,
206 cnp->cn_cred, p)) != 0)
207 {
208 return (error);
209 }
210 /*
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.
219 *
220 * NB - if the directory is unlocked, then this
221 * information cannot be used.
222 */
223 cnp->cn_flags |= SAVENAME;
224 if (!lockparent)
225 VOP_UNLOCK(dir_vnode, 0, p);
226 return (EJUSTRETURN);
227 }
228
229 /*
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 ".".
235 */
236 if (op == DELETE && (flags & ISLASTCN)) {
237 /*
238 * Write access to directory required to delete files.
239 */
240 if ((error = VOP_ACCESS(dir_vnode, VWRITE,
241 cnp->cn_cred, p)) != 0)
242 return (error);
243 /*
244 * we are trying to delete '.'. What does this mean? XXX
245 */
246 if (dir_node == node) {
247 VREF(dir_vnode);
248 *result_vnode = dir_vnode;
249 return (0);
250 }
251 #ifdef NOTYET
252 /*
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.
257 */
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);
263 return (EPERM);
264 }
265 #endif
266 if (!lockparent)
267 VOP_UNLOCK(dir_vnode, 0, p);
268 return (0);
269 }
270
271 /*
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.
276 */
277 if (op == RENAME && wantparent && (flags & ISLASTCN)) {
278 /*
279 * Are we allowed to change the holding directory?
280 */
281 if ((error = VOP_ACCESS(dir_vnode, VWRITE,
282 cnp->cn_cred, p)) != 0)
283 return (error);
284 /*
285 * Careful about locking second node.
286 * This can only occur if the target is ".".
287 */
288 if (dir_node == node)
289 return (EISDIR);
290 /* hmm save the 'from' name (we need to delete it) */
291 cnp->cn_flags |= SAVENAME;
292 if (!lockparent)
293 VOP_UNLOCK(dir_vnode, 0, p);
294 return (0);
295 }
296
297 /*
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.
316 */
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) {
322 #if 0
323 /*
324 * this next statement is wrong: we already did a vget in
325 * devfs_dntovn(); DWS 4/16/1999
326 */
327 VREF(dir_vnode); /* we want ourself, ie "." */
328 #endif
329 *result_vnode = dir_vnode;
330 } else {
331 if (!lockparent || (flags & ISLASTCN))
332 VOP_UNLOCK(dir_vnode, 0, p);
333 }
334
335 return (0);
336 }
337
338 static int
339 devfs_access(struct vop_access_args *ap)
340 /*struct vop_access_args {
341 struct vnode *a_vp;
342 int a_mode;
343 struct ucred *a_cred;
344 struct proc *a_p;
345 } */
346 {
347 /*
348 * mode is filled with a combination of VREAD, VWRITE,
349 * and/or VEXEC bits turned on. In an octal number these
350 * are the Y in 0Y00.
351 */
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;
356 gid_t *gp;
357 int i;
358 struct proc *p = ap->a_p;
359
360 file_node = VTODN(vp);
361 /*
362 * if we are not running as a process, we are in the
363 * kernel and we DO have permission
364 */
365 if (p == NULL)
366 return 0;
367
368 /*
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.
372 */
373 if (cred->cr_uid != file_node->dn_uid)
374 {
375 /* failing that.. try groups */
376 mode >>= 3;
377 gp = cred->cr_groups;
378 for (i = 0; i < cred->cr_ngroups; i++, gp++)
379 {
380 if (file_node->dn_gid == *gp)
381 {
382 goto found;
383 }
384 }
385 /* failing that.. try general access */
386 mode >>= 3;
387 found:
388 ;
389 }
390 if ((file_node->dn_mode & mode) == mode)
391 return (0);
392 /*
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)
396 */
397 if( suser(cred, &ap->a_p->p_acflag) == 0) /* XXX what if no proc? */
398 return 0;
399 return (EACCES);
400 }
401
402 static int
403 devfs_getattr(struct vop_getattr_args *ap)
404 /*struct vop_getattr_args {
405 struct vnode *a_vp;
406 struct vattr *a_vap;
407 struct ucred *a_cred;
408 struct proc *a_p;
409 } */
410 {
411 struct vnode *vp = ap->a_vp;
412 struct vattr *vap = ap->a_vap;
413 devnode_t * file_node;
414 struct timeval tv;
415
416 file_node = VTODN(vp);
417 tv = time;
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)
422 {
423 case DEV_DIR:
424 vap->va_rdev = (dev_t)file_node->dn_dvm;
425 vap->va_mode |= (S_IFDIR);
426 break;
427 case DEV_CDEV:
428 vap->va_rdev = file_node->dn_typeinfo.dev;
429 vap->va_mode |= (S_IFCHR);
430 break;
431 case DEV_BDEV:
432 vap->va_rdev = file_node->dn_typeinfo.dev;
433 vap->va_mode |= (S_IFBLK);
434 break;
435 case DEV_SLNK:
436 vap->va_mode |= (S_IFLNK);
437 break;
438 }
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;
451 else
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;
463 vap->va_gen = 0;
464 vap->va_flags = 0;
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 */
468 return 0;
469 }
470
471 static int
472 devfs_setattr(struct vop_setattr_args *ap)
473 /*struct vop_setattr_args {
474 struct vnode *a_vp;
475 struct vattr *a_vap;
476 struct ucred *a_cred;
477 struct proc *a_p;
478 } */
479 {
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;
484 int error = 0;
485 gid_t *gp;
486 int i;
487 devnode_t * file_node;
488 struct timeval atimeval, mtimeval;
489
490 if (vap->va_flags != VNOVAL) /* XXX needs to be implemented */
491 return (EOPNOTSUPP);
492
493 file_node = VTODN(vp);
494
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 ))
503 {
504 return EINVAL;
505 }
506
507 /*
508 * Go through the fields and update iff not VNOVAL.
509 */
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))))
515 return (error);
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))
525 return (error);
526 }
527
528 /*
529 * Change the permissions.. must be root or owner to do this.
530 */
531 if (vap->va_mode != (u_short)VNOVAL) {
532 if ((cred->cr_uid != file_node->dn_uid)
533 && (error = suser(cred, &p->p_acflag)))
534 return (error);
535 file_node->dn_mode &= ~07777;
536 file_node->dn_mode |= vap->va_mode & 07777;
537 }
538
539 /*
540 * Change the owner.. must be root to do this.
541 */
542 if (vap->va_uid != (uid_t)VNOVAL) {
543 if (error = suser(cred, &p->p_acflag))
544 return (error);
545 file_node->dn_uid = vap->va_uid;
546 }
547
548 /*
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.
553 */
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)
559 goto cando;
560 }
561 }
562 /*
563 * we can't do it with normal privs,
564 * do we have an ace up our sleeve?
565 */
566 if (error = suser(cred, &p->p_acflag))
567 return (error);
568 cando:
569 file_node->dn_gid = vap->va_gid;
570 }
571 #if 0
572 /*
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
576 */
577 if (vap->va_flags != VNOVAL) {
578 if (error = suser(cred, &p->p_acflag))
579 return error;
580 if (cred->cr_uid == 0)
581 ;
582 else {
583 }
584 }
585 #endif
586 return error;
587 }
588
589 static int
590 devfs_read(struct vop_read_args *ap)
591 /*struct vop_read_args {
592 struct vnode *a_vp;
593 struct uio *a_uio;
594 int a_ioflag;
595 struct ucred *a_cred;
596 } */
597 {
598 devnode_t * dn_p = VTODN(ap->a_vp);
599
600 switch (ap->a_vp->v_type) {
601 case VDIR: {
602 dn_p->dn_flags |= DN_ACCESS;
603 return VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred,
604 NULL, NULL, NULL);
605 }
606 default: {
607 printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
608 return(EINVAL);
609 break;
610 }
611 }
612 return (0); /* not reached */
613 }
614
615 static int
616 devfs_close(ap)
617 struct vop_close_args /* {
618 struct vnode *a_vp;
619 int a_fflag;
620 struct ucred *a_cred;
621 struct proc *a_p;
622 } */ *ap;
623 {
624 struct vnode * vp = ap->a_vp;
625 register devnode_t * dnp = VTODN(vp);
626
627 simple_lock(&vp->v_interlock);
628 if (vp->v_usecount > 1)
629 dn_times(dnp, time, time);
630 simple_unlock(&vp->v_interlock);
631 return (0);
632 }
633
634 static int
635 devfsspec_close(ap)
636 struct vop_close_args /* {
637 struct vnode *a_vp;
638 int a_fflag;
639 struct ucred *a_cred;
640 struct proc *a_p;
641 } */ *ap;
642 {
643 struct vnode * vp = ap->a_vp;
644 register devnode_t * dnp = VTODN(vp);
645
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));
651 }
652
653 static int
654 devfsspec_read(struct vop_read_args *ap)
655 /*struct vop_read_args {
656 struct vnode *a_vp;
657 struct uio *a_uio;
658 int a_ioflag;
659 struct ucred *a_cred;
660 } */
661 {
662 VTODN(ap->a_vp)->dn_flags |= DN_ACCESS;
663 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
664 }
665
666 static int
667 devfsspec_write(struct vop_write_args *ap)
668 /*struct vop_write_args {
669 struct vnode *a_vp;
670 struct uio *a_uio;
671 int a_ioflag;
672 struct ucred *a_cred;
673 } */
674 {
675 VTODN(ap->a_vp)->dn_flags |= DN_CHANGE | DN_UPDATE;
676 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
677 }
678
679 /*
680 * Write data to a file or directory.
681 */
682 static int
683 devfs_write(struct vop_write_args *ap)
684 /*struct vop_write_args {
685 struct vnode *a_vp;
686 struct uio *a_uio;
687 int a_ioflag;
688 struct ucred *a_cred;
689 } */
690 {
691 switch (ap->a_vp->v_type) {
692 case VDIR:
693 return(EISDIR);
694 default:
695 printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
696 return (EINVAL);
697 }
698 return 0; /* not reached */
699 }
700
701 static int
702 devfs_remove(struct vop_remove_args *ap)
703 /*struct vop_remove_args {
704 struct vnode *a_dvp;
705 struct vnode *a_vp;
706 struct componentname *a_cnp;
707 } */
708 {
709 struct vnode *vp = ap->a_vp;
710 struct vnode *dvp = ap->a_dvp;
711 struct componentname *cnp = ap->a_cnp;
712 devnode_t * tp;
713 devnode_t * tdp;
714 devdirent_t * tnp;
715 int doingdirectory = 0;
716 int error = 0;
717 uid_t ouruid = cnp->cn_cred->cr_uid;
718 struct proc *p = cnp->cn_proc;
719
720 /*
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
724 * devfs structures.
725 */
726 tp = VTODN(vp);
727 tdp = VTODN(dvp);
728 /*
729 * Assuming we are atomic, dev_lookup left this for us
730 */
731 tnp = tp->dn_last_lookup;
732
733 /*
734 * Check we are doing legal things WRT the new flags
735 */
736 if ((tp->dn_flags & (IMMUTABLE | APPEND))
737 || (tdp->dn_flags & APPEND) /*XXX eh?*/ ) {
738 error = EPERM;
739 goto abort;
740 }
741
742 /*
743 * Make sure that we don't try do something stupid
744 */
745 if ((tp->dn_type) == DEV_DIR) {
746 /*
747 * Avoid ".", "..", and aliases of "." for obvious reasons.
748 */
749 if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
750 || (cnp->cn_flags&ISDOTDOT) ) {
751 error = EINVAL;
752 goto abort;
753 }
754 doingdirectory++;
755 }
756
757 /***********************************
758 * Start actually doing things.... *
759 ***********************************/
760 tdp->dn_flags |= DN_CHANGE | DN_UPDATE;
761
762 /*
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?
767 */
768 if ((tdp->dn_mode & S_ISTXT)
769 && ouruid != 0
770 && ouruid != tdp->dn_uid
771 && ouruid != tp->dn_uid ) {
772 error = EPERM;
773 goto abort;
774 }
775 /*
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).
779 */
780 if (( doingdirectory) && (tp->dn_links > 2)) {
781 error = ENOTEMPTY;
782 goto abort;
783 }
784 DEVFS_LOCK(p);
785 dev_free_name(tnp);
786 DEVFS_UNLOCK(p);
787 abort:
788 if (dvp == vp)
789 vrele(vp);
790 else
791 vput(vp);
792 vput(dvp);
793 return (error);
794 }
795
796 /*
797 */
798 static int
799 devfs_link(struct vop_link_args *ap)
800 /*struct vop_link_args {
801 struct vnode *a_tdvp;
802 struct vnode *a_vp;
803 struct componentname *a_cnp;
804 } */
805 {
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;
810 devnode_t * fp;
811 devnode_t * tdp;
812 devdirent_t * tnp;
813 int error = 0;
814 struct timeval tv;
815
816 /*
817 * First catch an arbitrary restriction for this FS
818 */
819 if (cnp->cn_namelen > DEVMAXNAMESIZE) {
820 error = ENAMETOOLONG;
821 goto out1;
822 }
823
824 /*
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
828 * devfs structures.
829 */
830 tdp = VTODN(tdvp);
831 fp = VTODN(vp);
832
833 if (tdvp->v_mount != vp->v_mount) {
834 error = EXDEV;
835 VOP_ABORTOP(tdvp, cnp);
836 goto out2;
837 }
838 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
839 VOP_ABORTOP(tdvp, cnp);
840 goto out2;
841 }
842
843 /*
844 * Check we are doing legal things WRT the new flags
845 */
846 if (fp->dn_flags & (IMMUTABLE | APPEND)) {
847 VOP_ABORTOP(tdvp, cnp);
848 error = EPERM;
849 goto out1;
850 }
851
852 /***********************************
853 * Start actually doing things.... *
854 ***********************************/
855 fp->dn_flags |= DN_CHANGE;
856 tv = time;
857 error = VOP_UPDATE(vp, &tv, &tv, 1);
858 if (!error) {
859 DEVFS_LOCK(p);
860 error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
861 DEVFS_UNLOCK(p);
862 }
863 out1:
864 if (tdvp != vp)
865 VOP_UNLOCK(vp, 0, p);
866 out2:
867 vput(tdvp);
868 return (error);
869 }
870
871 /*
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.
875 */
876 int
877 devfs_checkpath(source, target)
878 devnode_t *source, *target;
879 {
880 int error = 0;
881 devnode_t * ntmp;
882 devnode_t * tmp;
883 struct vnode *vp;
884
885 vp = target->dn_vn;
886 tmp = target;
887
888 do {
889 if (tmp == source) {
890 error = EINVAL;
891 break;
892 }
893 ntmp = tmp;
894 } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
895
896 if (vp != NULL)
897 vput(vp);
898 return (error);
899 }
900
901 /*
902 * Rename system call. Seems overly complicated to me...
903 * rename("foo", "bar");
904 * is essentially
905 * unlink("bar");
906 * link("foo", "bar");
907 * unlink("foo");
908 * but ``atomically''.
909 *
910 * When the target exists, both the directory
911 * and target vnodes are locked.
912 * the source and source-parent vnodes are referenced
913 *
914 *
915 * Basic algorithm is:
916 *
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,
922 * delete it first.
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
926 * directory.
927 */
928 static int
929 devfs_rename(struct vop_rename_args *ap)
930 /*struct vop_rename_args {
931 struct vnode *a_fdvp;
932 struct vnode *a_fvp;
933 struct componentname *a_fcnp;
934 struct vnode *a_tdvp;
935 struct vnode *a_tvp;
936 struct componentname *a_tcnp;
937 } */
938 {
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;
949 int error = 0;
950 struct timeval tv;
951
952 /*
953 * First catch an arbitrary restriction for this FS
954 */
955 if(tcnp->cn_namelen > DEVMAXNAMESIZE) {
956 error = ENAMETOOLONG;
957 goto abortit;
958 }
959
960 /*
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
964 * devfs structures.
965 */
966 tdp = VTODN(tdvp);
967 fdp = VTODN(fdvp);
968 fp = VTODN(fvp);
969 fnp = fp->dn_last_lookup;
970 tp = NULL;
971 tnp = NULL;
972 if (tvp) {
973 tp = VTODN(tvp);
974 tnp = tp->dn_last_lookup;
975 }
976
977 /*
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
982 */
983 if ((fvp->v_mount != tdvp->v_mount) ||
984 (tvp && (fvp->v_mount != tvp->v_mount))) {
985 error = EXDEV;
986 abortit:
987 VOP_ABORTOP(tdvp, tcnp);
988 if (tdvp == tvp) /* eh? */
989 vrele(tdvp);
990 else
991 vput(tdvp);
992 if (tvp)
993 vput(tvp);
994 VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
995 vrele(fdvp);
996 vrele(fvp);
997 return (error);
998 }
999
1000 /*
1001 * Check we are doing legal things WRT the new flags
1002 */
1003 if ((tp && (tp->dn_flags & (IMMUTABLE | APPEND)))
1004 || (fp->dn_flags & (IMMUTABLE | APPEND))
1005 || (fdp->dn_flags & APPEND)) {
1006 error = EPERM;
1007 goto abortit;
1008 }
1009
1010 /*
1011 * Make sure that we don't try do something stupid
1012 */
1013 if ((fp->dn_type) == DEV_DIR) {
1014 /*
1015 * Avoid ".", "..", and aliases of "." for obvious reasons.
1016 */
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)
1021 || (tdp == fp )) {
1022 error = EINVAL;
1023 goto abortit;
1024 }
1025 doingdirectory++;
1026 }
1027
1028 /*
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 "..".
1035 */
1036 if (doingdirectory && (tdp != fdp)) {
1037 devnode_t * tmp, *ntmp;
1038 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
1039 tmp = tdp;
1040 do {
1041 if(tmp == fp) {
1042 /* XXX unlock stuff here probably */
1043 error = EINVAL;
1044 goto out;
1045 }
1046 ntmp = tmp;
1047 } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
1048 }
1049
1050 /***********************************
1051 * Start actually doing things.... *
1052 ***********************************/
1053 fp->dn_flags |= DN_CHANGE;
1054 tv = time;
1055 if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) {
1056 VOP_UNLOCK(fvp, 0, p);
1057 goto bad;
1058 }
1059 /*
1060 * Check if just deleting a link name.
1061 */
1062 if (fvp == tvp) {
1063 if (fvp->v_type == VDIR) {
1064 error = EINVAL;
1065 goto abortit;
1066 }
1067
1068 /* Release destination completely. */
1069 VOP_ABORTOP(tdvp, tcnp);
1070 vput(tdvp);
1071 vput(tvp);
1072
1073 /* Delete source. */
1074 VOP_ABORTOP(fdvp, fcnp); /*XXX*/
1075 vrele(fdvp);
1076 vrele(fvp);
1077 dev_free_name(fnp);
1078 return 0;
1079 }
1080
1081 vrele(fdvp);
1082
1083 /*
1084 * 1) Bump link count while we're moving stuff
1085 * around. If we crash somewhere before
1086 * completing our work, too bad :)
1087 */
1088 fp->dn_links++;
1089 /*
1090 * If the target exists zap it (unless it's a non-empty directory)
1091 * We could do that as well but won't
1092 */
1093 if (tp) {
1094 int ouruid = tcnp->cn_cred->cr_uid;
1095 /*
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?
1101 */
1102 if ((tdp->dn_mode & S_ISTXT)
1103 && ouruid != 0
1104 && ouruid != tdp->dn_uid
1105 && ouruid != tp->dn_uid ) {
1106 error = EPERM;
1107 goto bad;
1108 }
1109 /*
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).
1113 */
1114 if (( doingdirectory) && (tp->dn_links > 2)) {
1115 error = ENOTEMPTY;
1116 goto bad;
1117 }
1118 dev_free_name(tnp);
1119 tp = NULL;
1120 }
1121 dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp);
1122 fnp->de_dnp = NULL;
1123 fp->dn_links--; /* one less link to it.. */
1124 dev_free_name(fnp);
1125 fp->dn_links--; /* we added one earlier*/
1126 if (tdp)
1127 vput(tdvp);
1128 if (tp)
1129 vput(fvp);
1130 vrele(fvp);
1131 return (error);
1132
1133 bad:
1134 if (tp)
1135 vput(tvp);
1136 vput(tdvp);
1137 out:
1138 if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
1139 fp->dn_links--; /* we added one earlier*/
1140 vput(fvp);
1141 } else
1142 vrele(fvp);
1143 return (error);
1144 }
1145
1146 static int
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;
1153 char *a_target;
1154 } */
1155 {
1156 struct componentname * cnp = ap->a_cnp;
1157 struct vnode *vp = NULL;
1158 int error = 0;
1159 devnode_t * dir_p;
1160 devnode_type_t typeinfo;
1161 devdirent_t * nm_p;
1162 devnode_t * dev_p;
1163 struct vattr * vap = ap->a_vap;
1164 struct vnode * * vpp = ap->a_vpp;
1165 struct proc *p = cnp->cn_proc;
1166 struct timeval tv;
1167
1168 dir_p = VTODN(ap->a_dvp);
1169 typeinfo.Slnk.name = ap->a_target;
1170 typeinfo.Slnk.namelen = strlen(ap->a_target);
1171 DEVFS_LOCK(p);
1172 error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK,
1173 &typeinfo, NULL, NULL, &nm_p);
1174 DEVFS_UNLOCK(p);
1175 if (error) {
1176 goto failure;
1177 }
1178
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);
1185 if (error)
1186 goto failure;
1187 vp = *vpp;
1188 vput(vp);
1189 failure:
1190 if ((cnp->cn_flags & SAVESTART) == 0)
1191 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
1192 vput(ap->a_dvp);
1193 return error;
1194 }
1195
1196 /*
1197 * Mknod vnode call
1198 */
1199 /* ARGSUSED */
1200 int
1201 devfs_mknod(ap)
1202 struct vop_mknod_args /* {
1203 struct vnode *a_dvp;
1204 struct vnode **a_vpp;
1205 struct componentname *a_cnp;
1206 struct vattr *a_vap;
1207 } */ *ap;
1208 {
1209 struct componentname * cnp = ap->a_cnp;
1210 devnode_t * dev_p;
1211 devdirent_t * devent;
1212 devnode_t * dir_p; /* devnode for parent directory */
1213 struct vnode * dvp = ap->a_dvp;
1214 int error = 0;
1215 devnode_type_t typeinfo;
1216 struct vattr * vap = ap->a_vap;
1217 struct vnode ** vpp = ap->a_vpp;
1218 struct proc * p = cnp->cn_proc;
1219
1220 *vpp = NULL;
1221 if (!vap->va_type == VBLK && !vap->va_type == VCHR) {
1222 error = EINVAL; /* only support mknod of special files */
1223 goto failure;
1224 }
1225 dir_p = VTODN(dvp);
1226 typeinfo.dev = vap->va_rdev;
1227 DEVFS_LOCK(p);
1228 error = dev_add_entry(cnp->cn_nameptr, dir_p,
1229 (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
1230 &typeinfo, NULL, NULL, &devent);
1231 DEVFS_UNLOCK(p);
1232 if (error) {
1233 goto failure;
1234 }
1235 dev_p = devent->de_dnp;
1236 error = devfs_dntovn(dev_p, vpp, p);
1237 if (error)
1238 goto failure;
1239 dev_p->dn_uid = cnp->cn_cred->cr_uid;
1240 dev_p->dn_gid = dir_p->dn_gid;
1241 dev_p->dn_mode = vap->va_mode;
1242 failure:
1243 if (*vpp) {
1244 vput(*vpp);
1245 *vpp = 0;
1246 }
1247 if ((cnp->cn_flags & SAVESTART) == 0)
1248 FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
1249 vput(dvp);
1250 return (error);
1251 }
1252
1253 /*
1254 * Vnode op for readdir
1255 */
1256 static int
1257 devfs_readdir(struct vop_readdir_args *ap)
1258 /*struct vop_readdir_args {
1259 struct vnode *a_vp;
1260 struct uio *a_uio;
1261 struct ucred *a_cred;
1262 int *eofflag;
1263 int *ncookies;
1264 u_int **cookies;
1265 } */
1266 {
1267 struct vnode *vp = ap->a_vp;
1268 struct uio *uio = ap->a_uio;
1269 struct dirent dirent;
1270 devnode_t * dir_node;
1271 devdirent_t * name_node;
1272 char *name;
1273 int error = 0;
1274 int reclen;
1275 int nodenumber;
1276 int startpos,pos;
1277 struct proc * p = uio->uio_procp;
1278
1279 /* set up refs to dir */
1280 dir_node = VTODN(vp);
1281 if(dir_node->dn_type != DEV_DIR)
1282 return(ENOTDIR);
1283
1284 pos = 0;
1285 startpos = uio->uio_offset;
1286 DEVFS_LOCK(p);
1287 name_node = dir_node->dn_typeinfo.Dir.dirlist;
1288 nodenumber = 0;
1289 dir_node->dn_flags |= DN_ACCESS;
1290
1291 while ((name_node || (nodenumber < 2)) && (uio->uio_resid > 0))
1292 {
1293 switch(nodenumber)
1294 {
1295 case 0:
1296 dirent.d_fileno = (int32_t)(void *)dir_node;
1297 name = ".";
1298 dirent.d_namlen = 1;
1299 dirent.d_type = DT_DIR;
1300 break;
1301 case 1:
1302 if(dir_node->dn_typeinfo.Dir.parent)
1303 dirent.d_fileno
1304 = (int32_t)dir_node->dn_typeinfo.Dir.parent;
1305 else
1306 dirent.d_fileno = (u_int32_t)dir_node;
1307 name = "..";
1308 dirent.d_namlen = 2;
1309 dirent.d_type = DT_DIR;
1310 break;
1311 default:
1312 dirent.d_fileno = (int32_t)(void *)name_node->de_dnp;
1313 dirent.d_namlen = strlen(name_node->de_name);
1314 name = name_node->de_name;
1315 switch(name_node->de_dnp->dn_type) {
1316 case DEV_BDEV:
1317 dirent.d_type = DT_BLK;
1318 break;
1319 case DEV_CDEV:
1320 dirent.d_type = DT_CHR;
1321 break;
1322 case DEV_DIR:
1323 dirent.d_type = DT_DIR;
1324 break;
1325 case DEV_SLNK:
1326 dirent.d_type = DT_LNK;
1327 break;
1328 default:
1329 dirent.d_type = DT_UNKNOWN;
1330 }
1331 }
1332 #define GENERIC_DIRSIZ(dp) \
1333 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1334
1335 reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
1336
1337 if(pos >= startpos) /* made it to the offset yet? */
1338 {
1339 if (uio->uio_resid < reclen) /* will it fit? */
1340 break;
1341 strcpy( dirent.d_name,name);
1342 if ((error = uiomove ((caddr_t)&dirent,
1343 dirent.d_reclen, uio)) != 0)
1344 break;
1345 }
1346 pos += reclen;
1347 if((nodenumber >1) && name_node)
1348 name_node = name_node->de_next;
1349 nodenumber++;
1350 }
1351 DEVFS_UNLOCK(p);
1352 uio->uio_offset = pos;
1353
1354 return (error);
1355 }
1356
1357
1358 /*
1359 */
1360 static int
1361 devfs_readlink(struct vop_readlink_args *ap)
1362 /*struct vop_readlink_args {
1363 struct vnode *a_vp;
1364 struct uio *a_uio;
1365 struct ucred *a_cred;
1366 } */
1367 {
1368 struct vnode *vp = ap->a_vp;
1369 struct uio *uio = ap->a_uio;
1370 devnode_t * lnk_node;
1371 int error = 0;
1372
1373 /* set up refs to dir */
1374 lnk_node = VTODN(vp);
1375 if(lnk_node->dn_type != DEV_SLNK)
1376 return(EINVAL);
1377 if ((error = VOP_ACCESS(vp, VREAD, ap->a_cred, NULL)) != 0) { /* XXX */
1378 return error;
1379 }
1380 error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
1381 lnk_node->dn_typeinfo.Slnk.namelen, uio);
1382 return error;
1383 }
1384
1385 static int
1386 devfs_abortop(struct vop_abortop_args *ap)
1387 /*struct vop_abortop_args {
1388 struct vnode *a_dvp;
1389 struct componentname *a_cnp;
1390 } */
1391 {
1392 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
1393 FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
1394 }
1395 return 0;
1396 }
1397
1398
1399 static int
1400 devfs_reclaim(struct vop_reclaim_args *ap)
1401 /*struct vop_reclaim_args {
1402 struct vnode *a_vp;
1403 } */
1404 {
1405 struct vnode * vp = ap->a_vp;
1406 devnode_t * dnp = VTODN(vp);
1407
1408 if (dnp) {
1409 /*
1410 * do the same as devfs_inactive in case it is not called
1411 * before us (can that ever happen?)
1412 */
1413 dnp->dn_vn = NULL;
1414 vp->v_data = NULL;
1415 if (dnp->dn_delete) {
1416 devnode_free(dnp);
1417 }
1418 }
1419 return(0);
1420 }
1421
1422 /*
1423 * Print out the contents of a /devfs vnode.
1424 */
1425 static int
1426 devfs_print(struct vop_print_args *ap)
1427 /*struct vop_print_args {
1428 struct vnode *a_vp;
1429 } */
1430 {
1431
1432 return (0);
1433 }
1434
1435 /**************************************************************************\
1436 * pseudo ops *
1437 \**************************************************************************/
1438
1439 /*
1440 *
1441 * struct vop_inactive_args {
1442 * struct vnode *a_vp;
1443 * struct proc *a_p;
1444 * }
1445 */
1446
1447 static int
1448 devfs_inactive(struct vop_inactive_args *ap)
1449 {
1450 struct vnode * vp = ap->a_vp;
1451 devnode_t * dnp = VTODN(vp);
1452
1453 if (dnp) {
1454 dnp->dn_vn = NULL;
1455 vp->v_data = NULL;
1456 if (dnp->dn_delete) {
1457 devnode_free(dnp);
1458 }
1459 }
1460 VOP_UNLOCK(vp, 0, ap->a_p);
1461 return (0);
1462 }
1463
1464 int
1465 devfs_update(ap)
1466 struct vop_update_args /* {
1467 struct vnode *a_vp;
1468 struct timeval *a_access;
1469 struct timeval *a_modify;
1470 int a_waitfor;
1471 } */ *ap;
1472 {
1473 register struct fs *fs;
1474 int error;
1475 devnode_t * ip;
1476
1477 ip = VTODN(ap->a_vp);
1478 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
1479 ip->dn_flags &=
1480 ~(DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE);
1481 return (0);
1482 }
1483 if ((ip->dn_flags &
1484 (DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE)) == 0)
1485 return (0);
1486 dn_times(ip, time, time);
1487 return (0);
1488 }
1489
1490 #define VOPFUNC int (*)(void *)
1491
1492 /* The following ops are used by directories and symlinks */
1493 int (**devfs_vnodeop_p)(void *);
1494 static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
1495 { &vop_default_desc, (VOPFUNC)vn_default_error },
1496 { &vop_lookup_desc, (VOPFUNC)devfs_lookup }, /* lookup */
1497 { &vop_create_desc, (VOPFUNC)err_create }, /* create */
1498 { &vop_whiteout_desc, (VOPFUNC)err_whiteout }, /* whiteout */
1499 { &vop_mknod_desc, (VOPFUNC)devfs_mknod }, /* mknod */
1500 { &vop_open_desc, (VOPFUNC)nop_open }, /* open */
1501 { &vop_close_desc, (VOPFUNC)devfs_close }, /* close */
1502 { &vop_access_desc, (VOPFUNC)devfs_access }, /* access */
1503 { &vop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */
1504 { &vop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */
1505 { &vop_read_desc, (VOPFUNC)devfs_read }, /* read */
1506 { &vop_write_desc, (VOPFUNC)devfs_write }, /* write */
1507 { &vop_lease_desc, (VOPFUNC)nop_lease }, /* lease */
1508 { &vop_ioctl_desc, (VOPFUNC)err_ioctl }, /* ioctl */
1509 { &vop_select_desc, (VOPFUNC)err_select }, /* select */
1510 { &vop_revoke_desc, (VOPFUNC)err_revoke }, /* revoke */
1511 { &vop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
1512 { &vop_fsync_desc, (VOPFUNC)nop_fsync }, /* fsync */
1513 { &vop_seek_desc, (VOPFUNC)err_seek }, /* seek */
1514 { &vop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */
1515 { &vop_link_desc, (VOPFUNC)devfs_link }, /* link */
1516 { &vop_rename_desc, (VOPFUNC)devfs_rename }, /* rename */
1517 { &vop_mkdir_desc, (VOPFUNC)err_mkdir }, /* mkdir */
1518 { &vop_rmdir_desc, (VOPFUNC)err_rmdir }, /* rmdir */
1519 { &vop_symlink_desc, (VOPFUNC)devfs_symlink }, /* symlink */
1520 { &vop_readdir_desc, (VOPFUNC)devfs_readdir }, /* readdir */
1521 { &vop_readlink_desc, (VOPFUNC)devfs_readlink }, /* readlink */
1522 { &vop_abortop_desc, (VOPFUNC)devfs_abortop }, /* abortop */
1523 { &vop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */
1524 { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */
1525 { &vop_lock_desc, (VOPFUNC)nop_lock }, /* lock */
1526 { &vop_unlock_desc, (VOPFUNC)nop_unlock }, /* unlock */
1527 { &vop_bmap_desc, (VOPFUNC)err_bmap }, /* bmap */
1528 { &vop_strategy_desc, (VOPFUNC)err_strategy }, /* strategy */
1529 { &vop_print_desc, (VOPFUNC)err_print }, /* print */
1530 { &vop_islocked_desc, (VOPFUNC)nop_islocked }, /* islocked */
1531 { &vop_pathconf_desc, (VOPFUNC)err_pathconf }, /* pathconf */
1532 { &vop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
1533 { &vop_blkatoff_desc, (VOPFUNC)err_blkatoff }, /* blkatoff */
1534 { &vop_valloc_desc, (VOPFUNC)err_valloc }, /* valloc */
1535 { &vop_reallocblks_desc, (VOPFUNC)err_reallocblks }, /* reallocblks */
1536 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
1537 { &vop_truncate_desc, (VOPFUNC)err_truncate }, /* truncate */
1538 { &vop_update_desc, (VOPFUNC)devfs_update }, /* update */
1539 { &vop_bwrite_desc, (VOPFUNC)err_bwrite },
1540 { &vop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */
1541 { &vop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */
1542 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
1543 { &vop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff */
1544 { &vop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */
1545 { &vop_cmap_desc, (VOPFUNC)err_cmap }, /* cmap */
1546 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1547 };
1548 struct vnodeopv_desc devfs_vnodeop_opv_desc =
1549 { &devfs_vnodeop_p, devfs_vnodeop_entries };
1550
1551 /* The following ops are used by the device nodes */
1552 int (**devfs_spec_vnodeop_p)(void *);
1553 static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
1554 { &vop_default_desc, (VOPFUNC)vn_default_error },
1555 { &vop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
1556 { &vop_create_desc, (VOPFUNC)spec_create }, /* create */
1557 { &vop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
1558 { &vop_open_desc, (VOPFUNC)spec_open }, /* open */
1559 { &vop_close_desc, (VOPFUNC)devfsspec_close }, /* close */
1560 { &vop_access_desc, (VOPFUNC)devfs_access }, /* access */
1561 { &vop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */
1562 { &vop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */
1563 { &vop_read_desc, (VOPFUNC)devfsspec_read }, /* read */
1564 { &vop_write_desc, (VOPFUNC)devfsspec_write }, /* write */
1565 { &vop_lease_desc, (VOPFUNC)spec_lease_check }, /* lease */
1566 { &vop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
1567 { &vop_select_desc, (VOPFUNC)spec_select }, /* select */
1568 { &vop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
1569 { &vop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
1570 { &vop_fsync_desc, (VOPFUNC)spec_fsync }, /* fsync */
1571 { &vop_seek_desc, (VOPFUNC)spec_seek }, /* seek */
1572 { &vop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */
1573 { &vop_link_desc, (VOPFUNC)devfs_link }, /* link */
1574 { &vop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
1575 { &vop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
1576 { &vop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
1577 { &vop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
1578 { &vop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
1579 { &vop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
1580 { &vop_abortop_desc, (VOPFUNC)spec_abortop }, /* abortop */
1581 { &vop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */
1582 { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */
1583 { &vop_lock_desc, (VOPFUNC)nop_lock }, /* lock */
1584 { &vop_unlock_desc, (VOPFUNC)nop_unlock }, /* unlock */
1585 { &vop_bmap_desc, (VOPFUNC)spec_bmap }, /* bmap */
1586 { &vop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
1587 { &vop_print_desc, (VOPFUNC)devfs_print }, /* print */
1588 { &vop_islocked_desc, (VOPFUNC)nop_islocked }, /* islocked */
1589 { &vop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
1590 { &vop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */
1591 { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff }, /* blkatoff */
1592 { &vop_valloc_desc, (VOPFUNC)spec_valloc }, /* valloc */
1593 { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks }, /* reallocblks */
1594 { &vop_vfree_desc, (VOPFUNC)nop_vfree }, /* vfree */
1595 { &vop_truncate_desc, (VOPFUNC)spec_truncate }, /* truncate */
1596 { &vop_update_desc, (VOPFUNC)devfs_update }, /* update */
1597 { &vop_bwrite_desc, (VOPFUNC)vn_bwrite },
1598 { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
1599 { &vop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */
1600 { &vop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */
1601 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
1602 { &vop_blktooff_desc, (VOPFUNC)spec_blktooff }, /* blktooff */
1603 { &vop_blktooff_desc, (VOPFUNC)spec_offtoblk }, /* blkofftoblk */
1604 { &vop_cmap_desc, (VOPFUNC)spec_cmap }, /* cmap */
1605 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1606 };
1607 struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
1608 { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };
1609