1 /*
2 * Copyright 2011-2017 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/types.h>
18 #include <sys/stdbool.h>
19 #include <sys/sysmacros.h>
20 #include <sys/bootvfs.h>
21 #include <sys/filep.h>
22 #include <sys/sunddi.h>
23 #include <sys/ccompile.h>
24 #include <sys/queue.h>
25
26 /*
27 * A cpio archive is just a sequence of files, each consisting of a header
28 * (struct cpio_hdr) and the file contents.
29 */
30
31 struct cpio_hdr {
32 uint8_t magic[6];
33 uint8_t dev[6];
34 uint8_t ino[6];
35 uint8_t mode[6];
36 uint8_t uid[6];
37 uint8_t gid[6];
38 uint8_t nlink[6];
39 uint8_t rdev[6];
40 uint8_t mtime[11];
41 uint8_t namesize[6];
42 uint8_t filesize[11];
43 char data[];
44 };
45
46 /*
47 * This structure represents an open file. The list of all open files is
48 * rooted in the open_files global.
49 */
50 struct cpio_file {
51 /* pointers into the archive */
52 const struct cpio_hdr *hdr;
53 const char *path; /* pointer into the archive */
54 const void *data; /* pointer into the archive */
55
56 int fd;
57 off_t off;
58 struct bootstat stat;
59
60 SLIST_ENTRY(cpio_file) next;
61 };
62
63 extern void *bkmem_alloc(size_t);
64 extern void bkmem_free(void *, size_t);
65
66 static void cpio_closeall(int flag);
67
68 static bool mounted;
69 static SLIST_HEAD(cpio_file_list, cpio_file)
70 open_files = SLIST_HEAD_INITIALIZER(open_files);
71
72 static int
cpio_strcmp(const char * a,const char * b)73 cpio_strcmp(const char *a, const char *b)
74 {
75 while ((*a != '\0') && (*b != '\0') && (*a == *b)) {
76 a++;
77 b++;
78 }
79
80 if (*a == *b)
81 return (0);
82 if (*a < *b)
83 return (-1);
84 return (1);
85 }
86
87 /*
88 * Returns the parsed number on success, or UINT64_MAX on error. This is
89 * ok because we will never deal with numbers that large in a cpio archive.
90 */
91 static uint64_t
__get_uint64(const uint8_t * str,size_t len,const size_t output_size)92 __get_uint64(const uint8_t *str, size_t len, const size_t output_size)
93 {
94 uint64_t v;
95
96 /* check that we can represent every number */
97 if (len * 3 > output_size)
98 return (UINT64_MAX);
99
100 for (v = 0; len > 0; len--, str++) {
101 const uint8_t c = *str;
102
103 if ((c < '0') || (c > '7'))
104 return (UINT64_MAX);
105
106 v = (v * 8) + (c - '0');
107 }
108
109 return (v);
110 }
111
112 static bool
get_uint64(const uint8_t * str,size_t len,uint64_t * out)113 get_uint64(const uint8_t *str, size_t len, uint64_t *out)
114 {
115 *out = __get_uint64(str, len, NBBY * sizeof (*out));
116 return (*out != UINT64_MAX);
117 }
118
119 static bool
get_int64(const uint8_t * str,size_t len,int64_t * out)120 get_int64(const uint8_t *str, size_t len, int64_t *out)
121 {
122 uint64_t tmp;
123
124 tmp = __get_uint64(str, len, NBBY * sizeof (*out) - 1);
125
126 *out = tmp;
127
128 return (tmp != UINT64_MAX);
129 }
130
131 static bool
get_uint32(const uint8_t * str,size_t len,uint32_t * out)132 get_uint32(const uint8_t *str, size_t len, uint32_t *out)
133 {
134 uint64_t tmp;
135
136 tmp = __get_uint64(str, len, NBBY * sizeof (*out));
137
138 *out = tmp;
139
140 return (tmp != UINT64_MAX);
141 }
142
143 static bool
get_int32(const uint8_t * str,size_t len,int32_t * out)144 get_int32(const uint8_t *str, size_t len, int32_t *out)
145 {
146 uint64_t tmp;
147
148 tmp = __get_uint64(str, len, NBBY * sizeof (*out) - 1);
149
150 *out = tmp;
151
152 return (tmp != UINT64_MAX);
153 }
154
155 static void
add_open_file(struct cpio_file * file)156 add_open_file(struct cpio_file *file)
157 {
158 SLIST_INSERT_HEAD(&open_files, file, next);
159 }
160
161 static void
remove_open_file(struct cpio_file * file)162 remove_open_file(struct cpio_file *file)
163 {
164 SLIST_REMOVE(&open_files, file, cpio_file, next);
165 }
166
167 static struct cpio_file *
find_open_file(int fd)168 find_open_file(int fd)
169 {
170 struct cpio_file *file;
171
172 if (fd < 0)
173 return (NULL);
174
175 SLIST_FOREACH(file, &open_files, next)
176 if (file->fd == fd)
177 return (file);
178
179 return (NULL);
180 }
181
182 static const void *
read_ramdisk(size_t off,size_t len)183 read_ramdisk(size_t off, size_t len)
184 {
185 const size_t first_block_offset = off % DEV_BSIZE;
186 fileid_t tmpfile;
187
188 /* return a dummy non-NULL pointer */
189 if (len == 0)
190 return ("");
191
192 /* we have to read the stuff before the desired location as well */
193 len += first_block_offset;
194
195 tmpfile.fi_blocknum = off / DEV_BSIZE;
196 tmpfile.fi_count = P2ROUNDUP_TYPED(len, DEV_BSIZE, size_t);
197 tmpfile.fi_memp = NULL;
198
199 if (diskread(&tmpfile) != 0)
200 return (NULL);
201
202 return (tmpfile.fi_memp + first_block_offset);
203 }
204
205 static bool
parse_stat(const struct cpio_hdr * hdr,struct bootstat * stat)206 parse_stat(const struct cpio_hdr *hdr, struct bootstat *stat)
207 {
208 if (!get_uint64(hdr->dev, sizeof (hdr->dev), &stat->st_dev))
209 return (false);
210 if (!get_uint64(hdr->ino, sizeof (hdr->ino), &stat->st_ino))
211 return (false);
212 if (!get_uint32(hdr->mode, sizeof (hdr->mode), &stat->st_mode))
213 return (false);
214 if (!get_int32(hdr->uid, sizeof (hdr->uid), &stat->st_uid))
215 return (false);
216 if (!get_int32(hdr->gid, sizeof (hdr->gid), &stat->st_gid))
217 return (false);
218 if (!get_uint32(hdr->nlink, sizeof (hdr->nlink), &stat->st_nlink))
219 return (false);
220 if (!get_uint64(hdr->rdev, sizeof (hdr->rdev), &stat->st_rdev))
221 return (false);
222
223 stat->st_mtim.tv_nsec = 0;
224 if (!get_int64(hdr->mtime, sizeof (hdr->mtime), &stat->st_mtim.tv_sec))
225 return (false);
226
227 stat->st_atim = stat->st_mtim;
228 stat->st_ctim = stat->st_mtim;
229
230 if (!get_uint64(hdr->filesize, sizeof (hdr->filesize), &stat->st_size))
231 return (false);
232
233 stat->st_blksize = DEV_BSIZE;
234 stat->st_blocks = P2ROUNDUP(stat->st_size, DEV_BSIZE);
235
236 return (true);
237 }
238
239 /*
240 * Check if specified header is for a file with a specific path. If so,
241 * fill in the file struct and return 0. If not, return number of bytes to
242 * skip over to get to the next header. If an error occurs, -1 is returned.
243 * If end of archive is reached, return -2 instead.
244 */
245 static ssize_t
scan_archive_hdr(const struct cpio_hdr * hdr,size_t off,struct cpio_file * file,const char * wanted_path)246 scan_archive_hdr(const struct cpio_hdr *hdr, size_t off,
247 struct cpio_file *file, const char *wanted_path)
248 {
249 struct bootstat stat;
250 uint32_t namesize;
251 uint64_t filesize;
252 const char *path;
253 const void *data;
254
255 if ((hdr->magic[0] != '0') || (hdr->magic[1] != '7') ||
256 (hdr->magic[2] != '0') || (hdr->magic[3] != '7') ||
257 (hdr->magic[4] != '0') || (hdr->magic[5] != '7'))
258 return (-1);
259
260 if (!get_uint32(hdr->namesize, sizeof (hdr->namesize), &namesize))
261 return (-1);
262 if (!get_uint64(hdr->filesize, sizeof (hdr->filesize), &filesize))
263 return (-1);
264
265 /*
266 * We have the two sizes, let's try to read the name and file
267 * contents to make sure they are part of the ramdisk.
268 */
269
270 off += offsetof(struct cpio_hdr, data[0]);
271 path = read_ramdisk(off, namesize);
272 data = read_ramdisk(off + namesize, filesize);
273
274 /* either read failing is fatal */
275 if (path == NULL || data == NULL)
276 return (-1);
277
278 if (cpio_strcmp(path, "TRAILER!!!") == 0)
279 return (-2);
280
281 if (cpio_strcmp(path, wanted_path) != 0)
282 return (offsetof(struct cpio_hdr, data[namesize + filesize]));
283
284 /*
285 * This is the file we want!
286 */
287
288 if (!parse_stat(hdr, &stat))
289 return (-1);
290
291 file->hdr = hdr;
292 file->path = path;
293 file->data = data;
294 file->stat = stat;
295
296 return (0);
297 }
298
299 static int
find_filename(char * path,struct cpio_file * file)300 find_filename(char *path, struct cpio_file *file)
301 {
302 size_t off;
303
304 /*
305 * The paths in the cpio boot archive omit the leading '/'. So,
306 * skip checking for it. If the searched for path does not include
307 * the leading path (it's a relative path), fail the lookup.
308 */
309 if (path[0] != '/')
310 return (-1);
311
312 path++;
313
314 /* now scan the archive for the relevant file */
315
316 off = 0;
317
318 for (;;) {
319 const struct cpio_hdr *hdr;
320 ssize_t size;
321
322 hdr = read_ramdisk(off, sizeof (struct cpio_hdr));
323 if (hdr == NULL)
324 return (-1);
325
326 size = scan_archive_hdr(hdr, off, file, path);
327 if (size <= 0)
328 return (size);
329
330 off += size;
331 }
332 }
333
334 /* ARGSUSED */
335 static int
bcpio_mountroot(char * str __unused)336 bcpio_mountroot(char *str __unused)
337 {
338 if (mounted)
339 return (-1);
340
341 mounted = true;
342
343 return (0);
344 }
345
346 static int
bcpio_unmountroot(void)347 bcpio_unmountroot(void)
348 {
349 if (!mounted)
350 return (-1);
351
352 mounted = false;
353
354 return (0);
355 }
356
357 /* ARGSUSED */
358 static int
bcpio_open(char * path,int flags __unused)359 bcpio_open(char *path, int flags __unused)
360 {
361 static int filedes = 1;
362 struct cpio_file temp_file;
363 struct cpio_file *file;
364
365 if (find_filename(path, &temp_file) != 0)
366 return (-1);
367
368 file = bkmem_alloc(sizeof (struct cpio_file));
369 file->hdr = temp_file.hdr;
370 file->path = temp_file.path;
371 file->data = temp_file.data;
372 file->stat = temp_file.stat;
373 file->fd = filedes++;
374 file->off = 0;
375
376 add_open_file(file);
377
378 return (file->fd);
379 }
380
381 static int
bcpio_close(int fd)382 bcpio_close(int fd)
383 {
384 struct cpio_file *file;
385
386 file = find_open_file(fd);
387 if (file == NULL)
388 return (-1);
389
390 remove_open_file(file);
391
392 bkmem_free(file, sizeof (struct cpio_file));
393
394 return (0);
395 }
396
397 /* ARGSUSED */
398 static void
bcpio_closeall(int flag __unused)399 bcpio_closeall(int flag __unused)
400 {
401 struct cpio_file *file;
402
403 while (!SLIST_EMPTY(&open_files)) {
404 file = SLIST_FIRST(&open_files);
405
406 if (bcpio_close(file->fd) != 0)
407 printf("closeall invoked close(%d) failed\n", file->fd);
408 }
409 }
410
411 static ssize_t
bcpio_read(int fd,caddr_t buf,size_t size)412 bcpio_read(int fd, caddr_t buf, size_t size)
413 {
414 struct cpio_file *file;
415
416 file = find_open_file(fd);
417 if (file == NULL)
418 return (-1);
419
420 if (size == 0)
421 return (0);
422
423 if (file->off + size > file->stat.st_size)
424 size = file->stat.st_size - file->off;
425
426 bcopy((void *)((uintptr_t)file->data + file->off), buf, size);
427
428 file->off += size;
429
430 return (size);
431 }
432
433 static off_t
bcpio_lseek(int fd,off_t addr,int whence)434 bcpio_lseek(int fd, off_t addr, int whence)
435 {
436 struct cpio_file *file;
437
438 file = find_open_file(fd);
439 if (file == NULL)
440 return (-1);
441
442 switch (whence) {
443 case SEEK_CUR:
444 file->off += addr;
445 break;
446 case SEEK_SET:
447 file->off = addr;
448 break;
449 case SEEK_END:
450 file->off = file->stat.st_size;
451 break;
452 default:
453 printf("lseek(): invalid whence value %d\n", whence);
454 return (-1);
455 }
456
457 return (0);
458 }
459
460 static int
bcpio_fstat(int fd,struct bootstat * buf)461 bcpio_fstat(int fd, struct bootstat *buf)
462 {
463 const struct cpio_file *file;
464
465 file = find_open_file(fd);
466 if (file == NULL)
467 return (-1);
468
469 *buf = file->stat;
470
471 return (0);
472 }
473
474 struct boot_fs_ops bcpio_ops = {
475 .fsw_name = "boot_cpio",
476 .fsw_mountroot = bcpio_mountroot,
477 .fsw_unmountroot = bcpio_unmountroot,
478 .fsw_open = bcpio_open,
479 .fsw_close = bcpio_close,
480 .fsw_closeall = bcpio_closeall,
481 .fsw_read = bcpio_read,
482 .fsw_lseek = bcpio_lseek,
483 .fsw_fstat = bcpio_fstat,
484 };
485