]> git.saurik.com Git - apple/xnu.git/blame - bsd/miscfs/devfs/devfs_vnops.c
xnu-517.12.7.tar.gz
[apple/xnu.git] / bsd / miscfs / devfs / devfs_vnops.c
CommitLineData
1c79356b 1/*
55e303ae 2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
1c79356b 11 *
e5568f75
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright 1997,1998 Julian Elischer. All rights reserved.
24 * julian@freebsd.org
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions are
28 * met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright notice,
32 * this list of conditions and the following disclaimer in the documentation
33 * and/or other materials provided with the distribution.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
36 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
37 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
39 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
42 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 * SUCH DAMAGE.
46 *
47 * devfs_vnops.c
48 */
49
50/*
51 * HISTORY
52 * Clark Warner (warner_c@apple.com) Tue Feb 10 2000
53 * - Added err_copyfile to the vnode operations table
54 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
55 * - instead of duplicating specfs here, created a vnode-ops table
56 * that redirects most operations to specfs (as is done with ufs);
57 * - removed routines that made no sense
58 * - cleaned up reclaim: replaced devfs_vntodn() with a macro VTODN()
59 * - cleaned up symlink, link locking
60 * - added the devfs_lock to protect devfs data structures against
61 * driver's calling devfs_add_devswf()/etc.
62 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
63 * - free the devfs devnode in devfs_inactive(), not just in devfs_reclaim()
64 * to free up kernel memory as soon as it's available
65 * - got rid of devfsspec_{read, write}
66 * Dieter Siegmund (dieter@apple.com) Fri Sep 17 09:58:38 PDT 1999
67 * - update the mod/access times
68 */
69
70#include <sys/param.h>
71#include <sys/systm.h>
72#include <sys/buf.h>
73#include <sys/namei.h>
74#include <sys/kernel.h>
75#include <sys/fcntl.h>
76#include <sys/conf.h>
77#include <sys/disklabel.h>
78#include <sys/lock.h>
79#include <sys/stat.h>
80#include <sys/mount.h>
81#include <sys/proc.h>
82#include <sys/time.h>
83#include <sys/vnode.h>
84#include <miscfs/specfs/specdev.h>
85#include <sys/dirent.h>
86#include <sys/vmmeter.h>
87#include <sys/vm.h>
88
89#include "devfsdefs.h"
90
91/*
92 * Convert a component of a pathname into a pointer to a locked node.
93 * This is a very central and rather complicated routine.
94 * If the file system is not maintained in a strict tree hierarchy,
95 * this can result in a deadlock situation (see comments in code below).
96 *
97 * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
98 * whether the name is to be looked up, created, renamed, or deleted.
99 * When CREATE, RENAME, or DELETE is specified, information usable in
100 * creating, renaming, or deleting a directory entry may be calculated.
101 * If flag has LOCKPARENT or'ed into it and the target of the pathname
102 * exists, lookup returns both the target and its parent directory locked.
103 * When creating or renaming and LOCKPARENT is specified, the target may
104 * not be ".". When deleting and LOCKPARENT is specified, the target may
105 * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
106 * instead of two DNUNLOCKs.
107 *
108 * Overall outline of devfs_lookup:
109 *
110 * check accessibility of directory
111 * null terminate the component (lookup leaves the whole string alone)
112 * look for name in cache, if found, then if at end of path
113 * and deleting or creating, drop it, else return name
114 * search for name in directory, to found or notfound
115 * notfound:
116 * if creating, return locked directory,
117 * else return error
118 * found:
119 * if at end of path and deleting, return information to allow delete
120 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
121 * node and return info to allow rewrite
122 * if not at end, add name to cache; if at end and neither creating
123 * nor deleting, add name to cache
124 * On return to lookup, remove the null termination we put in at the start.
125 *
126 * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
127 */
128static int
129devfs_lookup(struct vop_lookup_args *ap)
130 /*struct vop_lookup_args {
131 struct vnode * a_dvp; directory vnode ptr
132 struct vnode ** a_vpp; where to put the result
133 struct componentname * a_cnp; the name we want
134 };*/
135{
136 struct componentname *cnp = ap->a_cnp;
137 struct vnode *dir_vnode = ap->a_dvp;
138 struct vnode **result_vnode = ap->a_vpp;
139 devnode_t * dir_node; /* the directory we are searching */
140 devnode_t * node = NULL; /* the node we are searching for */
141 devdirent_t * nodename;
142 int flags = cnp->cn_flags;
143 int op = cnp->cn_nameiop; /* LOOKUP, CREATE, RENAME, or DELETE */
144 int lockparent = flags & LOCKPARENT;
145 int wantparent = flags & (LOCKPARENT|WANTPARENT);
146 int error = 0;
147 struct proc *p = cnp->cn_proc;
148 char heldchar; /* the char at the end of the name componet */
149
150 *result_vnode = NULL; /* safe not sorry */ /*XXX*/
151
152 if (dir_vnode->v_usecount == 0)
153 printf("devfs_lookup: dir had no refs ");
154 dir_node = VTODN(dir_vnode);
155
156 /*
157 * Check accessiblity of directory.
158 */
159 if (dir_node->dn_type != DEV_DIR) {
160 return (ENOTDIR);
161 }
162
163 if ((error = VOP_ACCESS(dir_vnode, VEXEC, cnp->cn_cred, p)) != 0) {
164 return (error);
165 }
166
167 /* temporarily terminate string component */
168 heldchar = cnp->cn_nameptr[cnp->cn_namelen];
169 cnp->cn_nameptr[cnp->cn_namelen] = '\0';
170 DEVFS_LOCK(p);
171 nodename = dev_findname(dir_node,cnp->cn_nameptr);
172 if (nodename) {
173 /* entry exists */
174 node = nodename->de_dnp;
175 node->dn_last_lookup = nodename; /* for unlink */
176 /* Do potential vnode allocation here inside the lock
177 * to make sure that our device node has a non-NULL dn_vn
178 * associated with it. The device node might otherwise
179 * get deleted out from under us (see devfs_dn_free()).
180 */
181 error = devfs_dntovn(node, result_vnode, p);
182 }
183 DEVFS_UNLOCK(p);
184 /* restore saved character */
185 cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
186
187 if (error)
188 return (error);
189
190 if (!nodename) { /* no entry */
191 /* If it doesn't exist and we're not the last component,
192 * or we're at the last component, but we're not creating
193 * or renaming, return ENOENT.
194 */
195 if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
196 return ENOENT;
197 }
198 /*
199 * Access for write is interpreted as allowing
200 * creation of files in the directory.
201 */
202 if ((error = VOP_ACCESS(dir_vnode, VWRITE,
203 cnp->cn_cred, p)) != 0)
204 {
205 return (error);
206 }
207 /*
208 * We return with the directory locked, so that
209 * the parameters we set up above will still be
210 * valid if we actually decide to add a new entry.
211 * We return ni_vp == NULL to indicate that the entry
212 * does not currently exist; we leave a pointer to
213 * the (locked) directory vnode in namei_data->ni_dvp.
214 * The pathname buffer is saved so that the name
215 * can be obtained later.
216 *
217 * NB - if the directory is unlocked, then this
218 * information cannot be used.
219 */
220 cnp->cn_flags |= SAVENAME;
221 if (!lockparent)
222 VOP_UNLOCK(dir_vnode, 0, p);
223 return (EJUSTRETURN);
224 }
225
226 /*
227 * If deleting, and at end of pathname, return
228 * parameters which can be used to remove file.
229 * If the wantparent flag isn't set, we return only
230 * the directory (in namei_data->ni_dvp), otherwise we go
231 * on and lock the node, being careful with ".".
232 */
233 if (op == DELETE && (flags & ISLASTCN)) {
234 /*
235 * Write access to directory required to delete files.
236 */
237 if ((error = VOP_ACCESS(dir_vnode, VWRITE,
238 cnp->cn_cred, p)) != 0)
239 return (error);
240 /*
241 * we are trying to delete '.'. What does this mean? XXX
242 */
243 if (dir_node == node) {
244 VREF(dir_vnode);
245 *result_vnode = dir_vnode;
246 return (0);
247 }
248#ifdef NOTYET
249 /*
250 * If directory is "sticky", then user must own
251 * the directory, or the file in it, else she
252 * may not delete it (unless she's root). This
253 * implements append-only directories.
254 */
255 if ((dir_node->mode & ISVTX) &&
256 cnp->cn_cred->cr_uid != 0 &&
257 cnp->cn_cred->cr_uid != dir_node->uid &&
258 cnp->cn_cred->cr_uid != node->uid) {
259 VOP_UNLOCK(*result_vnode, 0, p);
260 return (EPERM);
261 }
262#endif
263 if (!lockparent)
264 VOP_UNLOCK(dir_vnode, 0, p);
265 return (0);
266 }
267
268 /*
269 * If rewriting (RENAME), return the vnode and the
270 * information required to rewrite the present directory
271 * Must get node of directory entry to verify it's a
272 * regular file, or empty directory.
273 */
274 if (op == RENAME && wantparent && (flags & ISLASTCN)) {
275 /*
276 * Are we allowed to change the holding directory?
277 */
278 if ((error = VOP_ACCESS(dir_vnode, VWRITE,
279 cnp->cn_cred, p)) != 0)
280 return (error);
281 /*
282 * Careful about locking second node.
283 * This can only occur if the target is ".".
284 */
285 if (dir_node == node)
286 return (EISDIR);
287 /* hmm save the 'from' name (we need to delete it) */
288 cnp->cn_flags |= SAVENAME;
289 if (!lockparent)
290 VOP_UNLOCK(dir_vnode, 0, p);
291 return (0);
292 }
293
294 /*
295 * Step through the translation in the name. We do not unlock the
296 * directory because we may need it again if a symbolic link
297 * is relative to the current directory. Instead we save it
298 * unlocked as "saved_dir_node" XXX. We must get the target
299 * node before unlocking
300 * the directory to insure that the node will not be removed
301 * before we get it. We prevent deadlock by always fetching
302 * nodes from the root, moving down the directory tree. Thus
303 * when following backward pointers ".." we must unlock the
304 * parent directory before getting the requested directory.
305 * There is a potential race condition here if both the current
306 * and parent directories are removed before the lock for the
307 * node associated with ".." returns. We hope that this occurs
308 * infrequently since we cannot avoid this race condition without
309 * implementing a sophisticated deadlock detection algorithm.
310 * Note also that this simple deadlock detection scheme will not
311 * work if the file system has any hard links other than ".."
312 * that point backwards in the directory structure.
313 */
314 if (flags & ISDOTDOT) {
315 VOP_UNLOCK(dir_vnode, 0, p); /* race to get the node */
316 if (lockparent && (flags & ISLASTCN))
317 vn_lock(dir_vnode, LK_EXCLUSIVE | LK_RETRY, p);
318 } else if (dir_node == node) {
319#if 0
320 /*
321 * this next statement is wrong: we already did a vget in
322 * devfs_dntovn(); DWS 4/16/1999
323 */
324 VREF(dir_vnode); /* we want ourself, ie "." */
325#endif
326 *result_vnode = dir_vnode;
327 } else {
328 if (!lockparent || (flags & ISLASTCN))
329 VOP_UNLOCK(dir_vnode, 0, p);
330 }
331
332 return (0);
333}
334
335static int
336devfs_access(struct vop_access_args *ap)
337 /*struct vop_access_args {
338 struct vnode *a_vp;
339 int a_mode;
340 struct ucred *a_cred;
341 struct proc *a_p;
342 } */
343{
344 /*
345 * mode is filled with a combination of VREAD, VWRITE,
346 * and/or VEXEC bits turned on. In an octal number these
347 * are the Y in 0Y00.
348 */
349 struct vnode *vp = ap->a_vp;
350 int mode = ap->a_mode;
351 struct ucred *cred = ap->a_cred;
352 devnode_t * file_node;
353 gid_t *gp;
354 int i;
355 struct proc *p = ap->a_p;
356
357 file_node = VTODN(vp);
358 /*
359 * if we are not running as a process, we are in the
360 * kernel and we DO have permission
361 */
362 if (p == NULL)
363 return 0;
364
365 /*
366 * Access check is based on only one of owner, group, public.
367 * If not owner, then check group. If not a member of the
368 * group, then check public access.
369 */
370 if (cred->cr_uid != file_node->dn_uid)
371 {
372 /* failing that.. try groups */
373 mode >>= 3;
374 gp = cred->cr_groups;
375 for (i = 0; i < cred->cr_ngroups; i++, gp++)
376 {
377 if (file_node->dn_gid == *gp)
378 {
379 goto found;
380 }
381 }
382 /* failing that.. try general access */
383 mode >>= 3;
384found:
385 ;
386 }
387 if ((file_node->dn_mode & mode) == mode)
388 return (0);
389 /*
390 * Root gets to do anything.
391 * but only use suser prives as a last resort
392 * (Use of super powers is recorded in ap->a_p->p_acflag)
393 */
394 if( suser(cred, &ap->a_p->p_acflag) == 0) /* XXX what if no proc? */
395 return 0;
396 return (EACCES);
397}
398
399static int
400devfs_getattr(struct vop_getattr_args *ap)
401 /*struct vop_getattr_args {
402 struct vnode *a_vp;
403 struct vattr *a_vap;
404 struct ucred *a_cred;
405 struct proc *a_p;
406 } */
407{
408 struct vnode *vp = ap->a_vp;
409 struct vattr *vap = ap->a_vap;
410 devnode_t * file_node;
411 struct timeval tv;
412
413 file_node = VTODN(vp);
414 tv = time;
415 dn_times(file_node, tv, tv);
416 vap->va_rdev = 0;/* default value only */
417 vap->va_mode = file_node->dn_mode;
418 switch (file_node->dn_type)
419 {
420 case DEV_DIR:
421 vap->va_rdev = (dev_t)file_node->dn_dvm;
422 vap->va_mode |= (S_IFDIR);
423 break;
424 case DEV_CDEV:
425 vap->va_rdev = file_node->dn_typeinfo.dev;
426 vap->va_mode |= (S_IFCHR);
427 break;
428 case DEV_BDEV:
429 vap->va_rdev = file_node->dn_typeinfo.dev;
430 vap->va_mode |= (S_IFBLK);
431 break;
432 case DEV_SLNK:
433 vap->va_mode |= (S_IFLNK);
434 break;
435 }
436 vap->va_type = vp->v_type;
437 vap->va_nlink = file_node->dn_links;
438 vap->va_uid = file_node->dn_uid;
439 vap->va_gid = file_node->dn_gid;
440 vap->va_fsid = (int32_t)(void *)file_node->dn_dvm;
441 vap->va_fileid = (int32_t)(void *)file_node;
442 vap->va_size = file_node->dn_len; /* now a u_quad_t */
443 /* this doesn't belong here */
444 if (vp->v_type == VBLK)
445 vap->va_blocksize = BLKDEV_IOSIZE;
446 else if (vp->v_type == VCHR)
447 vap->va_blocksize = MAXPHYSIO;
448 else
449 vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
450 /* if the time is bogus, set it to the boot time */
451 if (file_node->dn_ctime.tv_sec == 0)
452 file_node->dn_ctime.tv_sec = boottime.tv_sec;
453 if (file_node->dn_mtime.tv_sec == 0)
454 file_node->dn_mtime.tv_sec = boottime.tv_sec;
455 if (file_node->dn_atime.tv_sec == 0)
456 file_node->dn_atime.tv_sec = boottime.tv_sec;
457 vap->va_ctime = file_node->dn_ctime;
458 vap->va_mtime = file_node->dn_mtime;
459 vap->va_atime = file_node->dn_atime;
460 vap->va_gen = 0;
461 vap->va_flags = 0;
462 vap->va_bytes = file_node->dn_len; /* u_quad_t */
463 vap->va_filerev = 0; /* XXX */ /* u_quad_t */
464 vap->va_vaflags = 0; /* XXX */
465 return 0;
466}
467
468static int
469devfs_setattr(struct vop_setattr_args *ap)
470 /*struct vop_setattr_args {
471 struct vnode *a_vp;
472 struct vattr *a_vap;
473 struct ucred *a_cred;
474 struct proc *a_p;
475 } */
476{
477 struct vnode *vp = ap->a_vp;
478 struct vattr *vap = ap->a_vap;
479 struct ucred *cred = ap->a_cred;
480 struct proc *p = ap->a_p;
481 int error = 0;
482 gid_t *gp;
483 int i;
484 devnode_t * file_node;
485 struct timeval atimeval, mtimeval;
486
487 if (vap->va_flags != VNOVAL) /* XXX needs to be implemented */
488 return (EOPNOTSUPP);
489
490 file_node = VTODN(vp);
491
492 if ((vap->va_type != VNON) ||
493 (vap->va_nlink != VNOVAL) ||
494 (vap->va_fsid != VNOVAL) ||
495 (vap->va_fileid != VNOVAL) ||
496 (vap->va_blocksize != VNOVAL) ||
497 (vap->va_rdev != VNOVAL) ||
498 (vap->va_bytes != VNOVAL) ||
499 (vap->va_gen != VNOVAL ))
500 {
501 return EINVAL;
502 }
503
504 /*
505 * Go through the fields and update iff not VNOVAL.
506 */
507 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
508 if (cred->cr_uid != file_node->dn_uid &&
509 (error = suser(cred, &p->p_acflag)) &&
510 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
511 (error = VOP_ACCESS(vp, VWRITE, cred, p))))
512 return (error);
513 if (vap->va_atime.tv_sec != VNOVAL)
514 file_node->dn_flags |= DN_ACCESS;
515 if (vap->va_mtime.tv_sec != VNOVAL)
516 file_node->dn_flags |= DN_CHANGE | DN_UPDATE;
517 atimeval.tv_sec = vap->va_atime.tv_sec;
518 atimeval.tv_usec = vap->va_atime.tv_nsec / 1000;
519 mtimeval.tv_sec = vap->va_mtime.tv_sec;
520 mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000;
521 if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))
522 return (error);
523 }
524
525 /*
526 * Change the permissions.. must be root or owner to do this.
527 */
528 if (vap->va_mode != (u_short)VNOVAL) {
529 if ((cred->cr_uid != file_node->dn_uid)
530 && (error = suser(cred, &p->p_acflag)))
531 return (error);
532 file_node->dn_mode &= ~07777;
533 file_node->dn_mode |= vap->va_mode & 07777;
534 }
535
536 /*
537 * Change the owner.. must be root to do this.
538 */
539 if (vap->va_uid != (uid_t)VNOVAL) {
540 if (error = suser(cred, &p->p_acflag))
541 return (error);
542 file_node->dn_uid = vap->va_uid;
543 }
544
545 /*
546 * Change the group.. must be root or owner to do this.
547 * If we are the owner, we must be in the target group too.
548 * don't use suser() unless you have to as it reports
549 * whether you needed suser powers or not.
550 */
551 if (vap->va_gid != (gid_t)VNOVAL) {
552 if (cred->cr_uid == file_node->dn_uid){
553 gp = cred->cr_groups;
554 for (i = 0; i < cred->cr_ngroups; i++, gp++) {
555 if (vap->va_gid == *gp)
556 goto cando;
557 }
558 }
559 /*
560 * we can't do it with normal privs,
561 * do we have an ace up our sleeve?
562 */
563 if (error = suser(cred, &p->p_acflag))
564 return (error);
565cando:
566 file_node->dn_gid = vap->va_gid;
567 }
568#if 0
569 /*
570 * Copied from somewhere else
571 * but only kept as a marker and reminder of the fact that
572 * flags should be handled some day
573 */
574 if (vap->va_flags != VNOVAL) {
575 if (error = suser(cred, &p->p_acflag))
576 return error;
577 if (cred->cr_uid == 0)
578 ;
579 else {
580 }
581 }
582#endif
583 return error;
584}
585
586static int
587devfs_read(struct vop_read_args *ap)
588 /*struct vop_read_args {
589 struct vnode *a_vp;
590 struct uio *a_uio;
591 int a_ioflag;
592 struct ucred *a_cred;
593 } */
594{
595 devnode_t * dn_p = VTODN(ap->a_vp);
596
597 switch (ap->a_vp->v_type) {
598 case VDIR: {
599 dn_p->dn_flags |= DN_ACCESS;
600 return VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred,
601 NULL, NULL, NULL);
602 }
603 default: {
604 printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
605 return(EINVAL);
606 break;
607 }
608 }
609 return (0); /* not reached */
610}
611
612static int
613devfs_close(ap)
614 struct vop_close_args /* {
615 struct vnode *a_vp;
616 int a_fflag;
617 struct ucred *a_cred;
618 struct proc *a_p;
619 } */ *ap;
620{
621 struct vnode * vp = ap->a_vp;
622 register devnode_t * dnp = VTODN(vp);
623
624 simple_lock(&vp->v_interlock);
625 if (vp->v_usecount > 1)
626 dn_times(dnp, time, time);
627 simple_unlock(&vp->v_interlock);
628 return (0);
629}
630
631static int
632devfsspec_close(ap)
633 struct vop_close_args /* {
634 struct vnode *a_vp;
635 int a_fflag;
636 struct ucred *a_cred;
637 struct proc *a_p;
638 } */ *ap;
639{
640 struct vnode * vp = ap->a_vp;
641 register devnode_t * dnp = VTODN(vp);
642
643 simple_lock(&vp->v_interlock);
644 if (vp->v_usecount > 1)
645 dn_times(dnp, time, time);
646 simple_unlock(&vp->v_interlock);
647 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
648}
649
650static int
651devfsspec_read(struct vop_read_args *ap)
652 /*struct vop_read_args {
653 struct vnode *a_vp;
654 struct uio *a_uio;
655 int a_ioflag;
656 struct ucred *a_cred;
657 } */
658{
659 VTODN(ap->a_vp)->dn_flags |= DN_ACCESS;
660 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
661}
662
663static int
664devfsspec_write(struct vop_write_args *ap)
665 /*struct vop_write_args {
666 struct vnode *a_vp;
667 struct uio *a_uio;
668 int a_ioflag;
669 struct ucred *a_cred;
670 } */
671{
672 VTODN(ap->a_vp)->dn_flags |= DN_CHANGE | DN_UPDATE;
673 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
674}
675
676/*
677 * Write data to a file or directory.
678 */
679static int
680devfs_write(struct vop_write_args *ap)
681 /*struct vop_write_args {
682 struct vnode *a_vp;
683 struct uio *a_uio;
684 int a_ioflag;
685 struct ucred *a_cred;
686 } */
687{
688 switch (ap->a_vp->v_type) {
689 case VDIR:
690 return(EISDIR);
691 default:
692 printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
693 return (EINVAL);
694 }
695 return 0; /* not reached */
696}
697
698static int
699devfs_remove(struct vop_remove_args *ap)
700 /*struct vop_remove_args {
701 struct vnode *a_dvp;
702 struct vnode *a_vp;
703 struct componentname *a_cnp;
704 } */
705{
706 struct vnode *vp = ap->a_vp;
707 struct vnode *dvp = ap->a_dvp;
708 struct componentname *cnp = ap->a_cnp;
709 devnode_t * tp;
710 devnode_t * tdp;
711 devdirent_t * tnp;
712 int doingdirectory = 0;
713 int error = 0;
714 uid_t ouruid = cnp->cn_cred->cr_uid;
715 struct proc *p = cnp->cn_proc;
716
717 /*
718 * Lock our directories and get our name pointers
719 * assume that the names are null terminated as they
720 * are the end of the path. Get pointers to all our
721 * devfs structures.
722 */
723 tp = VTODN(vp);
724 tdp = VTODN(dvp);
725 /*
726 * Assuming we are atomic, dev_lookup left this for us
727 */
728 tnp = tp->dn_last_lookup;
729
730 /*
731 * Check we are doing legal things WRT the new flags
732 */
733 if ((tp->dn_flags & (IMMUTABLE | APPEND))
734 || (tdp->dn_flags & APPEND) /*XXX eh?*/ ) {
735 error = EPERM;
736 goto abort;
737 }
738
739 /*
740 * Make sure that we don't try do something stupid
741 */
742 if ((tp->dn_type) == DEV_DIR) {
743 /*
744 * Avoid ".", "..", and aliases of "." for obvious reasons.
745 */
746 if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')
747 || (cnp->cn_flags&ISDOTDOT) ) {
748 error = EINVAL;
749 goto abort;
750 }
751 doingdirectory++;
752 }
753
754 /***********************************
755 * Start actually doing things.... *
756 ***********************************/
757 tdp->dn_flags |= DN_CHANGE | DN_UPDATE;
758
759 /*
760 * own the parent directory, or the destination of the rename,
761 * otherwise the destination may not be changed (except by
762 * root). This implements append-only directories.
763 * XXX shoudn't this be in generic code?
764 */
765 if ((tdp->dn_mode & S_ISTXT)
766 && ouruid != 0
767 && ouruid != tdp->dn_uid
768 && ouruid != tp->dn_uid ) {
769 error = EPERM;
770 goto abort;
771 }
772 /*
773 * Target must be empty if a directory and have no links
774 * to it. Also, ensure source and target are compatible
775 * (both directories, or both not directories).
776 */
777 if (( doingdirectory) && (tp->dn_links > 2)) {
778 error = ENOTEMPTY;
779 goto abort;
780 }
781 DEVFS_LOCK(p);
782 dev_free_name(tnp);
783 DEVFS_UNLOCK(p);
784 abort:
785 if (dvp == vp)
786 vrele(vp);
787 else
788 vput(vp);
789 vput(dvp);
790 return (error);
791}
792
793/*
794 */
795static int
796devfs_link(struct vop_link_args *ap)
797 /*struct vop_link_args {
798 struct vnode *a_tdvp;
799 struct vnode *a_vp;
800 struct componentname *a_cnp;
801 } */
802{
803 struct vnode *vp = ap->a_vp;
804 struct vnode *tdvp = ap->a_tdvp;
805 struct componentname *cnp = ap->a_cnp;
806 struct proc *p = cnp->cn_proc;
807 devnode_t * fp;
808 devnode_t * tdp;
809 devdirent_t * tnp;
810 int error = 0;
811 struct timeval tv;
812
813 /*
814 * First catch an arbitrary restriction for this FS
815 */
816 if (cnp->cn_namelen > DEVMAXNAMESIZE) {
817 error = ENAMETOOLONG;
818 goto out1;
819 }
820
821 /*
822 * Lock our directories and get our name pointers
823 * assume that the names are null terminated as they
824 * are the end of the path. Get pointers to all our
825 * devfs structures.
826 */
827 tdp = VTODN(tdvp);
828 fp = VTODN(vp);
829
830 if (tdvp->v_mount != vp->v_mount) {
831 error = EXDEV;
832 VOP_ABORTOP(tdvp, cnp);
833 goto out2;
834 }
835 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
836 VOP_ABORTOP(tdvp, cnp);
837 goto out2;
838 }
839
840 /*
841 * Check we are doing legal things WRT the new flags
842 */
843 if (fp->dn_flags & (IMMUTABLE | APPEND)) {
844 VOP_ABORTOP(tdvp, cnp);
845 error = EPERM;
846 goto out1;
847 }
848
849 /***********************************
850 * Start actually doing things.... *
851 ***********************************/
852 fp->dn_flags |= DN_CHANGE;
853 tv = time;
854 error = VOP_UPDATE(vp, &tv, &tv, 1);
855 if (!error) {
856 DEVFS_LOCK(p);
857 error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
858 DEVFS_UNLOCK(p);
859 }
860out1:
861 if (tdvp != vp)
862 VOP_UNLOCK(vp, 0, p);
863out2:
864 vput(tdvp);
865 return (error);
866}
867
868/*
869 * Check if source directory is in the path of the target directory.
870 * Target is supplied locked, source is unlocked.
871 * The target is always vput before returning.
872 */
873int
874devfs_checkpath(source, target)
875 devnode_t *source, *target;
876{
877 int error = 0;
878 devnode_t * ntmp;
879 devnode_t * tmp;
880 struct vnode *vp;
881
882 vp = target->dn_vn;
883 tmp = target;
884
885 do {
886 if (tmp == source) {
887 error = EINVAL;
888 break;
889 }
890 ntmp = tmp;
891 } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
892
893 if (vp != NULL)
894 vput(vp);
895 return (error);
896}
897
898/*
899 * Rename system call. Seems overly complicated to me...
900 * rename("foo", "bar");
901 * is essentially
902 * unlink("bar");
903 * link("foo", "bar");
904 * unlink("foo");
905 * but ``atomically''.
906 *
907 * When the target exists, both the directory
908 * and target vnodes are locked.
909 * the source and source-parent vnodes are referenced
910 *
911 *
912 * Basic algorithm is:
913 *
914 * 1) Bump link count on source while we're linking it to the
915 * target. This also ensure the inode won't be deleted out
916 * from underneath us while we work (it may be truncated by
917 * a concurrent `trunc' or `open' for creation).
918 * 2) Link source to destination. If destination already exists,
919 * delete it first.
920 * 3) Unlink source reference to node if still around. If a
921 * directory was moved and the parent of the destination
922 * is different from the source, patch the ".." entry in the
923 * directory.
924 */
925static int
926devfs_rename(struct vop_rename_args *ap)
927 /*struct vop_rename_args {
928 struct vnode *a_fdvp;
929 struct vnode *a_fvp;
930 struct componentname *a_fcnp;
931 struct vnode *a_tdvp;
932 struct vnode *a_tvp;
933 struct componentname *a_tcnp;
934 } */
935{
936 struct vnode *tvp = ap->a_tvp;
937 struct vnode *tdvp = ap->a_tdvp;
938 struct vnode *fvp = ap->a_fvp;
939 struct vnode *fdvp = ap->a_fdvp;
940 struct componentname *tcnp = ap->a_tcnp;
941 struct componentname *fcnp = ap->a_fcnp;
942 struct proc *p = fcnp->cn_proc;
943 devnode_t *fp, *fdp, *tp, *tdp;
944 devdirent_t *fnp,*tnp;
945 int doingdirectory = 0;
946 int error = 0;
947 struct timeval tv;
948
949 /*
950 * First catch an arbitrary restriction for this FS
951 */
952 if(tcnp->cn_namelen > DEVMAXNAMESIZE) {
953 error = ENAMETOOLONG;
954 goto abortit;
955 }
956
957 /*
958 * Lock our directories and get our name pointers
959 * assume that the names are null terminated as they
960 * are the end of the path. Get pointers to all our
961 * devfs structures.
962 */
963 tdp = VTODN(tdvp);
964 fdp = VTODN(fdvp);
965 fp = VTODN(fvp);
966 fnp = fp->dn_last_lookup;
967 tp = NULL;
968 tnp = NULL;
969 if (tvp) {
970 tp = VTODN(tvp);
971 tnp = tp->dn_last_lookup;
972 }
973
974 /*
975 * trying to move it out of devfs?
976 * if we move a dir across mnt points. we need to fix all
977 * the mountpoint pointers! XXX
978 * so for now keep dirs within the same mount
979 */
980 if ((fvp->v_mount != tdvp->v_mount) ||
981 (tvp && (fvp->v_mount != tvp->v_mount))) {
982 error = EXDEV;
983abortit:
984 VOP_ABORTOP(tdvp, tcnp);
985 if (tdvp == tvp) /* eh? */
986 vrele(tdvp);
987 else
988 vput(tdvp);
989 if (tvp)
990 vput(tvp);
991 VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
992 vrele(fdvp);
993 vrele(fvp);
994 return (error);
995 }
996
997 /*
998 * Check we are doing legal things WRT the new flags
999 */
1000 if ((tp && (tp->dn_flags & (IMMUTABLE | APPEND)))
1001 || (fp->dn_flags & (IMMUTABLE | APPEND))
1002 || (fdp->dn_flags & APPEND)) {
1003 error = EPERM;
1004 goto abortit;
1005 }
1006
1007 /*
1008 * Make sure that we don't try do something stupid
1009 */
1010 if ((fp->dn_type) == DEV_DIR) {
1011 /*
1012 * Avoid ".", "..", and aliases of "." for obvious reasons.
1013 */
1014 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
1015 || (fcnp->cn_flags&ISDOTDOT)
1016 || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.')
1017 || (tcnp->cn_flags&ISDOTDOT)
1018 || (tdp == fp )) {
1019 error = EINVAL;
1020 goto abortit;
1021 }
1022 doingdirectory++;
1023 }
1024
1025 /*
1026 * If ".." must be changed (ie the directory gets a new
1027 * parent) then the source directory must not be in the
1028 * directory hierarchy above the target, as this would
1029 * orphan everything below the source directory. Also
1030 * the user must have write permission in the source so
1031 * as to be able to change "..".
1032 */
1033 if (doingdirectory && (tdp != fdp)) {
1034 devnode_t * tmp, *ntmp;
1035 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
1036 tmp = tdp;
1037 do {
1038 if(tmp == fp) {
1039 /* XXX unlock stuff here probably */
1040 error = EINVAL;
1041 goto out;
1042 }
1043 ntmp = tmp;
1044 } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
1045 }
1046
1047 /***********************************
1048 * Start actually doing things.... *
1049 ***********************************/
1050 fp->dn_flags |= DN_CHANGE;
1051 tv = time;
1052 if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) {
1053 VOP_UNLOCK(fvp, 0, p);
1054 goto bad;
1055 }
1056 /*
1057 * Check if just deleting a link name.
1058 */
1059 if (fvp == tvp) {
1060 if (fvp->v_type == VDIR) {
1061 error = EINVAL;
1062 goto abortit;
1063 }
1064
1065 /* Release destination completely. */
1066 VOP_ABORTOP(tdvp, tcnp);
1067 vput(tdvp);
1068 vput(tvp);
1069
1070 /* Delete source. */
1071 VOP_ABORTOP(fdvp, fcnp); /*XXX*/
1072 vrele(fdvp);
1073 vrele(fvp);
1074 dev_free_name(fnp);
1075 return 0;
1076 }
1077
1078 vrele(fdvp);
1079
1080 /*
1081 * 1) Bump link count while we're moving stuff
1082 * around. If we crash somewhere before
1083 * completing our work, too bad :)
1084 */
1085 fp->dn_links++;
1086 /*
1087 * If the target exists zap it (unless it's a non-empty directory)
1088 * We could do that as well but won't
1089 */
1090 if (tp) {
1091 int ouruid = tcnp->cn_cred->cr_uid;
1092 /*
1093 * If the parent directory is "sticky", then the user must
1094 * own the parent directory, or the destination of the rename,
1095 * otherwise the destination may not be changed (except by
1096 * root). This implements append-only directories.
1097 * XXX shoudn't this be in generic code?
1098 */
1099 if ((tdp->dn_mode & S_ISTXT)
1100 && ouruid != 0
1101 && ouruid != tdp->dn_uid
1102 && ouruid != tp->dn_uid ) {
1103 error = EPERM;
1104 goto bad;
1105 }
1106 /*
1107 * Target must be empty if a directory and have no links
1108 * to it. Also, ensure source and target are compatible
1109 * (both directories, or both not directories).
1110 */
1111 if (( doingdirectory) && (tp->dn_links > 2)) {
1112 error = ENOTEMPTY;
1113 goto bad;
1114 }
1115 dev_free_name(tnp);
1116 tp = NULL;
1117 }
1118 dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp);
1119 fnp->de_dnp = NULL;
1120 fp->dn_links--; /* one less link to it.. */
1121 dev_free_name(fnp);
1122 fp->dn_links--; /* we added one earlier*/
1123 if (tdp)
1124 vput(tdvp);
1125 if (tp)
1126 vput(fvp);
1127 vrele(fvp);
1128 return (error);
1129
1130bad:
1131 if (tp)
1132 vput(tvp);
1133 vput(tdvp);
1134out:
1135 if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
1136 fp->dn_links--; /* we added one earlier*/
1137 vput(fvp);
1138 } else
1139 vrele(fvp);
1140 return (error);
1141}
1142
1143static int
1144devfs_symlink(struct vop_symlink_args *ap)
1145 /*struct vop_symlink_args {
1146 struct vnode *a_dvp;
1147 struct vnode **a_vpp;
1148 struct componentname *a_cnp;
1149 struct vattr *a_vap;
1150 char *a_target;
1151 } */
1152{
55e303ae 1153 struct componentname * cnp = ap->a_cnp;
1c79356b
A
1154 struct vnode *vp = NULL;
1155 int error = 0;
1156 devnode_t * dir_p;
1157 devnode_type_t typeinfo;
1158 devdirent_t * nm_p;
1159 devnode_t * dev_p;
1160 struct vattr * vap = ap->a_vap;
1161 struct vnode * * vpp = ap->a_vpp;
1162 struct proc *p = cnp->cn_proc;
1163 struct timeval tv;
1164
1165 dir_p = VTODN(ap->a_dvp);
1166 typeinfo.Slnk.name = ap->a_target;
1167 typeinfo.Slnk.namelen = strlen(ap->a_target);
1168 DEVFS_LOCK(p);
1169 error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK,
1170 &typeinfo, NULL, NULL, &nm_p);
1171 DEVFS_UNLOCK(p);
1172 if (error) {
1173 goto failure;
1174 }
1175
1176 dev_p = nm_p->de_dnp;
1177 dev_p->dn_uid = dir_p->dn_uid;
1178 dev_p->dn_gid = dir_p->dn_gid;
1179 dev_p->dn_mode = vap->va_mode;
1180 dn_copy_times(dev_p, dir_p);
1181 error = devfs_dntovn(dev_p, vpp, p);
1182 if (error)
1183 goto failure;
1184 vp = *vpp;
1185 vput(vp);
55e303ae
A
1186failure:
1187 if ((cnp->cn_flags & SAVESTART) == 0) {
1188 char *tmp = cnp->cn_pnbuf;
1189 cnp->cn_pnbuf = NULL;
1190 cnp->cn_flags &= ~HASBUF;
1191 FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI);
1192 }
1c79356b
A
1193 vput(ap->a_dvp);
1194 return error;
1195}
1196
1197/*
1198 * Mknod vnode call
1199 */
1200/* ARGSUSED */
1201int
1202devfs_mknod(ap)
1203 struct vop_mknod_args /* {
1204 struct vnode *a_dvp;
1205 struct vnode **a_vpp;
1206 struct componentname *a_cnp;
1207 struct vattr *a_vap;
1208 } */ *ap;
1209{
1210 struct componentname * cnp = ap->a_cnp;
1211 devnode_t * dev_p;
1212 devdirent_t * devent;
1213 devnode_t * dir_p; /* devnode for parent directory */
1214 struct vnode * dvp = ap->a_dvp;
1215 int error = 0;
1216 devnode_type_t typeinfo;
1217 struct vattr * vap = ap->a_vap;
1218 struct vnode ** vpp = ap->a_vpp;
1219 struct proc * p = cnp->cn_proc;
1220
1221 *vpp = NULL;
1222 if (!vap->va_type == VBLK && !vap->va_type == VCHR) {
1223 error = EINVAL; /* only support mknod of special files */
1224 goto failure;
1225 }
1226 dir_p = VTODN(dvp);
1227 typeinfo.dev = vap->va_rdev;
1228 DEVFS_LOCK(p);
1229 error = dev_add_entry(cnp->cn_nameptr, dir_p,
1230 (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
1231 &typeinfo, NULL, NULL, &devent);
1232 DEVFS_UNLOCK(p);
1233 if (error) {
1234 goto failure;
1235 }
1236 dev_p = devent->de_dnp;
1237 error = devfs_dntovn(dev_p, vpp, p);
1238 if (error)
1239 goto failure;
1240 dev_p->dn_uid = cnp->cn_cred->cr_uid;
1241 dev_p->dn_gid = dir_p->dn_gid;
1242 dev_p->dn_mode = vap->va_mode;
55e303ae 1243failure:
1c79356b
A
1244 if (*vpp) {
1245 vput(*vpp);
1246 *vpp = 0;
1247 }
55e303ae
A
1248 if ((cnp->cn_flags & SAVESTART) == 0) {
1249 char *tmp = cnp->cn_pnbuf;
1250 cnp->cn_pnbuf = NULL;
1251 cnp->cn_flags &= ~HASBUF;
1252 FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI);
1253 }
1c79356b
A
1254 vput(dvp);
1255 return (error);
1256}
1257
1258/*
1259 * Vnode op for readdir
1260 */
1261static int
1262devfs_readdir(struct vop_readdir_args *ap)
1263 /*struct vop_readdir_args {
1264 struct vnode *a_vp;
1265 struct uio *a_uio;
1266 struct ucred *a_cred;
1267 int *eofflag;
1268 int *ncookies;
1269 u_int **cookies;
1270 } */
1271{
1272 struct vnode *vp = ap->a_vp;
1273 struct uio *uio = ap->a_uio;
1274 struct dirent dirent;
1275 devnode_t * dir_node;
1276 devdirent_t * name_node;
1277 char *name;
1278 int error = 0;
1279 int reclen;
1280 int nodenumber;
1281 int startpos,pos;
1282 struct proc * p = uio->uio_procp;
1283
1284 /* set up refs to dir */
1285 dir_node = VTODN(vp);
1286 if(dir_node->dn_type != DEV_DIR)
1287 return(ENOTDIR);
1288
1289 pos = 0;
1290 startpos = uio->uio_offset;
1291 DEVFS_LOCK(p);
1292 name_node = dir_node->dn_typeinfo.Dir.dirlist;
1293 nodenumber = 0;
1294 dir_node->dn_flags |= DN_ACCESS;
1295
1296 while ((name_node || (nodenumber < 2)) && (uio->uio_resid > 0))
1297 {
1298 switch(nodenumber)
1299 {
1300 case 0:
1301 dirent.d_fileno = (int32_t)(void *)dir_node;
1302 name = ".";
1303 dirent.d_namlen = 1;
1304 dirent.d_type = DT_DIR;
1305 break;
1306 case 1:
1307 if(dir_node->dn_typeinfo.Dir.parent)
1308 dirent.d_fileno
1309 = (int32_t)dir_node->dn_typeinfo.Dir.parent;
1310 else
1311 dirent.d_fileno = (u_int32_t)dir_node;
1312 name = "..";
1313 dirent.d_namlen = 2;
1314 dirent.d_type = DT_DIR;
1315 break;
1316 default:
1317 dirent.d_fileno = (int32_t)(void *)name_node->de_dnp;
1318 dirent.d_namlen = strlen(name_node->de_name);
1319 name = name_node->de_name;
1320 switch(name_node->de_dnp->dn_type) {
1321 case DEV_BDEV:
1322 dirent.d_type = DT_BLK;
1323 break;
1324 case DEV_CDEV:
1325 dirent.d_type = DT_CHR;
1326 break;
1327 case DEV_DIR:
1328 dirent.d_type = DT_DIR;
1329 break;
1330 case DEV_SLNK:
1331 dirent.d_type = DT_LNK;
1332 break;
1333 default:
1334 dirent.d_type = DT_UNKNOWN;
1335 }
1336 }
1337#define GENERIC_DIRSIZ(dp) \
1338 ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
1339
1340 reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
1341
1342 if(pos >= startpos) /* made it to the offset yet? */
1343 {
1344 if (uio->uio_resid < reclen) /* will it fit? */
1345 break;
1346 strcpy( dirent.d_name,name);
1347 if ((error = uiomove ((caddr_t)&dirent,
1348 dirent.d_reclen, uio)) != 0)
1349 break;
1350 }
1351 pos += reclen;
1352 if((nodenumber >1) && name_node)
1353 name_node = name_node->de_next;
1354 nodenumber++;
1355 }
1356 DEVFS_UNLOCK(p);
1357 uio->uio_offset = pos;
1358
1359 return (error);
1360}
1361
1362
1363/*
1364 */
1365static int
1366devfs_readlink(struct vop_readlink_args *ap)
1367 /*struct vop_readlink_args {
1368 struct vnode *a_vp;
1369 struct uio *a_uio;
1370 struct ucred *a_cred;
1371 } */
1372{
1373 struct vnode *vp = ap->a_vp;
1374 struct uio *uio = ap->a_uio;
1375 devnode_t * lnk_node;
1376 int error = 0;
1377
1378 /* set up refs to dir */
1379 lnk_node = VTODN(vp);
1380 if(lnk_node->dn_type != DEV_SLNK)
1381 return(EINVAL);
1382 if ((error = VOP_ACCESS(vp, VREAD, ap->a_cred, NULL)) != 0) { /* XXX */
1383 return error;
1384 }
1385 error = uiomove(lnk_node->dn_typeinfo.Slnk.name,
1386 lnk_node->dn_typeinfo.Slnk.namelen, uio);
1387 return error;
1388}
1389
1c79356b
A
1390static int
1391devfs_reclaim(struct vop_reclaim_args *ap)
1392 /*struct vop_reclaim_args {
1393 struct vnode *a_vp;
1394 } */
1395{
1396 struct vnode * vp = ap->a_vp;
1397 devnode_t * dnp = VTODN(vp);
1398
1399 if (dnp) {
1400 /*
1401 * do the same as devfs_inactive in case it is not called
1402 * before us (can that ever happen?)
1403 */
1404 dnp->dn_vn = NULL;
1405 vp->v_data = NULL;
1406 if (dnp->dn_delete) {
1407 devnode_free(dnp);
1408 }
1409 }
1410 return(0);
1411}
1412
1413/*
1414 * Print out the contents of a /devfs vnode.
1415 */
1416static int
1417devfs_print(struct vop_print_args *ap)
1418 /*struct vop_print_args {
1419 struct vnode *a_vp;
1420 } */
1421{
1422
1423 return (0);
1424}
1425
1426/**************************************************************************\
1427* pseudo ops *
1428\**************************************************************************/
1429
1430/*
1431 *
1432 * struct vop_inactive_args {
1433 * struct vnode *a_vp;
1434 * struct proc *a_p;
1435 * }
1436 */
1437
1438static int
1439devfs_inactive(struct vop_inactive_args *ap)
1440{
1441 struct vnode * vp = ap->a_vp;
1442 devnode_t * dnp = VTODN(vp);
1443
1444 if (dnp) {
1445 dnp->dn_vn = NULL;
1446 vp->v_data = NULL;
1447 if (dnp->dn_delete) {
1448 devnode_free(dnp);
1449 }
1450 }
1451 VOP_UNLOCK(vp, 0, ap->a_p);
1452 return (0);
1453}
1454
1455int
1456devfs_update(ap)
1457 struct vop_update_args /* {
1458 struct vnode *a_vp;
1459 struct timeval *a_access;
1460 struct timeval *a_modify;
1461 int a_waitfor;
1462 } */ *ap;
1463{
1464 register struct fs *fs;
1465 int error;
1466 devnode_t * ip;
1467
1468 ip = VTODN(ap->a_vp);
1469 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
1470 ip->dn_flags &=
1471 ~(DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE);
1472 return (0);
1473 }
1474 if ((ip->dn_flags &
1475 (DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE)) == 0)
1476 return (0);
1477 dn_times(ip, time, time);
1478 return (0);
1479}
1480
1481#define VOPFUNC int (*)(void *)
1482
1483/* The following ops are used by directories and symlinks */
1484int (**devfs_vnodeop_p)(void *);
1485static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
1486 { &vop_default_desc, (VOPFUNC)vn_default_error },
1487 { &vop_lookup_desc, (VOPFUNC)devfs_lookup }, /* lookup */
1488 { &vop_create_desc, (VOPFUNC)err_create }, /* create */
1489 { &vop_whiteout_desc, (VOPFUNC)err_whiteout }, /* whiteout */
1490 { &vop_mknod_desc, (VOPFUNC)devfs_mknod }, /* mknod */
1491 { &vop_open_desc, (VOPFUNC)nop_open }, /* open */
1492 { &vop_close_desc, (VOPFUNC)devfs_close }, /* close */
1493 { &vop_access_desc, (VOPFUNC)devfs_access }, /* access */
1494 { &vop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */
1495 { &vop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */
1496 { &vop_read_desc, (VOPFUNC)devfs_read }, /* read */
1497 { &vop_write_desc, (VOPFUNC)devfs_write }, /* write */
1498 { &vop_lease_desc, (VOPFUNC)nop_lease }, /* lease */
1499 { &vop_ioctl_desc, (VOPFUNC)err_ioctl }, /* ioctl */
1500 { &vop_select_desc, (VOPFUNC)err_select }, /* select */
1501 { &vop_revoke_desc, (VOPFUNC)err_revoke }, /* revoke */
1502 { &vop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
1503 { &vop_fsync_desc, (VOPFUNC)nop_fsync }, /* fsync */
1504 { &vop_seek_desc, (VOPFUNC)err_seek }, /* seek */
1505 { &vop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */
1506 { &vop_link_desc, (VOPFUNC)devfs_link }, /* link */
1507 { &vop_rename_desc, (VOPFUNC)devfs_rename }, /* rename */
1508 { &vop_mkdir_desc, (VOPFUNC)err_mkdir }, /* mkdir */
1509 { &vop_rmdir_desc, (VOPFUNC)err_rmdir }, /* rmdir */
1510 { &vop_symlink_desc, (VOPFUNC)devfs_symlink }, /* symlink */
1511 { &vop_readdir_desc, (VOPFUNC)devfs_readdir }, /* readdir */
1512 { &vop_readlink_desc, (VOPFUNC)devfs_readlink }, /* readlink */
55e303ae 1513 { &vop_abortop_desc, (VOPFUNC)nop_abortop }, /* abortop */
1c79356b
A
1514 { &vop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */
1515 { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */
1516 { &vop_lock_desc, (VOPFUNC)nop_lock }, /* lock */
1517 { &vop_unlock_desc, (VOPFUNC)nop_unlock }, /* unlock */
1518 { &vop_bmap_desc, (VOPFUNC)err_bmap }, /* bmap */
1519 { &vop_strategy_desc, (VOPFUNC)err_strategy }, /* strategy */
1520 { &vop_print_desc, (VOPFUNC)err_print }, /* print */
1521 { &vop_islocked_desc, (VOPFUNC)nop_islocked }, /* islocked */
1522 { &vop_pathconf_desc, (VOPFUNC)err_pathconf }, /* pathconf */
1523 { &vop_advlock_desc, (VOPFUNC)err_advlock }, /* advlock */
1524 { &vop_blkatoff_desc, (VOPFUNC)err_blkatoff }, /* blkatoff */
1525 { &vop_valloc_desc, (VOPFUNC)err_valloc }, /* valloc */
1526 { &vop_reallocblks_desc, (VOPFUNC)err_reallocblks }, /* reallocblks */
1527 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
1528 { &vop_truncate_desc, (VOPFUNC)err_truncate }, /* truncate */
1529 { &vop_update_desc, (VOPFUNC)devfs_update }, /* update */
1530 { &vop_bwrite_desc, (VOPFUNC)err_bwrite },
1531 { &vop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */
1532 { &vop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */
1533 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
1534 { &vop_blktooff_desc, (VOPFUNC)err_blktooff }, /* blktooff */
1535 { &vop_offtoblk_desc, (VOPFUNC)err_offtoblk }, /* offtoblk */
1536 { &vop_cmap_desc, (VOPFUNC)err_cmap }, /* cmap */
1537 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1538};
1539struct vnodeopv_desc devfs_vnodeop_opv_desc =
1540 { &devfs_vnodeop_p, devfs_vnodeop_entries };
1541
1542/* The following ops are used by the device nodes */
1543int (**devfs_spec_vnodeop_p)(void *);
1544static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
1545 { &vop_default_desc, (VOPFUNC)vn_default_error },
1546 { &vop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
1547 { &vop_create_desc, (VOPFUNC)spec_create }, /* create */
1548 { &vop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
1549 { &vop_open_desc, (VOPFUNC)spec_open }, /* open */
1550 { &vop_close_desc, (VOPFUNC)devfsspec_close }, /* close */
1551 { &vop_access_desc, (VOPFUNC)devfs_access }, /* access */
1552 { &vop_getattr_desc, (VOPFUNC)devfs_getattr }, /* getattr */
1553 { &vop_setattr_desc, (VOPFUNC)devfs_setattr }, /* setattr */
1554 { &vop_read_desc, (VOPFUNC)devfsspec_read }, /* read */
1555 { &vop_write_desc, (VOPFUNC)devfsspec_write }, /* write */
1556 { &vop_lease_desc, (VOPFUNC)spec_lease_check }, /* lease */
1557 { &vop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
1558 { &vop_select_desc, (VOPFUNC)spec_select }, /* select */
1559 { &vop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
1560 { &vop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
1561 { &vop_fsync_desc, (VOPFUNC)spec_fsync }, /* fsync */
1562 { &vop_seek_desc, (VOPFUNC)spec_seek }, /* seek */
1563 { &vop_remove_desc, (VOPFUNC)devfs_remove }, /* remove */
1564 { &vop_link_desc, (VOPFUNC)devfs_link }, /* link */
1565 { &vop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
1566 { &vop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
1567 { &vop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
1568 { &vop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
1569 { &vop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
1570 { &vop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
1571 { &vop_abortop_desc, (VOPFUNC)spec_abortop }, /* abortop */
1572 { &vop_inactive_desc, (VOPFUNC)devfs_inactive }, /* inactive */
1573 { &vop_reclaim_desc, (VOPFUNC)devfs_reclaim }, /* reclaim */
1574 { &vop_lock_desc, (VOPFUNC)nop_lock }, /* lock */
1575 { &vop_unlock_desc, (VOPFUNC)nop_unlock }, /* unlock */
1576 { &vop_bmap_desc, (VOPFUNC)spec_bmap }, /* bmap */
1577 { &vop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
1578 { &vop_print_desc, (VOPFUNC)devfs_print }, /* print */
1579 { &vop_islocked_desc, (VOPFUNC)nop_islocked }, /* islocked */
1580 { &vop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
1581 { &vop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */
1582 { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff }, /* blkatoff */
1583 { &vop_valloc_desc, (VOPFUNC)spec_valloc }, /* valloc */
1584 { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks }, /* reallocblks */
1585 { &vop_vfree_desc, (VOPFUNC)nop_vfree }, /* vfree */
1586 { &vop_truncate_desc, (VOPFUNC)spec_truncate }, /* truncate */
1587 { &vop_update_desc, (VOPFUNC)devfs_update }, /* update */
1588 { &vop_bwrite_desc, (VOPFUNC)vn_bwrite },
1589 { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
1590 { &vop_pagein_desc, (VOPFUNC)err_pagein }, /* Pagein */
1591 { &vop_pageout_desc, (VOPFUNC)err_pageout }, /* Pageout */
1592 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* Copyfile */
1593 { &vop_blktooff_desc, (VOPFUNC)spec_blktooff }, /* blktooff */
1594 { &vop_blktooff_desc, (VOPFUNC)spec_offtoblk }, /* blkofftoblk */
1595 { &vop_cmap_desc, (VOPFUNC)spec_cmap }, /* cmap */
1596 { (struct vnodeop_desc*)NULL, (int(*)())NULL }
1597};
1598struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
1599 { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };
1600