devfs_devs.c revision 139776
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 139776 2005-01-06 18:10:42Z imp $
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>
4964880Sphk
50130585Sphkstatic struct cdev *devfs_inot[NDEVFSINO];
51130585Sphkstatic struct cdev **devfs_overflow;
5265515Sphkstatic int devfs_ref[NDEVFSINO];
5365515Sphkstatic int *devfs_refoverflow;
5465515Sphkstatic int devfs_nextino = 3;
5565515Sphkstatic int devfs_numino;
5665515Sphkstatic int devfs_topino;
5765515Sphkstatic int devfs_noverflowwant = NDEVFSOVERFLOW;
5865515Sphkstatic int devfs_noverflow;
5965515Sphkstatic unsigned devfs_generation;
6065515Sphk
6169767Sphkstatic struct devfs_dirent *devfs_find (struct devfs_dirent *dd, const char *name, int namelen);
6269767Sphk
6365515SphkSYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem");
6465515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, noverflow, CTLFLAG_RW,
6565515Sphk	&devfs_noverflowwant, 0, "Size of DEVFS overflow table");
6665515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
6765515Sphk	&devfs_generation, 0, "DEVFS generation number");
6865515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, inodes, CTLFLAG_RD,
6965515Sphk	&devfs_numino, 0, "DEVFS inodes");
7065515SphkSYSCTL_UINT(_vfs_devfs, OID_AUTO, topinode, CTLFLAG_RD,
7165515Sphk	&devfs_topino, 0, "DEVFS highest inode#");
7265515Sphk
7365515Sphkstatic int *
7465515Sphkdevfs_itor(int inode)
7565515Sphk{
7665515Sphk	if (inode < NDEVFSINO)
7765515Sphk		return (&devfs_ref[inode]);
7865515Sphk	else if (inode < NDEVFSINO + devfs_noverflow)
7965515Sphk		return (&devfs_refoverflow[inode - NDEVFSINO]);
8065515Sphk	else
8165515Sphk		panic ("YRK!");
8265515Sphk}
8365515Sphk
8465515Sphkstatic void
8565515Sphkdevfs_dropref(int inode)
8665515Sphk{
8765515Sphk	int *ip;
8865515Sphk
8965515Sphk	ip = devfs_itor(inode);
9065515Sphk	atomic_add_int(ip, -1);
9165515Sphk}
9265515Sphk
9365515Sphkstatic int
9465515Sphkdevfs_getref(int inode)
9565515Sphk{
9665515Sphk	int *ip, i, j;
97130585Sphk	struct cdev **dp;
9865515Sphk
9965515Sphk	ip = devfs_itor(inode);
10065515Sphk	dp = devfs_itod(inode);
10165515Sphk	for (;;) {
10265515Sphk		i = *ip;
10365515Sphk		j = i + 1;
10465515Sphk		if (!atomic_cmpset_int(ip, i, j))
10565515Sphk			continue;
10665515Sphk		if (*dp != NULL)
10765515Sphk			return (1);
10865515Sphk		atomic_add_int(ip, -1);
10965515Sphk		return(0);
11065515Sphk	}
11165515Sphk}
11265515Sphk
11365515Sphkstruct devfs_dirent **
11465515Sphkdevfs_itode (struct devfs_mount *dm, int inode)
11565515Sphk{
11665515Sphk
11765515Sphk	if (inode < NDEVFSINO)
11865515Sphk		return (&dm->dm_dirent[inode]);
11965515Sphk	if (devfs_overflow == NULL)
12065515Sphk		return (NULL);
12165515Sphk	if (inode < NDEVFSINO + devfs_noverflow)
12265515Sphk		return (&dm->dm_overflow[inode - NDEVFSINO]);
12365515Sphk	return (NULL);
12465515Sphk}
12565515Sphk
126130585Sphkstruct cdev **
12765515Sphkdevfs_itod (int inode)
12865515Sphk{
12965515Sphk
13065515Sphk	if (inode < NDEVFSINO)
13165515Sphk		return (&devfs_inot[inode]);
13265515Sphk	if (devfs_overflow == NULL)
13365515Sphk		return (NULL);
13465515Sphk	if (inode < NDEVFSINO + devfs_noverflow)
13565515Sphk		return (&devfs_overflow[inode - NDEVFSINO]);
13665515Sphk	return (NULL);
13765515Sphk}
13865515Sphk
13969767Sphkstatic struct devfs_dirent *
14065132Sphkdevfs_find(struct devfs_dirent *dd, const char *name, int namelen)
14165132Sphk{
14265132Sphk	struct devfs_dirent *de;
14365132Sphk
14465132Sphk	TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
14565132Sphk		if (namelen != de->de_dirent->d_namlen)
14665132Sphk			continue;
14765132Sphk		if (bcmp(name, de->de_dirent->d_name, namelen) != 0)
14865132Sphk			continue;
14965132Sphk		break;
15065132Sphk	}
15165132Sphk	return (de);
15265132Sphk}
15365132Sphk
15465132Sphkstruct devfs_dirent *
15564880Sphkdevfs_newdirent(char *name, int namelen)
15664880Sphk{
15764880Sphk	int i;
15864880Sphk	struct devfs_dirent *de;
15964880Sphk	struct dirent d;
16064880Sphk
16164880Sphk	d.d_namlen = namelen;
16264880Sphk	i = sizeof (*de) + GENERIC_DIRSIZ(&d);
163111119Simp	MALLOC(de, struct devfs_dirent *, i, M_DEVFS, M_WAITOK | M_ZERO);
16464880Sphk	de->de_dirent = (struct dirent *)(de + 1);
16564880Sphk	de->de_dirent->d_namlen = namelen;
16664880Sphk	de->de_dirent->d_reclen = GENERIC_DIRSIZ(&d);
16771822Sphk	bcopy(name, de->de_dirent->d_name, namelen);
16871822Sphk	de->de_dirent->d_name[namelen] = '\0';
16985979Sphk	vfs_timestamp(&de->de_ctime);
17065051Sphk	de->de_mtime = de->de_atime = de->de_ctime;
17165051Sphk	de->de_links = 1;
172101069Srwatson#ifdef MAC
173101069Srwatson	mac_init_devfsdirent(de);
174101069Srwatson#endif
17564880Sphk	return (de);
17664880Sphk}
17764880Sphk
17865051Sphkstruct devfs_dirent *
17965051Sphkdevfs_vmkdir(char *name, int namelen, struct devfs_dirent *dotdot)
18064880Sphk{
18165051Sphk	struct devfs_dirent *dd;
18264880Sphk	struct devfs_dirent *de;
18364880Sphk
18465051Sphk	dd = devfs_newdirent(name, namelen);
18564880Sphk
18665051Sphk	TAILQ_INIT(&dd->de_dlist);
18765051Sphk
18865051Sphk	dd->de_dirent->d_type = DT_DIR;
18985979Sphk	dd->de_mode = 0555;
19065051Sphk	dd->de_links = 2;
19165051Sphk	dd->de_dir = dd;
19265051Sphk
19364880Sphk	de = devfs_newdirent(".", 1);
19464880Sphk	de->de_dirent->d_type = DT_DIR;
19565051Sphk	de->de_dir = dd;
19665051Sphk	de->de_flags |= DE_DOT;
19765051Sphk	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
19865051Sphk
19965051Sphk	de = devfs_newdirent("..", 2);
20065051Sphk	de->de_dirent->d_type = DT_DIR;
20165051Sphk	if (dotdot == NULL)
20265051Sphk		de->de_dir = dd;
20365051Sphk	else
20465051Sphk		de->de_dir = dotdot;
20565051Sphk	de->de_flags |= DE_DOTDOT;
20665051Sphk	TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
20765051Sphk
20864880Sphk	return (dd);
20964880Sphk}
21064880Sphk
21165051Sphkstatic void
21265051Sphkdevfs_delete(struct devfs_dirent *dd, struct devfs_dirent *de)
21364880Sphk{
21464880Sphk
21564880Sphk	if (de->de_symlink) {
21664880Sphk		FREE(de->de_symlink, M_DEVFS);
21764880Sphk		de->de_symlink = NULL;
21864880Sphk	}
21966877Sphk	if (de->de_vnode)
22064880Sphk		de->de_vnode->v_data = NULL;
22165051Sphk	TAILQ_REMOVE(&dd->de_dlist, de, de_list);
222101069Srwatson#ifdef MAC
223101069Srwatson	mac_destroy_devfsdirent(de);
224101069Srwatson#endif
22564880Sphk	FREE(de, M_DEVFS);
22664880Sphk}
22764880Sphk
22864880Sphkvoid
22965051Sphkdevfs_purge(struct devfs_dirent *dd)
23064880Sphk{
23164880Sphk	struct devfs_dirent *de;
23264880Sphk
23364880Sphk	for (;;) {
23465051Sphk		de = TAILQ_FIRST(&dd->de_dlist);
23564880Sphk		if (de == NULL)
23664880Sphk			break;
23764880Sphk		devfs_delete(dd, de);
23864880Sphk	}
23964880Sphk	FREE(dd, M_DEVFS);
24064880Sphk}
24164880Sphk
24264880Sphk
24364880Sphkint
24464880Sphkdevfs_populate(struct devfs_mount *dm)
24564880Sphk{
24664880Sphk	int i, j;
247130585Sphk	struct cdev *dev, *pdev;
24865051Sphk	struct devfs_dirent *dd;
24965515Sphk	struct devfs_dirent *de, **dep;
25064880Sphk	char *q, *s;
25164880Sphk
25266877Sphk	if (dm->dm_generation == devfs_generation)
25365515Sphk		return (0);
25483366Sjulian	lockmgr(&dm->dm_lock, LK_UPGRADE, 0, curthread);
25565515Sphk	if (devfs_noverflow && dm->dm_overflow == NULL) {
25665515Sphk		i = devfs_noverflow * sizeof (struct devfs_dirent *);
25765515Sphk		MALLOC(dm->dm_overflow, struct devfs_dirent **, i,
258111119Simp			M_DEVFS, M_WAITOK | M_ZERO);
25965515Sphk	}
26064880Sphk	while (dm->dm_generation != devfs_generation) {
26164880Sphk		dm->dm_generation = devfs_generation;
26265515Sphk		for (i = 0; i <= devfs_topino; i++) {
26365515Sphk			dev = *devfs_itod(i);
26465515Sphk			dep = devfs_itode(dm, i);
26565515Sphk			de = *dep;
26665051Sphk			if (dev == NULL && de == DE_DELETED) {
26765515Sphk				*dep = NULL;
26865051Sphk				continue;
26965051Sphk			}
27064880Sphk			if (dev == NULL && de != NULL) {
27164880Sphk				dd = de->de_dir;
27265515Sphk				*dep = NULL;
27365051Sphk				TAILQ_REMOVE(&dd->de_dlist, de, de_list);
27466877Sphk				if (de->de_vnode)
27564880Sphk					de->de_vnode->v_data = NULL;
27664880Sphk				FREE(de, M_DEVFS);
27765515Sphk				devfs_dropref(i);
27864880Sphk				continue;
27964880Sphk			}
28064880Sphk			if (dev == NULL)
28164880Sphk				continue;
28264880Sphk			if (de != NULL)
28364880Sphk				continue;
28465515Sphk			if (!devfs_getref(i))
28565515Sphk				continue;
28664880Sphk			dd = dm->dm_basedir;
28764880Sphk			s = dev->si_name;
28865132Sphk			for (;;) {
28965132Sphk				for (q = s; *q != '/' && *q != '\0'; q++)
29065132Sphk					continue;
29166877Sphk				if (*q != '/')
29265132Sphk					break;
29365132Sphk				de = devfs_find(dd, s, q - s);
29465132Sphk				if (de == NULL) {
29565132Sphk					de = devfs_vmkdir(s, q - s, dd);
296101069Srwatson#ifdef MAC
297107698Srwatson					mac_create_devfs_directory(
298107698Srwatson					    dm->dm_mount, s, q - s, de);
299101069Srwatson#endif
30065132Sphk					de->de_inode = dm->dm_inode++;
30165132Sphk					TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
30265132Sphk					dd->de_links++;
30365051Sphk				}
30465051Sphk				s = q + 1;
30565051Sphk				dd = de;
30664880Sphk			}
30764880Sphk			de = devfs_newdirent(s, q - s);
30864880Sphk			if (dev->si_flags & SI_ALIAS) {
30964880Sphk				de->de_inode = dm->dm_inode++;
31064880Sphk				de->de_uid = 0;
31164880Sphk				de->de_gid = 0;
31285979Sphk				de->de_mode = 0755;
31364880Sphk				de->de_dirent->d_type = DT_LNK;
31477215Sphk				pdev = dev->si_parent;
31564880Sphk				j = strlen(pdev->si_name) + 1;
316111119Simp				MALLOC(de->de_symlink, char *, j, M_DEVFS, M_WAITOK);
31764880Sphk				bcopy(pdev->si_name, de->de_symlink, j);
31864880Sphk			} else {
31964880Sphk				de->de_inode = i;
32064880Sphk				de->de_uid = dev->si_uid;
32164880Sphk				de->de_gid = dev->si_gid;
32264880Sphk				de->de_mode = dev->si_mode;
32364880Sphk				de->de_dirent->d_type = DT_CHR;
32464880Sphk			}
325101069Srwatson#ifdef MAC
326107698Srwatson			mac_create_devfs_device(dm->dm_mount, dev, de);
327101069Srwatson#endif
32865515Sphk			*dep = de;
32965132Sphk			de->de_dir = dd;
330100206Sdd			devfs_rules_apply(dm, de);
33165051Sphk			TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
33264880Sphk		}
33364880Sphk	}
33483366Sjulian	lockmgr(&dm->dm_lock, LK_DOWNGRADE, 0, curthread);
33564880Sphk	return (0);
33664880Sphk}
33764880Sphk
338130678Sphk/*
339130678Sphk * devfs_create() and devfs_destroy() are called from kern_conf.c and
340130678Sphk * in both cases the devlock() mutex is held, so no further locking
341130678Sphk * is necesary and no sleeping allowed.
342130678Sphk */
343130678Sphk
344111730Sphkvoid
345130585Sphkdevfs_create(struct cdev *dev)
34664880Sphk{
34765515Sphk	int ino, i, *ip;
348130585Sphk	struct cdev **dp;
349130678Sphk	struct cdev **ot;
350130678Sphk	int *or;
351130678Sphk	int n;
35265515Sphk
35365515Sphk	for (;;) {
35465515Sphk		/* Grab the next inode number */
35565515Sphk		ino = devfs_nextino;
35665515Sphk		i = ino + 1;
35765515Sphk		/* wrap around when we reach the end */
35865515Sphk		if (i >= NDEVFSINO + devfs_noverflow)
35965515Sphk			i = 3;
360130678Sphk		devfs_nextino = i;
36165515Sphk
36265515Sphk		/* see if it was occupied */
36365515Sphk		dp = devfs_itod(ino);
364130678Sphk		KASSERT(dp != NULL, ("DEVFS: No devptr inode %d", ino));
36565515Sphk		if (*dp != NULL)
36665515Sphk			continue;
36765515Sphk		ip = devfs_itor(ino);
368130678Sphk		KASSERT(ip != NULL, ("DEVFS: No iptr inode %d", ino));
36965515Sphk		if (*ip != 0)
37065515Sphk			continue;
371130678Sphk		break;
372130678Sphk	}
37365515Sphk
374130678Sphk	*dp = dev;
375130678Sphk	dev->si_inode = ino;
376130678Sphk	if (i > devfs_topino)
377130678Sphk		devfs_topino = i;
37865515Sphk
379130678Sphk	devfs_numino++;
380130678Sphk	devfs_generation++;
381130678Sphk
382130678Sphk	if (devfs_overflow != NULL || devfs_numino + 100 < NDEVFSINO)
383130678Sphk		return;
384130678Sphk
385130678Sphk	/*
386130678Sphk	 * Try to allocate overflow table
387130678Sphk	 * XXX: we can probably be less panicy these days and a linked
388130678Sphk	 * XXX: list of PAGESIZE/PTRSIZE entries might be a better idea.
389130678Sphk	 *
390130678Sphk	 * XXX: we may be into witness unlove here.
391130678Sphk	 */
392130678Sphk	n = devfs_noverflowwant;
393130678Sphk	ot = malloc(sizeof(*ot) * n, M_DEVFS, M_NOWAIT | M_ZERO);
394130678Sphk	if (ot == NULL)
395130678Sphk		return;
396130678Sphk	or = malloc(sizeof(*or) * n, M_DEVFS, M_NOWAIT | M_ZERO);
397130678Sphk	if (or == NULL) {
398130678Sphk		free(ot, M_DEVFS);
399130678Sphk		return;
40064880Sphk	}
401130678Sphk	devfs_overflow = ot;
402130678Sphk	devfs_refoverflow = or;
403130678Sphk	devfs_noverflow = n;
404130678Sphk	printf("DEVFS Overflow table with %d entries allocated\n", n);
405130678Sphk	return;
40664880Sphk}
40764880Sphk
408111730Sphkvoid
409130585Sphkdevfs_destroy(struct cdev *dev)
41064880Sphk{
411130678Sphk	int ino;
412130678Sphk	struct cdev **dp;
41365515Sphk
41465515Sphk	ino = dev->si_inode;
41565515Sphk	dev->si_inode = 0;
41665515Sphk	if (ino == 0)
41765515Sphk		return;
418130678Sphk	dp = devfs_itod(ino);
419130678Sphk	KASSERT(*dp == dev,
420130678Sphk	    ("DEVFS: destroying wrong cdev ino %d", ino));
421130678Sphk	*dp = NULL;
422130678Sphk	devfs_numino--;
423130678Sphk	devfs_generation++;
424130678Sphk	if (ino < devfs_nextino)
425130678Sphk		devfs_nextino = ino;
42664880Sphk}
427