xref: /freebsd/sys/fs/devfs/devfs_devs.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
1 /*-
2  * Copyright (c) 2000,2004
3  *	Poul-Henning Kamp.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Neither the name of the University nor the names of its contributors
11  *    may be used to endorse or promote products derived from this software
12  *    without specific prior written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
27  *
28  * $FreeBSD$
29  */
30 
31 #include "opt_devfs.h"
32 #include "opt_mac.h"
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/conf.h>
37 #include <sys/dirent.h>
38 #include <sys/kernel.h>
39 #include <sys/lock.h>
40 #include <sys/mac.h>
41 #include <sys/malloc.h>
42 #include <sys/proc.h>
43 #include <sys/sysctl.h>
44 #include <sys/vnode.h>
45 
46 #include <machine/atomic.h>
47 
48 #include <fs/devfs/devfs.h>
49 
50 static struct cdev *devfs_inot[NDEVFSINO];
51 static struct cdev **devfs_overflow;
52 static int devfs_ref[NDEVFSINO];
53 static int *devfs_refoverflow;
54 static int devfs_nextino = 3;
55 static int devfs_numino;
56 static int devfs_topino;
57 static int devfs_noverflowwant = NDEVFSOVERFLOW;
58 static int devfs_noverflow;
59 static unsigned devfs_generation;
60 
61 static struct devfs_dirent *devfs_find (struct devfs_dirent *dd, const char *name, int namelen);
62 
63 static SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem");
64 SYSCTL_UINT(_vfs_devfs, OID_AUTO, noverflow, CTLFLAG_RW,
65 	&devfs_noverflowwant, 0, "Size of DEVFS overflow table");
66 SYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
67 	&devfs_generation, 0, "DEVFS generation number");
68 SYSCTL_UINT(_vfs_devfs, OID_AUTO, inodes, CTLFLAG_RD,
69 	&devfs_numino, 0, "DEVFS inodes");
70 SYSCTL_UINT(_vfs_devfs, OID_AUTO, topinode, CTLFLAG_RD,
71 	&devfs_topino, 0, "DEVFS highest inode#");
72 
73 static int *
74 devfs_itor(int inode)
75 {
76 	if (inode < NDEVFSINO)
77 		return (&devfs_ref[inode]);
78 	else if (inode < NDEVFSINO + devfs_noverflow)
79 		return (&devfs_refoverflow[inode - NDEVFSINO]);
80 	else
81 		panic ("YRK!");
82 }
83 
84 static void
85 devfs_dropref(int inode)
86 {
87 	int *ip;
88 
89 	ip = devfs_itor(inode);
90 	atomic_add_int(ip, -1);
91 }
92 
93 static int
94 devfs_getref(int inode)
95 {
96 	int *ip, i, j;
97 	struct cdev **dp;
98 
99 	ip = devfs_itor(inode);
100 	dp = devfs_itod(inode);
101 	for (;;) {
102 		i = *ip;
103 		j = i + 1;
104 		if (!atomic_cmpset_int(ip, i, j))
105 			continue;
106 		if (*dp != NULL)
107 			return (1);
108 		atomic_add_int(ip, -1);
109 		return(0);
110 	}
111 }
112 
113 struct devfs_dirent **
114 devfs_itode (struct devfs_mount *dm, int inode)
115 {
116 
117 	if (inode < NDEVFSINO)
118 		return (&dm->dm_dirent[inode]);
119 	if (devfs_overflow == NULL)
120 		return (NULL);
121 	if (inode < NDEVFSINO + devfs_noverflow)
122 		return (&dm->dm_overflow[inode - NDEVFSINO]);
123 	return (NULL);
124 }
125 
126 struct cdev **
127 devfs_itod (int inode)
128 {
129 
130 	if (inode < NDEVFSINO)
131 		return (&devfs_inot[inode]);
132 	if (devfs_overflow == NULL)
133 		return (NULL);
134 	if (inode < NDEVFSINO + devfs_noverflow)
135 		return (&devfs_overflow[inode - NDEVFSINO]);
136 	return (NULL);
137 }
138 
139 static struct devfs_dirent *
140 devfs_find(struct devfs_dirent *dd, const char *name, int namelen)
141 {
142 	struct devfs_dirent *de;
143 
144 	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
145 		if (namelen != de->de_dirent->d_namlen)
146 			continue;
147 		if (bcmp(name, de->de_dirent->d_name, namelen) != 0)
148 			continue;
149 		break;
150 	}
151 	return (de);
152 }
153 
154 struct devfs_dirent *
155 devfs_newdirent(char *name, int namelen)
156 {
157 	int i;
158 	struct devfs_dirent *de;
159 	struct dirent d;
160 
161 	d.d_namlen = namelen;
162 	i = sizeof (*de) + GENERIC_DIRSIZ(&d);
163 	MALLOC(de, struct devfs_dirent *, i, M_DEVFS, M_WAITOK | M_ZERO);
164 	de->de_dirent = (struct dirent *)(de + 1);
165 	de->de_dirent->d_namlen = namelen;
166 	de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d);
167 	bcopy(name, de->de_dirent->d_name, namelen);
168 	de->de_dirent->d_name[namelen] = '\0';
169 	vfs_timestamp(&de->de_ctime);
170 	de->de_mtime = de->de_atime = de->de_ctime;
171 	de->de_links = 1;
172 #ifdef MAC
173 	mac_init_devfsdirent(de);
174 #endif
175 	return (de);
176 }
177 
178 struct devfs_dirent *
179 devfs_vmkdir(char *name, int namelen, struct devfs_dirent *dotdot)
180 {
181 	struct devfs_dirent *dd;
182 	struct devfs_dirent *de;
183 
184 	dd = devfs_newdirent(name, namelen);
185 
186 	TAILQ_INIT(&dd->de_dlist);
187 
188 	dd->de_dirent->d_type = DT_DIR;
189 	dd->de_mode = 0555;
190 	dd->de_links = 2;
191 	dd->de_dir = dd;
192 
193 	de = devfs_newdirent(".", 1);
194 	de->de_dirent->d_type = DT_DIR;
195 	de->de_dir = dd;
196 	de->de_flags |= DE_DOT;
197 	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
198 
199 	de = devfs_newdirent("..", 2);
200 	de->de_dirent->d_type = DT_DIR;
201 	if (dotdot == NULL)
202 		de->de_dir = dd;
203 	else
204 		de->de_dir = dotdot;
205 	de->de_flags |= DE_DOTDOT;
206 	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
207 
208 	return (dd);
209 }
210 
211 static void
212 devfs_delete(struct devfs_dirent *dd, struct devfs_dirent *de)
213 {
214 
215 	if (de->de_symlink) {
216 		FREE(de->de_symlink, M_DEVFS);
217 		de->de_symlink = NULL;
218 	}
219 	if (de->de_vnode)
220 		de->de_vnode->v_data = NULL;
221 	TAILQ_REMOVE(&dd->de_dlist, de, de_list);
222 #ifdef MAC
223 	mac_destroy_devfsdirent(de);
224 #endif
225 	FREE(de, M_DEVFS);
226 }
227 
228 void
229 devfs_purge(struct devfs_dirent *dd)
230 {
231 	struct devfs_dirent *de;
232 
233 	for (;;) {
234 		de = TAILQ_FIRST(&dd->de_dlist);
235 		if (de == NULL)
236 			break;
237 		devfs_delete(dd, de);
238 	}
239 	FREE(dd, M_DEVFS);
240 }
241 
242 
243 int
244 devfs_populate(struct devfs_mount *dm)
245 {
246 	int i, j;
247 	struct cdev *dev, *pdev;
248 	struct devfs_dirent *dd;
249 	struct devfs_dirent *de, **dep;
250 	char *q, *s;
251 
252 	if (dm->dm_generation == devfs_generation)
253 		return (0);
254 	lockmgr(&dm->dm_lock, LK_UPGRADE, 0, curthread);
255 	if (devfs_noverflow && dm->dm_overflow == NULL) {
256 		i = devfs_noverflow * sizeof (struct devfs_dirent *);
257 		MALLOC(dm->dm_overflow, struct devfs_dirent **, i,
258 			M_DEVFS, M_WAITOK | M_ZERO);
259 	}
260 	while (dm->dm_generation != devfs_generation) {
261 		dm->dm_generation = devfs_generation;
262 		for (i = 0; i <= devfs_topino; i++) {
263 			dev = *devfs_itod(i);
264 			dep = devfs_itode(dm, i);
265 			de = *dep;
266 			if (dev == NULL && de == DE_DELETED) {
267 				*dep = NULL;
268 				continue;
269 			}
270 			if (dev == NULL && de != NULL) {
271 				dd = de->de_dir;
272 				*dep = NULL;
273 				TAILQ_REMOVE(&dd->de_dlist, de, de_list);
274 				if (de->de_vnode)
275 					de->de_vnode->v_data = NULL;
276 				FREE(de, M_DEVFS);
277 				devfs_dropref(i);
278 				continue;
279 			}
280 			if (dev == NULL)
281 				continue;
282 			if (de != NULL)
283 				continue;
284 			if (!devfs_getref(i))
285 				continue;
286 			dd = dm->dm_basedir;
287 			s = dev->si_name;
288 			for (;;) {
289 				for (q = s; *q != '/' && *q != '\0'; q++)
290 					continue;
291 				if (*q != '/')
292 					break;
293 				de = devfs_find(dd, s, q - s);
294 				if (de == NULL) {
295 					de = devfs_vmkdir(s, q - s, dd);
296 #ifdef MAC
297 					mac_create_devfs_directory(
298 					    dm->dm_mount, s, q - s, de);
299 #endif
300 					de->de_inode = dm->dm_inode++;
301 					TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
302 					dd->de_links++;
303 				}
304 				s = q + 1;
305 				dd = de;
306 			}
307 			de = devfs_newdirent(s, q - s);
308 			if (dev->si_flags & SI_ALIAS) {
309 				de->de_inode = dm->dm_inode++;
310 				de->de_uid = 0;
311 				de->de_gid = 0;
312 				de->de_mode = 0755;
313 				de->de_dirent->d_type = DT_LNK;
314 				pdev = dev->si_parent;
315 				j = strlen(pdev->si_name) + 1;
316 				MALLOC(de->de_symlink, char *, j, M_DEVFS, M_WAITOK);
317 				bcopy(pdev->si_name, de->de_symlink, j);
318 			} else {
319 				de->de_inode = i;
320 				de->de_uid = dev->si_uid;
321 				de->de_gid = dev->si_gid;
322 				de->de_mode = dev->si_mode;
323 				de->de_dirent->d_type = DT_CHR;
324 			}
325 #ifdef MAC
326 			mac_create_devfs_device(dm->dm_mount, dev, de);
327 #endif
328 			*dep = de;
329 			de->de_dir = dd;
330 			devfs_rules_apply(dm, de);
331 			TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
332 		}
333 	}
334 	lockmgr(&dm->dm_lock, LK_DOWNGRADE, 0, curthread);
335 	return (0);
336 }
337 
338 /*
339  * devfs_create() and devfs_destroy() are called from kern_conf.c and
340  * in both cases the devlock() mutex is held, so no further locking
341  * is necesary and no sleeping allowed.
342  */
343 
344 void
345 devfs_create(struct cdev *dev)
346 {
347 	int ino, i, *ip;
348 	struct cdev **dp;
349 	struct cdev **ot;
350 	int *or;
351 	int n;
352 
353 	for (;;) {
354 		/* Grab the next inode number */
355 		ino = devfs_nextino;
356 		i = ino + 1;
357 		/* wrap around when we reach the end */
358 		if (i >= NDEVFSINO + devfs_noverflow)
359 			i = 3;
360 		devfs_nextino = i;
361 
362 		/* see if it was occupied */
363 		dp = devfs_itod(ino);
364 		KASSERT(dp != NULL, ("DEVFS: No devptr inode %d", ino));
365 		if (*dp != NULL)
366 			continue;
367 		ip = devfs_itor(ino);
368 		KASSERT(ip != NULL, ("DEVFS: No iptr inode %d", ino));
369 		if (*ip != 0)
370 			continue;
371 		break;
372 	}
373 
374 	*dp = dev;
375 	dev->si_inode = ino;
376 	if (i > devfs_topino)
377 		devfs_topino = i;
378 
379 	devfs_numino++;
380 	devfs_generation++;
381 
382 	if (devfs_overflow != NULL || devfs_numino + 100 < NDEVFSINO)
383 		return;
384 
385 	/*
386 	 * Try to allocate overflow table
387 	 * XXX: we can probably be less panicy these days and a linked
388 	 * XXX: list of PAGESIZE/PTRSIZE entries might be a better idea.
389 	 *
390 	 * XXX: we may be into witness unlove here.
391 	 */
392 	n = devfs_noverflowwant;
393 	ot = malloc(sizeof(*ot) * n, M_DEVFS, M_NOWAIT | M_ZERO);
394 	if (ot == NULL)
395 		return;
396 	or = malloc(sizeof(*or) * n, M_DEVFS, M_NOWAIT | M_ZERO);
397 	if (or == NULL) {
398 		free(ot, M_DEVFS);
399 		return;
400 	}
401 	devfs_overflow = ot;
402 	devfs_refoverflow = or;
403 	devfs_noverflow = n;
404 	printf("DEVFS Overflow table with %d entries allocated\n", n);
405 	return;
406 }
407 
408 void
409 devfs_destroy(struct cdev *dev)
410 {
411 	int ino;
412 	struct cdev **dp;
413 
414 	ino = dev->si_inode;
415 	dev->si_inode = 0;
416 	if (ino == 0)
417 		return;
418 	dp = devfs_itod(ino);
419 	KASSERT(*dp == dev,
420 	    ("DEVFS: destroying wrong cdev ino %d", ino));
421 	*dp = NULL;
422 	devfs_numino--;
423 	devfs_generation++;
424 	if (ino < devfs_nextino)
425 		devfs_nextino = ino;
426 }
427