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