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