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