]> git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/devfs/devfs_tree.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / bsd / miscfs / devfs / devfs_tree.c
1 /*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * Copyright 1997,1998 Julian Elischer. All rights reserved.
31 * julian@freebsd.org
32 *
33 * Redistribution and use in source and binary forms, with or without
34 * modification, are permitted provided that the following conditions are
35 * met:
36 * 1. Redistributions of source code must retain the above copyright
37 * notice, this list of conditions and the following disclaimer.
38 * 2. Redistributions in binary form must reproduce the above copyright notice,
39 * this list of conditions and the following disclaimer in the documentation
40 * and/or other materials provided with the distribution.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
43 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
44 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
45 * DISCLAIMED. IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
46 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 *
54 * devfs_tree.c
55 */
56 /*
57 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
58 * support for mandatory and extensible security protections. This notice
59 * is included in support of clause 2.2 (b) of the Apple Public License,
60 * Version 2.0.
61 */
62
63 /*
64 * HISTORY
65 * Dieter Siegmund (dieter@apple.com) Thu Apr 8 14:08:19 PDT 1999
66 * - removed mounting of "hidden" mountpoint
67 * - fixed problem in which devnode->dn_vn pointer was not
68 * updated with the vnode returned from checkalias()
69 * - replaced devfs_vntodn() with a macro VTODN()
70 * - rewrote dev_finddir() to not use recursion
71 * - added locking to avoid data structure corruption (DEVFS_(UN)LOCK())
72 * Dieter Siegmund (dieter@apple.com) Wed Jul 14 13:37:59 PDT 1999
73 * - fixed problem with devfs_dntovn() checking the v_id against the
74 * value cached in the device node; a union mount on top of us causes
75 * the v_id to get incremented thus, we would end up returning a new
76 * vnode instead of the existing one that has the mounted_here
77 * field filled in; the net effect was that the filesystem mounted
78 * on top of us would never show up
79 * - added devfs_stats to store how many data structures are actually
80 * allocated
81 */
82
83 /* SPLIT_DEVS means each devfs uses a different devnode for the same device */
84 /* Otherwise the same device always ends up at the same vnode even if */
85 /* reached througgh a different devfs instance. The practical difference */
86 /* is that with the same vnode, chmods and chowns show up on all instances of */
87 /* a device. (etc) */
88
89 #define SPLIT_DEVS 1 /* maybe make this an option */
90 /*#define SPLIT_DEVS 1*/
91
92 #include <sys/param.h>
93 #include <sys/systm.h>
94 #include <sys/kernel.h>
95 #include <sys/conf.h>
96 #include <sys/malloc.h>
97 #include <sys/mount_internal.h>
98 #include <sys/proc.h>
99 #include <sys/vnode_internal.h>
100 #include <stdarg.h>
101 #include <libkern/OSAtomic.h>
102 #define BSD_KERNEL_PRIVATE 1 /* devfs_make_link() prototype */
103 #include "devfs.h"
104 #include "devfsdefs.h"
105
106 #if CONFIG_MACF
107 #include <security/mac_framework.h>
108 #endif
109
110 #if FDESC
111 #include "fdesc.h"
112 #endif
113
114 typedef struct devfs_vnode_event {
115 vnode_t dve_vp;
116 uint32_t dve_vid;
117 uint32_t dve_events;
118 } *devfs_vnode_event_t;
119
120 /*
121 * Size of stack buffer (fast path) for notifications. If
122 * the number of mounts is small, no need to malloc a buffer.
123 */
124 #define NUM_STACK_ENTRIES 5
125
126 typedef struct devfs_event_log {
127 size_t del_max;
128 size_t del_used;
129 devfs_vnode_event_t del_entries;
130 } *devfs_event_log_t;
131
132
133 static void dev_free_hier(devdirent_t *);
134 static int devfs_propogate(devdirent_t *, devdirent_t *, devfs_event_log_t);
135 static int dev_finddir(const char *, devnode_t *, int, devnode_t **, devfs_event_log_t);
136 static int dev_dup_entry(devnode_t *, devdirent_t *, devdirent_t **, struct devfsmount *);
137 void devfs_ref_node(devnode_t *);
138 void devfs_rele_node(devnode_t *);
139 static void devfs_record_event(devfs_event_log_t, devnode_t*, uint32_t);
140 static int devfs_init_event_log(devfs_event_log_t, uint32_t, devfs_vnode_event_t);
141 static void devfs_release_event_log(devfs_event_log_t, int);
142 static void devfs_bulk_notify(devfs_event_log_t);
143 static devdirent_t *devfs_make_node_internal(dev_t, devfstype_t type, uid_t, gid_t, int,
144 int (*clone)(dev_t dev, int action), const char *fmt, va_list ap);
145
146
147 lck_grp_t * devfs_lck_grp;
148 lck_grp_attr_t * devfs_lck_grp_attr;
149 lck_attr_t * devfs_lck_attr;
150 lck_mtx_t devfs_mutex;
151
152 devdirent_t * dev_root = NULL; /* root of backing tree */
153 struct devfs_stats devfs_stats; /* hold stats */
154
155 static ino_t devfs_unique_fileno = 0;
156
157 #ifdef HIDDEN_MOUNTPOINT
158 static struct mount *devfs_hidden_mount;
159 #endif /* HIDDEN_MOINTPOINT */
160
161 static int devfs_ready = 0;
162 static uint32_t devfs_nmountplanes = 0; /* The first plane is not used for a mount */
163
164 #define DEVFS_NOCREATE FALSE
165 #define DEVFS_CREATE TRUE
166
167 /*
168 * Set up the root directory node in the backing plane
169 * This is happenning before the vfs system has been
170 * set up yet, so be careful about what we reference..
171 * Notice that the ops are by indirection.. as they haven't
172 * been set up yet!
173 * DEVFS has a hidden mountpoint that is used as the anchor point
174 * for the internal 'blueprint' version of the dev filesystem tree.
175 */
176 /*proto*/
177 int
178 devfs_sinit(void)
179 {
180 int error;
181
182 devfs_lck_grp_attr = lck_grp_attr_alloc_init();
183 devfs_lck_grp = lck_grp_alloc_init("devfs_lock", devfs_lck_grp_attr);
184
185 devfs_lck_attr = lck_attr_alloc_init();
186
187 lck_mtx_init(&devfs_mutex, devfs_lck_grp, devfs_lck_attr);
188
189 DEVFS_LOCK();
190 error = dev_add_entry("root", NULL, DEV_DIR, NULL, NULL, NULL, &dev_root);
191 DEVFS_UNLOCK();
192
193 if (error) {
194 printf("devfs_sinit: dev_add_entry failed ");
195 return (ENOTSUP);
196 }
197 #ifdef HIDDEN_MOUNTPOINT
198 MALLOC(devfs_hidden_mount, struct mount *, sizeof(struct mount),
199 M_MOUNT, M_WAITOK);
200 bzero(devfs_hidden_mount,sizeof(struct mount));
201 mount_lock_init(devfs_hidden_mount);
202 TAILQ_INIT(&devfs_hidden_mount->mnt_vnodelist);
203 TAILQ_INIT(&devfs_hidden_mount->mnt_workerqueue);
204 TAILQ_INIT(&devfs_hidden_mount->mnt_newvnodes);
205 #if CONFIG_MACF
206 mac_mount_label_init(devfs_hidden_mount);
207 mac_mount_label_associate(vfs_context_kernel(), devfs_hidden_mount);
208 #endif
209
210 /* Initialize the default IO constraints */
211 mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
212 mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
213 mp->mnt_ioflags = 0;
214 mp->mnt_realrootvp = NULLVP;
215 mp->mnt_authcache_ttl = CACHED_LOOKUP_RIGHT_TTL;
216
217 devfs_mount(devfs_hidden_mount,"dummy",NULL,NULL,NULL);
218 dev_root->de_dnp->dn_dvm
219 = (struct devfsmount *)devfs_hidden_mount->mnt_data;
220 #endif /* HIDDEN_MOUNTPOINT */
221 #if CONFIG_MACF
222 mac_devfs_label_associate_directory("/", strlen("/"),
223 dev_root->de_dnp, "/");
224 #endif
225 devfs_ready = 1;
226 return (0);
227 }
228
229 /***********************************************************************\
230 *************************************************************************
231 * Routines used to find our way to a point in the tree *
232 *************************************************************************
233 \***********************************************************************/
234
235
236
237 /***************************************************************
238 * Search down the linked list off a dir to find "name"
239 * return the devnode_t * for that node.
240 *
241 * called with DEVFS_LOCK held
242 ***************************************************************/
243 devdirent_t *
244 dev_findname(devnode_t * dir, const char *name)
245 {
246 devdirent_t * newfp;
247 if (dir->dn_type != DEV_DIR) return 0;/*XXX*/ /* printf?*/
248
249 if (name[0] == '.')
250 {
251 if(name[1] == 0)
252 {
253 return dir->dn_typeinfo.Dir.myname;
254 }
255 if((name[1] == '.') && (name[2] == 0))
256 {
257 /* for root, .. == . */
258 return dir->dn_typeinfo.Dir.parent->dn_typeinfo.Dir.myname;
259 }
260 }
261 newfp = dir->dn_typeinfo.Dir.dirlist;
262
263 while(newfp)
264 {
265 if(!(strncmp(name, newfp->de_name, sizeof(newfp->de_name))))
266 return newfp;
267 newfp = newfp->de_next;
268 }
269 return NULL;
270 }
271
272 /***********************************************************************
273 * Given a starting node (0 for root) and a pathname, return the node
274 * for the end item on the path. It MUST BE A DIRECTORY. If the 'DEVFS_CREATE'
275 * option is true, then create any missing nodes in the path and create
276 * and return the final node as well.
277 * This is used to set up a directory, before making nodes in it..
278 *
279 * called with DEVFS_LOCK held
280 ***********************************************************************/
281 static int
282 dev_finddir(const char * path,
283 devnode_t * dirnode,
284 int create,
285 devnode_t * * dn_pp,
286 devfs_event_log_t delp)
287 {
288 devnode_t * dnp = NULL;
289 int error = 0;
290 const char * scan;
291 #if CONFIG_MACF
292 char fullpath[DEVMAXPATHSIZE];
293 #endif
294
295
296 if (!dirnode) /* dirnode == NULL means start at root */
297 dirnode = dev_root->de_dnp;
298
299 if (dirnode->dn_type != DEV_DIR)
300 return ENOTDIR;
301
302 if (strlen(path) > (DEVMAXPATHSIZE - 1))
303 return ENAMETOOLONG;
304
305 #if CONFIG_MACF
306 strlcpy (fullpath, path, DEVMAXPATHSIZE);
307 #endif
308 scan = path;
309
310 while (*scan == '/')
311 scan++;
312
313 *dn_pp = NULL;
314
315 while (1) {
316 char component[DEVMAXPATHSIZE];
317 devdirent_t * dirent_p;
318 const char * start;
319
320 if (*scan == 0) {
321 /* we hit the end of the string, we're done */
322 *dn_pp = dirnode;
323 break;
324 }
325 start = scan;
326 while (*scan != '/' && *scan)
327 scan++;
328
329 strlcpy(component, start, scan - start);
330 if (*scan == '/')
331 scan++;
332
333 dirent_p = dev_findname(dirnode, component);
334 if (dirent_p) {
335 dnp = dirent_p->de_dnp;
336 if (dnp->dn_type != DEV_DIR) {
337 error = ENOTDIR;
338 break;
339 }
340 }
341 else {
342 if (!create) {
343 error = ENOENT;
344 break;
345 }
346 error = dev_add_entry(component, dirnode,
347 DEV_DIR, NULL, NULL, NULL, &dirent_p);
348 if (error)
349 break;
350 dnp = dirent_p->de_dnp;
351 #if CONFIG_MACF
352 mac_devfs_label_associate_directory(
353 dirnode->dn_typeinfo.Dir.myname->de_name,
354 strlen(dirnode->dn_typeinfo.Dir.myname->de_name),
355 dnp, fullpath);
356 #endif
357 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, dirent_p, delp);
358 }
359 dirnode = dnp; /* continue relative to this directory */
360 }
361 return (error);
362 }
363
364
365 /***********************************************************************
366 * Add a new NAME element to the devfs
367 * If we're creating a root node, then dirname is NULL
368 * Basically this creates a new namespace entry for the device node
369 *
370 * Creates a name node, and links it to the supplied node
371 *
372 * called with DEVFS_LOCK held
373 ***********************************************************************/
374 int
375 dev_add_name(const char * name, devnode_t * dirnode, __unused devdirent_t * back,
376 devnode_t * dnp, devdirent_t * *dirent_pp)
377 {
378 devdirent_t * dirent_p = NULL;
379
380 if(dirnode != NULL ) {
381 if(dirnode->dn_type != DEV_DIR) return(ENOTDIR);
382
383 if( dev_findname(dirnode,name))
384 return(EEXIST);
385 }
386 /*
387 * make sure the name is legal
388 * slightly misleading in the case of NULL
389 */
390 if (!name || (strlen(name) > (DEVMAXNAMESIZE - 1)))
391 return (ENAMETOOLONG);
392
393 /*
394 * Allocate and fill out a new directory entry
395 */
396 MALLOC(dirent_p, devdirent_t *, sizeof(devdirent_t),
397 M_DEVFSNAME, M_WAITOK);
398 if (!dirent_p) {
399 return ENOMEM;
400 }
401 bzero(dirent_p,sizeof(devdirent_t));
402
403 /* inherrit our parent's mount info */ /*XXX*/
404 /* a kludge but.... */
405 if(dirnode && ( dnp->dn_dvm == NULL)) {
406 dnp->dn_dvm = dirnode->dn_dvm;
407 /* if(!dnp->dn_dvm) printf("parent had null dvm "); */
408 }
409
410 /*
411 * Link the two together
412 * include the implicit link in the count of links to the devnode..
413 * this stops it from being accidentally freed later.
414 */
415 dirent_p->de_dnp = dnp;
416 dnp->dn_links++ ; /* implicit from our own name-node */
417
418 /*
419 * Make sure that we can find all the links that reference a node
420 * so that we can get them all if we need to zap the node.
421 */
422 if(dnp->dn_linklist) {
423 dirent_p->de_nextlink = dnp->dn_linklist;
424 dirent_p->de_prevlinkp = dirent_p->de_nextlink->de_prevlinkp;
425 dirent_p->de_nextlink->de_prevlinkp = &(dirent_p->de_nextlink);
426 *dirent_p->de_prevlinkp = dirent_p;
427 } else {
428 dirent_p->de_nextlink = dirent_p;
429 dirent_p->de_prevlinkp = &(dirent_p->de_nextlink);
430 }
431 dnp->dn_linklist = dirent_p;
432
433 /*
434 * If the node is a directory, then we need to handle the
435 * creation of the .. link.
436 * A NULL dirnode indicates a root node, so point to ourself.
437 */
438 if(dnp->dn_type == DEV_DIR) {
439 dnp->dn_typeinfo.Dir.myname = dirent_p;
440 /*
441 * If we are unlinking from an old dir, decrement its links
442 * as we point our '..' elsewhere
443 * Note: it's up to the calling code to remove the
444 * us from the original directory's list
445 */
446 if(dnp->dn_typeinfo.Dir.parent) {
447 dnp->dn_typeinfo.Dir.parent->dn_links--;
448 }
449 if(dirnode) {
450 dnp->dn_typeinfo.Dir.parent = dirnode;
451 } else {
452 dnp->dn_typeinfo.Dir.parent = dnp;
453 }
454 dnp->dn_typeinfo.Dir.parent->dn_links++; /* account for the new '..' */
455 }
456
457 /*
458 * put the name into the directory entry.
459 */
460 strlcpy(dirent_p->de_name, name, DEVMAXNAMESIZE);
461
462
463 /*
464 * Check if we are not making a root node..
465 * (i.e. have parent)
466 */
467 if(dirnode) {
468 /*
469 * Put it on the END of the linked list of directory entries
470 */
471 dirent_p->de_parent = dirnode; /* null for root */
472 dirent_p->de_prevp = dirnode->dn_typeinfo.Dir.dirlast;
473 dirent_p->de_next = *(dirent_p->de_prevp); /* should be NULL */
474 /*right?*/
475 *(dirent_p->de_prevp) = dirent_p;
476 dirnode->dn_typeinfo.Dir.dirlast = &(dirent_p->de_next);
477 dirnode->dn_typeinfo.Dir.entrycount++;
478 dirnode->dn_len += strlen(name) + 8;/*ok, ok?*/
479 }
480
481 *dirent_pp = dirent_p;
482 DEVFS_INCR_ENTRIES();
483 return 0 ;
484 }
485
486
487 /***********************************************************************
488 * Add a new element to the devfs plane.
489 *
490 * Creates a new dev_node to go with it if the prototype should not be
491 * reused. (Is a DIR, or we select SPLIT_DEVS at compile time)
492 * typeinfo gives us info to make our node if we don't have a prototype.
493 * If typeinfo is null and proto exists, then the typeinfo field of
494 * the proto is used intead in the DEVFS_CREATE case.
495 * note the 'links' count is 0 (except if a dir)
496 * but it is only cleared on a transition
497 * so this is ok till we link it to something
498 * Even in SPLIT_DEVS mode,
499 * if the node already exists on the wanted plane, just return it
500 *
501 * called with DEVFS_LOCK held
502 ***********************************************************************/
503 int
504 dev_add_node(int entrytype, devnode_type_t * typeinfo, devnode_t * proto,
505 devnode_t * *dn_pp, struct devfsmount *dvm)
506 {
507 devnode_t * dnp = NULL;
508
509 #if defined SPLIT_DEVS
510 /*
511 * If we have a prototype, then check if there is already a sibling
512 * on the mount plane we are looking at, if so, just return it.
513 */
514 if (proto) {
515 dnp = proto->dn_nextsibling;
516 while( dnp != proto) {
517 if (dnp->dn_dvm == dvm) {
518 *dn_pp = dnp;
519 return (0);
520 }
521 dnp = dnp->dn_nextsibling;
522 }
523 if (typeinfo == NULL)
524 typeinfo = &(proto->dn_typeinfo);
525 }
526 #else /* SPLIT_DEVS */
527 if ( proto ) {
528 switch (proto->type) {
529 case DEV_BDEV:
530 case DEV_CDEV:
531 *dn_pp = proto;
532 return 0;
533 }
534 }
535 #endif /* SPLIT_DEVS */
536 MALLOC(dnp, devnode_t *, sizeof(devnode_t), M_DEVFSNODE, M_WAITOK);
537 if (!dnp) {
538 return ENOMEM;
539 }
540
541 /*
542 * If we have a proto, that means that we are duplicating some
543 * other device, which can only happen if we are not at the back plane
544 */
545 if (proto) {
546 bcopy(proto, dnp, sizeof(devnode_t));
547 dnp->dn_links = 0;
548 dnp->dn_linklist = NULL;
549 dnp->dn_vn = NULL;
550 dnp->dn_len = 0;
551 /* add to END of siblings list */
552 dnp->dn_prevsiblingp = proto->dn_prevsiblingp;
553 *(dnp->dn_prevsiblingp) = dnp;
554 dnp->dn_nextsibling = proto;
555 proto->dn_prevsiblingp = &(dnp->dn_nextsibling);
556 #if CONFIG_MACF
557 mac_devfs_label_init(dnp);
558 mac_devfs_label_copy(proto->dn_label, dnp->dn_label);
559 #endif
560 } else {
561 struct timeval tv;
562
563 /*
564 * We have no prototype, so start off with a clean slate
565 */
566 microtime(&tv);
567 bzero(dnp, sizeof(devnode_t));
568 dnp->dn_type = entrytype;
569 dnp->dn_nextsibling = dnp;
570 dnp->dn_prevsiblingp = &(dnp->dn_nextsibling);
571 dnp->dn_atime.tv_sec = tv.tv_sec;
572 dnp->dn_mtime.tv_sec = tv.tv_sec;
573 dnp->dn_ctime.tv_sec = tv.tv_sec;
574 #if CONFIG_MACF
575 mac_devfs_label_init(dnp);
576 #endif
577 }
578 dnp->dn_dvm = dvm;
579 dnp->dn_refcount = 0;
580 dnp->dn_ino = devfs_unique_fileno;
581 devfs_unique_fileno++;
582
583 /*
584 * fill out the dev node according to type
585 */
586 switch(entrytype) {
587 case DEV_DIR:
588 /*
589 * As it's a directory, make sure
590 * it has a null entries list
591 */
592 dnp->dn_typeinfo.Dir.dirlast = &(dnp->dn_typeinfo.Dir.dirlist);
593 dnp->dn_typeinfo.Dir.dirlist = (devdirent_t *)0;
594 dnp->dn_typeinfo.Dir.entrycount = 0;
595 /* until we know better, it has a null parent pointer*/
596 dnp->dn_typeinfo.Dir.parent = NULL;
597 dnp->dn_links++; /* for .*/
598 dnp->dn_typeinfo.Dir.myname = NULL;
599 /*
600 * make sure that the ops associated with it are the ops
601 * that we use (by default) for directories
602 */
603 dnp->dn_ops = &devfs_vnodeop_p;
604 dnp->dn_mode |= 0555; /* default perms */
605 break;
606 case DEV_SLNK:
607 /*
608 * As it's a symlink allocate and store the link info
609 * Symlinks should only ever be created by the user,
610 * so they are not on the back plane and should not be
611 * propogated forward.. a bit like directories in that way..
612 * A symlink only exists on one plane and has its own
613 * node.. therefore we might be on any random plane.
614 */
615 MALLOC(dnp->dn_typeinfo.Slnk.name, char *,
616 typeinfo->Slnk.namelen+1,
617 M_DEVFSNODE, M_WAITOK);
618 if (!dnp->dn_typeinfo.Slnk.name) {
619 FREE(dnp,M_DEVFSNODE);
620 return ENOMEM;
621 }
622 strlcpy(dnp->dn_typeinfo.Slnk.name, typeinfo->Slnk.name,
623 typeinfo->Slnk.namelen + 1);
624 dnp->dn_typeinfo.Slnk.namelen = typeinfo->Slnk.namelen;
625 DEVFS_INCR_STRINGSPACE(dnp->dn_typeinfo.Slnk.namelen + 1);
626 dnp->dn_ops = &devfs_vnodeop_p;
627 dnp->dn_mode |= 0555; /* default perms */
628 break;
629 case DEV_CDEV:
630 case DEV_BDEV:
631 /*
632 * Make sure it has DEVICE type ops
633 * and device specific fields are correct
634 */
635 dnp->dn_ops = &devfs_spec_vnodeop_p;
636 dnp->dn_typeinfo.dev = typeinfo->dev;
637 break;
638
639 #if FDESC
640 /* /dev/fd is special */
641 case DEV_DEVFD:
642 dnp->dn_ops = &devfs_devfd_vnodeop_p;
643 dnp->dn_mode |= 0555; /* default perms */
644 break;
645
646 #endif /* FDESC */
647 default:
648 return EINVAL;
649 }
650
651 *dn_pp = dnp;
652 DEVFS_INCR_NODES();
653 return 0 ;
654 }
655
656
657 /***********************************************************************
658 * called with DEVFS_LOCK held
659 **********************************************************************/
660 void
661 devnode_free(devnode_t * dnp)
662 {
663 #if CONFIG_MACF
664 mac_devfs_label_destroy(dnp);
665 #endif
666 if (dnp->dn_type == DEV_SLNK) {
667 DEVFS_DECR_STRINGSPACE(dnp->dn_typeinfo.Slnk.namelen + 1);
668 FREE(dnp->dn_typeinfo.Slnk.name, M_DEVFSNODE);
669 }
670 DEVFS_DECR_NODES();
671 FREE(dnp, M_DEVFSNODE);
672 }
673
674
675 /***********************************************************************
676 * called with DEVFS_LOCK held
677 **********************************************************************/
678 static void
679 devfs_dn_free(devnode_t * dnp)
680 {
681 if(--dnp->dn_links <= 0 ) /* can be -1 for initial free, on error */
682 {
683 /*probably need to do other cleanups XXX */
684 if (dnp->dn_nextsibling != dnp) {
685 devnode_t * * prevp = dnp->dn_prevsiblingp;
686 *prevp = dnp->dn_nextsibling;
687 dnp->dn_nextsibling->dn_prevsiblingp = prevp;
688
689 }
690
691 /* Can only free if there are no references; otherwise, wait for last vnode to be reclaimed */
692 if (dnp->dn_refcount == 0) {
693 devnode_free(dnp);
694 }
695 else {
696 dnp->dn_lflags |= DN_DELETE;
697 }
698 }
699 }
700
701 /***********************************************************************\
702 * Front Node Operations *
703 * Add or delete a chain of front nodes *
704 \***********************************************************************/
705
706
707 /***********************************************************************
708 * Given a directory backing node, and a child backing node, add the
709 * appropriate front nodes to the front nodes of the directory to
710 * represent the child node to the user
711 *
712 * on failure, front nodes will either be correct or not exist for each
713 * front dir, however dirs completed will not be stripped of completed
714 * frontnodes on failure of a later frontnode
715 *
716 * This allows a new node to be propogated through all mounted planes
717 *
718 * called with DEVFS_LOCK held
719 ***********************************************************************/
720 static int
721 devfs_propogate(devdirent_t * parent,devdirent_t * child, devfs_event_log_t delp)
722 {
723 int error;
724 devdirent_t * newnmp;
725 devnode_t * dnp = child->de_dnp;
726 devnode_t * pdnp = parent->de_dnp;
727 devnode_t * adnp = parent->de_dnp;
728 int type = child->de_dnp->dn_type;
729 uint32_t events;
730
731 events = (dnp->dn_type == DEV_DIR ? VNODE_EVENT_DIR_CREATED : VNODE_EVENT_FILE_CREATED);
732 if (delp != NULL) {
733 devfs_record_event(delp, pdnp, events);
734 }
735
736 /***********************************************
737 * Find the other instances of the parent node
738 ***********************************************/
739 for (adnp = pdnp->dn_nextsibling;
740 adnp != pdnp;
741 adnp = adnp->dn_nextsibling)
742 {
743 /*
744 * Make the node, using the original as a prototype)
745 * if the node already exists on that plane it won't be
746 * re-made..
747 */
748 if ((error = dev_add_entry(child->de_name, adnp, type,
749 NULL, dnp, adnp->dn_dvm,
750 &newnmp)) != 0) {
751 printf("duplicating %s failed\n",child->de_name);
752 } else {
753 if (delp != NULL) {
754 devfs_record_event(delp, adnp, events);
755
756 /*
757 * Slightly subtle. We're guaranteed that there will
758 * only be a vnode hooked into this devnode if we're creating
759 * a new link to an existing node; otherwise, the devnode is new
760 * and no one can have looked it up yet. If we're making a link,
761 * then the buffer is large enough for two nodes in each
762 * plane; otherwise, there's no vnode and this call will
763 * do nothing.
764 */
765 devfs_record_event(delp, newnmp->de_dnp, VNODE_EVENT_LINK);
766 }
767 }
768 }
769 return 0; /* for now always succeed */
770 }
771
772 static uint32_t
773 remove_notify_count(devnode_t *dnp)
774 {
775 uint32_t notify_count = 0;
776 devnode_t *dnp2;
777
778 /*
779 * Could need to notify for one removed node on each mount and
780 * one parent for each such node.
781 */
782 notify_count = devfs_nmountplanes;
783 notify_count += dnp->dn_links;
784 for (dnp2 = dnp->dn_nextsibling; dnp2 != dnp; dnp2 = dnp2->dn_nextsibling) {
785 notify_count += dnp2->dn_links;
786 }
787
788 return notify_count;
789
790 }
791
792 /***********************************************************************
793 * remove all instances of this devicename [for backing nodes..]
794 * note.. if there is another link to the node (non dir nodes only)
795 * then the devfs_node will still exist as the ref count will be non-0
796 * removing a directory node will remove all sup-nodes on all planes (ZAP)
797 *
798 * Used by device drivers to remove nodes that are no longer relevant
799 * The argument is the 'cookie' they were given when they created the node
800 * this function is exported.. see devfs.h
801 ***********************************************************************/
802 void
803 devfs_remove(void *dirent_p)
804 {
805 devnode_t * dnp = ((devdirent_t *)dirent_p)->de_dnp;
806 devnode_t * dnp2;
807 boolean_t lastlink;
808 struct devfs_event_log event_log;
809 uint32_t log_count = 0;
810 int do_notify = 0;
811 int need_free = 0;
812 struct devfs_vnode_event stackbuf[NUM_STACK_ENTRIES];
813
814 DEVFS_LOCK();
815
816 if (!devfs_ready) {
817 printf("devfs_remove: not ready for devices!\n");
818 goto out;
819 }
820
821 log_count = remove_notify_count(dnp);
822
823 if (log_count > NUM_STACK_ENTRIES) {
824 uint32_t new_count;
825 wrongsize:
826 DEVFS_UNLOCK();
827 if (devfs_init_event_log(&event_log, log_count, NULL) == 0) {
828 do_notify = 1;
829 need_free = 1;
830 }
831 DEVFS_LOCK();
832
833 new_count = remove_notify_count(dnp);
834 if (need_free && (new_count > log_count)) {
835 devfs_release_event_log(&event_log, 1);
836 need_free = 0;
837 do_notify = 0;
838 log_count = log_count * 2;
839 goto wrongsize;
840 }
841 } else {
842 if (devfs_init_event_log(&event_log, NUM_STACK_ENTRIES, &stackbuf[0]) == 0) {
843 do_notify = 1;
844 }
845 }
846
847 /* This file has been deleted */
848 if (do_notify != 0) {
849 devfs_record_event(&event_log, dnp, VNODE_EVENT_DELETE);
850 }
851
852 /* keep removing the next sibling till only we exist. */
853 while ((dnp2 = dnp->dn_nextsibling) != dnp) {
854
855 /*
856 * Keep removing the next front node till no more exist
857 */
858 dnp->dn_nextsibling = dnp2->dn_nextsibling;
859 dnp->dn_nextsibling->dn_prevsiblingp = &(dnp->dn_nextsibling);
860 dnp2->dn_nextsibling = dnp2;
861 dnp2->dn_prevsiblingp = &(dnp2->dn_nextsibling);
862
863 /* This file has been deleted in this plane */
864 if (do_notify != 0) {
865 devfs_record_event(&event_log, dnp2, VNODE_EVENT_DELETE);
866 }
867
868 if (dnp2->dn_linklist) {
869 do {
870 lastlink = (1 == dnp2->dn_links);
871 /* Each parent of a link to this file has lost a child in this plane */
872 if (do_notify != 0) {
873 devfs_record_event(&event_log, dnp2->dn_linklist->de_parent, VNODE_EVENT_FILE_REMOVED);
874 }
875 dev_free_name(dnp2->dn_linklist);
876 } while (!lastlink);
877 }
878 }
879
880 /*
881 * then free the main node
882 * If we are not running in SPLIT_DEVS mode, then
883 * THIS is what gets rid of the propogated nodes.
884 */
885 if (dnp->dn_linklist) {
886 do {
887 lastlink = (1 == dnp->dn_links);
888 /* Each parent of a link to this file has lost a child */
889 if (do_notify != 0) {
890 devfs_record_event(&event_log, dnp->dn_linklist->de_parent, VNODE_EVENT_FILE_REMOVED);
891 }
892 dev_free_name(dnp->dn_linklist);
893 } while (!lastlink);
894 }
895 out:
896 DEVFS_UNLOCK();
897 if (do_notify != 0) {
898 devfs_bulk_notify(&event_log);
899 devfs_release_event_log(&event_log, need_free);
900 }
901
902 return ;
903 }
904
905
906
907 /***************************************************************
908 * duplicate the backing tree into a tree of nodes hung off the
909 * mount point given as the argument. Do this by
910 * calling dev_dup_entry which recurses all the way
911 * up the tree..
912 *
913 * called with DEVFS_LOCK held
914 **************************************************************/
915 int
916 dev_dup_plane(struct devfsmount *devfs_mp_p)
917 {
918 devdirent_t * new;
919 int error = 0;
920
921 if ((error = dev_dup_entry(NULL, dev_root, &new, devfs_mp_p)))
922 return error;
923 devfs_mp_p->plane_root = new;
924 devfs_nmountplanes++;
925 return error;
926 }
927
928
929
930 /***************************************************************
931 * Free a whole plane
932 *
933 * called with DEVFS_LOCK held
934 ***************************************************************/
935 void
936 devfs_free_plane(struct devfsmount *devfs_mp_p)
937 {
938 devdirent_t * dirent_p;
939
940 dirent_p = devfs_mp_p->plane_root;
941 if (dirent_p) {
942 dev_free_hier(dirent_p);
943 dev_free_name(dirent_p);
944 }
945 devfs_mp_p->plane_root = NULL;
946 devfs_nmountplanes--;
947
948 if (devfs_nmountplanes > (devfs_nmountplanes+1)) {
949 panic("plane count wrapped around.\n");
950 }
951 }
952
953
954 /***************************************************************
955 * Create and link in a new front element..
956 * Parent can be 0 for a root node
957 * Not presently usable to make a symlink XXX
958 * (Ok, symlinks don't propogate)
959 * recursively will create subnodes corresponding to equivalent
960 * child nodes in the base level
961 *
962 * called with DEVFS_LOCK held
963 ***************************************************************/
964 static int
965 dev_dup_entry(devnode_t * parent, devdirent_t * back, devdirent_t * *dnm_pp,
966 struct devfsmount *dvm)
967 {
968 devdirent_t * entry_p;
969 devdirent_t * newback;
970 devdirent_t * newfront;
971 int error;
972 devnode_t * dnp = back->de_dnp;
973 int type = dnp->dn_type;
974
975 /*
976 * go get the node made (if we need to)
977 * use the back one as a prototype
978 */
979 if ((error = dev_add_entry(back->de_name, parent, type,
980 NULL, dnp,
981 parent?parent->dn_dvm:dvm, &entry_p)) != 0) {
982 printf("duplicating %s failed\n",back->de_name);
983 }
984
985 /*
986 * If we have just made the root, then insert the pointer to the
987 * mount information
988 */
989 if(dvm) {
990 entry_p->de_dnp->dn_dvm = dvm;
991 }
992
993 /*
994 * If it is a directory, then recurse down all the other
995 * subnodes in it....
996 * note that this time we don't pass on the mount info..
997 */
998 if (type == DEV_DIR)
999 {
1000 for(newback = back->de_dnp->dn_typeinfo.Dir.dirlist;
1001 newback; newback = newback->de_next)
1002 {
1003 if((error = dev_dup_entry(entry_p->de_dnp,
1004 newback, &newfront, NULL)) != 0)
1005 {
1006 break; /* back out with an error */
1007 }
1008 }
1009 }
1010 *dnm_pp = entry_p;
1011 return error;
1012 }
1013
1014
1015 /***************************************************************
1016 * Free a name node
1017 * remember that if there are other names pointing to the
1018 * dev_node then it may not get freed yet
1019 * can handle if there is no dnp
1020 *
1021 * called with DEVFS_LOCK held
1022 ***************************************************************/
1023
1024 int
1025 dev_free_name(devdirent_t * dirent_p)
1026 {
1027 devnode_t * parent = dirent_p->de_parent;
1028 devnode_t * dnp = dirent_p->de_dnp;
1029
1030 if(dnp) {
1031 if(dnp->dn_type == DEV_DIR)
1032 {
1033 devnode_t * p;
1034
1035 if(dnp->dn_typeinfo.Dir.dirlist)
1036 return (ENOTEMPTY);
1037 p = dnp->dn_typeinfo.Dir.parent;
1038 devfs_dn_free(dnp); /* account for '.' */
1039 devfs_dn_free(p); /* '..' */
1040 }
1041 /*
1042 * unlink us from the list of links for this node
1043 * If we are the only link, it's easy!
1044 * if we are a DIR of course there should not be any
1045 * other links.
1046 */
1047 if(dirent_p->de_nextlink == dirent_p) {
1048 dnp->dn_linklist = NULL;
1049 } else {
1050 if(dnp->dn_linklist == dirent_p) {
1051 dnp->dn_linklist = dirent_p->de_nextlink;
1052 }
1053 dirent_p->de_nextlink->de_prevlinkp
1054 = dirent_p->de_prevlinkp;
1055 *dirent_p->de_prevlinkp = dirent_p->de_nextlink;
1056 }
1057 devfs_dn_free(dnp);
1058 }
1059
1060 /*
1061 * unlink ourselves from the directory on this plane
1062 */
1063 if(parent) /* if not fs root */
1064 {
1065 if( (*dirent_p->de_prevp = dirent_p->de_next) )/* yes, assign */
1066 {
1067 dirent_p->de_next->de_prevp = dirent_p->de_prevp;
1068 }
1069 else
1070 {
1071 parent->dn_typeinfo.Dir.dirlast
1072 = dirent_p->de_prevp;
1073 }
1074 parent->dn_typeinfo.Dir.entrycount--;
1075 parent->dn_len -= strlen(dirent_p->de_name) + 8;
1076 }
1077
1078 DEVFS_DECR_ENTRIES();
1079 FREE(dirent_p, M_DEVFSNAME);
1080 return 0;
1081 }
1082
1083
1084 /***************************************************************
1085 * Free a hierarchy starting at a directory node name
1086 * remember that if there are other names pointing to the
1087 * dev_node then it may not get freed yet
1088 * can handle if there is no dnp
1089 * leave the node itself allocated.
1090 *
1091 * called with DEVFS_LOCK held
1092 ***************************************************************/
1093
1094 static void
1095 dev_free_hier(devdirent_t * dirent_p)
1096 {
1097 devnode_t * dnp = dirent_p->de_dnp;
1098
1099 if(dnp) {
1100 if(dnp->dn_type == DEV_DIR)
1101 {
1102 while(dnp->dn_typeinfo.Dir.dirlist)
1103 {
1104 dev_free_hier(dnp->dn_typeinfo.Dir.dirlist);
1105 dev_free_name(dnp->dn_typeinfo.Dir.dirlist);
1106 }
1107 }
1108 }
1109 }
1110
1111
1112 /***************************************************************
1113 * given a dev_node, find the appropriate vnode if one is already
1114 * associated, or get a new one and associate it with the dev_node
1115 *
1116 * called with DEVFS_LOCK held
1117 *
1118 * If an error is returned, then the dnp may have been freed (we
1119 * raced with a delete and lost). A devnode should not be accessed
1120 * after devfs_dntovn() fails.
1121 ****************************************************************/
1122 int
1123 devfs_dntovn(devnode_t * dnp, struct vnode **vn_pp, __unused struct proc * p)
1124 {
1125 struct vnode *vn_p;
1126 int error = 0;
1127 struct vnode_fsparam vfsp;
1128 enum vtype vtype = 0;
1129 int markroot = 0;
1130 int n_minor = DEVFS_CLONE_ALLOC; /* new minor number for clone device */
1131
1132 /*
1133 * We should never come in and find that our devnode has been marked for delete.
1134 * The lookup should have held the lock from entry until now; it should not have
1135 * been able to find a removed entry. Any other pathway would have just created
1136 * the devnode and come here without dropping the devfs lock, so no one would
1137 * have a chance to delete.
1138 */
1139 if (dnp->dn_lflags & DN_DELETE) {
1140 panic("devfs_dntovn: DN_DELETE set on a devnode upon entry.");
1141 }
1142
1143 devfs_ref_node(dnp);
1144
1145 retry:
1146 *vn_pp = NULL;
1147 vn_p = dnp->dn_vn;
1148
1149 if (vn_p) { /* already has a vnode */
1150 uint32_t vid;
1151
1152 vid = vnode_vid(vn_p);
1153
1154 DEVFS_UNLOCK();
1155
1156 error = vnode_getwithvid(vn_p, vid);
1157
1158 DEVFS_LOCK();
1159
1160 if (dnp->dn_lflags & DN_DELETE) {
1161 /*
1162 * our BUSY node got marked for
1163 * deletion while the DEVFS lock
1164 * was dropped...
1165 */
1166 if (error == 0) {
1167 /*
1168 * vnode_getwithvid returned a valid ref
1169 * which we need to drop
1170 */
1171 vnode_put(vn_p);
1172 }
1173
1174 /*
1175 * This entry is no longer in the namespace. This is only
1176 * possible for lookup: no other path would not find an existing
1177 * vnode. Therefore, ENOENT is a valid result.
1178 */
1179 error = ENOENT;
1180 }
1181 if ( !error)
1182 *vn_pp = vn_p;
1183
1184 goto out;
1185 }
1186
1187 /*
1188 * If we get here, then we've beaten any deletes;
1189 * if someone sets DN_DELETE during a subsequent drop
1190 * of the devfs lock, we'll still vend a vnode.
1191 */
1192
1193 if (dnp->dn_lflags & DN_CREATE) {
1194 dnp->dn_lflags |= DN_CREATEWAIT;
1195 msleep(&dnp->dn_lflags, &devfs_mutex, PRIBIO, 0 , 0);
1196 goto retry;
1197 }
1198
1199 dnp->dn_lflags |= DN_CREATE;
1200
1201 switch (dnp->dn_type) {
1202 case DEV_SLNK:
1203 vtype = VLNK;
1204 break;
1205 case DEV_DIR:
1206 if (dnp->dn_typeinfo.Dir.parent == dnp) {
1207 markroot = 1;
1208 }
1209 vtype = VDIR;
1210 break;
1211 case DEV_BDEV:
1212 case DEV_CDEV:
1213 vtype = (dnp->dn_type == DEV_BDEV) ? VBLK : VCHR;
1214 break;
1215 #if FDESC
1216 case DEV_DEVFD:
1217 vtype = VDIR;
1218 break;
1219 #endif /* FDESC */
1220 }
1221 vfsp.vnfs_mp = dnp->dn_dvm->mount;
1222 vfsp.vnfs_vtype = vtype;
1223 vfsp.vnfs_str = "devfs";
1224 vfsp.vnfs_dvp = 0;
1225 vfsp.vnfs_fsnode = dnp;
1226 vfsp.vnfs_cnp = 0;
1227 vfsp.vnfs_vops = *(dnp->dn_ops);
1228
1229 if (vtype == VBLK || vtype == VCHR) {
1230 /*
1231 * Ask the clone minor number function for a new minor number
1232 * to use for the next device instance. If an administative
1233 * limit has been reached, this function will return -1.
1234 */
1235 if (dnp->dn_clone != NULL) {
1236 int n_major = major(dnp->dn_typeinfo.dev);
1237
1238 n_minor = (*dnp->dn_clone)(dnp->dn_typeinfo.dev, DEVFS_CLONE_ALLOC);
1239 if (n_minor == -1) {
1240 error = ENOMEM;
1241 goto out;
1242 }
1243
1244 vfsp.vnfs_rdev = makedev(n_major, n_minor);;
1245 } else {
1246 vfsp.vnfs_rdev = dnp->dn_typeinfo.dev;
1247 }
1248 } else {
1249 vfsp.vnfs_rdev = 0;
1250 }
1251 vfsp.vnfs_filesize = 0;
1252 vfsp.vnfs_flags = VNFS_NOCACHE | VNFS_CANTCACHE;
1253 /* Tag system files */
1254 vfsp.vnfs_marksystem = 0;
1255 vfsp.vnfs_markroot = markroot;
1256
1257 DEVFS_UNLOCK();
1258
1259 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &vn_p);
1260
1261 /* Do this before grabbing the lock */
1262 if (error == 0) {
1263 vnode_setneedinactive(vn_p);
1264 }
1265
1266 DEVFS_LOCK();
1267
1268 if (error == 0) {
1269 vnode_settag(vn_p, VT_DEVFS);
1270
1271 if ((dnp->dn_clone != NULL) && (dnp->dn_vn != NULLVP) )
1272 panic("devfs_dntovn: cloning device with a vnode?\n");
1273
1274 *vn_pp = vn_p;
1275
1276 /*
1277 * Another vnode that has this devnode as its v_data.
1278 * This reference, unlike the one taken at the start
1279 * of the function, persists until a VNOP_RECLAIM
1280 * comes through for this vnode.
1281 */
1282 devfs_ref_node(dnp);
1283
1284 /*
1285 * A cloned vnode is not hooked into the devnode; every lookup
1286 * gets a new vnode.
1287 */
1288 if (dnp->dn_clone == NULL) {
1289 dnp->dn_vn = vn_p;
1290 }
1291 } else if (n_minor != DEVFS_CLONE_ALLOC) {
1292 /*
1293 * If we failed the create, we need to release the cloned minor
1294 * back to the free list. In general, this is only useful if
1295 * the clone function results in a state change in the cloned
1296 * device for which the minor number was obtained. If we get
1297 * past this point withouth falling into this case, it's
1298 * assumed that any state to be released will be released when
1299 * the vnode is dropped, instead.
1300 */
1301 (void)(*dnp->dn_clone)(dnp->dn_typeinfo.dev, DEVFS_CLONE_FREE);
1302 }
1303
1304 dnp->dn_lflags &= ~DN_CREATE;
1305 if (dnp->dn_lflags & DN_CREATEWAIT) {
1306 dnp->dn_lflags &= ~DN_CREATEWAIT;
1307 wakeup(&dnp->dn_lflags);
1308 }
1309
1310 out:
1311 /*
1312 * Release the reference we took to prevent deletion while we weren't holding the lock.
1313 * If not returning success, then dropping this reference could delete the devnode;
1314 * no one should access a devnode after a call to devfs_dntovn fails.
1315 */
1316 devfs_rele_node(dnp);
1317
1318 return error;
1319 }
1320
1321 /*
1322 * Increment refcount on a devnode; prevents free of the node
1323 * while the devfs lock is not held.
1324 */
1325 void
1326 devfs_ref_node(devnode_t *dnp)
1327 {
1328 dnp->dn_refcount++;
1329 }
1330
1331 /*
1332 * Release a reference on a devnode. If the devnode is marked for
1333 * free and the refcount is dropped to zero, do the free.
1334 */
1335 void
1336 devfs_rele_node(devnode_t *dnp)
1337 {
1338 dnp->dn_refcount--;
1339 if (dnp->dn_refcount < 0) {
1340 panic("devfs_rele_node: devnode with a negative refcount!\n");
1341 } else if ((dnp->dn_refcount == 0) && (dnp->dn_lflags & DN_DELETE)) {
1342 devnode_free(dnp);
1343 }
1344
1345 }
1346
1347 /***********************************************************************
1348 * add a whole device, with no prototype.. make name element and node
1349 * Used for adding the original device entries
1350 *
1351 * called with DEVFS_LOCK held
1352 ***********************************************************************/
1353 int
1354 dev_add_entry(const char *name, devnode_t * parent, int type, devnode_type_t * typeinfo,
1355 devnode_t * proto, struct devfsmount *dvm, devdirent_t * *nm_pp)
1356 {
1357 devnode_t * dnp;
1358 int error = 0;
1359
1360 if ((error = dev_add_node(type, typeinfo, proto, &dnp,
1361 (parent?parent->dn_dvm:dvm))) != 0)
1362 {
1363 printf("devfs: %s: base node allocation failed (Errno=%d)\n",
1364 name,error);
1365 return error;
1366 }
1367 if ((error = dev_add_name(name ,parent ,NULL, dnp, nm_pp)) != 0)
1368 {
1369 devfs_dn_free(dnp); /* 1->0 for dir, 0->(-1) for other */
1370 printf("devfs: %s: name slot allocation failed (Errno=%d)\n",
1371 name,error);
1372
1373 }
1374 return error;
1375 }
1376
1377 static void
1378 devfs_bulk_notify(devfs_event_log_t delp)
1379 {
1380 uint32_t i;
1381 for (i = 0; i < delp->del_used; i++) {
1382 devfs_vnode_event_t dvep = &delp->del_entries[i];
1383 if (vnode_getwithvid(dvep->dve_vp, dvep->dve_vid) == 0) {
1384 vnode_notify(dvep->dve_vp, dvep->dve_events, NULL);
1385 vnode_put(dvep->dve_vp);
1386 }
1387 }
1388 }
1389
1390 static void
1391 devfs_record_event(devfs_event_log_t delp, devnode_t *dnp, uint32_t events)
1392 {
1393 if (delp->del_used >= delp->del_max) {
1394 panic("devfs event log overflowed.\n");
1395 }
1396
1397 /* Can only notify for nodes that have an associated vnode */
1398 if (dnp->dn_vn != NULLVP && vnode_ismonitored(dnp->dn_vn)) {
1399 devfs_vnode_event_t dvep = &delp->del_entries[delp->del_used];
1400 dvep->dve_vp = dnp->dn_vn;
1401 dvep->dve_vid = vnode_vid(dnp->dn_vn);
1402 dvep->dve_events = events;
1403 delp->del_used++;
1404 }
1405 }
1406
1407 static int
1408 devfs_init_event_log(devfs_event_log_t delp, uint32_t count, devfs_vnode_event_t buf)
1409 {
1410 devfs_vnode_event_t dvearr;
1411
1412 if (buf == NULL) {
1413 MALLOC(dvearr, devfs_vnode_event_t, count * sizeof(struct devfs_vnode_event), M_TEMP, M_WAITOK | M_ZERO);
1414 if (dvearr == NULL) {
1415 return ENOMEM;
1416 }
1417 } else {
1418 dvearr = buf;
1419 }
1420
1421 delp->del_max = count;
1422 delp->del_used = 0;
1423 delp->del_entries = dvearr;
1424 return 0;
1425 }
1426
1427 static void
1428 devfs_release_event_log(devfs_event_log_t delp, int need_free)
1429 {
1430 if (delp->del_entries == NULL) {
1431 panic("Free of devfs notify info that has not been intialized.\n");
1432 }
1433
1434 if (need_free) {
1435 FREE(delp->del_entries, M_TEMP);
1436 }
1437
1438 delp->del_entries = NULL;
1439 }
1440
1441 /*
1442 * Function: devfs_make_node
1443 *
1444 * Purpose
1445 * Create a device node with the given pathname in the devfs namespace.
1446 *
1447 * Parameters:
1448 * dev - the dev_t value to associate
1449 * chrblk - block or character device (DEVFS_CHAR or DEVFS_BLOCK)
1450 * uid, gid - ownership
1451 * perms - permissions
1452 * clone - minor number cloning function
1453 * fmt, ... - path format string with printf args to format the path name
1454 * Returns:
1455 * A handle to a device node if successful, NULL otherwise.
1456 */
1457 void *
1458 devfs_make_node_clone(dev_t dev, int chrblk, uid_t uid,
1459 gid_t gid, int perms, int (*clone)(dev_t dev, int action),
1460 const char *fmt, ...)
1461 {
1462 devdirent_t * new_dev = NULL;
1463 devfstype_t type;
1464 va_list ap;
1465
1466 switch (chrblk) {
1467 case DEVFS_CHAR:
1468 type = DEV_CDEV;
1469 break;
1470 case DEVFS_BLOCK:
1471 type = DEV_BDEV;
1472 break;
1473 default:
1474 goto out;
1475 }
1476
1477 va_start(ap, fmt);
1478 new_dev = devfs_make_node_internal(dev, type, uid, gid, perms, clone, fmt, ap);
1479 va_end(ap);
1480 out:
1481 return new_dev;
1482 }
1483
1484
1485 /*
1486 * Function: devfs_make_node
1487 *
1488 * Purpose
1489 * Create a device node with the given pathname in the devfs namespace.
1490 *
1491 * Parameters:
1492 * dev - the dev_t value to associate
1493 * chrblk - block or character device (DEVFS_CHAR or DEVFS_BLOCK)
1494 * uid, gid - ownership
1495 * perms - permissions
1496 * fmt, ... - path format string with printf args to format the path name
1497 * Returns:
1498 * A handle to a device node if successful, NULL otherwise.
1499 */
1500 void *
1501 devfs_make_node(dev_t dev, int chrblk, uid_t uid,
1502 gid_t gid, int perms, const char *fmt, ...)
1503 {
1504 devdirent_t * new_dev = NULL;
1505 devfstype_t type;
1506 va_list ap;
1507
1508 if (chrblk != DEVFS_CHAR && chrblk != DEVFS_BLOCK)
1509 goto out;
1510
1511 type = (chrblk == DEVFS_BLOCK ? DEV_BDEV : DEV_CDEV);
1512
1513 va_start(ap, fmt);
1514 new_dev = devfs_make_node_internal(dev, type, uid, gid, perms, NULL, fmt, ap);
1515 va_end(ap);
1516
1517 out:
1518 return new_dev;
1519 }
1520
1521 static devdirent_t *
1522 devfs_make_node_internal(dev_t dev, devfstype_t type, uid_t uid,
1523 gid_t gid, int perms, int (*clone)(dev_t dev, int action), const char *fmt, va_list ap)
1524 {
1525 devdirent_t * new_dev = NULL;
1526 devnode_t * dnp;
1527 devnode_type_t typeinfo;
1528
1529 char *name, buf[256]; /* XXX */
1530 const char *path;
1531 #if CONFIG_MACF
1532 char buff[sizeof(buf)];
1533 #endif
1534 int i;
1535 uint32_t log_count;
1536 struct devfs_event_log event_log;
1537 struct devfs_vnode_event stackbuf[NUM_STACK_ENTRIES];
1538 int need_free = 0;
1539
1540 vsnprintf(buf, sizeof(buf), fmt, ap);
1541
1542 #if CONFIG_MACF
1543 bcopy(buf, buff, sizeof(buff));
1544 buff[sizeof(buff)-1] = 0;
1545 #endif
1546 name = NULL;
1547
1548 for(i=strlen(buf); i>0; i--)
1549 if(buf[i] == '/') {
1550 name=&buf[i];
1551 buf[i]=0;
1552 break;
1553 }
1554
1555 if (name) {
1556 *name++ = '\0';
1557 path = buf;
1558 } else {
1559 name = buf;
1560 path = "/";
1561 }
1562
1563 log_count = devfs_nmountplanes;
1564 if (log_count > NUM_STACK_ENTRIES) {
1565 wrongsize:
1566 need_free = 1;
1567 if (devfs_init_event_log(&event_log, log_count, NULL) != 0) {
1568 return NULL;
1569 }
1570 } else {
1571 need_free = 0;
1572 log_count = NUM_STACK_ENTRIES;
1573 if (devfs_init_event_log(&event_log, log_count, &stackbuf[0]) != 0) {
1574 return NULL;
1575 }
1576 }
1577
1578 DEVFS_LOCK();
1579 if (log_count < devfs_nmountplanes) {
1580 DEVFS_UNLOCK();
1581 devfs_release_event_log(&event_log, need_free);
1582 log_count = log_count * 2;
1583 goto wrongsize;
1584 }
1585
1586 if (!devfs_ready) {
1587 printf("devfs_make_node: not ready for devices!\n");
1588 goto out;
1589 }
1590
1591 /* find/create directory path ie. mkdir -p */
1592 if (dev_finddir(path, NULL, DEVFS_CREATE, &dnp, &event_log) == 0) {
1593 typeinfo.dev = dev;
1594 if (dev_add_entry(name, dnp, type, &typeinfo, NULL, NULL, &new_dev) == 0) {
1595 new_dev->de_dnp->dn_gid = gid;
1596 new_dev->de_dnp->dn_uid = uid;
1597 new_dev->de_dnp->dn_mode |= perms;
1598 new_dev->de_dnp->dn_clone = clone;
1599 #if CONFIG_MACF
1600 mac_devfs_label_associate_device(dev, new_dev->de_dnp, buff);
1601 #endif
1602 devfs_propogate(dnp->dn_typeinfo.Dir.myname, new_dev, &event_log);
1603 }
1604 }
1605
1606 out:
1607 DEVFS_UNLOCK();
1608
1609 devfs_bulk_notify(&event_log);
1610 devfs_release_event_log(&event_log, need_free);
1611 return new_dev;
1612 }
1613
1614 /*
1615 * Function: devfs_make_link
1616 *
1617 * Purpose:
1618 * Create a link to a previously created device node.
1619 *
1620 * Returns:
1621 * 0 if successful, -1 if failed
1622 */
1623 int
1624 devfs_make_link(void *original, char *fmt, ...)
1625 {
1626 devdirent_t * new_dev = NULL;
1627 devdirent_t * orig = (devdirent_t *) original;
1628 devnode_t * dirnode; /* devnode for parent directory */
1629 struct devfs_event_log event_log;
1630 uint32_t log_count;
1631
1632 va_list ap;
1633 char *p, buf[256]; /* XXX */
1634 int i;
1635
1636 DEVFS_LOCK();
1637
1638 if (!devfs_ready) {
1639 DEVFS_UNLOCK();
1640 printf("devfs_make_link: not ready for devices!\n");
1641 return -1;
1642 }
1643 DEVFS_UNLOCK();
1644
1645 va_start(ap, fmt);
1646 vsnprintf(buf, sizeof(buf), fmt, ap);
1647 va_end(ap);
1648
1649 p = NULL;
1650
1651 for(i=strlen(buf); i>0; i--) {
1652 if(buf[i] == '/') {
1653 p=&buf[i];
1654 buf[i]=0;
1655 break;
1656 }
1657 }
1658
1659 /*
1660 * One slot for each directory, one for each devnode
1661 * whose link count changes
1662 */
1663 log_count = devfs_nmountplanes * 2;
1664 wrongsize:
1665 if (devfs_init_event_log(&event_log, log_count, NULL) != 0) {
1666 /* No lock held, no allocations done, can just return */
1667 return -1;
1668 }
1669
1670 DEVFS_LOCK();
1671
1672 if (log_count < devfs_nmountplanes) {
1673 DEVFS_UNLOCK();
1674 devfs_release_event_log(&event_log, 1);
1675 log_count = log_count * 2;
1676 goto wrongsize;
1677 }
1678
1679 if (p) {
1680 *p++ = '\0';
1681
1682 if (dev_finddir(buf, NULL, DEVFS_CREATE, &dirnode, &event_log)
1683 || dev_add_name(p, dirnode, NULL, orig->de_dnp, &new_dev))
1684 goto fail;
1685 } else {
1686 if (dev_finddir("", NULL, DEVFS_CREATE, &dirnode, &event_log)
1687 || dev_add_name(buf, dirnode, NULL, orig->de_dnp, &new_dev))
1688 goto fail;
1689 }
1690 devfs_propogate(dirnode->dn_typeinfo.Dir.myname, new_dev, &event_log);
1691 fail:
1692 DEVFS_UNLOCK();
1693 devfs_bulk_notify(&event_log);
1694 devfs_release_event_log(&event_log, 1);
1695
1696 return ((new_dev != NULL) ? 0 : -1);
1697 }