devfs_devs.c revision 150147
1139776Simp/*-
2130678Sphk * Copyright (c) 2000,2004
364880Sphk *	Poul-Henning Kamp.  All rights reserved.
464880Sphk *
564880Sphk * Redistribution and use in source and binary forms, with or without
664880Sphk * modification, are permitted provided that the following conditions
764880Sphk * are met:
864880Sphk * 1. Redistributions of source code must retain the above copyright
964880Sphk *    notice, this list of conditions and the following disclaimer.
1064880Sphk * 2. Neither the name of the University nor the names of its contributors
1164880Sphk *    may be used to endorse or promote products derived from this software
1264880Sphk *    without specific prior written permission.
1364880Sphk *
1464880Sphk * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1564880Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1664880Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1764880Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
1864880Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1964880Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2064880Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2164880Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2264880Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2364880Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2464880Sphk * SUCH DAMAGE.
2564880Sphk *
2664880Sphk * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vfsops.c 1.36
2764880Sphk *
2864880Sphk * $FreeBSD: head/sys/fs/devfs/devfs_devs.c 150147 2005-09-15 06:57:28Z phk $
2964880Sphk */
3064880Sphk
3165515Sphk#include "opt_devfs.h"
32101069Srwatson#include "opt_mac.h"
3365515Sphk
3464880Sphk#include <sys/param.h>
3564880Sphk#include <sys/systm.h>
3676166Smarkm#include <sys/conf.h>
3764880Sphk#include <sys/dirent.h>
3876166Smarkm#include <sys/kernel.h>
3976166Smarkm#include <sys/lock.h>
40101069Srwatson#include <sys/mac.h>
4164880Sphk#include <sys/malloc.h>
4276166Smarkm#include <sys/proc.h>
4365515Sphk#include <sys/sysctl.h>
4476166Smarkm#include <sys/vnode.h>
4564880Sphk
4665515Sphk#include <machine/atomic.h>
4765515Sphk
4864880Sphk#include <fs/devfs/devfs.h>
49149144Sphk#include <fs/devfs/devfs_int.h>
5064880Sphk
51130585Sphkstatic struct cdev *devfs_inot[NDEVFSINO];
52130585Sphkstatic struct cdev **devfs_overflow;
5365515Sphkstatic int devfs_ref[NDEVFSINO];
5465515Sphkstatic int *devfs_refoverflow;
5565515Sphkstatic int devfs_nextino = 3;
5665515Sphkstatic int devfs_numino;
5765515Sphkstatic int devfs_topino;
5865515Sphkstatic int devfs_noverflowwant = NDEVFSOVERFLOW;
5965515Sphkstatic int devfs_noverflow;
6065515Sphkstatic unsigned devfs_generation;
6165515Sphk
6269767Sphkstatic struct devfs_dirent *devfs_find (struct devfs_dirent *dd, const char *name, int namelen);
6369767Sphk
64141633Sphkstatic SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem");
6565515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, noverflow, CTLFLAG_RW,
6665515Sphk	&devfs_noverflowwant, 0, "Size of DEVFS overflow table");
6765515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
6865515Sphk	&devfs_generation, 0, "DEVFS generation number");
6965515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, inodes, CTLFLAG_RD,
7065515Sphk	&devfs_numino, 0, "DEVFS inodes");
7165515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, topinode, CTLFLAG_RD,
7265515Sphk	&devfs_topino, 0, "DEVFS highest inode#");
7365515Sphk
74150147Sphkunsigned devfs_rule_depth = 1;
75150147SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, rule_depth, CTLFLAG_RW,
76150147Sphk    &devfs_rule_depth, 0, "Max depth of ruleset include");
77150147Sphk
78149146Sphk/*
79149146Sphk * Helper sysctl for devname(3).  We're given a struct cdev * and return
80149146Sphk * the name, if any, registered by the device driver.
81149146Sphk */
82149146Sphkstatic int
83149146Sphksysctl_devname(SYSCTL_HANDLER_ARGS)
84149146Sphk{
85149146Sphk	int error;
86149146Sphk	dev_t ud;
87149146Sphk	struct cdev *dev, **dp;
88149146Sphk
89149146Sphk	error = SYSCTL_IN(req, &ud, sizeof (ud));
90149146Sphk	if (error)
91149146Sphk		return (error);
92149146Sphk	if (ud == NODEV)
93149146Sphk		return(EINVAL);
94149146Sphk	dp = devfs_itod(ud);
95149146Sphk	if (dp == NULL)
96149146Sphk		return(ENOENT);
97149146Sphk	dev = *dp;
98149146Sphk	if (dev == NULL)
99149146Sphk		return(ENOENT);
100149146Sphk	return(SYSCTL_OUT(req, dev->si_name, strlen(dev->si_name) + 1));
101149146Sphk	return (error);
102149146Sphk}
103149146Sphk
104149146SphkSYSCTL_PROC(_kern, OID_AUTO, devname, CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_ANYBODY,
105149146Sphk	NULL, 0, sysctl_devname, "", "devname(3) handler");
106149146Sphk
107149146SphkSYSCTL_INT(_debug_sizeof, OID_AUTO, cdev, CTLFLAG_RD,
108149146Sphk    0, sizeof(struct cdev), "sizeof(struct cdev)");
109149146Sphk
11065515Sphkstatic int *
11165515Sphkdevfs_itor(int inode)
11265515Sphk{
11365515Sphk	if (inode < NDEVFSINO)
11465515Sphk		return (&devfs_ref[inode]);
11565515Sphk	else if (inode < NDEVFSINO + devfs_noverflow)
11665515Sphk		return (&devfs_refoverflow[inode - NDEVFSINO]);
11765515Sphk	else
11865515Sphk		panic ("YRK!");
11965515Sphk}
12065515Sphk
12165515Sphkstatic void
12265515Sphkdevfs_dropref(int inode)
12365515Sphk{
12465515Sphk	int *ip;
12565515Sphk
12665515Sphk	ip = devfs_itor(inode);
12765515Sphk	atomic_add_int(ip, -1);
12865515Sphk}
12965515Sphk
13065515Sphkstatic int
13165515Sphkdevfs_getref(int inode)
13265515Sphk{
13365515Sphk	int *ip, i, j;
134130585Sphk	struct cdev **dp;
13565515Sphk
13665515Sphk	ip = devfs_itor(inode);
13765515Sphk	dp = devfs_itod(inode);
13865515Sphk	for (;;) {
13965515Sphk		i = *ip;
14065515Sphk		j = i + 1;
14165515Sphk		if (!atomic_cmpset_int(ip, i, j))
14265515Sphk			continue;
14365515Sphk		if (*dp != NULL)
14465515Sphk			return (1);
14565515Sphk		atomic_add_int(ip, -1);
14665515Sphk		return(0);
14765515Sphk	}
14865515Sphk}
14965515Sphk
15065515Sphkstruct devfs_dirent **
15165515Sphkdevfs_itode (struct devfs_mount *dm, int inode)
15265515Sphk{
15365515Sphk
154143381Sphk	if (inode < 0)
155143381Sphk		return (NULL);
15665515Sphk	if (inode < NDEVFSINO)
15765515Sphk		return (&dm->dm_dirent[inode]);
15865515Sphk	if (devfs_overflow == NULL)
15965515Sphk		return (NULL);
16065515Sphk	if (inode < NDEVFSINO + devfs_noverflow)
16165515Sphk		return (&dm->dm_overflow[inode - NDEVFSINO]);
16265515Sphk	return (NULL);
16365515Sphk}
16465515Sphk
165130585Sphkstruct cdev **
16665515Sphkdevfs_itod (int inode)
16765515Sphk{
16865515Sphk
169143381Sphk	if (inode < 0)
170143381Sphk		return (NULL);
17165515Sphk	if (inode < NDEVFSINO)
17265515Sphk		return (&devfs_inot[inode]);
17365515Sphk	if (devfs_overflow == NULL)
17465515Sphk		return (NULL);
17565515Sphk	if (inode < NDEVFSINO + devfs_noverflow)
17665515Sphk		return (&devfs_overflow[inode - NDEVFSINO]);
17765515Sphk	return (NULL);
17865515Sphk}
17965515Sphk
18069767Sphkstatic struct devfs_dirent *
18165132Sphkdevfs_find(struct devfs_dirent *dd, const char *name, int namelen)
18265132Sphk{
18365132Sphk	struct devfs_dirent *de;
18465132Sphk
18565132Sphk	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
18665132Sphk		if (namelen != de->de_dirent->d_namlen)
18765132Sphk			continue;
18865132Sphk		if (bcmp(name, de->de_dirent->d_name, namelen) != 0)
18965132Sphk			continue;
19065132Sphk		break;
19165132Sphk	}
19265132Sphk	return (de);
19365132Sphk}
19465132Sphk
19565132Sphkstruct devfs_dirent *
19664880Sphkdevfs_newdirent(char *name, int namelen)
19764880Sphk{
19864880Sphk	int i;
19964880Sphk	struct devfs_dirent *de;
20064880Sphk	struct dirent d;
20164880Sphk
20264880Sphk	d.d_namlen = namelen;
20364880Sphk	i = sizeof (*de) + GENERIC_DIRSIZ(&d);
204111119Simp	MALLOC(de, struct devfs_dirent *, i, M_DEVFS, M_WAITOK | M_ZERO);
20564880Sphk	de->de_dirent = (struct dirent *)(de + 1);
20664880Sphk	de->de_dirent->d_namlen = namelen;
20764880Sphk	de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d);
20871822Sphk	bcopy(name, de->de_dirent->d_name, namelen);
20971822Sphk	de->de_dirent->d_name[namelen] = '\0';
21085979Sphk	vfs_timestamp(&de->de_ctime);
21165051Sphk	de->de_mtime = de->de_atime = de->de_ctime;
21265051Sphk	de->de_links = 1;
213101069Srwatson#ifdef MAC
214101069Srwatson	mac_init_devfsdirent(de);
215101069Srwatson#endif
21664880Sphk	return (de);
21764880Sphk}
21864880Sphk
21965051Sphkstruct devfs_dirent *
22065051Sphkdevfs_vmkdir(char *name, int namelen, struct devfs_dirent *dotdot)
22164880Sphk{
22265051Sphk	struct devfs_dirent *dd;
22364880Sphk	struct devfs_dirent *de;
22464880Sphk
22565051Sphk	dd = devfs_newdirent(name, namelen);
22664880Sphk
22765051Sphk	TAILQ_INIT(&dd->de_dlist);
22865051Sphk
22965051Sphk	dd->de_dirent->d_type = DT_DIR;
23085979Sphk	dd->de_mode = 0555;
23165051Sphk	dd->de_links = 2;
23265051Sphk	dd->de_dir = dd;
23365051Sphk
23464880Sphk	de = devfs_newdirent(".", 1);
23564880Sphk	de->de_dirent->d_type = DT_DIR;
23665051Sphk	de->de_dir = dd;
23765051Sphk	de->de_flags |= DE_DOT;
23865051Sphk	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
23965051Sphk
24065051Sphk	de = devfs_newdirent("..", 2);
24165051Sphk	de->de_dirent->d_type = DT_DIR;
24265051Sphk	if (dotdot == NULL)
24365051Sphk		de->de_dir = dd;
24465051Sphk	else
24565051Sphk		de->de_dir = dotdot;
24665051Sphk	de->de_flags |= DE_DOTDOT;
24765051Sphk	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
24865051Sphk
24964880Sphk	return (dd);
25064880Sphk}
25164880Sphk
25265051Sphkstatic void
25365051Sphkdevfs_delete(struct devfs_dirent *dd, struct devfs_dirent *de)
25464880Sphk{
25564880Sphk
25664880Sphk	if (de->de_symlink) {
25764880Sphk		FREE(de->de_symlink, M_DEVFS);
25864880Sphk		de->de_symlink = NULL;
25964880Sphk	}
26066877Sphk	if (de->de_vnode)
26164880Sphk		de->de_vnode->v_data = NULL;
26265051Sphk	TAILQ_REMOVE(&dd->de_dlist, de, de_list);
263101069Srwatson#ifdef MAC
264101069Srwatson	mac_destroy_devfsdirent(de);
265101069Srwatson#endif
26664880Sphk	FREE(de, M_DEVFS);
26764880Sphk}
26864880Sphk
26964880Sphkvoid
27065051Sphkdevfs_purge(struct devfs_dirent *dd)
27164880Sphk{
27264880Sphk	struct devfs_dirent *de;
27364880Sphk
27464880Sphk	for (;;) {
27565051Sphk		de = TAILQ_FIRST(&dd->de_dlist);
27664880Sphk		if (de == NULL)
27764880Sphk			break;
27864880Sphk		devfs_delete(dd, de);
27964880Sphk	}
28064880Sphk	FREE(dd, M_DEVFS);
28164880Sphk}
28264880Sphk
28364880Sphk
28464880Sphkint
28564880Sphkdevfs_populate(struct devfs_mount *dm)
28664880Sphk{
28764880Sphk	int i, j;
288130585Sphk	struct cdev *dev, *pdev;
28965051Sphk	struct devfs_dirent *dd;
29065515Sphk	struct devfs_dirent *de, **dep;
29164880Sphk	char *q, *s;
29264880Sphk
29366877Sphk	if (dm->dm_generation == devfs_generation)
29465515Sphk		return (0);
29583366Sjulian	lockmgr(&dm->dm_lock, LK_UPGRADE, 0, curthread);
29665515Sphk	if (devfs_noverflow && dm->dm_overflow == NULL) {
29765515Sphk		i = devfs_noverflow * sizeof (struct devfs_dirent *);
29865515Sphk		MALLOC(dm->dm_overflow, struct devfs_dirent **, i,
299111119Simp			M_DEVFS, M_WAITOK | M_ZERO);
30065515Sphk	}
30164880Sphk	while (dm->dm_generation != devfs_generation) {
30264880Sphk		dm->dm_generation = devfs_generation;
30365515Sphk		for (i = 0; i <= devfs_topino; i++) {
30465515Sphk			dev = *devfs_itod(i);
30565515Sphk			dep = devfs_itode(dm, i);
30665515Sphk			de = *dep;
30765051Sphk			if (dev == NULL && de == DE_DELETED) {
30865515Sphk				*dep = NULL;
30965051Sphk				continue;
31065051Sphk			}
31164880Sphk			if (dev == NULL && de != NULL) {
31264880Sphk				dd = de->de_dir;
31365515Sphk				*dep = NULL;
314143381Sphk				devfs_delete(dd, de);
31565515Sphk				devfs_dropref(i);
31664880Sphk				continue;
31764880Sphk			}
31864880Sphk			if (dev == NULL)
31964880Sphk				continue;
32064880Sphk			if (de != NULL)
32164880Sphk				continue;
32265515Sphk			if (!devfs_getref(i))
32365515Sphk				continue;
324149107Sphk			dd = dm->dm_rootdir;
32564880Sphk			s = dev->si_name;
32665132Sphk			for (;;) {
32765132Sphk				for (q = s; *q != '/' && *q != '\0'; q++)
32865132Sphk					continue;
32966877Sphk				if (*q != '/')
33065132Sphk					break;
33165132Sphk				de = devfs_find(dd, s, q - s);
33265132Sphk				if (de == NULL) {
33365132Sphk					de = devfs_vmkdir(s, q - s, dd);
334101069Srwatson#ifdef MAC
335107698Srwatson					mac_create_devfs_directory(
336107698Srwatson					    dm->dm_mount, s, q - s, de);
337101069Srwatson#endif
33865132Sphk					de->de_inode = dm->dm_inode++;
33965132Sphk					TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
34065132Sphk					dd->de_links++;
34165051Sphk				}
34265051Sphk				s = q + 1;
34365051Sphk				dd = de;
34464880Sphk			}
34564880Sphk			de = devfs_newdirent(s, q - s);
34664880Sphk			if (dev->si_flags & SI_ALIAS) {
34764880Sphk				de->de_inode = dm->dm_inode++;
34864880Sphk				de->de_uid = 0;
34964880Sphk				de->de_gid = 0;
35085979Sphk				de->de_mode = 0755;
35164880Sphk				de->de_dirent->d_type = DT_LNK;
35277215Sphk				pdev = dev->si_parent;
35364880Sphk				j = strlen(pdev->si_name) + 1;
354111119Simp				MALLOC(de->de_symlink, char *, j, M_DEVFS, M_WAITOK);
35564880Sphk				bcopy(pdev->si_name, de->de_symlink, j);
35664880Sphk			} else {
35764880Sphk				de->de_inode = i;
358144385Sphk				de->de_uid = dev->si_uid;
359144385Sphk				de->de_gid = dev->si_gid;
360144385Sphk				de->de_mode = dev->si_mode;
36164880Sphk				de->de_dirent->d_type = DT_CHR;
36264880Sphk			}
363101069Srwatson#ifdef MAC
364147982Srwatson			mac_create_devfs_device(dev->si_cred, dm->dm_mount,
365147982Srwatson			    dev, de);
366101069Srwatson#endif
36765515Sphk			*dep = de;
36865132Sphk			de->de_dir = dd;
369100206Sdd			devfs_rules_apply(dm, de);
37065051Sphk			TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
37164880Sphk		}
37264880Sphk	}
37383366Sjulian	lockmgr(&dm->dm_lock, LK_DOWNGRADE, 0, curthread);
37464880Sphk	return (0);
37564880Sphk}
37664880Sphk
377130678Sphk/*
378130678Sphk * devfs_create() and devfs_destroy() are called from kern_conf.c and
379130678Sphk * in both cases the devlock() mutex is held, so no further locking
380130678Sphk * is necesary and no sleeping allowed.
381130678Sphk */
382130678Sphk
383111730Sphkvoid
384130585Sphkdevfs_create(struct cdev *dev)
38564880Sphk{
38665515Sphk	int ino, i, *ip;
387130585Sphk	struct cdev **dp;
388130678Sphk	struct cdev **ot;
389130678Sphk	int *or;
390130678Sphk	int n;
39165515Sphk
39265515Sphk	for (;;) {
39365515Sphk		/* Grab the next inode number */
39465515Sphk		ino = devfs_nextino;
39565515Sphk		i = ino + 1;
39665515Sphk		/* wrap around when we reach the end */
39765515Sphk		if (i >= NDEVFSINO + devfs_noverflow)
39865515Sphk			i = 3;
399130678Sphk		devfs_nextino = i;
40065515Sphk
40165515Sphk		/* see if it was occupied */
40265515Sphk		dp = devfs_itod(ino);
403130678Sphk		KASSERT(dp != NULL, ("DEVFS: No devptr inode %d", ino));
40465515Sphk		if (*dp != NULL)
40565515Sphk			continue;
40665515Sphk		ip = devfs_itor(ino);
407130678Sphk		KASSERT(ip != NULL, ("DEVFS: No iptr inode %d", ino));
40865515Sphk		if (*ip != 0)
40965515Sphk			continue;
410130678Sphk		break;
411130678Sphk	}
41265515Sphk
413130678Sphk	*dp = dev;
414130678Sphk	dev->si_inode = ino;
415130678Sphk	if (i > devfs_topino)
416130678Sphk		devfs_topino = i;
41765515Sphk
418130678Sphk	devfs_numino++;
419130678Sphk	devfs_generation++;
420130678Sphk
421130678Sphk	if (devfs_overflow != NULL || devfs_numino + 100 < NDEVFSINO)
422130678Sphk		return;
423130678Sphk
424130678Sphk	/*
425130678Sphk	 * Try to allocate overflow table
426130678Sphk	 * XXX: we can probably be less panicy these days and a linked
427130678Sphk	 * XXX: list of PAGESIZE/PTRSIZE entries might be a better idea.
428130678Sphk	 *
429130678Sphk	 * XXX: we may be into witness unlove here.
430130678Sphk	 */
431130678Sphk	n = devfs_noverflowwant;
432130678Sphk	ot = malloc(sizeof(*ot) * n, M_DEVFS, M_NOWAIT | M_ZERO);
433130678Sphk	if (ot == NULL)
434130678Sphk		return;
435130678Sphk	or = malloc(sizeof(*or) * n, M_DEVFS, M_NOWAIT | M_ZERO);
436130678Sphk	if (or == NULL) {
437130678Sphk		free(ot, M_DEVFS);
438130678Sphk		return;
43964880Sphk	}
440130678Sphk	devfs_overflow = ot;
441130678Sphk	devfs_refoverflow = or;
442130678Sphk	devfs_noverflow = n;
443130678Sphk	printf("DEVFS Overflow table with %d entries allocated\n", n);
444130678Sphk	return;
44564880Sphk}
44664880Sphk
447111730Sphkvoid
448130585Sphkdevfs_destroy(struct cdev *dev)
44964880Sphk{
450130678Sphk	int ino;
451130678Sphk	struct cdev **dp;
45265515Sphk
45365515Sphk	ino = dev->si_inode;
45465515Sphk	dev->si_inode = 0;
45565515Sphk	if (ino == 0)
45665515Sphk		return;
457130678Sphk	dp = devfs_itod(ino);
458130678Sphk	KASSERT(*dp == dev,
459130678Sphk	    ("DEVFS: destroying wrong cdev ino %d", ino));
460130678Sphk	*dp = NULL;
461130678Sphk	devfs_numino--;
462130678Sphk	devfs_generation++;
463130678Sphk	if (ino < devfs_nextino)
464130678Sphk		devfs_nextino = ino;
46564880Sphk}
466