devfs_devs.c revision 149144
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: head/sys/fs/devfs/devfs_devs.c 149144 2005-08-16 19:08:01Z phk $
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#include <fs/devfs/devfs_int.h>
50
51static struct cdev *devfs_inot[NDEVFSINO];
52static struct cdev **devfs_overflow;
53static int devfs_ref[NDEVFSINO];
54static int *devfs_refoverflow;
55static int devfs_nextino = 3;
56static int devfs_numino;
57static int devfs_topino;
58static int devfs_noverflowwant = NDEVFSOVERFLOW;
59static int devfs_noverflow;
60static unsigned devfs_generation;
61
62static struct devfs_dirent *devfs_find (struct devfs_dirent *dd, const char *name, int namelen);
63
64static SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem");
65SYSCTL_UINT(_vfs_devfs, OID_AUTO, noverflow, CTLFLAG_RW,
66	&devfs_noverflowwant, 0, "Size of DEVFS overflow table");
67SYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
68	&devfs_generation, 0, "DEVFS generation number");
69SYSCTL_UINT(_vfs_devfs, OID_AUTO, inodes, CTLFLAG_RD,
70	&devfs_numino, 0, "DEVFS inodes");
71SYSCTL_UINT(_vfs_devfs, OID_AUTO, topinode, CTLFLAG_RD,
72	&devfs_topino, 0, "DEVFS highest inode#");
73
74static int *
75devfs_itor(int inode)
76{
77	if (inode < NDEVFSINO)
78		return (&devfs_ref[inode]);
79	else if (inode < NDEVFSINO + devfs_noverflow)
80		return (&devfs_refoverflow[inode - NDEVFSINO]);
81	else
82		panic ("YRK!");
83}
84
85static void
86devfs_dropref(int inode)
87{
88	int *ip;
89
90	ip = devfs_itor(inode);
91	atomic_add_int(ip, -1);
92}
93
94static int
95devfs_getref(int inode)
96{
97	int *ip, i, j;
98	struct cdev **dp;
99
100	ip = devfs_itor(inode);
101	dp = devfs_itod(inode);
102	for (;;) {
103		i = *ip;
104		j = i + 1;
105		if (!atomic_cmpset_int(ip, i, j))
106			continue;
107		if (*dp != NULL)
108			return (1);
109		atomic_add_int(ip, -1);
110		return(0);
111	}
112}
113
114struct devfs_dirent **
115devfs_itode (struct devfs_mount *dm, int inode)
116{
117
118	if (inode < 0)
119		return (NULL);
120	if (inode < NDEVFSINO)
121		return (&dm->dm_dirent[inode]);
122	if (devfs_overflow == NULL)
123		return (NULL);
124	if (inode < NDEVFSINO + devfs_noverflow)
125		return (&dm->dm_overflow[inode - NDEVFSINO]);
126	return (NULL);
127}
128
129struct cdev **
130devfs_itod (int inode)
131{
132
133	if (inode < 0)
134		return (NULL);
135	if (inode < NDEVFSINO)
136		return (&devfs_inot[inode]);
137	if (devfs_overflow == NULL)
138		return (NULL);
139	if (inode < NDEVFSINO + devfs_noverflow)
140		return (&devfs_overflow[inode - NDEVFSINO]);
141	return (NULL);
142}
143
144static struct devfs_dirent *
145devfs_find(struct devfs_dirent *dd, const char *name, int namelen)
146{
147	struct devfs_dirent *de;
148
149	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
150		if (namelen != de->de_dirent->d_namlen)
151			continue;
152		if (bcmp(name, de->de_dirent->d_name, namelen) != 0)
153			continue;
154		break;
155	}
156	return (de);
157}
158
159struct devfs_dirent *
160devfs_newdirent(char *name, int namelen)
161{
162	int i;
163	struct devfs_dirent *de;
164	struct dirent d;
165
166	d.d_namlen = namelen;
167	i = sizeof (*de) + GENERIC_DIRSIZ(&d);
168	MALLOC(de, struct devfs_dirent *, i, M_DEVFS, M_WAITOK | M_ZERO);
169	de->de_dirent = (struct dirent *)(de + 1);
170	de->de_dirent->d_namlen = namelen;
171	de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d);
172	bcopy(name, de->de_dirent->d_name, namelen);
173	de->de_dirent->d_name[namelen] = '\0';
174	vfs_timestamp(&de->de_ctime);
175	de->de_mtime = de->de_atime = de->de_ctime;
176	de->de_links = 1;
177#ifdef MAC
178	mac_init_devfsdirent(de);
179#endif
180	return (de);
181}
182
183struct devfs_dirent *
184devfs_vmkdir(char *name, int namelen, struct devfs_dirent *dotdot)
185{
186	struct devfs_dirent *dd;
187	struct devfs_dirent *de;
188
189	dd = devfs_newdirent(name, namelen);
190
191	TAILQ_INIT(&dd->de_dlist);
192
193	dd->de_dirent->d_type = DT_DIR;
194	dd->de_mode = 0555;
195	dd->de_links = 2;
196	dd->de_dir = dd;
197
198	de = devfs_newdirent(".", 1);
199	de->de_dirent->d_type = DT_DIR;
200	de->de_dir = dd;
201	de->de_flags |= DE_DOT;
202	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
203
204	de = devfs_newdirent("..", 2);
205	de->de_dirent->d_type = DT_DIR;
206	if (dotdot == NULL)
207		de->de_dir = dd;
208	else
209		de->de_dir = dotdot;
210	de->de_flags |= DE_DOTDOT;
211	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
212
213	return (dd);
214}
215
216static void
217devfs_delete(struct devfs_dirent *dd, struct devfs_dirent *de)
218{
219
220	if (de->de_symlink) {
221		FREE(de->de_symlink, M_DEVFS);
222		de->de_symlink = NULL;
223	}
224	if (de->de_vnode)
225		de->de_vnode->v_data = NULL;
226	TAILQ_REMOVE(&dd->de_dlist, de, de_list);
227#ifdef MAC
228	mac_destroy_devfsdirent(de);
229#endif
230	FREE(de, M_DEVFS);
231}
232
233void
234devfs_purge(struct devfs_dirent *dd)
235{
236	struct devfs_dirent *de;
237
238	for (;;) {
239		de = TAILQ_FIRST(&dd->de_dlist);
240		if (de == NULL)
241			break;
242		devfs_delete(dd, de);
243	}
244	FREE(dd, M_DEVFS);
245}
246
247
248int
249devfs_populate(struct devfs_mount *dm)
250{
251	int i, j;
252	struct cdev *dev, *pdev;
253	struct devfs_dirent *dd;
254	struct devfs_dirent *de, **dep;
255	char *q, *s;
256
257	if (dm->dm_generation == devfs_generation)
258		return (0);
259	lockmgr(&dm->dm_lock, LK_UPGRADE, 0, curthread);
260	if (devfs_noverflow && dm->dm_overflow == NULL) {
261		i = devfs_noverflow * sizeof (struct devfs_dirent *);
262		MALLOC(dm->dm_overflow, struct devfs_dirent **, i,
263			M_DEVFS, M_WAITOK | M_ZERO);
264	}
265	while (dm->dm_generation != devfs_generation) {
266		dm->dm_generation = devfs_generation;
267		for (i = 0; i <= devfs_topino; i++) {
268			dev = *devfs_itod(i);
269			dep = devfs_itode(dm, i);
270			de = *dep;
271			if (dev == NULL && de == DE_DELETED) {
272				*dep = NULL;
273				continue;
274			}
275			if (dev == NULL && de != NULL) {
276				dd = de->de_dir;
277				*dep = NULL;
278				devfs_delete(dd, de);
279				devfs_dropref(i);
280				continue;
281			}
282			if (dev == NULL)
283				continue;
284			if (de != NULL)
285				continue;
286			if (!devfs_getref(i))
287				continue;
288			dd = dm->dm_rootdir;
289			s = dev->si_name;
290			for (;;) {
291				for (q = s; *q != '/' && *q != '\0'; q++)
292					continue;
293				if (*q != '/')
294					break;
295				de = devfs_find(dd, s, q - s);
296				if (de == NULL) {
297					de = devfs_vmkdir(s, q - s, dd);
298#ifdef MAC
299					mac_create_devfs_directory(
300					    dm->dm_mount, s, q - s, de);
301#endif
302					de->de_inode = dm->dm_inode++;
303					TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
304					dd->de_links++;
305				}
306				s = q + 1;
307				dd = de;
308			}
309			de = devfs_newdirent(s, q - s);
310			if (dev->si_flags & SI_ALIAS) {
311				de->de_inode = dm->dm_inode++;
312				de->de_uid = 0;
313				de->de_gid = 0;
314				de->de_mode = 0755;
315				de->de_dirent->d_type = DT_LNK;
316				pdev = dev->si_parent;
317				j = strlen(pdev->si_name) + 1;
318				MALLOC(de->de_symlink, char *, j, M_DEVFS, M_WAITOK);
319				bcopy(pdev->si_name, de->de_symlink, j);
320			} else {
321				de->de_inode = i;
322				de->de_uid = dev->si_uid;
323				de->de_gid = dev->si_gid;
324				de->de_mode = dev->si_mode;
325				de->de_dirent->d_type = DT_CHR;
326			}
327#ifdef MAC
328			mac_create_devfs_device(dev->si_cred, dm->dm_mount,
329			    dev, de);
330#endif
331			*dep = de;
332			de->de_dir = dd;
333			devfs_rules_apply(dm, de);
334			TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
335		}
336	}
337	lockmgr(&dm->dm_lock, LK_DOWNGRADE, 0, curthread);
338	return (0);
339}
340
341/*
342 * devfs_create() and devfs_destroy() are called from kern_conf.c and
343 * in both cases the devlock() mutex is held, so no further locking
344 * is necesary and no sleeping allowed.
345 */
346
347void
348devfs_create(struct cdev *dev)
349{
350	int ino, i, *ip;
351	struct cdev **dp;
352	struct cdev **ot;
353	int *or;
354	int n;
355
356	for (;;) {
357		/* Grab the next inode number */
358		ino = devfs_nextino;
359		i = ino + 1;
360		/* wrap around when we reach the end */
361		if (i >= NDEVFSINO + devfs_noverflow)
362			i = 3;
363		devfs_nextino = i;
364
365		/* see if it was occupied */
366		dp = devfs_itod(ino);
367		KASSERT(dp != NULL, ("DEVFS: No devptr inode %d", ino));
368		if (*dp != NULL)
369			continue;
370		ip = devfs_itor(ino);
371		KASSERT(ip != NULL, ("DEVFS: No iptr inode %d", ino));
372		if (*ip != 0)
373			continue;
374		break;
375	}
376
377	*dp = dev;
378	dev->si_inode = ino;
379	if (i > devfs_topino)
380		devfs_topino = i;
381
382	devfs_numino++;
383	devfs_generation++;
384
385	if (devfs_overflow != NULL || devfs_numino + 100 < NDEVFSINO)
386		return;
387
388	/*
389	 * Try to allocate overflow table
390	 * XXX: we can probably be less panicy these days and a linked
391	 * XXX: list of PAGESIZE/PTRSIZE entries might be a better idea.
392	 *
393	 * XXX: we may be into witness unlove here.
394	 */
395	n = devfs_noverflowwant;
396	ot = malloc(sizeof(*ot) * n, M_DEVFS, M_NOWAIT | M_ZERO);
397	if (ot == NULL)
398		return;
399	or = malloc(sizeof(*or) * n, M_DEVFS, M_NOWAIT | M_ZERO);
400	if (or == NULL) {
401		free(ot, M_DEVFS);
402		return;
403	}
404	devfs_overflow = ot;
405	devfs_refoverflow = or;
406	devfs_noverflow = n;
407	printf("DEVFS Overflow table with %d entries allocated\n", n);
408	return;
409}
410
411void
412devfs_destroy(struct cdev *dev)
413{
414	int ino;
415	struct cdev **dp;
416
417	ino = dev->si_inode;
418	dev->si_inode = 0;
419	if (ino == 0)
420		return;
421	dp = devfs_itod(ino);
422	KASSERT(*dp == dev,
423	    ("DEVFS: destroying wrong cdev ino %d", ino));
424	*dp = NULL;
425	devfs_numino--;
426	devfs_generation++;
427	if (ino < devfs_nextino)
428		devfs_nextino = ino;
429}
430