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