xref: /illumos-gate/usr/src/uts/common/fs/bootfs/bootfs_construct.c (revision 8808ac5dae118369991f158b6ab736cb2691ecde)
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.  All rights reserved.
14  */
15 
16 /*
17  * This file takes care of reading the boot time modules and constructing them
18  * into the appropriate series of vnodes.
19  */
20 
21 #include <sys/conf.h>
22 #include <sys/ddi.h>
23 #include <sys/sunddi.h>
24 #include <sys/vfs.h>
25 #include <sys/sysmacros.h>
26 #include <sys/stat.h>
27 
28 #include <sys/fs/bootfs_impl.h>
29 
30 kmem_cache_t *bootfs_node_cache;
31 
32 static const vattr_t bootfs_vattr_dir = {
33 	AT_ALL,					/* va_mask */
34 	VDIR,					/* va_type */
35 	S_IFDIR | 0555,				/* va_mode */
36 	0,					/* va_uid */
37 	0,					/* va_gid */
38 	0,					/* va_fsid */
39 	0,					/* va_nodeid */
40 	1,					/* va_nlink */
41 	0,					/* va_size */
42 	0,					/* va_atime */
43 	0,					/* va_mtime */
44 	0,					/* va_ctime */
45 	0,					/* va_rdev */
46 	0,					/* va_blksize */
47 	0,					/* va_nblocks */
48 	0					/* va_seq */
49 };
50 
51 static const vattr_t bootfs_vattr_reg = {
52 	AT_ALL,					/* va_mask */
53 	VREG,					/* va_type */
54 	S_IFREG | 0555,				/* va_mode */
55 	0,					/* va_uid */
56 	0,					/* va_gid */
57 	0,					/* va_fsid */
58 	0,					/* va_nodeid */
59 	1,					/* va_nlink */
60 	0,					/* va_size */
61 	0,					/* va_atime */
62 	0,					/* va_mtime */
63 	0,					/* va_ctime */
64 	0,					/* va_rdev */
65 	0,					/* va_blksize */
66 	0,					/* va_nblocks */
67 	0					/* va_seq */
68 };
69 
70 /*ARGSUSED*/
71 int
72 bootfs_node_constructor(void *buf, void *arg, int kmflags)
73 {
74 	bootfs_node_t *bnp = buf;
75 
76 	bnp->bvn_vnp = vn_alloc(kmflags);
77 	if (bnp->bvn_vnp == NULL)
78 		return (-1);
79 
80 	return (0);
81 }
82 
83 /*ARGSUSED*/
84 void
85 bootfs_node_destructor(void *buf, void *arg)
86 {
87 	bootfs_node_t *bnp = buf;
88 
89 	vn_free(bnp->bvn_vnp);
90 }
91 
92 static int
93 bootfs_comparator(const void *a, const void *b)
94 {
95 	const bootfs_node_t *lfs, *rfs;
96 	int ret;
97 
98 	lfs = a;
99 	rfs = b;
100 
101 	ret = strcmp(lfs->bvn_name, rfs->bvn_name);
102 	if (ret > 0)
103 		ret = 1;
104 	if (ret < 0)
105 		ret = -1;
106 	return (ret);
107 }
108 
109 static void
110 bootfs_node_init(bootfs_t *bfs, bootfs_node_t *bnp, const struct vattr *vap,
111     const char *name, size_t namelen)
112 {
113 	timestruc_t now;
114 
115 	vn_reinit(bnp->bvn_vnp);
116 
117 	bnp->bvn_vnp->v_flag |= VNOSWAP;
118 	bnp->bvn_vnp->v_type = vap->va_type;
119 	bnp->bvn_vnp->v_vfsp = bfs->bfs_vfsp;
120 	bnp->bvn_vnp->v_rdev = 0;
121 	bnp->bvn_vnp->v_data = (caddr_t)bnp;
122 	vn_setops(bnp->bvn_vnp, bootfs_vnodeops);
123 
124 	bnp->bvn_name = kmem_alloc(namelen + 1, KM_SLEEP);
125 	bcopy(name, bnp->bvn_name, namelen);
126 	bnp->bvn_name[namelen] = '\0';
127 	if (vap->va_type == VDIR) {
128 		avl_create(&bnp->bvn_dir, bootfs_comparator,
129 		    sizeof (bootfs_node_t),
130 		    offsetof(bootfs_node_t, bvn_link));
131 	}
132 	bzero(&bnp->bvn_link, sizeof (avl_node_t));
133 	bcopy(vap, &bnp->bvn_attr, sizeof (vattr_t));
134 
135 	gethrestime(&now);
136 	bnp->bvn_attr.va_atime = now;
137 	bnp->bvn_attr.va_ctime = now;
138 	bnp->bvn_attr.va_mtime = now;
139 	bnp->bvn_attr.va_fsid = makedevice(bootfs_major, bfs->bfs_minor);
140 	bnp->bvn_attr.va_nodeid = bfs->bfs_ninode;
141 	bnp->bvn_attr.va_blksize = PAGESIZE;
142 	bfs->bfs_ninode++;
143 	list_insert_tail(&bfs->bfs_nodes, bnp);
144 }
145 
146 static void
147 bootfs_mkroot(bootfs_t *bfs)
148 {
149 	bootfs_node_t *bnp;
150 
151 	bnp = kmem_cache_alloc(bootfs_node_cache, KM_SLEEP);
152 	bootfs_node_init(bfs, bnp, &bootfs_vattr_dir, "/", 1);
153 	bnp->bvn_vnp->v_flag |= VROOT;
154 	bnp->bvn_parent = bnp;
155 	bfs->bfs_rootvn = bnp;
156 	bfs->bfs_stat.bfss_ndirs.value.ui32++;
157 	vn_exists(bnp->bvn_vnp);
158 }
159 
160 static int
161 bootfs_mknode(bootfs_t *bfs, bootfs_node_t *parent, bootfs_node_t **outp,
162     const char *name, size_t namelen, const vattr_t *vap, uintptr_t addr,
163     uint64_t size)
164 {
165 	bootfs_node_t *bnp;
166 	bootfs_node_t sn;
167 	avl_index_t where;
168 	char *buf;
169 
170 	ASSERT(parent->bvn_attr.va_type == VDIR);
171 	buf = kmem_alloc(namelen + 1, KM_SLEEP);
172 	bcopy(name, buf, namelen);
173 	buf[namelen] = '\0';
174 	sn.bvn_name = buf;
175 	if ((bnp = avl_find(&parent->bvn_dir, &sn, &where)) != NULL) {
176 		kmem_free(buf, namelen + 1);
177 		/* Directories can collide, files cannot */
178 		if (vap->va_type == VDIR) {
179 			*outp = bnp;
180 			return (0);
181 		}
182 		return (EEXIST);
183 	}
184 	kmem_free(buf, namelen + 1);
185 
186 	bnp = kmem_cache_alloc(bootfs_node_cache, KM_SLEEP);
187 	bootfs_node_init(bfs, bnp, vap, name, namelen);
188 	bnp->bvn_parent = parent;
189 	avl_add(&parent->bvn_dir, bnp);
190 	*outp = bnp;
191 
192 	if (vap->va_type == VDIR) {
193 		parent->bvn_attr.va_size++;
194 		parent->bvn_attr.va_nlink++;
195 		bfs->bfs_stat.bfss_ndirs.value.ui32++;
196 	} else {
197 		bnp->bvn_addr = addr;
198 		bnp->bvn_size = size;
199 		bfs->bfs_stat.bfss_nfiles.value.ui32++;
200 		bfs->bfs_stat.bfss_nbytes.value.ui64 += size;
201 		bnp->bvn_attr.va_nblocks = P2ROUNDUP(size, 512) >> 9;
202 		bnp->bvn_attr.va_size = size;
203 	}
204 
205 	vn_exists(bnp->bvn_vnp);
206 
207 	return (0);
208 }
209 
210 /*
211  * Given the address, size, and path a boot-time module would like, go through
212  * and create all of the directory entries that are required and then the file
213  * itself. If someone has passed in a module that has the same name as another
214  * one, we honor the first one.
215  */
216 static int
217 bootfs_construct_entry(bootfs_t *bfs, uintptr_t addr, uint64_t size,
218     const char *mname)
219 {
220 	char *sp;
221 	size_t nlen;
222 	int ret;
223 	bootfs_node_t *nbnp;
224 
225 	const char *p = mname;
226 	bootfs_node_t *bnp = bfs->bfs_rootvn;
227 
228 	if (*p == '\0')
229 		return (EINVAL);
230 
231 	for (;;) {
232 		/* First eliminate all leading / characters. */
233 		while (*p == '/')
234 			p++;
235 
236 		/* A name with all slashes or ending in a / */
237 		if (*p == '\0')
238 			return (EINVAL);
239 
240 		sp = strchr(p, '/');
241 		if (sp == NULL)
242 			break;
243 		nlen = (ptrdiff_t)sp - (ptrdiff_t)p;
244 		if (strncmp(p, ".", nlen) == 0) {
245 			p = sp + 1;
246 			continue;
247 		}
248 
249 		if (strncmp(p, "..", nlen) == 0) {
250 			bnp = bnp->bvn_parent;
251 			p = sp + 1;
252 			continue;
253 		}
254 
255 		VERIFY(bootfs_mknode(bfs, bnp, &nbnp, p, nlen,
256 		    &bootfs_vattr_dir, addr, size) == 0);
257 		p = sp + 1;
258 		bnp = nbnp;
259 	}
260 
261 	nlen = strlen(p);
262 	ret = bootfs_mknode(bfs, bnp, &nbnp, p, nlen, &bootfs_vattr_reg,
263 	    addr, size);
264 	if (ret != 0)
265 		return (ret);
266 
267 	return (0);
268 }
269 
270 /*
271  * We're going to go through every boot time module and construct the
272  * appropriate vnodes for them now. Because there are very few of these that
273  * exist, generally on the order of a handful, we're going to create them all
274  * when the file system is initialized and then tear them all down when the
275  * module gets unloaded.
276  *
277  * The information about the modules is contained in properties on the root of
278  * the devinfo tree. Specifically there are three properties per module:
279  *
280  *   - module-size-%d	int64_t size, in bytes, of the boot time module.
281  *   - module-addr-%d	The address of the boot time module
282  *   - module-name-%d	The string name of the boot time module
283  *
284  * Note that the module-size and module-addr fields are always 64-bit values
285  * regardless of being on a 32-bit or 64-bit kernel. module-name is a string
286  * property.
287  *
288  * There is no property that indicates the total number of such modules. Modules
289  * start at 0 and work their way up incrementally. The first time we can't find
290  * a module or a property, then we stop.
291  */
292 void
293 bootfs_construct(bootfs_t *bfs)
294 {
295 	uint_t id = 0, ndata;
296 	char paddr[64], psize[64], pname[64], *mname;
297 	dev_info_t *root;
298 	uchar_t *datap;
299 	uint64_t size = 0, addr = 0;
300 	int ret;
301 
302 	bootfs_mkroot(bfs);
303 	root = ddi_root_node();
304 
305 	for (;;) {
306 		if (id == UINT32_MAX)
307 			break;
308 
309 		if (snprintf(paddr, sizeof (paddr), "module-addr-%d", id) >
310 		    sizeof (paddr))
311 			break;
312 
313 		if (snprintf(psize, sizeof (paddr), "module-size-%d", id) >
314 		    sizeof (paddr))
315 			break;
316 
317 		if (snprintf(pname, sizeof (paddr), "module-name-%d", id) >
318 		    sizeof (paddr))
319 			break;
320 
321 		if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, root,
322 		    DDI_PROP_DONTPASS, paddr, &datap, &ndata) !=
323 		    DDI_PROP_SUCCESS)
324 			break;
325 
326 		if (ndata == 8)
327 			bcopy(datap, &addr, sizeof (uint64_t));
328 		ddi_prop_free(datap);
329 		if (ndata != 8)
330 			break;
331 
332 		if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, root,
333 		    DDI_PROP_DONTPASS, psize, &datap, &ndata) !=
334 		    DDI_PROP_SUCCESS)
335 			break;
336 		if (ndata == 8)
337 			bcopy(datap, &size, sizeof (uint64_t));
338 		ddi_prop_free(datap);
339 		if (ndata != 8)
340 			break;
341 
342 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
343 		    DDI_PROP_DONTPASS, pname, &mname) != DDI_PROP_SUCCESS)
344 			break;
345 
346 		ret = bootfs_construct_entry(bfs, addr, size, mname);
347 		if (ret == EINVAL)
348 			bfs->bfs_stat.bfss_ndiscards.value.ui32++;
349 		if (ret == EEXIST)
350 			bfs->bfs_stat.bfss_ndups.value.ui32++;
351 		ddi_prop_free(mname);
352 
353 		id++;
354 	}
355 }
356 
357 void
358 bootfs_destruct(bootfs_t *bfs)
359 {
360 	bootfs_node_t *bnp;
361 
362 	while ((bnp = list_remove_head(&bfs->bfs_nodes)) != NULL) {
363 		ASSERT(bnp->bvn_vnp->v_count == 1);
364 		VN_RELE(bnp->bvn_vnp);
365 		kmem_free(bnp->bvn_name, strlen(bnp->bvn_name) + 1);
366 		kmem_cache_free(bootfs_node_cache, bnp);
367 	}
368 }
369