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