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