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