xref: /illumos-gate/usr/src/common/fs/bootfsops.c (revision 44af466baa3420f5636d8d7d1c9279f8bf27ce23)
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 2013 Joyent, Inc.  All rights reserved.
14  * Copyright 2025 MNX Cloud, Inc.
15  */
16 
17 #include <sys/bootconf.h>
18 #include <sys/types.h>
19 #include <sys/param.h>
20 #include <sys/vnode.h>
21 #include <sys/fs/ufs_fsdir.h>
22 #include <sys/fs/ufs_fs.h>
23 #include <sys/fs/ufs_inode.h>
24 #include <sys/sysmacros.h>
25 #include <sys/bootvfs.h>
26 #include <sys/bootinfo.h>
27 #include <sys/filep.h>
28 #include <sys/sunddi.h>
29 
30 #define	MAX_FILES	MAX_BOOT_MODULES
31 #define	MAX_FDS		256
32 
33 extern void *bkmem_alloc(size_t);
34 extern void bkmem_free(void *, size_t);
35 
36 /*
37  * TODO: Replace these declarations with inclusion of the ordinary userland
38  * bootfs headers once they're available.
39  */
40 typedef struct bfile {
41 	char bf_name[MAXPATHLEN];
42 	caddr_t bf_addr;
43 	size_t bf_size;
44 	struct bfile *bf_next;
45 	uint64_t bf_ino;
46 } bfile_t;
47 
48 typedef struct bf_fd {
49 	bfile_t *fd_file;
50 	off_t fd_pos;
51 } bf_fd_t;
52 
53 static bfile_t *head;
54 static uint_t init_done;
55 static bf_fd_t fds[MAX_FDS];
56 
57 static char cpath[MAXPATHLEN];	/* For canonicalising filenames */
58 
59 static void bbootfs_closeall(int);
60 
61 static void
canonicalise(const char * fn,char * out)62 canonicalise(const char *fn, char *out)
63 {
64 	const char *p;
65 	char *q, *s;
66 	char *last;
67 	char *oc;
68 	int is_slash = 0;
69 	static char scratch[MAXPATHLEN];
70 
71 	if (fn == NULL) {
72 		*out = '\0';
73 		return;
74 	}
75 
76 	/*
77 	 * Remove leading slashes and condense all multiple slashes into one.
78 	 */
79 	p = fn;
80 	while (*p == '/')
81 		++p;
82 
83 	for (q = scratch; *p != '\0'; p++) {
84 		if (*p == '/' && !is_slash) {
85 			*q++ = '/';
86 			is_slash = 1;
87 		} else if (*p != '/') {
88 			*q++ = *p;
89 			is_slash = 0;
90 		}
91 	}
92 	*q = '\0';
93 
94 	if (strncmp(scratch, "system/boot/", 12) == 0 ||
95 	    strcmp(scratch, "system/boot") == 0) {
96 		s = scratch + 12;
97 	} else {
98 		s = scratch;
99 	}
100 
101 	for (last = strsep(&s, "/"), q = oc = out; last != NULL;
102 	    last = strsep(&s, "/")) {
103 		if (strcmp(last, ".") == 0)
104 			continue;
105 		if (strcmp(last, "..") == 0) {
106 			for (oc = q; oc > out && *oc != '/'; oc--)
107 				;
108 			q = oc;
109 			continue;
110 		}
111 		if (q > out)
112 			*q++ = '/';
113 		q += snprintf(q, MAXPATHLEN - (q - out), "%s", last);
114 	}
115 
116 	*q = '\0';
117 }
118 
119 static int
bbootfs_mountroot(char * str __unused)120 bbootfs_mountroot(char *str __unused)
121 {
122 	return (-1);
123 }
124 
125 static int
bbootfs_unmountroot(void)126 bbootfs_unmountroot(void)
127 {
128 	return (-1);
129 }
130 
131 static int
bbootfs_init(void)132 bbootfs_init(void)
133 {
134 	bfile_t *fp;
135 	char propname[32];
136 	uint64_t propval;
137 	uint_t i;
138 
139 	for (i = 0; i < MAX_FILES; i++) {
140 		(void) snprintf(propname, sizeof (propname),
141 		    "module-name-%u", i);
142 		if (do_bsys_getproplen(NULL, propname) < 0)
143 			break;
144 
145 		if ((fp = bkmem_alloc(sizeof (bfile_t))) == NULL) {
146 			bbootfs_closeall(1);
147 			return (-1);
148 		}
149 
150 		(void) do_bsys_getprop(NULL, propname, cpath);
151 		canonicalise(cpath, fp->bf_name);
152 
153 		(void) snprintf(propname, sizeof (propname),
154 		    "module-addr-%u", i);
155 		if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) {
156 			bkmem_free(fp, sizeof (bfile_t));
157 			continue;
158 		}
159 		(void) do_bsys_getprop(NULL, propname, &propval);
160 		fp->bf_addr = (void *)(uintptr_t)propval;
161 
162 		(void) snprintf(propname, sizeof (propname),
163 		    "module-size-%u", i);
164 		if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) {
165 			bkmem_free(fp, sizeof (bfile_t));
166 			continue;
167 		}
168 		(void) do_bsys_getprop(NULL, propname, &propval);
169 		fp->bf_size = (size_t)propval;
170 		fp->bf_ino = i;
171 
172 		fp->bf_next = head;
173 		head = fp;
174 	}
175 
176 	return (0);
177 }
178 
179 static int
bbootfs_open(char * fn,int flags __unused)180 bbootfs_open(char *fn, int flags __unused)
181 {
182 	uint_t i;
183 	bfile_t *fp;
184 
185 	if (!init_done) {
186 		if (bbootfs_init() != 0)
187 			return (-1);
188 
189 		init_done = 1;
190 	}
191 
192 	canonicalise(fn, cpath);
193 
194 	for (fp = head; fp != NULL; fp = fp->bf_next) {
195 		if (strcmp(fp->bf_name, cpath) == 0)
196 			break;
197 	}
198 
199 	if (fp == NULL)
200 		return (-1);
201 
202 	for (i = 0; i < MAX_FDS; i++) {
203 		if (fds[i].fd_file == NULL) {
204 			fds[i].fd_file = fp;
205 			fds[i].fd_pos = 0;
206 			return (i);
207 		}
208 	}
209 
210 	return (-1);
211 }
212 
213 static int
bbootfs_close(int fd)214 bbootfs_close(int fd)
215 {
216 	if (fds[fd].fd_file == NULL)
217 		return (-1);
218 
219 	fds[fd].fd_file = NULL;
220 	fds[fd].fd_pos = 0;
221 
222 	return (0);
223 }
224 
225 static ssize_t
bbootfs_read(int fd,caddr_t buf,size_t size)226 bbootfs_read(int fd, caddr_t buf, size_t size)
227 {
228 	ssize_t len;
229 	bf_fd_t *fdp = &fds[fd];
230 
231 	if (fdp->fd_file == NULL)
232 		return (-1);
233 
234 	if (fdp->fd_pos >= fdp->fd_file->bf_size)
235 		return (-1);
236 
237 	if (fdp->fd_pos + size > fdp->fd_file->bf_size)
238 		len = fdp->fd_file->bf_size - fdp->fd_pos;
239 	else
240 		len = size;
241 
242 	bcopy(fdp->fd_file->bf_addr + fdp->fd_pos, buf, len);
243 
244 	fdp->fd_pos += len;
245 
246 	return (len);
247 }
248 
249 static off_t
bbootfs_lseek(int fd,off_t addr,int whence)250 bbootfs_lseek(int fd, off_t addr, int whence)
251 {
252 	bf_fd_t *fdp = &fds[fd];
253 
254 	if (fdp->fd_file == NULL)
255 		return (-1);
256 
257 	switch (whence) {
258 	case SEEK_CUR:
259 		fdp->fd_pos += addr;
260 		break;
261 	case SEEK_SET:
262 		fdp->fd_pos = addr;
263 		break;
264 	case SEEK_END:
265 		fdp->fd_pos = fdp->fd_file->bf_size;
266 		break;
267 	default:
268 		return (-1);
269 	}
270 
271 	return (0);
272 }
273 
274 static int
bbootfs_fstat(int fd,struct bootstat * bsp)275 bbootfs_fstat(int fd, struct bootstat *bsp)
276 {
277 	bf_fd_t *fdp = &fds[fd];
278 
279 	if (fdp->fd_file == NULL)
280 		return (-1);
281 
282 	bsp->st_dev = 1;
283 	bsp->st_ino = fdp->fd_file->bf_ino;
284 	bsp->st_mode = 0444;
285 	bsp->st_nlink = 1;
286 	bsp->st_uid = bsp->st_gid = 0;
287 	bsp->st_rdev = 0;
288 	bsp->st_size = fdp->fd_file->bf_size;
289 	bsp->st_blksize = 1;
290 	bsp->st_blocks = fdp->fd_file->bf_size;
291 	(void) strcpy(bsp->st_fstype, "bootfs");
292 
293 	return (0);
294 }
295 
296 static void
bbootfs_closeall(int flag __unused)297 bbootfs_closeall(int flag __unused)
298 {
299 	bfile_t *fp;
300 
301 	while (head != NULL) {
302 		fp = head;
303 		head = head->bf_next;
304 
305 		bkmem_free(fp, sizeof (bfile_t));
306 	}
307 
308 	init_done = 0;
309 }
310 
311 struct boot_fs_ops bbootfs_ops = {
312 	"bootfs",
313 	bbootfs_mountroot,
314 	bbootfs_unmountroot,
315 	bbootfs_open,
316 	bbootfs_close,
317 	bbootfs_read,
318 	bbootfs_lseek,
319 	bbootfs_fstat,
320 	bbootfs_closeall,
321 	NULL
322 };
323