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