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