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