1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2015 Joyent, Inc. 14 */ 15 16 #include <sys/errno.h> 17 #include <sys/modctl.h> 18 #include <sys/types.h> 19 #include <sys/mkdev.h> 20 #include <sys/ddi.h> 21 #include <sys/sunddi.h> 22 #include <sys/vfs.h> 23 #include <sys/vfs_opreg.h> 24 #include <sys/systm.h> 25 #include <sys/id_space.h> 26 #include <sys/cmn_err.h> 27 #include <sys/ksynch.h> 28 #include <sys/policy.h> 29 #include <sys/mount.h> 30 #include <sys/sysmacros.h> 31 32 #include <sys/fs/bootfs_impl.h> 33 34 /* 35 * While booting, additional types of modules and files can be passed in to the 36 * loader. These include the familiar boot archive, as well as, a module hash 37 * and additional modules that are interpreted as files. As part of the handoff 38 * in early boot, information about these modules are saved as properties on the 39 * root of the devinfo tree, similar to other boot-time properties. 40 * 41 * This file system provides a read-only view of those additional files. Due to 42 * its limited scope, it has a slightly simpler construction than several other 43 * file systems. When mounted, it looks for the corresponding properties and 44 * creates bootfs_node_t's and vnodes for all of the corresponding files and 45 * directories that exist along the way. At this time, there are currently a 46 * rather small number of files passed in this way. 47 * 48 * This does lead to one behavior that folks used to other file systems might 49 * find peculiar. Because we are not always actively creating and destroying the 50 * required vnodes on demand, the count on the root vnode will not be going up 51 * accordingly with the existence of other vnodes. This means that a bootfs file 52 * system that is not in use will have all of its vnodes exist with a v_count of 53 * one. 54 */ 55 56 major_t bootfs_major; 57 static int bootfs_fstype; 58 static id_space_t *bootfs_idspace; 59 static uint64_t bootfs_nactive; 60 static kmutex_t bootfs_lock; 61 62 static const char *bootfs_name = "bootfs"; 63 64 static int 65 bootfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) 66 { 67 int ret; 68 bootfs_t *bfs; 69 struct pathname dpn; 70 dev_t fsdev; 71 72 if ((ret = secpolicy_fs_mount(cr, mvp, vfsp)) != 0) 73 return (ret); 74 75 if (mvp->v_type != VDIR) 76 return (ENOTDIR); 77 78 if (uap->flags & MS_REMOUNT) 79 return (EBUSY); 80 81 mutex_enter(&mvp->v_lock); 82 if ((uap->flags & MS_OVERLAY) == 0 && 83 (mvp->v_count != 1 || (mvp->v_flag & VROOT))) { 84 mutex_exit(&mvp->v_lock); 85 return (EBUSY); 86 } 87 mutex_exit(&mvp->v_lock); 88 89 /* 90 * We indicate that the backing store is bootfs. We don't want to use 91 * swap, because folks might think that this is putting all the data 92 * into memory ala tmpfs. Rather these modules are always in memory and 93 * there's nothing to be done about that. 94 */ 95 vfs_setresource(vfsp, bootfs_name, 0); 96 bfs = kmem_zalloc(sizeof (bootfs_t), KM_NOSLEEP_LAZY); 97 if (bfs == NULL) 98 return (ENOMEM); 99 100 ret = pn_get(uap->dir, 101 (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn); 102 if (ret != 0) { 103 kmem_free(bfs, sizeof (bfs)); 104 return (ret); 105 } 106 107 bfs->bfs_minor = id_alloc(bootfs_idspace); 108 bfs->bfs_kstat = kstat_create_zone("bootfs", bfs->bfs_minor, "bootfs", 109 "fs", KSTAT_TYPE_NAMED, 110 sizeof (bootfs_stat_t) / sizeof (kstat_named_t), 111 KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID); 112 if (bfs->bfs_kstat == NULL) { 113 id_free(bootfs_idspace, bfs->bfs_minor); 114 pn_free(&dpn); 115 kmem_free(bfs, sizeof (bfs)); 116 return (ENOMEM); 117 } 118 bfs->bfs_kstat->ks_data = &bfs->bfs_stat; 119 120 fsdev = makedevice(bootfs_major, bfs->bfs_minor); 121 bfs->bfs_vfsp = vfsp; 122 123 vfsp->vfs_data = (caddr_t)bfs; 124 vfsp->vfs_fstype = bootfs_fstype; 125 vfsp->vfs_dev = fsdev; 126 vfsp->vfs_bsize = PAGESIZE; 127 vfsp->vfs_flag |= VFS_RDONLY | VFS_NOSETUID | VFS_NOTRUNC | 128 VFS_UNLINKABLE; 129 vfs_make_fsid(&vfsp->vfs_fsid, fsdev, bootfs_fstype); 130 bfs->bfs_mntpath = kmem_alloc(dpn.pn_pathlen + 1, KM_SLEEP); 131 bcopy(dpn.pn_path, bfs->bfs_mntpath, dpn.pn_pathlen); 132 bfs->bfs_mntpath[dpn.pn_pathlen] = '\0'; 133 pn_free(&dpn); 134 list_create(&bfs->bfs_nodes, sizeof (bootfs_node_t), 135 offsetof(bootfs_node_t, bvn_alink)); 136 137 kstat_named_init(&bfs->bfs_stat.bfss_nfiles, "nfiles", 138 KSTAT_DATA_UINT32); 139 kstat_named_init(&bfs->bfs_stat.bfss_ndirs, "ndirs", 140 KSTAT_DATA_UINT32); 141 kstat_named_init(&bfs->bfs_stat.bfss_nbytes, "nbytes", 142 KSTAT_DATA_UINT64); 143 kstat_named_init(&bfs->bfs_stat.bfss_ndups, "ndup", 144 KSTAT_DATA_UINT32); 145 kstat_named_init(&bfs->bfs_stat.bfss_ndiscards, "ndiscard", 146 KSTAT_DATA_UINT32); 147 148 bootfs_construct(bfs); 149 150 kstat_install(bfs->bfs_kstat); 151 152 return (0); 153 } 154 155 static int 156 bootfs_unmount(vfs_t *vfsp, int flag, cred_t *cr) 157 { 158 int ret; 159 bootfs_t *bfs = vfsp->vfs_data; 160 bootfs_node_t *bnp; 161 162 if ((ret = secpolicy_fs_unmount(cr, vfsp)) != 0) 163 return (ret); 164 165 if (flag & MS_FORCE) 166 return (ENOTSUP); 167 168 for (bnp = list_head(&bfs->bfs_nodes); bnp != NULL; 169 bnp = list_next(&bfs->bfs_nodes, bnp)) { 170 mutex_enter(&bnp->bvn_vnp->v_lock); 171 if (bnp->bvn_vnp->v_count > 1) { 172 mutex_exit(&bnp->bvn_vnp->v_lock); 173 return (EBUSY); 174 } 175 mutex_exit(&bnp->bvn_vnp->v_lock); 176 } 177 178 kstat_delete(bfs->bfs_kstat); 179 bootfs_destruct(bfs); 180 list_destroy(&bfs->bfs_nodes); 181 kmem_free(bfs->bfs_mntpath, strlen(bfs->bfs_mntpath) + 1); 182 id_free(bootfs_idspace, bfs->bfs_minor); 183 kmem_free(bfs, sizeof (bootfs_t)); 184 return (0); 185 } 186 187 static int 188 bootfs_root(vfs_t *vfsp, vnode_t **vpp) 189 { 190 bootfs_t *bfs; 191 192 bfs = (bootfs_t *)vfsp->vfs_data; 193 *vpp = bfs->bfs_rootvn->bvn_vnp; 194 VN_HOLD(*vpp) 195 196 return (0); 197 } 198 199 static int 200 bootfs_statvfs(vfs_t *vfsp, struct statvfs64 *sbp) 201 { 202 const bootfs_t *bfs = (bootfs_t *)vfsp; 203 dev32_t d32; 204 205 sbp->f_bsize = PAGESIZE; 206 sbp->f_frsize = PAGESIZE; 207 208 sbp->f_blocks = bfs->bfs_stat.bfss_nbytes.value.ui64 >> PAGESHIFT; 209 sbp->f_bfree = 0; 210 sbp->f_bavail = 0; 211 212 sbp->f_files = bfs->bfs_stat.bfss_nfiles.value.ui32 + 213 bfs->bfs_stat.bfss_ndirs.value.ui32; 214 sbp->f_ffree = 0; 215 sbp->f_favail = 0; 216 217 (void) cmpldev(&d32, vfsp->vfs_dev); 218 sbp->f_fsid = d32; 219 (void) strlcpy(sbp->f_basetype, bootfs_name, FSTYPSZ); 220 bzero(sbp->f_fstr, sizeof (sbp->f_fstr)); 221 222 return (0); 223 } 224 225 static const fs_operation_def_t bootfs_vfsops_tmpl[] = { 226 VFSNAME_MOUNT, { .vfs_mount = bootfs_mount }, 227 VFSNAME_UNMOUNT, { .vfs_unmount = bootfs_unmount }, 228 VFSNAME_ROOT, { .vfs_root = bootfs_root }, 229 VFSNAME_STATVFS, { .vfs_statvfs = bootfs_statvfs }, 230 NULL, NULL 231 }; 232 233 static int 234 bootfs_init(int fstype, char *name) 235 { 236 int ret; 237 238 bootfs_fstype = fstype; 239 ASSERT(bootfs_fstype != 0); 240 241 ret = vfs_setfsops(fstype, bootfs_vfsops_tmpl, NULL); 242 if (ret != 0) 243 return (ret); 244 245 ret = vn_make_ops(name, bootfs_vnodeops_template, &bootfs_vnodeops); 246 if (ret != 0) { 247 (void) vfs_freevfsops_by_type(bootfs_fstype); 248 return (ret); 249 } 250 251 bootfs_major = getudev(); 252 if (bootfs_major == (major_t)-1) { 253 cmn_err(CE_WARN, "bootfs_init: Can't get unique device number"); 254 bootfs_major = 0; 255 } 256 257 bootfs_nactive = 0; 258 return (0); 259 } 260 261 static mntopts_t bootfs_mntopts = { 262 0, NULL 263 }; 264 265 static vfsdef_t bootfs_vfsdef = { 266 VFSDEF_VERSION, 267 "bootfs", 268 bootfs_init, 269 VSW_HASPROTO|VSW_STATS, 270 &bootfs_mntopts 271 }; 272 273 static struct modlfs bootfs_modlfs = { 274 &mod_fsops, "boot-time modules file system", &bootfs_vfsdef 275 }; 276 277 static struct modlinkage bootfs_modlinkage = { 278 MODREV_1, &bootfs_modlfs, NULL 279 }; 280 281 int 282 _init(void) 283 { 284 bootfs_node_cache = kmem_cache_create("bootfs_node_cache", 285 sizeof (bootfs_node_t), 0, bootfs_node_constructor, 286 bootfs_node_destructor, NULL, NULL, NULL, 0); 287 bootfs_idspace = id_space_create("bootfs_minors", 1, INT32_MAX); 288 mutex_init(&bootfs_lock, NULL, MUTEX_DEFAULT, NULL); 289 290 return (mod_install(&bootfs_modlinkage)); 291 } 292 293 int 294 _info(struct modinfo *modinfop) 295 { 296 return (mod_info(&bootfs_modlinkage, modinfop)); 297 } 298 299 int 300 _fini(void) 301 { 302 int err; 303 304 mutex_enter(&bootfs_lock); 305 if (bootfs_nactive > 0) { 306 mutex_exit(&bootfs_lock); 307 return (EBUSY); 308 } 309 mutex_exit(&bootfs_lock); 310 311 err = mod_remove(&bootfs_modlinkage); 312 if (err != 0) 313 return (err); 314 315 (void) vfs_freevfsops_by_type(bootfs_fstype); 316 vn_freevnodeops(bootfs_vnodeops); 317 id_space_destroy(bootfs_idspace); 318 mutex_destroy(&bootfs_lock); 319 kmem_cache_destroy(bootfs_node_cache); 320 return (err); 321 } 322