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