xref: /freebsd/sys/fs/devfs/devfs_devs.c (revision d63027b668c055c83bad6191a9986616380c86e4)
1d167cf6fSWarner Losh /*-
2*d63027b6SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*d63027b6SPedro F. Giffuni  *
49d960907SPoul-Henning Kamp  * Copyright (c) 2000,2004
53f54a085SPoul-Henning Kamp  *	Poul-Henning Kamp.  All rights reserved.
63f54a085SPoul-Henning Kamp  *
73f54a085SPoul-Henning Kamp  * Redistribution and use in source and binary forms, with or without
83f54a085SPoul-Henning Kamp  * modification, are permitted provided that the following conditions
93f54a085SPoul-Henning Kamp  * are met:
103f54a085SPoul-Henning Kamp  * 1. Redistributions of source code must retain the above copyright
113f54a085SPoul-Henning Kamp  *    notice, this list of conditions and the following disclaimer.
123f54a085SPoul-Henning Kamp  * 2. Neither the name of the University nor the names of its contributors
133f54a085SPoul-Henning Kamp  *    may be used to endorse or promote products derived from this software
143f54a085SPoul-Henning Kamp  *    without specific prior written permission.
153f54a085SPoul-Henning Kamp  *
163f54a085SPoul-Henning Kamp  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
173f54a085SPoul-Henning Kamp  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
183f54a085SPoul-Henning Kamp  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193f54a085SPoul-Henning Kamp  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
203f54a085SPoul-Henning Kamp  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213f54a085SPoul-Henning Kamp  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
223f54a085SPoul-Henning Kamp  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
233f54a085SPoul-Henning Kamp  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243f54a085SPoul-Henning Kamp  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
253f54a085SPoul-Henning Kamp  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263f54a085SPoul-Henning Kamp  * SUCH DAMAGE.
273f54a085SPoul-Henning Kamp  *
283f54a085SPoul-Henning Kamp  * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
293f54a085SPoul-Henning Kamp  *
303f54a085SPoul-Henning Kamp  * $FreeBSD$
313f54a085SPoul-Henning Kamp  */
323f54a085SPoul-Henning Kamp 
3369921123SKonstantin Belousov #include "opt_compat.h"
3469921123SKonstantin Belousov 
353f54a085SPoul-Henning Kamp #include <sys/param.h>
363f54a085SPoul-Henning Kamp #include <sys/systm.h>
373f54a085SPoul-Henning Kamp #include <sys/conf.h>
38fb919e4dSMark Murray #include <sys/dirent.h>
3993bcdfe2SPoul-Henning Kamp #include <sys/kernel.h>
40e606a3c6SPoul-Henning Kamp #include <sys/limits.h>
4193bcdfe2SPoul-Henning Kamp #include <sys/lock.h>
42fb919e4dSMark Murray #include <sys/malloc.h>
43fb919e4dSMark Murray #include <sys/proc.h>
44e606a3c6SPoul-Henning Kamp #include <sys/sx.h>
45fb919e4dSMark Murray #include <sys/sysctl.h>
46fb919e4dSMark Murray #include <sys/vnode.h>
473f54a085SPoul-Henning Kamp 
48e606a3c6SPoul-Henning Kamp #include <sys/kdb.h>
4993bcdfe2SPoul-Henning Kamp 
503f54a085SPoul-Henning Kamp #include <fs/devfs/devfs.h>
519c0af131SPoul-Henning Kamp #include <fs/devfs/devfs_int.h>
523f54a085SPoul-Henning Kamp 
53aed55708SRobert Watson #include <security/mac/mac_framework.h>
54aed55708SRobert Watson 
55e606a3c6SPoul-Henning Kamp /*
56e606a3c6SPoul-Henning Kamp  * The one true (but secret) list of active devices in the system.
57e606a3c6SPoul-Henning Kamp  * Locked by dev_lock()/devmtx
58e606a3c6SPoul-Henning Kamp  */
59828d6d12SKonstantin Belousov struct cdev_priv_list cdevp_list = TAILQ_HEAD_INITIALIZER(cdevp_list);
6093bcdfe2SPoul-Henning Kamp 
61e606a3c6SPoul-Henning Kamp struct unrhdr *devfs_inos;
62e606a3c6SPoul-Henning Kamp 
63e606a3c6SPoul-Henning Kamp 
64e606a3c6SPoul-Henning Kamp static MALLOC_DEFINE(M_DEVFS2, "DEVFS2", "DEVFS data 2");
65e606a3c6SPoul-Henning Kamp static MALLOC_DEFINE(M_DEVFS3, "DEVFS3", "DEVFS data 3");
66e606a3c6SPoul-Henning Kamp static MALLOC_DEFINE(M_CDEVP, "DEVFS1", "DEVFS cdev_priv storage");
672a043678SPoul-Henning Kamp 
684f9343fcSXin LI SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem");
69e606a3c6SPoul-Henning Kamp 
70e606a3c6SPoul-Henning Kamp static unsigned devfs_generation;
7193bcdfe2SPoul-Henning Kamp SYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
7293bcdfe2SPoul-Henning Kamp 	&devfs_generation, 0, "DEVFS generation number");
7393bcdfe2SPoul-Henning Kamp 
745e080af4SPoul-Henning Kamp unsigned devfs_rule_depth = 1;
755e080af4SPoul-Henning Kamp SYSCTL_UINT(_vfs_devfs, OID_AUTO, rule_depth, CTLFLAG_RW,
765e080af4SPoul-Henning Kamp 	&devfs_rule_depth, 0, "Max depth of ruleset include");
775e080af4SPoul-Henning Kamp 
7831cc57cdSPoul-Henning Kamp /*
79bf2296beSEd Schouten  * Helper sysctl for devname(3).  We're given a dev_t and return the
80bf2296beSEd Schouten  * name, if any, registered by the device driver.
8131cc57cdSPoul-Henning Kamp  */
8231cc57cdSPoul-Henning Kamp static int
8331cc57cdSPoul-Henning Kamp sysctl_devname(SYSCTL_HANDLER_ARGS)
8431cc57cdSPoul-Henning Kamp {
8531cc57cdSPoul-Henning Kamp 	int error;
8631cc57cdSPoul-Henning Kamp 	dev_t ud;
8769921123SKonstantin Belousov #ifdef COMPAT_FREEBSD11
8869921123SKonstantin Belousov 	uint32_t ud_compat;
8969921123SKonstantin Belousov #endif
90e606a3c6SPoul-Henning Kamp 	struct cdev_priv *cdp;
91bf2296beSEd Schouten 	struct cdev *dev;
9231cc57cdSPoul-Henning Kamp 
9369921123SKonstantin Belousov #ifdef COMPAT_FREEBSD11
9469921123SKonstantin Belousov 	if (req->newlen == sizeof(ud_compat)) {
9569921123SKonstantin Belousov 		error = SYSCTL_IN(req, &ud_compat, sizeof(ud_compat));
9669921123SKonstantin Belousov 		if (error == 0)
9769921123SKonstantin Belousov 			ud = ud_compat == (uint32_t)NODEV ? NODEV : ud_compat;
9869921123SKonstantin Belousov 	} else
9969921123SKonstantin Belousov #endif
10031cc57cdSPoul-Henning Kamp 		error = SYSCTL_IN(req, &ud, sizeof (ud));
10131cc57cdSPoul-Henning Kamp 	if (error)
10231cc57cdSPoul-Henning Kamp 		return (error);
10331cc57cdSPoul-Henning Kamp 	if (ud == NODEV)
10431cc57cdSPoul-Henning Kamp 		return (EINVAL);
105bf2296beSEd Schouten 	dev = NULL;
106e606a3c6SPoul-Henning Kamp 	dev_lock();
107e606a3c6SPoul-Henning Kamp 	TAILQ_FOREACH(cdp, &cdevp_list, cdp_list)
108bf2296beSEd Schouten 		if (cdp->cdp_inode == ud) {
109bf2296beSEd Schouten 			dev = &cdp->cdp_c;
110bf2296beSEd Schouten 			dev_refl(dev);
111e606a3c6SPoul-Henning Kamp 			break;
112bf2296beSEd Schouten 		}
113e606a3c6SPoul-Henning Kamp 	dev_unlock();
114bf2296beSEd Schouten 	if (dev == NULL)
11531cc57cdSPoul-Henning Kamp 		return (ENOENT);
116bf2296beSEd Schouten 	error = SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1);
117bf2296beSEd Schouten 	dev_rel(dev);
11831cc57cdSPoul-Henning Kamp 	return (error);
11931cc57cdSPoul-Henning Kamp }
12031cc57cdSPoul-Henning Kamp 
121f3b86a5fSEd Schouten SYSCTL_PROC(_kern, OID_AUTO, devname,
122f3b86a5fSEd Schouten     CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY|CTLFLAG_MPSAFE,
12331cc57cdSPoul-Henning Kamp     NULL, 0, sysctl_devname, "", "devname(3) handler");
12431cc57cdSPoul-Henning Kamp 
12531cc57cdSPoul-Henning Kamp SYSCTL_INT(_debug_sizeof, OID_AUTO, cdev, CTLFLAG_RD,
126f0188618SHans Petter Selasky     SYSCTL_NULL_INT_PTR, sizeof(struct cdev), "sizeof(struct cdev)");
12731cc57cdSPoul-Henning Kamp 
128e606a3c6SPoul-Henning Kamp SYSCTL_INT(_debug_sizeof, OID_AUTO, cdev_priv, CTLFLAG_RD,
129f0188618SHans Petter Selasky     SYSCTL_NULL_INT_PTR, sizeof(struct cdev_priv), "sizeof(struct cdev_priv)");
130e606a3c6SPoul-Henning Kamp 
131e606a3c6SPoul-Henning Kamp struct cdev *
132d2ba618aSKonstantin Belousov devfs_alloc(int flags)
13393bcdfe2SPoul-Henning Kamp {
134e606a3c6SPoul-Henning Kamp 	struct cdev_priv *cdp;
135e606a3c6SPoul-Henning Kamp 	struct cdev *cdev;
136219cc949SEd Schouten 	struct timespec ts;
137e606a3c6SPoul-Henning Kamp 
1386feceb86SKonstantin Belousov 	cdp = malloc(sizeof *cdp, M_CDEVP, M_ZERO |
139d2ba618aSKonstantin Belousov 	    ((flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK));
140d2ba618aSKonstantin Belousov 	if (cdp == NULL)
141d2ba618aSKonstantin Belousov 		return (NULL);
142e606a3c6SPoul-Henning Kamp 
143e606a3c6SPoul-Henning Kamp 	cdp->cdp_dirents = &cdp->cdp_dirent0;
144e606a3c6SPoul-Henning Kamp 
145e606a3c6SPoul-Henning Kamp 	cdev = &cdp->cdp_c;
146e606a3c6SPoul-Henning Kamp 	LIST_INIT(&cdev->si_children);
147219cc949SEd Schouten 	vfs_timestamp(&ts);
148219cc949SEd Schouten 	cdev->si_atime = cdev->si_mtime = cdev->si_ctime = ts;
149219cc949SEd Schouten 
150e606a3c6SPoul-Henning Kamp 	return (cdev);
15193bcdfe2SPoul-Henning Kamp }
15293bcdfe2SPoul-Henning Kamp 
15347bcfb64SJaakko Heinonen int
15447bcfb64SJaakko Heinonen devfs_dev_exists(const char *name)
15547bcfb64SJaakko Heinonen {
15647bcfb64SJaakko Heinonen 	struct cdev_priv *cdp;
15747bcfb64SJaakko Heinonen 
15847bcfb64SJaakko Heinonen 	mtx_assert(&devmtx, MA_OWNED);
15947bcfb64SJaakko Heinonen 
16047bcfb64SJaakko Heinonen 	TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) {
16147bcfb64SJaakko Heinonen 		if ((cdp->cdp_flags & CDP_ACTIVE) == 0)
16247bcfb64SJaakko Heinonen 			continue;
16347bcfb64SJaakko Heinonen 		if (devfs_pathpath(cdp->cdp_c.si_name, name) != 0)
16447bcfb64SJaakko Heinonen 			return (1);
16547bcfb64SJaakko Heinonen 		if (devfs_pathpath(name, cdp->cdp_c.si_name) != 0)
16647bcfb64SJaakko Heinonen 			return (1);
16747bcfb64SJaakko Heinonen 	}
16847bcfb64SJaakko Heinonen 	if (devfs_dir_find(name) != 0)
16947bcfb64SJaakko Heinonen 		return (1);
17047bcfb64SJaakko Heinonen 
17147bcfb64SJaakko Heinonen 	return (0);
17247bcfb64SJaakko Heinonen }
17347bcfb64SJaakko Heinonen 
174e606a3c6SPoul-Henning Kamp void
175e606a3c6SPoul-Henning Kamp devfs_free(struct cdev *cdev)
17693bcdfe2SPoul-Henning Kamp {
177e606a3c6SPoul-Henning Kamp 	struct cdev_priv *cdp;
17893bcdfe2SPoul-Henning Kamp 
17905427aafSKonstantin Belousov 	cdp = cdev2priv(cdev);
180e606a3c6SPoul-Henning Kamp 	if (cdev->si_cred != NULL)
181e606a3c6SPoul-Henning Kamp 		crfree(cdev->si_cred);
182084e62e9SKonstantin Belousov 	devfs_free_cdp_inode(cdp->cdp_inode);
183e606a3c6SPoul-Henning Kamp 	if (cdp->cdp_maxdirent > 0)
184e606a3c6SPoul-Henning Kamp 		free(cdp->cdp_dirents, M_DEVFS2);
185e606a3c6SPoul-Henning Kamp 	free(cdp, M_CDEVP);
18693bcdfe2SPoul-Henning Kamp }
18793bcdfe2SPoul-Henning Kamp 
188e606a3c6SPoul-Henning Kamp struct devfs_dirent *
18964040d39SJaakko Heinonen devfs_find(struct devfs_dirent *dd, const char *name, int namelen, int type)
190c32d0a1dSPoul-Henning Kamp {
191c32d0a1dSPoul-Henning Kamp 	struct devfs_dirent *de;
192c32d0a1dSPoul-Henning Kamp 
193c32d0a1dSPoul-Henning Kamp 	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
194c32d0a1dSPoul-Henning Kamp 		if (namelen != de->de_dirent->d_namlen)
195c32d0a1dSPoul-Henning Kamp 			continue;
19664040d39SJaakko Heinonen 		if (type != 0 && type != de->de_dirent->d_type)
19764040d39SJaakko Heinonen 			continue;
198a57a934aSKonstantin Belousov 
199a57a934aSKonstantin Belousov 		/*
200a57a934aSKonstantin Belousov 		 * The race with finding non-active name is not
201a57a934aSKonstantin Belousov 		 * completely closed by the check, but it is similar
202a57a934aSKonstantin Belousov 		 * to the devfs_allocv() in making it unlikely enough.
203a57a934aSKonstantin Belousov 		 */
204a57a934aSKonstantin Belousov 		if (de->de_dirent->d_type == DT_CHR &&
205a57a934aSKonstantin Belousov 		    (de->de_cdp->cdp_flags & CDP_ACTIVE) == 0)
206a57a934aSKonstantin Belousov 			continue;
207a57a934aSKonstantin Belousov 
208c32d0a1dSPoul-Henning Kamp 		if (bcmp(name, de->de_dirent->d_name, namelen) != 0)
209c32d0a1dSPoul-Henning Kamp 			continue;
210c32d0a1dSPoul-Henning Kamp 		break;
211c32d0a1dSPoul-Henning Kamp 	}
2128570d045SJaakko Heinonen 	KASSERT(de == NULL || (de->de_flags & DE_DOOMED) == 0,
2138570d045SJaakko Heinonen 	    ("devfs_find: returning a doomed entry"));
214c32d0a1dSPoul-Henning Kamp 	return (de);
215c32d0a1dSPoul-Henning Kamp }
216c32d0a1dSPoul-Henning Kamp 
217c32d0a1dSPoul-Henning Kamp struct devfs_dirent *
2183f54a085SPoul-Henning Kamp devfs_newdirent(char *name, int namelen)
2193f54a085SPoul-Henning Kamp {
2203f54a085SPoul-Henning Kamp 	int i;
2213f54a085SPoul-Henning Kamp 	struct devfs_dirent *de;
2223f54a085SPoul-Henning Kamp 	struct dirent d;
2233f54a085SPoul-Henning Kamp 
2243f54a085SPoul-Henning Kamp 	d.d_namlen = namelen;
2253f54a085SPoul-Henning Kamp 	i = sizeof(*de) + GENERIC_DIRSIZ(&d);
226e606a3c6SPoul-Henning Kamp 	de = malloc(i, M_DEVFS3, M_WAITOK | M_ZERO);
2273f54a085SPoul-Henning Kamp 	de->de_dirent = (struct dirent *)(de + 1);
2283f54a085SPoul-Henning Kamp 	de->de_dirent->d_namlen = namelen;
2293f54a085SPoul-Henning Kamp 	de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d);
230aadf2655SPoul-Henning Kamp 	bcopy(name, de->de_dirent->d_name, namelen);
231aadf2655SPoul-Henning Kamp 	de->de_dirent->d_name[namelen] = '\0';
23293432a92SPoul-Henning Kamp 	vfs_timestamp(&de->de_ctime);
233a481b90bSPoul-Henning Kamp 	de->de_mtime = de->de_atime = de->de_ctime;
234a481b90bSPoul-Henning Kamp 	de->de_links = 1;
235e7f9b744SKonstantin Belousov 	de->de_holdcnt = 1;
2366742f328SRobert Watson #ifdef MAC
23730d239bcSRobert Watson 	mac_devfs_init(de);
2386742f328SRobert Watson #endif
2393f54a085SPoul-Henning Kamp 	return (de);
2403f54a085SPoul-Henning Kamp }
2413f54a085SPoul-Henning Kamp 
242a481b90bSPoul-Henning Kamp struct devfs_dirent *
243f40645c8SJaakko Heinonen devfs_parent_dirent(struct devfs_dirent *de)
244f40645c8SJaakko Heinonen {
245f40645c8SJaakko Heinonen 
246f40645c8SJaakko Heinonen 	if (de->de_dirent->d_type != DT_DIR)
247f40645c8SJaakko Heinonen 		return (de->de_dir);
248f40645c8SJaakko Heinonen 
249f40645c8SJaakko Heinonen 	if (de->de_flags & (DE_DOT | DE_DOTDOT))
250f40645c8SJaakko Heinonen 		return (NULL);
251f40645c8SJaakko Heinonen 
252f40645c8SJaakko Heinonen 	de = TAILQ_FIRST(&de->de_dlist);	/* "." */
253f40645c8SJaakko Heinonen 	if (de == NULL)
254f40645c8SJaakko Heinonen 		return (NULL);
255f40645c8SJaakko Heinonen 	de = TAILQ_NEXT(de, de_list);		/* ".." */
256f40645c8SJaakko Heinonen 	if (de == NULL)
257f40645c8SJaakko Heinonen 		return (NULL);
258f40645c8SJaakko Heinonen 
259f40645c8SJaakko Heinonen 	return (de->de_dir);
260f40645c8SJaakko Heinonen }
261f40645c8SJaakko Heinonen 
262f40645c8SJaakko Heinonen struct devfs_dirent *
263cf53034fSKonstantin Belousov devfs_vmkdir(struct devfs_mount *dmp, char *name, int namelen,
264cf53034fSKonstantin Belousov     struct devfs_dirent *dotdot, u_int inode)
2653f54a085SPoul-Henning Kamp {
266a481b90bSPoul-Henning Kamp 	struct devfs_dirent *dd;
2673f54a085SPoul-Henning Kamp 	struct devfs_dirent *de;
2683f54a085SPoul-Henning Kamp 
269e606a3c6SPoul-Henning Kamp 	/* Create the new directory */
270a481b90bSPoul-Henning Kamp 	dd = devfs_newdirent(name, namelen);
271a481b90bSPoul-Henning Kamp 	TAILQ_INIT(&dd->de_dlist);
272a481b90bSPoul-Henning Kamp 	dd->de_dirent->d_type = DT_DIR;
27393432a92SPoul-Henning Kamp 	dd->de_mode = 0555;
274a481b90bSPoul-Henning Kamp 	dd->de_links = 2;
275a481b90bSPoul-Henning Kamp 	dd->de_dir = dd;
276e606a3c6SPoul-Henning Kamp 	if (inode != 0)
277e606a3c6SPoul-Henning Kamp 		dd->de_inode = inode;
278e606a3c6SPoul-Henning Kamp 	else
279e606a3c6SPoul-Henning Kamp 		dd->de_inode = alloc_unr(devfs_inos);
2803f54a085SPoul-Henning Kamp 
28164040d39SJaakko Heinonen 	/*
28264040d39SJaakko Heinonen 	 * "." and ".." are always the two first entries in the
28364040d39SJaakko Heinonen 	 * de_dlist list.
28464040d39SJaakko Heinonen 	 *
28564040d39SJaakko Heinonen 	 * Create the "." entry in the new directory.
28664040d39SJaakko Heinonen 	 */
2873f54a085SPoul-Henning Kamp 	de = devfs_newdirent(".", 1);
2883f54a085SPoul-Henning Kamp 	de->de_dirent->d_type = DT_DIR;
289a481b90bSPoul-Henning Kamp 	de->de_flags |= DE_DOT;
290a481b90bSPoul-Henning Kamp 	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
291e606a3c6SPoul-Henning Kamp 	de->de_dir = dd;
292a481b90bSPoul-Henning Kamp 
29364040d39SJaakko Heinonen 	/* Create the ".." entry in the new directory. */
294a481b90bSPoul-Henning Kamp 	de = devfs_newdirent("..", 2);
295a481b90bSPoul-Henning Kamp 	de->de_dirent->d_type = DT_DIR;
296a481b90bSPoul-Henning Kamp 	de->de_flags |= DE_DOTDOT;
297a481b90bSPoul-Henning Kamp 	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
298e606a3c6SPoul-Henning Kamp 	if (dotdot == NULL) {
299e606a3c6SPoul-Henning Kamp 		de->de_dir = dd;
300e606a3c6SPoul-Henning Kamp 	} else {
301e606a3c6SPoul-Henning Kamp 		de->de_dir = dotdot;
302ef456eecSJaakko Heinonen 		sx_assert(&dmp->dm_lock, SX_XLOCKED);
303e606a3c6SPoul-Henning Kamp 		TAILQ_INSERT_TAIL(&dotdot->de_dlist, dd, de_list);
304e606a3c6SPoul-Henning Kamp 		dotdot->de_links++;
305ef456eecSJaakko Heinonen 		devfs_rules_apply(dmp, dd);
306e606a3c6SPoul-Henning Kamp 	}
307a481b90bSPoul-Henning Kamp 
308e606a3c6SPoul-Henning Kamp #ifdef MAC
30930d239bcSRobert Watson 	mac_devfs_create_directory(dmp->dm_mount, name, namelen, dd);
310e606a3c6SPoul-Henning Kamp #endif
3113f54a085SPoul-Henning Kamp 	return (dd);
3123f54a085SPoul-Henning Kamp }
3133f54a085SPoul-Henning Kamp 
314e606a3c6SPoul-Henning Kamp void
315e7f9b744SKonstantin Belousov devfs_dirent_free(struct devfs_dirent *de)
316e7f9b744SKonstantin Belousov {
3171a2dd035SKonstantin Belousov 	struct vnode *vp;
3181a2dd035SKonstantin Belousov 
3191a2dd035SKonstantin Belousov 	vp = de->de_vnode;
3201a2dd035SKonstantin Belousov 	mtx_lock(&devfs_de_interlock);
3211a2dd035SKonstantin Belousov 	if (vp != NULL && vp->v_data == de)
3221a2dd035SKonstantin Belousov 		vp->v_data = NULL;
3231a2dd035SKonstantin Belousov 	mtx_unlock(&devfs_de_interlock);
324e7f9b744SKonstantin Belousov 	free(de, M_DEVFS3);
325e7f9b744SKonstantin Belousov }
326e7f9b744SKonstantin Belousov 
327828d6d12SKonstantin Belousov /*
32889d10571SJaakko Heinonen  * Removes a directory if it is empty. Also empty parent directories are
32989d10571SJaakko Heinonen  * removed recursively.
33089d10571SJaakko Heinonen  */
33189d10571SJaakko Heinonen static void
33289d10571SJaakko Heinonen devfs_rmdir_empty(struct devfs_mount *dm, struct devfs_dirent *de)
33389d10571SJaakko Heinonen {
33489d10571SJaakko Heinonen 	struct devfs_dirent *dd, *de_dot, *de_dotdot;
33589d10571SJaakko Heinonen 
33689d10571SJaakko Heinonen 	sx_assert(&dm->dm_lock, SX_XLOCKED);
33789d10571SJaakko Heinonen 
33889d10571SJaakko Heinonen 	for (;;) {
33989d10571SJaakko Heinonen 		KASSERT(de->de_dirent->d_type == DT_DIR,
34089d10571SJaakko Heinonen 		    ("devfs_rmdir_empty: de is not a directory"));
34189d10571SJaakko Heinonen 
34289d10571SJaakko Heinonen 		if ((de->de_flags & DE_DOOMED) != 0 || de == dm->dm_rootdir)
34389d10571SJaakko Heinonen 			return;
34489d10571SJaakko Heinonen 
34589d10571SJaakko Heinonen 		de_dot = TAILQ_FIRST(&de->de_dlist);
34689d10571SJaakko Heinonen 		KASSERT(de_dot != NULL, ("devfs_rmdir_empty: . missing"));
34789d10571SJaakko Heinonen 		de_dotdot = TAILQ_NEXT(de_dot, de_list);
34889d10571SJaakko Heinonen 		KASSERT(de_dotdot != NULL, ("devfs_rmdir_empty: .. missing"));
34989d10571SJaakko Heinonen 		/* Return if the directory is not empty. */
35089d10571SJaakko Heinonen 		if (TAILQ_NEXT(de_dotdot, de_list) != NULL)
35189d10571SJaakko Heinonen 			return;
35289d10571SJaakko Heinonen 
35389d10571SJaakko Heinonen 		dd = devfs_parent_dirent(de);
35489d10571SJaakko Heinonen 		KASSERT(dd != NULL, ("devfs_rmdir_empty: NULL dd"));
3558570d045SJaakko Heinonen 		TAILQ_REMOVE(&de->de_dlist, de_dot, de_list);
3568570d045SJaakko Heinonen 		TAILQ_REMOVE(&de->de_dlist, de_dotdot, de_list);
35789d10571SJaakko Heinonen 		TAILQ_REMOVE(&dd->de_dlist, de, de_list);
35889d10571SJaakko Heinonen 		DEVFS_DE_HOLD(dd);
35989d10571SJaakko Heinonen 		devfs_delete(dm, de, DEVFS_DEL_NORECURSE);
36089d10571SJaakko Heinonen 		devfs_delete(dm, de_dot, DEVFS_DEL_NORECURSE);
36189d10571SJaakko Heinonen 		devfs_delete(dm, de_dotdot, DEVFS_DEL_NORECURSE);
36289d10571SJaakko Heinonen 		if (DEVFS_DE_DROP(dd)) {
36389d10571SJaakko Heinonen 			devfs_dirent_free(dd);
36489d10571SJaakko Heinonen 			return;
36589d10571SJaakko Heinonen 		}
36689d10571SJaakko Heinonen 
36789d10571SJaakko Heinonen 		de = dd;
36889d10571SJaakko Heinonen 	}
36989d10571SJaakko Heinonen }
37089d10571SJaakko Heinonen 
37189d10571SJaakko Heinonen /*
372828d6d12SKonstantin Belousov  * The caller needs to hold the dm for the duration of the call since
373828d6d12SKonstantin Belousov  * dm->dm_lock may be temporary dropped.
374828d6d12SKonstantin Belousov  */
375e7f9b744SKonstantin Belousov void
37689d10571SJaakko Heinonen devfs_delete(struct devfs_mount *dm, struct devfs_dirent *de, int flags)
3773f54a085SPoul-Henning Kamp {
37889d10571SJaakko Heinonen 	struct devfs_dirent *dd;
379828d6d12SKonstantin Belousov 	struct vnode *vp;
3803f54a085SPoul-Henning Kamp 
381e7f9b744SKonstantin Belousov 	KASSERT((de->de_flags & DE_DOOMED) == 0,
382e7f9b744SKonstantin Belousov 		("devfs_delete doomed dirent"));
383e7f9b744SKonstantin Belousov 	de->de_flags |= DE_DOOMED;
38489d10571SJaakko Heinonen 
38589d10571SJaakko Heinonen 	if ((flags & DEVFS_DEL_NORECURSE) == 0) {
38689d10571SJaakko Heinonen 		dd = devfs_parent_dirent(de);
38789d10571SJaakko Heinonen 		if (dd != NULL)
38889d10571SJaakko Heinonen 			DEVFS_DE_HOLD(dd);
389d318c565SJaakko Heinonen 		if (de->de_flags & DE_USER) {
390d318c565SJaakko Heinonen 			KASSERT(dd != NULL, ("devfs_delete: NULL dd"));
391d318c565SJaakko Heinonen 			devfs_dir_unref_de(dm, dd);
392d318c565SJaakko Heinonen 		}
39389d10571SJaakko Heinonen 	} else
39489d10571SJaakko Heinonen 		dd = NULL;
39589d10571SJaakko Heinonen 
396828d6d12SKonstantin Belousov 	mtx_lock(&devfs_de_interlock);
397828d6d12SKonstantin Belousov 	vp = de->de_vnode;
398828d6d12SKonstantin Belousov 	if (vp != NULL) {
399828d6d12SKonstantin Belousov 		VI_LOCK(vp);
400828d6d12SKonstantin Belousov 		mtx_unlock(&devfs_de_interlock);
401828d6d12SKonstantin Belousov 		vholdl(vp);
402828d6d12SKonstantin Belousov 		sx_unlock(&dm->dm_lock);
40389d10571SJaakko Heinonen 		if ((flags & DEVFS_DEL_VNLOCKED) == 0)
404cb05b60aSAttilio Rao 			vn_lock(vp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY);
405828d6d12SKonstantin Belousov 		else
406828d6d12SKonstantin Belousov 			VI_UNLOCK(vp);
407828d6d12SKonstantin Belousov 		vgone(vp);
40889d10571SJaakko Heinonen 		if ((flags & DEVFS_DEL_VNLOCKED) == 0)
40922db15c0SAttilio Rao 			VOP_UNLOCK(vp, 0);
410828d6d12SKonstantin Belousov 		vdrop(vp);
411828d6d12SKonstantin Belousov 		sx_xlock(&dm->dm_lock);
412828d6d12SKonstantin Belousov 	} else
413828d6d12SKonstantin Belousov 		mtx_unlock(&devfs_de_interlock);
4143f54a085SPoul-Henning Kamp 	if (de->de_symlink) {
415214c8ff0SPoul-Henning Kamp 		free(de->de_symlink, M_DEVFS);
4163f54a085SPoul-Henning Kamp 		de->de_symlink = NULL;
4173f54a085SPoul-Henning Kamp 	}
4186742f328SRobert Watson #ifdef MAC
41930d239bcSRobert Watson 	mac_devfs_destroy(de);
4206742f328SRobert Watson #endif
421e606a3c6SPoul-Henning Kamp 	if (de->de_inode > DEVFS_ROOTINO) {
422084e62e9SKonstantin Belousov 		devfs_free_cdp_inode(de->de_inode);
423e606a3c6SPoul-Henning Kamp 		de->de_inode = 0;
424e606a3c6SPoul-Henning Kamp 	}
425e7f9b744SKonstantin Belousov 	if (DEVFS_DE_DROP(de))
426e7f9b744SKonstantin Belousov 		devfs_dirent_free(de);
42789d10571SJaakko Heinonen 
42889d10571SJaakko Heinonen 	if (dd != NULL) {
42989d10571SJaakko Heinonen 		if (DEVFS_DE_DROP(dd))
43089d10571SJaakko Heinonen 			devfs_dirent_free(dd);
43189d10571SJaakko Heinonen 		else
43289d10571SJaakko Heinonen 			devfs_rmdir_empty(dm, dd);
43389d10571SJaakko Heinonen 	}
4343f54a085SPoul-Henning Kamp }
4353f54a085SPoul-Henning Kamp 
436e606a3c6SPoul-Henning Kamp /*
437e606a3c6SPoul-Henning Kamp  * Called on unmount.
438828d6d12SKonstantin Belousov  * Recursively removes the entire tree.
439828d6d12SKonstantin Belousov  * The caller needs to hold the dm for the duration of the call.
440e606a3c6SPoul-Henning Kamp  */
441e606a3c6SPoul-Henning Kamp 
442e606a3c6SPoul-Henning Kamp static void
443e606a3c6SPoul-Henning Kamp devfs_purge(struct devfs_mount *dm, struct devfs_dirent *dd)
4443f54a085SPoul-Henning Kamp {
4453f54a085SPoul-Henning Kamp 	struct devfs_dirent *de;
4463f54a085SPoul-Henning Kamp 
447e606a3c6SPoul-Henning Kamp 	sx_assert(&dm->dm_lock, SX_XLOCKED);
44889d10571SJaakko Heinonen 
44989d10571SJaakko Heinonen 	DEVFS_DE_HOLD(dd);
4503f54a085SPoul-Henning Kamp 	for (;;) {
451d318c565SJaakko Heinonen 		/*
452d318c565SJaakko Heinonen 		 * Use TAILQ_LAST() to remove "." and ".." last.
453d318c565SJaakko Heinonen 		 * We might need ".." to resolve a path in
454d318c565SJaakko Heinonen 		 * devfs_dir_unref_de().
455d318c565SJaakko Heinonen 		 */
456d318c565SJaakko Heinonen 		de = TAILQ_LAST(&dd->de_dlist, devfs_dlist_head);
4573f54a085SPoul-Henning Kamp 		if (de == NULL)
4583f54a085SPoul-Henning Kamp 			break;
459e606a3c6SPoul-Henning Kamp 		TAILQ_REMOVE(&dd->de_dlist, de, de_list);
460d318c565SJaakko Heinonen 		if (de->de_flags & DE_USER)
461d318c565SJaakko Heinonen 			devfs_dir_unref_de(dm, dd);
462e606a3c6SPoul-Henning Kamp 		if (de->de_flags & (DE_DOT | DE_DOTDOT))
46389d10571SJaakko Heinonen 			devfs_delete(dm, de, DEVFS_DEL_NORECURSE);
464e606a3c6SPoul-Henning Kamp 		else if (de->de_dirent->d_type == DT_DIR)
465e606a3c6SPoul-Henning Kamp 			devfs_purge(dm, de);
466e606a3c6SPoul-Henning Kamp 		else
46789d10571SJaakko Heinonen 			devfs_delete(dm, de, DEVFS_DEL_NORECURSE);
4683f54a085SPoul-Henning Kamp 	}
46989d10571SJaakko Heinonen 	if (DEVFS_DE_DROP(dd))
47089d10571SJaakko Heinonen 		devfs_dirent_free(dd);
47189d10571SJaakko Heinonen 	else if ((dd->de_flags & DE_DOOMED) == 0)
47289d10571SJaakko Heinonen 		devfs_delete(dm, dd, DEVFS_DEL_NORECURSE);
4733f54a085SPoul-Henning Kamp }
4743f54a085SPoul-Henning Kamp 
475e606a3c6SPoul-Henning Kamp /*
476e606a3c6SPoul-Henning Kamp  * Each cdev_priv has an array of pointers to devfs_dirent which is indexed
477e606a3c6SPoul-Henning Kamp  * by the mount points dm_idx.
478e606a3c6SPoul-Henning Kamp  * This function extends the array when necessary, taking into account that
479e606a3c6SPoul-Henning Kamp  * the default array is 1 element and not malloc'ed.
480e606a3c6SPoul-Henning Kamp  */
481e606a3c6SPoul-Henning Kamp static void
482e606a3c6SPoul-Henning Kamp devfs_metoo(struct cdev_priv *cdp, struct devfs_mount *dm)
4833f54a085SPoul-Henning Kamp {
484e606a3c6SPoul-Henning Kamp 	struct devfs_dirent **dep;
485e606a3c6SPoul-Henning Kamp 	int siz;
486e606a3c6SPoul-Henning Kamp 
487e606a3c6SPoul-Henning Kamp 	siz = (dm->dm_idx + 1) * sizeof *dep;
488e606a3c6SPoul-Henning Kamp 	dep = malloc(siz, M_DEVFS2, M_WAITOK | M_ZERO);
489e606a3c6SPoul-Henning Kamp 	dev_lock();
490e606a3c6SPoul-Henning Kamp 	if (dm->dm_idx <= cdp->cdp_maxdirent) {
491e606a3c6SPoul-Henning Kamp 		/* We got raced */
492e606a3c6SPoul-Henning Kamp 		dev_unlock();
493e606a3c6SPoul-Henning Kamp 		free(dep, M_DEVFS2);
494e606a3c6SPoul-Henning Kamp 		return;
495e606a3c6SPoul-Henning Kamp 	}
496e606a3c6SPoul-Henning Kamp 	memcpy(dep, cdp->cdp_dirents, (cdp->cdp_maxdirent + 1) * sizeof *dep);
497e606a3c6SPoul-Henning Kamp 	if (cdp->cdp_maxdirent > 0)
498e606a3c6SPoul-Henning Kamp 		free(cdp->cdp_dirents, M_DEVFS2);
499e606a3c6SPoul-Henning Kamp 	cdp->cdp_dirents = dep;
500e606a3c6SPoul-Henning Kamp 	/*
501e606a3c6SPoul-Henning Kamp 	 * XXX: if malloc told us how much we actually got this could
502e606a3c6SPoul-Henning Kamp 	 * XXX: be optimized.
503e606a3c6SPoul-Henning Kamp 	 */
504e606a3c6SPoul-Henning Kamp 	cdp->cdp_maxdirent = dm->dm_idx;
505e606a3c6SPoul-Henning Kamp 	dev_unlock();
506e606a3c6SPoul-Henning Kamp }
507e606a3c6SPoul-Henning Kamp 
508828d6d12SKonstantin Belousov /*
509828d6d12SKonstantin Belousov  * The caller needs to hold the dm for the duration of the call.
510828d6d12SKonstantin Belousov  */
511e606a3c6SPoul-Henning Kamp static int
512e606a3c6SPoul-Henning Kamp devfs_populate_loop(struct devfs_mount *dm, int cleanup)
513e606a3c6SPoul-Henning Kamp {
514e606a3c6SPoul-Henning Kamp 	struct cdev_priv *cdp;
515e606a3c6SPoul-Henning Kamp 	struct devfs_dirent *de;
516ca187878SAlexander Motin 	struct devfs_dirent *dd, *dt;
517e606a3c6SPoul-Henning Kamp 	struct cdev *pdev;
518ca187878SAlexander Motin 	int de_flags, depth, j;
5193f54a085SPoul-Henning Kamp 	char *q, *s;
5203f54a085SPoul-Henning Kamp 
521e606a3c6SPoul-Henning Kamp 	sx_assert(&dm->dm_lock, SX_XLOCKED);
522e606a3c6SPoul-Henning Kamp 	dev_lock();
523e606a3c6SPoul-Henning Kamp 	TAILQ_FOREACH(cdp, &cdevp_list, cdp_list) {
524e606a3c6SPoul-Henning Kamp 
525e606a3c6SPoul-Henning Kamp 		KASSERT(cdp->cdp_dirents != NULL, ("NULL cdp_dirents"));
526e606a3c6SPoul-Henning Kamp 
527e606a3c6SPoul-Henning Kamp 		/*
528e606a3c6SPoul-Henning Kamp 		 * If we are unmounting, or the device has been destroyed,
529e606a3c6SPoul-Henning Kamp 		 * clean up our dirent.
530e606a3c6SPoul-Henning Kamp 		 */
531e606a3c6SPoul-Henning Kamp 		if ((cleanup || !(cdp->cdp_flags & CDP_ACTIVE)) &&
532e606a3c6SPoul-Henning Kamp 		    dm->dm_idx <= cdp->cdp_maxdirent &&
533e606a3c6SPoul-Henning Kamp 		    cdp->cdp_dirents[dm->dm_idx] != NULL) {
534e606a3c6SPoul-Henning Kamp 			de = cdp->cdp_dirents[dm->dm_idx];
535e606a3c6SPoul-Henning Kamp 			cdp->cdp_dirents[dm->dm_idx] = NULL;
536e606a3c6SPoul-Henning Kamp 			KASSERT(cdp == de->de_cdp,
537e606a3c6SPoul-Henning Kamp 			    ("%s %d %s %p %p", __func__, __LINE__,
538e606a3c6SPoul-Henning Kamp 			    cdp->cdp_c.si_name, cdp, de->de_cdp));
539e606a3c6SPoul-Henning Kamp 			KASSERT(de->de_dir != NULL, ("Null de->de_dir"));
540e606a3c6SPoul-Henning Kamp 			dev_unlock();
541e606a3c6SPoul-Henning Kamp 
542e606a3c6SPoul-Henning Kamp 			TAILQ_REMOVE(&de->de_dir->de_dlist, de, de_list);
543e606a3c6SPoul-Henning Kamp 			de->de_cdp = NULL;
544e606a3c6SPoul-Henning Kamp 			de->de_inode = 0;
545828d6d12SKonstantin Belousov 			devfs_delete(dm, de, 0);
546828d6d12SKonstantin Belousov 			dev_lock();
547828d6d12SKonstantin Belousov 			cdp->cdp_inuse--;
548828d6d12SKonstantin Belousov 			dev_unlock();
549e606a3c6SPoul-Henning Kamp 			return (1);
55093bcdfe2SPoul-Henning Kamp 		}
551e606a3c6SPoul-Henning Kamp 		/*
552e606a3c6SPoul-Henning Kamp 	 	 * GC any lingering devices
553e606a3c6SPoul-Henning Kamp 		 */
554e606a3c6SPoul-Henning Kamp 		if (!(cdp->cdp_flags & CDP_ACTIVE)) {
555e606a3c6SPoul-Henning Kamp 			if (cdp->cdp_inuse > 0)
556e606a3c6SPoul-Henning Kamp 				continue;
557e606a3c6SPoul-Henning Kamp 			TAILQ_REMOVE(&cdevp_list, cdp, cdp_list);
558e606a3c6SPoul-Henning Kamp 			dev_unlock();
559e606a3c6SPoul-Henning Kamp 			dev_rel(&cdp->cdp_c);
560e606a3c6SPoul-Henning Kamp 			return (1);
561e606a3c6SPoul-Henning Kamp 		}
562e606a3c6SPoul-Henning Kamp 		/*
563e606a3c6SPoul-Henning Kamp 		 * Don't create any new dirents if we are unmounting
564e606a3c6SPoul-Henning Kamp 		 */
565e606a3c6SPoul-Henning Kamp 		if (cleanup)
566e606a3c6SPoul-Henning Kamp 			continue;
567e606a3c6SPoul-Henning Kamp 		KASSERT((cdp->cdp_flags & CDP_ACTIVE), ("Bogons, I tell ya'!"));
568e606a3c6SPoul-Henning Kamp 
569e606a3c6SPoul-Henning Kamp 		if (dm->dm_idx <= cdp->cdp_maxdirent &&
570e606a3c6SPoul-Henning Kamp 		    cdp->cdp_dirents[dm->dm_idx] != NULL) {
571e606a3c6SPoul-Henning Kamp 			de = cdp->cdp_dirents[dm->dm_idx];
572e606a3c6SPoul-Henning Kamp 			KASSERT(cdp == de->de_cdp, ("inconsistent cdp"));
573a481b90bSPoul-Henning Kamp 			continue;
574a481b90bSPoul-Henning Kamp 		}
575e606a3c6SPoul-Henning Kamp 
576e606a3c6SPoul-Henning Kamp 
577e606a3c6SPoul-Henning Kamp 		cdp->cdp_inuse++;
578e606a3c6SPoul-Henning Kamp 		dev_unlock();
579e606a3c6SPoul-Henning Kamp 
580e606a3c6SPoul-Henning Kamp 		if (dm->dm_idx > cdp->cdp_maxdirent)
581e606a3c6SPoul-Henning Kamp 		        devfs_metoo(cdp, dm);
582e606a3c6SPoul-Henning Kamp 
583d785dfefSPoul-Henning Kamp 		dd = dm->dm_rootdir;
584e606a3c6SPoul-Henning Kamp 		s = cdp->cdp_c.si_name;
585c32d0a1dSPoul-Henning Kamp 		for (;;) {
5863f54a085SPoul-Henning Kamp 			for (q = s; *q != '/' && *q != '\0'; q++)
5873f54a085SPoul-Henning Kamp 				continue;
588c32d0a1dSPoul-Henning Kamp 			if (*q != '/')
589c32d0a1dSPoul-Henning Kamp 				break;
59064040d39SJaakko Heinonen 			de = devfs_find(dd, s, q - s, 0);
591e606a3c6SPoul-Henning Kamp 			if (de == NULL)
592e606a3c6SPoul-Henning Kamp 				de = devfs_vmkdir(dm, s, q - s, dd, 0);
59364040d39SJaakko Heinonen 			else if (de->de_dirent->d_type == DT_LNK) {
59464040d39SJaakko Heinonen 				de = devfs_find(dd, s, q - s, DT_DIR);
59564040d39SJaakko Heinonen 				if (de == NULL)
59664040d39SJaakko Heinonen 					de = devfs_vmkdir(dm, s, q - s, dd, 0);
59764040d39SJaakko Heinonen 				de->de_flags |= DE_COVERED;
59864040d39SJaakko Heinonen 			}
599a481b90bSPoul-Henning Kamp 			s = q + 1;
600a481b90bSPoul-Henning Kamp 			dd = de;
60164040d39SJaakko Heinonen 			KASSERT(dd->de_dirent->d_type == DT_DIR &&
60264040d39SJaakko Heinonen 			    (dd->de_flags & (DE_DOT | DE_DOTDOT)) == 0,
60364040d39SJaakko Heinonen 			    ("%s: invalid directory (si_name=%s)",
60464040d39SJaakko Heinonen 			    __func__, cdp->cdp_c.si_name));
60564040d39SJaakko Heinonen 
6063f54a085SPoul-Henning Kamp 		}
60764040d39SJaakko Heinonen 		de_flags = 0;
60864040d39SJaakko Heinonen 		de = devfs_find(dd, s, q - s, DT_LNK);
60964040d39SJaakko Heinonen 		if (de != NULL)
61064040d39SJaakko Heinonen 			de_flags |= DE_COVERED;
611e606a3c6SPoul-Henning Kamp 
6123f54a085SPoul-Henning Kamp 		de = devfs_newdirent(s, q - s);
613e606a3c6SPoul-Henning Kamp 		if (cdp->cdp_c.si_flags & SI_ALIAS) {
6143f54a085SPoul-Henning Kamp 			de->de_uid = 0;
6153f54a085SPoul-Henning Kamp 			de->de_gid = 0;
61693432a92SPoul-Henning Kamp 			de->de_mode = 0755;
6173f54a085SPoul-Henning Kamp 			de->de_dirent->d_type = DT_LNK;
618e606a3c6SPoul-Henning Kamp 			pdev = cdp->cdp_c.si_parent;
619ca187878SAlexander Motin 			dt = dd;
620ca187878SAlexander Motin 			depth = 0;
621ca187878SAlexander Motin 			while (dt != dm->dm_rootdir &&
622ca187878SAlexander Motin 			    (dt = devfs_parent_dirent(dt)) != NULL)
623ca187878SAlexander Motin 				depth++;
624ca187878SAlexander Motin 			j = depth * 3 + strlen(pdev->si_name) + 1;
625e606a3c6SPoul-Henning Kamp 			de->de_symlink = malloc(j, M_DEVFS, M_WAITOK);
626ca187878SAlexander Motin 			de->de_symlink[0] = 0;
627ca187878SAlexander Motin 			while (depth-- > 0)
628ca187878SAlexander Motin 				strcat(de->de_symlink, "../");
629ca187878SAlexander Motin 			strcat(de->de_symlink, pdev->si_name);
6303f54a085SPoul-Henning Kamp 		} else {
631e606a3c6SPoul-Henning Kamp 			de->de_uid = cdp->cdp_c.si_uid;
632e606a3c6SPoul-Henning Kamp 			de->de_gid = cdp->cdp_c.si_gid;
633e606a3c6SPoul-Henning Kamp 			de->de_mode = cdp->cdp_c.si_mode;
6343f54a085SPoul-Henning Kamp 			de->de_dirent->d_type = DT_CHR;
6353f54a085SPoul-Henning Kamp 		}
63664040d39SJaakko Heinonen 		de->de_flags |= de_flags;
637e606a3c6SPoul-Henning Kamp 		de->de_inode = cdp->cdp_inode;
638e606a3c6SPoul-Henning Kamp 		de->de_cdp = cdp;
6396742f328SRobert Watson #ifdef MAC
64030d239bcSRobert Watson 		mac_devfs_create_device(cdp->cdp_c.si_cred, dm->dm_mount,
641e606a3c6SPoul-Henning Kamp 		    &cdp->cdp_c, de);
6426742f328SRobert Watson #endif
643c32d0a1dSPoul-Henning Kamp 		de->de_dir = dd;
644a481b90bSPoul-Henning Kamp 		TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
645e606a3c6SPoul-Henning Kamp 		devfs_rules_apply(dm, de);
646e606a3c6SPoul-Henning Kamp 		dev_lock();
647e606a3c6SPoul-Henning Kamp 		/* XXX: could check that cdp is still active here */
648e606a3c6SPoul-Henning Kamp 		KASSERT(cdp->cdp_dirents[dm->dm_idx] == NULL,
649e606a3c6SPoul-Henning Kamp 		    ("%s %d\n", __func__, __LINE__));
650e606a3c6SPoul-Henning Kamp 		cdp->cdp_dirents[dm->dm_idx] = de;
651e606a3c6SPoul-Henning Kamp 		KASSERT(de->de_cdp != (void *)0xdeadc0de,
652e606a3c6SPoul-Henning Kamp 		    ("%s %d\n", __func__, __LINE__));
653e606a3c6SPoul-Henning Kamp 		dev_unlock();
654e606a3c6SPoul-Henning Kamp 		return (1);
6553f54a085SPoul-Henning Kamp 	}
656e606a3c6SPoul-Henning Kamp 	dev_unlock();
657e606a3c6SPoul-Henning Kamp 	return (0);
6583f54a085SPoul-Henning Kamp }
659e606a3c6SPoul-Henning Kamp 
660828d6d12SKonstantin Belousov /*
661828d6d12SKonstantin Belousov  * The caller needs to hold the dm for the duration of the call.
662828d6d12SKonstantin Belousov  */
663e606a3c6SPoul-Henning Kamp void
664e606a3c6SPoul-Henning Kamp devfs_populate(struct devfs_mount *dm)
665e606a3c6SPoul-Henning Kamp {
666e047ade9SKonstantin Belousov 	unsigned gen;
667e606a3c6SPoul-Henning Kamp 
668e606a3c6SPoul-Henning Kamp 	sx_assert(&dm->dm_lock, SX_XLOCKED);
669e047ade9SKonstantin Belousov 	gen = devfs_generation;
670e047ade9SKonstantin Belousov 	if (dm->dm_generation == gen)
671e606a3c6SPoul-Henning Kamp 		return;
672e606a3c6SPoul-Henning Kamp 	while (devfs_populate_loop(dm, 0))
673e606a3c6SPoul-Henning Kamp 		continue;
674e047ade9SKonstantin Belousov 	dm->dm_generation = gen;
675e606a3c6SPoul-Henning Kamp }
676e606a3c6SPoul-Henning Kamp 
677828d6d12SKonstantin Belousov /*
678828d6d12SKonstantin Belousov  * The caller needs to hold the dm for the duration of the call.
679828d6d12SKonstantin Belousov  */
680e606a3c6SPoul-Henning Kamp void
681e606a3c6SPoul-Henning Kamp devfs_cleanup(struct devfs_mount *dm)
682e606a3c6SPoul-Henning Kamp {
683e606a3c6SPoul-Henning Kamp 
684e606a3c6SPoul-Henning Kamp 	sx_assert(&dm->dm_lock, SX_XLOCKED);
685e606a3c6SPoul-Henning Kamp 	while (devfs_populate_loop(dm, 1))
686e606a3c6SPoul-Henning Kamp 		continue;
687e606a3c6SPoul-Henning Kamp 	devfs_purge(dm, dm->dm_rootdir);
6883f54a085SPoul-Henning Kamp }
6893f54a085SPoul-Henning Kamp 
6909d960907SPoul-Henning Kamp /*
6919d960907SPoul-Henning Kamp  * devfs_create() and devfs_destroy() are called from kern_conf.c and
6929d960907SPoul-Henning Kamp  * in both cases the devlock() mutex is held, so no further locking
693a2098feaSGabor Kovesdan  * is necessary and no sleeping allowed.
6949d960907SPoul-Henning Kamp  */
6959d960907SPoul-Henning Kamp 
6969285a87eSPoul-Henning Kamp void
69789c9c53dSPoul-Henning Kamp devfs_create(struct cdev *dev)
6983f54a085SPoul-Henning Kamp {
699e606a3c6SPoul-Henning Kamp 	struct cdev_priv *cdp;
70093bcdfe2SPoul-Henning Kamp 
701e606a3c6SPoul-Henning Kamp 	mtx_assert(&devmtx, MA_OWNED);
70205427aafSKonstantin Belousov 	cdp = cdev2priv(dev);
703e606a3c6SPoul-Henning Kamp 	cdp->cdp_flags |= CDP_ACTIVE;
704e606a3c6SPoul-Henning Kamp 	cdp->cdp_inode = alloc_unrl(devfs_inos);
705e606a3c6SPoul-Henning Kamp 	dev_refl(dev);
706e606a3c6SPoul-Henning Kamp 	TAILQ_INSERT_TAIL(&cdevp_list, cdp, cdp_list);
7079d960907SPoul-Henning Kamp 	devfs_generation++;
7083f54a085SPoul-Henning Kamp }
7093f54a085SPoul-Henning Kamp 
7109285a87eSPoul-Henning Kamp void
71189c9c53dSPoul-Henning Kamp devfs_destroy(struct cdev *dev)
7123f54a085SPoul-Henning Kamp {
713e606a3c6SPoul-Henning Kamp 	struct cdev_priv *cdp;
71493bcdfe2SPoul-Henning Kamp 
715e606a3c6SPoul-Henning Kamp 	mtx_assert(&devmtx, MA_OWNED);
71605427aafSKonstantin Belousov 	cdp = cdev2priv(dev);
717e606a3c6SPoul-Henning Kamp 	cdp->cdp_flags &= ~CDP_ACTIVE;
7189d960907SPoul-Henning Kamp 	devfs_generation++;
7193f54a085SPoul-Henning Kamp }
720e606a3c6SPoul-Henning Kamp 
721084e62e9SKonstantin Belousov ino_t
722084e62e9SKonstantin Belousov devfs_alloc_cdp_inode(void)
723084e62e9SKonstantin Belousov {
724084e62e9SKonstantin Belousov 
725084e62e9SKonstantin Belousov 	return (alloc_unr(devfs_inos));
726084e62e9SKonstantin Belousov }
727084e62e9SKonstantin Belousov 
728084e62e9SKonstantin Belousov void
729084e62e9SKonstantin Belousov devfs_free_cdp_inode(ino_t ino)
730084e62e9SKonstantin Belousov {
731084e62e9SKonstantin Belousov 
732084e62e9SKonstantin Belousov 	if (ino > 0)
733084e62e9SKonstantin Belousov 		free_unr(devfs_inos, ino);
734084e62e9SKonstantin Belousov }
735084e62e9SKonstantin Belousov 
736e606a3c6SPoul-Henning Kamp static void
737e606a3c6SPoul-Henning Kamp devfs_devs_init(void *junk __unused)
738e606a3c6SPoul-Henning Kamp {
739e606a3c6SPoul-Henning Kamp 
740e606a3c6SPoul-Henning Kamp 	devfs_inos = new_unrhdr(DEVFS_ROOTINO + 1, INT_MAX, &devmtx);
741e606a3c6SPoul-Henning Kamp }
742e606a3c6SPoul-Henning Kamp 
743e606a3c6SPoul-Henning Kamp SYSINIT(devfs_devs, SI_SUB_DEVFS, SI_ORDER_FIRST, devfs_devs_init, NULL);
744