1 /*- 2 * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav 3 * 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 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "opt_pseudofs.h" 33 34 #include <sys/param.h> 35 #include <sys/kernel.h> 36 #include <sys/systm.h> 37 #include <sys/lock.h> 38 #include <sys/malloc.h> 39 #include <sys/module.h> 40 #include <sys/mount.h> 41 #include <sys/mutex.h> 42 #include <sys/proc.h> 43 #include <sys/sbuf.h> 44 #include <sys/sysctl.h> 45 #include <sys/vnode.h> 46 47 #include <fs/pseudofs/pseudofs.h> 48 #include <fs/pseudofs/pseudofs_internal.h> 49 50 static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes"); 51 52 SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW, 0, 53 "pseudofs"); 54 55 #ifdef PSEUDOFS_TRACE 56 int pfs_trace; 57 SYSCTL_INT(_vfs_pfs, OID_AUTO, trace, CTLFLAG_RW, &pfs_trace, 0, 58 "enable tracing of pseudofs vnode operations"); 59 #endif 60 61 #if PFS_FSNAMELEN != MFSNAMELEN 62 #error "PFS_FSNAMELEN is not equal to MFSNAMELEN" 63 #endif 64 65 /* 66 * Allocate and initialize a node 67 */ 68 static struct pfs_node * 69 pfs_alloc_node(struct pfs_info *pi, const char *name, pfs_type_t type) 70 { 71 struct pfs_node *pn; 72 73 KASSERT(strlen(name) < PFS_NAMELEN, 74 ("%s(): node name is too long", __func__)); 75 76 pn = malloc(sizeof *pn, 77 M_PFSNODES, M_WAITOK|M_ZERO); 78 mtx_init(&pn->pn_mutex, "pfs_node", NULL, MTX_DEF | MTX_DUPOK); 79 strlcpy(pn->pn_name, name, sizeof pn->pn_name); 80 pn->pn_type = type; 81 pn->pn_info = pi; 82 return (pn); 83 } 84 85 /* 86 * Add a node to a directory 87 */ 88 static void 89 pfs_add_node(struct pfs_node *parent, struct pfs_node *pn) 90 { 91 #ifdef INVARIANTS 92 struct pfs_node *iter; 93 #endif 94 95 KASSERT(parent != NULL, 96 ("%s(): parent is NULL", __func__)); 97 KASSERT(pn->pn_parent == NULL, 98 ("%s(): node already has a parent", __func__)); 99 KASSERT(parent->pn_info != NULL, 100 ("%s(): parent has no pn_info", __func__)); 101 KASSERT(parent->pn_type == pfstype_dir || 102 parent->pn_type == pfstype_procdir || 103 parent->pn_type == pfstype_root, 104 ("%s(): parent is not a directory", __func__)); 105 106 #ifdef INVARIANTS 107 /* XXX no locking! */ 108 if (pn->pn_type == pfstype_procdir) 109 for (iter = parent; iter != NULL; iter = iter->pn_parent) 110 KASSERT(iter->pn_type != pfstype_procdir, 111 ("%s(): nested process directories", __func__)); 112 for (iter = parent->pn_nodes; iter != NULL; iter = iter->pn_next) { 113 KASSERT(strcmp(pn->pn_name, iter->pn_name) != 0, 114 ("%s(): homonymous siblings", __func__)); 115 if (pn->pn_type == pfstype_procdir) 116 KASSERT(iter->pn_type != pfstype_procdir, 117 ("%s(): sibling process directories", __func__)); 118 } 119 #endif 120 121 pn->pn_parent = parent; 122 pfs_fileno_alloc(pn); 123 124 pfs_lock(parent); 125 pn->pn_next = parent->pn_nodes; 126 if ((parent->pn_flags & PFS_PROCDEP) != 0) 127 pn->pn_flags |= PFS_PROCDEP; 128 parent->pn_nodes = pn; 129 pfs_unlock(parent); 130 } 131 132 /* 133 * Detach a node from its aprent 134 */ 135 static void 136 pfs_detach_node(struct pfs_node *pn) 137 { 138 struct pfs_node *parent = pn->pn_parent; 139 struct pfs_node **iter; 140 141 KASSERT(parent != NULL, ("%s(): node has no parent", __func__)); 142 KASSERT(parent->pn_info == pn->pn_info, 143 ("%s(): parent has different pn_info", __func__)); 144 145 pfs_lock(parent); 146 iter = &parent->pn_nodes; 147 while (*iter != NULL) { 148 if (*iter == pn) { 149 *iter = pn->pn_next; 150 break; 151 } 152 iter = &(*iter)->pn_next; 153 } 154 pn->pn_parent = NULL; 155 pfs_unlock(parent); 156 } 157 158 /* 159 * Add . and .. to a directory 160 */ 161 static void 162 pfs_fixup_dir(struct pfs_node *parent) 163 { 164 struct pfs_node *pn; 165 166 pn = pfs_alloc_node(parent->pn_info, ".", pfstype_this); 167 pfs_add_node(parent, pn); 168 pn = pfs_alloc_node(parent->pn_info, "..", pfstype_parent); 169 pfs_add_node(parent, pn); 170 } 171 172 /* 173 * Create a directory 174 */ 175 struct pfs_node * 176 pfs_create_dir(struct pfs_node *parent, const char *name, 177 pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, 178 int flags) 179 { 180 struct pfs_node *pn; 181 182 pn = pfs_alloc_node(parent->pn_info, name, 183 (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir); 184 pn->pn_attr = attr; 185 pn->pn_vis = vis; 186 pn->pn_destroy = destroy; 187 pn->pn_flags = flags; 188 pfs_add_node(parent, pn); 189 pfs_fixup_dir(pn); 190 191 return (pn); 192 } 193 194 /* 195 * Create a file 196 */ 197 struct pfs_node * 198 pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill, 199 pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, 200 int flags) 201 { 202 struct pfs_node *pn; 203 204 pn = pfs_alloc_node(parent->pn_info, name, pfstype_file); 205 pn->pn_fill = fill; 206 pn->pn_attr = attr; 207 pn->pn_vis = vis; 208 pn->pn_destroy = destroy; 209 pn->pn_flags = flags; 210 pfs_add_node(parent, pn); 211 212 return (pn); 213 } 214 215 /* 216 * Create a symlink 217 */ 218 struct pfs_node * 219 pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill, 220 pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, 221 int flags) 222 { 223 struct pfs_node *pn; 224 225 pn = pfs_alloc_node(parent->pn_info, name, pfstype_symlink); 226 pn->pn_fill = fill; 227 pn->pn_attr = attr; 228 pn->pn_vis = vis; 229 pn->pn_destroy = destroy; 230 pn->pn_flags = flags; 231 pfs_add_node(parent, pn); 232 233 return (pn); 234 } 235 236 /* 237 * Locate a node by name 238 */ 239 struct pfs_node * 240 pfs_find_node(struct pfs_node *parent, const char *name) 241 { 242 struct pfs_node *pn; 243 244 pfs_lock(parent); 245 for (pn = parent->pn_nodes; pn != NULL; pn = pn->pn_next) 246 if (strcmp(pn->pn_name, name) == 0) 247 break; 248 pfs_unlock(parent); 249 return (pn); 250 } 251 252 /* 253 * Destroy a node and all its descendants. If the node to be destroyed 254 * has a parent, the parent's mutex must be held. 255 */ 256 int 257 pfs_destroy(struct pfs_node *pn) 258 { 259 struct pfs_node *iter; 260 261 KASSERT(pn != NULL, 262 ("%s(): node is NULL", __func__)); 263 KASSERT(pn->pn_info != NULL, 264 ("%s(): node has no pn_info", __func__)); 265 266 if (pn->pn_parent) 267 pfs_detach_node(pn); 268 269 /* destroy children */ 270 if (pn->pn_type == pfstype_dir || 271 pn->pn_type == pfstype_procdir || 272 pn->pn_type == pfstype_root) { 273 pfs_lock(pn); 274 while (pn->pn_nodes != NULL) { 275 iter = pn->pn_nodes; 276 pn->pn_nodes = iter->pn_next; 277 iter->pn_parent = NULL; 278 pfs_unlock(pn); 279 pfs_destroy(iter); 280 pfs_lock(pn); 281 } 282 pfs_unlock(pn); 283 } 284 285 /* revoke vnodes and fileno */ 286 pfs_purge(pn); 287 288 /* callback to free any private resources */ 289 if (pn->pn_destroy != NULL) 290 pn_destroy(pn); 291 292 /* destroy the node */ 293 pfs_fileno_free(pn); 294 mtx_destroy(&pn->pn_mutex); 295 free(pn, M_PFSNODES); 296 297 return (0); 298 } 299 300 /* 301 * Mount a pseudofs instance 302 */ 303 int 304 pfs_mount(struct pfs_info *pi, struct mount *mp) 305 { 306 struct statfs *sbp; 307 308 if (mp->mnt_flag & MNT_UPDATE) 309 return (EOPNOTSUPP); 310 311 MNT_ILOCK(mp); 312 mp->mnt_flag |= MNT_LOCAL; 313 MNT_IUNLOCK(mp); 314 mp->mnt_data = pi; 315 vfs_getnewfsid(mp); 316 317 sbp = &mp->mnt_stat; 318 vfs_mountedfrom(mp, pi->pi_name); 319 sbp->f_bsize = PAGE_SIZE; 320 sbp->f_iosize = PAGE_SIZE; 321 sbp->f_blocks = 1; 322 sbp->f_bfree = 0; 323 sbp->f_bavail = 0; 324 sbp->f_files = 1; 325 sbp->f_ffree = 0; 326 327 return (0); 328 } 329 330 /* 331 * Compatibility shim for old mount(2) system call 332 */ 333 int 334 pfs_cmount(struct mntarg *ma, void *data, uint64_t flags) 335 { 336 int error; 337 338 error = kernel_mount(ma, flags); 339 return (error); 340 } 341 342 /* 343 * Unmount a pseudofs instance 344 */ 345 int 346 pfs_unmount(struct mount *mp, int mntflags) 347 { 348 int error; 349 350 error = vflush(mp, 0, (mntflags & MNT_FORCE) ? FORCECLOSE : 0, 351 curthread); 352 return (error); 353 } 354 355 /* 356 * Return a root vnode 357 */ 358 int 359 pfs_root(struct mount *mp, int flags, struct vnode **vpp) 360 { 361 struct pfs_info *pi; 362 363 pi = (struct pfs_info *)mp->mnt_data; 364 return (pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID)); 365 } 366 367 /* 368 * Return filesystem stats 369 */ 370 int 371 pfs_statfs(struct mount *mp, struct statfs *sbp) 372 { 373 /* no-op: always called with mp->mnt_stat */ 374 return (0); 375 } 376 377 /* 378 * Initialize a pseudofs instance 379 */ 380 int 381 pfs_init(struct pfs_info *pi, struct vfsconf *vfc) 382 { 383 struct pfs_node *root; 384 int error; 385 386 pfs_fileno_init(pi); 387 388 /* set up the root directory */ 389 root = pfs_alloc_node(pi, "/", pfstype_root); 390 pi->pi_root = root; 391 pfs_fileno_alloc(root); 392 pfs_fixup_dir(root); 393 394 /* construct file hierarchy */ 395 error = (pi->pi_init)(pi, vfc); 396 if (error) { 397 pfs_destroy(root); 398 pi->pi_root = NULL; 399 return (error); 400 } 401 402 if (bootverbose) 403 printf("%s registered\n", pi->pi_name); 404 return (0); 405 } 406 407 /* 408 * Destroy a pseudofs instance 409 */ 410 int 411 pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc) 412 { 413 int error; 414 415 pfs_destroy(pi->pi_root); 416 pi->pi_root = NULL; 417 pfs_fileno_uninit(pi); 418 if (bootverbose) 419 printf("%s unregistered\n", pi->pi_name); 420 error = (pi->pi_uninit)(pi, vfc); 421 return (error); 422 } 423 424 /* 425 * Handle load / unload events 426 */ 427 static int 428 pfs_modevent(module_t mod, int evt, void *arg) 429 { 430 switch (evt) { 431 case MOD_LOAD: 432 pfs_vncache_load(); 433 break; 434 case MOD_UNLOAD: 435 case MOD_SHUTDOWN: 436 pfs_vncache_unload(); 437 break; 438 default: 439 return EOPNOTSUPP; 440 break; 441 } 442 return 0; 443 } 444 445 /* 446 * Module declaration 447 */ 448 static moduledata_t pseudofs_data = { 449 "pseudofs", 450 pfs_modevent, 451 NULL 452 }; 453 DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST); 454 MODULE_VERSION(pseudofs, 1); 455