dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / kernel / krtld / bootrd_cpio.c
blobd23fa2c1cbfa5a89ce9d74917ee4caeac61be5d1
1 /*
2 * Copyright 2011-2017 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
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.
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.
17 #include <sys/types.h>
18 #include <sys/sysmacros.h>
19 #include <sys/bootvfs.h>
20 #include <sys/filep.h>
21 #include <sys/sunddi.h>
22 #include <sys/ccompile.h>
25 * A cpio archive is just a sequence of files, each consisting of a header
26 * (struct cpio_hdr) and the file contents.
29 struct cpio_hdr {
30 uint8_t magic[6];
31 uint8_t dev[6];
32 uint8_t ino[6];
33 uint8_t mode[6];
34 uint8_t uid[6];
35 uint8_t gid[6];
36 uint8_t nlink[6];
37 uint8_t rdev[6];
38 uint8_t mtime[11];
39 uint8_t namesize[6];
40 uint8_t filesize[11];
41 char data[];
45 * On mount, we parse the whole archive and instantiate a struct cpio_file
46 * for each. While this may be a bit wasteful, it makes the subsequent
47 * operations much faster and the code much simpler. The list of all files
48 * is held by the open_files global.
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 */
56 int fd;
57 off_t off;
58 struct bootstat stat;
60 struct cpio_file *next;
61 struct cpio_file *prev;
64 extern void *bkmem_alloc(size_t);
65 extern void bkmem_free(void *, size_t);
67 static void cpio_closeall(int flag);
69 static bool mounted;
70 static struct cpio_file *open_files;
72 static int
73 cpio_strcmp(const char *a, const char *b)
75 while ((*a != '\0') && (*b != '\0') && (*a == *b)) {
76 a++;
77 b++;
80 if (*a == *b)
81 return 0;
82 if (*a < *b)
83 return -1;
84 return 1;
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.
91 static uint64_t
92 __get_uint64(const uint8_t *str, size_t len, const size_t output_size)
94 uint64_t v;
96 /* check that we can represent every number */
97 if (len * 3 > output_size)
98 return UINT64_MAX;
100 for (v = 0; len > 0; len--, str++) {
101 const uint8_t c = *str;
103 if ((c < '0') || (c > '7'))
104 return UINT64_MAX;
106 v = (v * 8) + (c - '0');
109 return v;
112 static bool
113 get_uint64(const uint8_t *str, size_t len, uint64_t *out)
115 *out = __get_uint64(str, len, NBBY * sizeof(*out));
116 return *out != UINT64_MAX;
119 static bool
120 get_int64(const uint8_t *str, size_t len, int64_t *out)
122 uint64_t tmp;
124 tmp = __get_uint64(str, len, NBBY * sizeof(*out) - 1);
126 *out = tmp;
128 return tmp != UINT64_MAX;
131 static bool
132 get_uint32(const uint8_t *str, size_t len, uint32_t *out)
134 uint64_t tmp;
136 tmp = __get_uint64(str, len, NBBY * sizeof(*out));
138 *out = tmp;
140 return tmp != UINT64_MAX;
143 static bool
144 get_int32(const uint8_t *str, size_t len, int32_t *out)
146 uint64_t tmp;
148 tmp = __get_uint64(str, len, NBBY * sizeof(*out) - 1);
150 *out = tmp;
152 return tmp != UINT64_MAX;
155 static void
156 add_open_file(struct cpio_file *file)
158 file->next = open_files;
159 file->prev = NULL;
160 open_files = file;
163 static void
164 remove_open_file(struct cpio_file *file)
166 if (file == open_files)
167 open_files = file->next;
168 else
169 file->prev->next = file->next;
171 if (file->next != NULL)
172 file->next->prev = file->prev;
175 static struct cpio_file *
176 find_open_file(int fd)
178 struct cpio_file *file;
180 if (fd < 0)
181 return NULL;
183 for (file = open_files; file != NULL; file = file->next)
184 if (file->fd == fd)
185 return file;
187 return NULL;
190 static const void *
191 read_ramdisk(size_t off, size_t len)
193 const size_t first_block_offset = off % DEV_BSIZE;
194 fileid_t tmpfile;
196 /* return a dummy non-NULL pointer */
197 if (len == 0)
198 return "";
200 /* we have to read the stuff before the desired location as well */
201 len += first_block_offset;
203 tmpfile.fi_blocknum = off / DEV_BSIZE;
204 tmpfile.fi_count = P2ROUNDUP_TYPED(len, DEV_BSIZE, size_t);
205 tmpfile.fi_memp = NULL;
207 if (diskread(&tmpfile) != 0)
208 return NULL;
210 return tmpfile.fi_memp + first_block_offset;
213 static bool
214 parse_stat(const struct cpio_hdr *hdr, struct bootstat *stat)
216 if (!get_uint64(hdr->dev, sizeof(hdr->dev), &stat->st_dev))
217 return false;
218 if (!get_uint64(hdr->ino, sizeof(hdr->ino), &stat->st_ino))
219 return false;
220 if (!get_uint32(hdr->mode, sizeof(hdr->mode), &stat->st_mode))
221 return false;
222 if (!get_int32(hdr->uid, sizeof(hdr->uid), &stat->st_uid))
223 return false;
224 if (!get_int32(hdr->gid, sizeof(hdr->gid), &stat->st_gid))
225 return false;
226 if (!get_uint32(hdr->nlink, sizeof(hdr->nlink), &stat->st_nlink))
227 return false;
228 if (!get_uint64(hdr->rdev, sizeof(hdr->rdev), &stat->st_rdev))
229 return false;
231 stat->st_mtim.tv_nsec = 0;
232 if (!get_int64(hdr->mtime, sizeof(hdr->mtime), &stat->st_mtim.tv_sec))
233 return false;
235 stat->st_atim = stat->st_mtim;
236 stat->st_ctim = stat->st_mtim;
238 if (!get_uint64(hdr->filesize, sizeof(hdr->filesize), &stat->st_size))
239 return false;
241 stat->st_blksize = DEV_BSIZE;
242 stat->st_blocks = P2ROUNDUP(stat->st_size, DEV_BSIZE);
244 return true;
248 * Check if specified header is for a file with a specific path. If so,
249 * fill in the file struct and return 0. If not, return number of bytes to
250 * skip over to get to the next header. If an error occurs, -1 is returned.
251 * If end of archive is reached, return -2 instead.
253 static ssize_t
254 scan_archive_hdr(const struct cpio_hdr *hdr, size_t off,
255 struct cpio_file *file, const char *wanted_path)
257 struct bootstat stat;
258 uint32_t namesize;
259 uint64_t filesize;
260 const char *path;
261 const void *data;
263 if ((hdr->magic[0] != '0') || (hdr->magic[1] != '7') ||
264 (hdr->magic[2] != '0') || (hdr->magic[3] != '7') ||
265 (hdr->magic[4] != '0') || (hdr->magic[5] != '7'))
266 return -1;
268 if (!get_uint32(hdr->namesize, sizeof(hdr->namesize), &namesize))
269 return -1;
270 if (!get_uint64(hdr->filesize, sizeof(hdr->filesize), &filesize))
271 return -1;
274 * We have the two sizes, let's try to read the name and file
275 * contents to make sure they are part of the ramdisk.
278 off += offsetof(struct cpio_hdr, data[0]);
279 path = read_ramdisk(off, namesize);
280 data = read_ramdisk(off + namesize, filesize);
282 /* either read failing is fatal */
283 if (path == NULL || data == NULL)
284 return -1;
286 if (cpio_strcmp(path, "TRAILER!!!") == 0)
287 return -2;
289 if (cpio_strcmp(path, wanted_path) != 0)
290 return offsetof(struct cpio_hdr, data[namesize + filesize]);
293 * This is the file we want!
296 if (!parse_stat(hdr, &stat))
297 return -1;
299 file->hdr = hdr;
300 file->path = path;
301 file->data = data;
302 file->stat = stat;
304 return 0;
307 static int
308 find_filename(char *path, struct cpio_file *file)
310 size_t off;
313 * The paths in the cpio boot archive omit the leading '/'. So,
314 * skip checking for it. If the searched for path does not include
315 * the leading path (it's a relative path), fail the lookup.
317 if (path[0] != '/')
318 return -1;
320 path++;
322 /* now scan the archive for the relevant file */
324 off = 0;
326 for (;;) {
327 const struct cpio_hdr *hdr;
328 ssize_t size;
330 hdr = read_ramdisk(off, sizeof(struct cpio_hdr));
331 if (hdr == NULL)
332 return -1;
334 size = scan_archive_hdr(hdr, off, file, path);
335 if (size <= 0)
336 return size;
338 off += size;
342 static int
343 bcpio_mountroot(char *str)
345 if (mounted)
346 return -1;
348 mounted = true;
350 return 0;
353 static int
354 bcpio_unmountroot(void)
356 if (!mounted)
357 return -1;
359 mounted = false;
361 return 0;
364 static int
365 bcpio_open(char *path, int flags)
367 static int filedes = 1;
368 struct cpio_file temp_file;
369 struct cpio_file *file;
371 if (find_filename(path, &temp_file) != 0)
372 return -1;
374 file = bkmem_alloc(sizeof(struct cpio_file));
375 file->hdr = temp_file.hdr;
376 file->path = temp_file.path;
377 file->data = temp_file.data;
378 file->stat = temp_file.stat;
379 file->fd = filedes++;
380 file->off = 0;
382 add_open_file(file);
384 return file->fd;
387 static int
388 bcpio_close(int fd)
390 struct cpio_file *file;
392 file = find_open_file(fd);
393 if (file == NULL)
394 return -1;
396 remove_open_file(file);
398 bkmem_free(file, sizeof(struct cpio_file));
400 return 0;
403 static void
404 bcpio_closeall(int flag)
406 struct cpio_file *file, *next;
408 file = open_files;
410 while (file != NULL) {
411 int fd = file->fd;
413 next = file->next;
415 if (bcpio_close(fd) != 0)
416 printf("closeall invoked close(%d) failed\n", fd);
418 file = next;
422 static ssize_t
423 bcpio_read(int fd, caddr_t buf, size_t size)
425 struct cpio_file *file;
427 file = find_open_file(fd);
428 if (file == NULL)
429 return -1;
431 if (size == 0)
432 return 0;
434 if (file->off + size > file->stat.st_size)
435 size = file->stat.st_size - file->off;
437 bcopy(file->data + file->off, buf, size);
439 file->off += size;
441 return size;
444 static off_t
445 bcpio_lseek(int fd, off_t addr, int whence)
447 struct cpio_file *file;
449 file = find_open_file(fd);
450 if (file == NULL)
451 return -1;
453 switch (whence) {
454 case SEEK_CUR:
455 file->off += addr;
456 break;
457 case SEEK_SET:
458 file->off = addr;
459 break;
460 case SEEK_END:
461 file->off = file->stat.st_size;
462 break;
463 default:
464 printf("lseek(): invalid whence value %d\n", whence);
465 return -1;
468 return 0;
471 static int
472 bcpio_fstat(int fd, struct bootstat *buf)
474 const struct cpio_file *file;
476 file = find_open_file(fd);
477 if (file == NULL)
478 return -1;
480 *buf = file->stat;
482 return 0;
485 struct boot_fs_ops bcpio_ops = {
486 .fsw_name = "boot_cpio",
487 .fsw_mountroot = bcpio_mountroot,
488 .fsw_unmountroot = bcpio_unmountroot,
489 .fsw_open = bcpio_open,
490 .fsw_close = bcpio_close,
491 .fsw_closeall = bcpio_closeall,
492 .fsw_read = bcpio_read,
493 .fsw_lseek = bcpio_lseek,
494 .fsw_fstat = bcpio_fstat,