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