bootrd_cpio: use SLIST from sys/queue.h
[unleashed.git] / kernel / krtld / bootrd_cpio.c
blob848e4621330789dedacfb531df8f4eb8da3ce2c2
1 /*
2 * Copyright 2011-2018 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>
23 #include <sys/queue.h>
26 * A cpio archive is just a sequence of files, each consisting of a header
27 * (struct cpio_hdr) and the file contents.
30 struct cpio_hdr {
31 uint8_t magic[6];
32 uint8_t dev[6];
33 uint8_t ino[6];
34 uint8_t mode[6];
35 uint8_t uid[6];
36 uint8_t gid[6];
37 uint8_t nlink[6];
38 uint8_t rdev[6];
39 uint8_t mtime[11];
40 uint8_t namesize[6];
41 uint8_t filesize[11];
42 char data[];
46 * This structure represents an open file. The list of all open files is
47 * rooted in the open_files global.
49 struct cpio_file {
50 /* pointers into the archive */
51 const struct cpio_hdr *hdr;
52 const char *path; /* pointer into the archive */
53 const void *data; /* pointer into the archive */
55 int fd;
56 off_t off;
57 struct bootstat stat;
59 SLIST_ENTRY(cpio_file) next;
62 extern void *bkmem_alloc(size_t);
63 extern void bkmem_free(void *, size_t);
65 static void cpio_closeall(int flag);
67 static bool mounted;
68 static SLIST_HEAD(cpio_file_list, cpio_file)
69 open_files = SLIST_HEAD_INITIALIZER(open_files);
71 static int
72 cpio_strcmp(const char *a, const char *b)
74 while ((*a != '\0') && (*b != '\0') && (*a == *b)) {
75 a++;
76 b++;
79 if (*a == *b)
80 return 0;
81 if (*a < *b)
82 return -1;
83 return 1;
87 * Returns the parsed number on success, or UINT64_MAX on error. This is
88 * ok because we will never deal with numbers that large in a cpio archive.
90 static uint64_t
91 __get_uint64(const uint8_t *str, size_t len, const size_t output_size)
93 uint64_t v;
95 /* check that we can represent every number */
96 if (len * 3 > output_size)
97 return UINT64_MAX;
99 for (v = 0; len > 0; len--, str++) {
100 const uint8_t c = *str;
102 if ((c < '0') || (c > '7'))
103 return UINT64_MAX;
105 v = (v * 8) + (c - '0');
108 return v;
111 static bool
112 get_uint64(const uint8_t *str, size_t len, uint64_t *out)
114 *out = __get_uint64(str, len, NBBY * sizeof(*out));
115 return *out != UINT64_MAX;
118 static bool
119 get_int64(const uint8_t *str, size_t len, int64_t *out)
121 uint64_t tmp;
123 tmp = __get_uint64(str, len, NBBY * sizeof(*out) - 1);
125 *out = tmp;
127 return tmp != UINT64_MAX;
130 static bool
131 get_uint32(const uint8_t *str, size_t len, uint32_t *out)
133 uint64_t tmp;
135 tmp = __get_uint64(str, len, NBBY * sizeof(*out));
137 *out = tmp;
139 return tmp != UINT64_MAX;
142 static bool
143 get_int32(const uint8_t *str, size_t len, int32_t *out)
145 uint64_t tmp;
147 tmp = __get_uint64(str, len, NBBY * sizeof(*out) - 1);
149 *out = tmp;
151 return tmp != UINT64_MAX;
154 static void
155 add_open_file(struct cpio_file *file)
157 SLIST_INSERT_HEAD(&open_files, file, next);
160 static void
161 remove_open_file(struct cpio_file *file)
163 SLIST_REMOVE(&open_files, file, cpio_file, next);
166 static struct cpio_file *
167 find_open_file(int fd)
169 struct cpio_file *file;
171 if (fd < 0)
172 return NULL;
174 SLIST_FOREACH(file, &open_files, next)
175 if (file->fd == fd)
176 return file;
178 return NULL;
181 static const void *
182 read_ramdisk(size_t off, size_t len)
184 const size_t first_block_offset = off % DEV_BSIZE;
185 fileid_t tmpfile;
187 /* return a dummy non-NULL pointer */
188 if (len == 0)
189 return "";
191 /* we have to read the stuff before the desired location as well */
192 len += first_block_offset;
194 tmpfile.fi_blocknum = off / DEV_BSIZE;
195 tmpfile.fi_count = P2ROUNDUP_TYPED(len, DEV_BSIZE, size_t);
196 tmpfile.fi_memp = NULL;
198 if (diskread(&tmpfile) != 0)
199 return NULL;
201 return tmpfile.fi_memp + first_block_offset;
204 static bool
205 parse_stat(const struct cpio_hdr *hdr, struct bootstat *stat)
207 if (!get_uint64(hdr->dev, sizeof(hdr->dev), &stat->st_dev))
208 return false;
209 if (!get_uint64(hdr->ino, sizeof(hdr->ino), &stat->st_ino))
210 return false;
211 if (!get_uint32(hdr->mode, sizeof(hdr->mode), &stat->st_mode))
212 return false;
213 if (!get_int32(hdr->uid, sizeof(hdr->uid), &stat->st_uid))
214 return false;
215 if (!get_int32(hdr->gid, sizeof(hdr->gid), &stat->st_gid))
216 return false;
217 if (!get_uint32(hdr->nlink, sizeof(hdr->nlink), &stat->st_nlink))
218 return false;
219 if (!get_uint64(hdr->rdev, sizeof(hdr->rdev), &stat->st_rdev))
220 return false;
222 stat->st_mtim.tv_nsec = 0;
223 if (!get_int64(hdr->mtime, sizeof(hdr->mtime), &stat->st_mtim.tv_sec))
224 return false;
226 stat->st_atim = stat->st_mtim;
227 stat->st_ctim = stat->st_mtim;
229 if (!get_uint64(hdr->filesize, sizeof(hdr->filesize), &stat->st_size))
230 return false;
232 stat->st_blksize = DEV_BSIZE;
233 stat->st_blocks = P2ROUNDUP(stat->st_size, DEV_BSIZE);
235 return true;
239 * Check if specified header is for a file with a specific path. If so,
240 * fill in the file struct and return 0. If not, return number of bytes to
241 * skip over to get to the next header. If an error occurs, -1 is returned.
242 * If end of archive is reached, return -2 instead.
244 static ssize_t
245 scan_archive_hdr(const struct cpio_hdr *hdr, size_t off,
246 struct cpio_file *file, const char *wanted_path)
248 struct bootstat stat;
249 uint32_t namesize;
250 uint64_t filesize;
251 const char *path;
252 const void *data;
254 if ((hdr->magic[0] != '0') || (hdr->magic[1] != '7') ||
255 (hdr->magic[2] != '0') || (hdr->magic[3] != '7') ||
256 (hdr->magic[4] != '0') || (hdr->magic[5] != '7'))
257 return -1;
259 if (!get_uint32(hdr->namesize, sizeof(hdr->namesize), &namesize))
260 return -1;
261 if (!get_uint64(hdr->filesize, sizeof(hdr->filesize), &filesize))
262 return -1;
265 * We have the two sizes, let's try to read the name and file
266 * contents to make sure they are part of the ramdisk.
269 off += offsetof(struct cpio_hdr, data[0]);
270 path = read_ramdisk(off, namesize);
271 data = read_ramdisk(off + namesize, filesize);
273 /* either read failing is fatal */
274 if (path == NULL || data == NULL)
275 return -1;
277 if (cpio_strcmp(path, "TRAILER!!!") == 0)
278 return -2;
280 if (cpio_strcmp(path, wanted_path) != 0)
281 return offsetof(struct cpio_hdr, data[namesize + filesize]);
284 * This is the file we want!
287 if (!parse_stat(hdr, &stat))
288 return -1;
290 file->hdr = hdr;
291 file->path = path;
292 file->data = data;
293 file->stat = stat;
295 return 0;
298 static int
299 find_filename(char *path, struct cpio_file *file)
301 size_t off;
304 * The paths in the cpio boot archive omit the leading '/'. So,
305 * skip checking for it. If the searched for path does not include
306 * the leading path (it's a relative path), fail the lookup.
308 if (path[0] != '/')
309 return -1;
311 path++;
313 /* now scan the archive for the relevant file */
315 off = 0;
317 for (;;) {
318 const struct cpio_hdr *hdr;
319 ssize_t size;
321 hdr = read_ramdisk(off, sizeof(struct cpio_hdr));
322 if (hdr == NULL)
323 return -1;
325 size = scan_archive_hdr(hdr, off, file, path);
326 if (size <= 0)
327 return size;
329 off += size;
333 static int
334 bcpio_mountroot(char *str)
336 if (mounted)
337 return -1;
339 mounted = true;
341 return 0;
344 static int
345 bcpio_unmountroot(void)
347 if (!mounted)
348 return -1;
350 mounted = false;
352 return 0;
355 static int
356 bcpio_open(char *path, int flags)
358 static int filedes = 1;
359 struct cpio_file temp_file;
360 struct cpio_file *file;
362 if (find_filename(path, &temp_file) != 0)
363 return -1;
365 file = bkmem_alloc(sizeof(struct cpio_file));
366 file->hdr = temp_file.hdr;
367 file->path = temp_file.path;
368 file->data = temp_file.data;
369 file->stat = temp_file.stat;
370 file->fd = filedes++;
371 file->off = 0;
373 add_open_file(file);
375 return file->fd;
378 static int
379 bcpio_close(int fd)
381 struct cpio_file *file;
383 file = find_open_file(fd);
384 if (file == NULL)
385 return -1;
387 remove_open_file(file);
389 bkmem_free(file, sizeof(struct cpio_file));
391 return 0;
394 static void
395 bcpio_closeall(int flag)
397 struct cpio_file *file;
399 while (!SLIST_EMPTY(&open_files)) {
400 file = SLIST_FIRST(&open_files);
402 if (bcpio_close(file->fd) != 0)
403 printf("closeall invoked close(%d) failed\n", file->fd);
407 static ssize_t
408 bcpio_read(int fd, caddr_t buf, size_t size)
410 struct cpio_file *file;
412 file = find_open_file(fd);
413 if (file == NULL)
414 return -1;
416 if (size == 0)
417 return 0;
419 if (file->off + size > file->stat.st_size)
420 size = file->stat.st_size - file->off;
422 bcopy(file->data + file->off, buf, size);
424 file->off += size;
426 return size;
429 static off_t
430 bcpio_lseek(int fd, off_t addr, int whence)
432 struct cpio_file *file;
434 file = find_open_file(fd);
435 if (file == NULL)
436 return -1;
438 switch (whence) {
439 case SEEK_CUR:
440 file->off += addr;
441 break;
442 case SEEK_SET:
443 file->off = addr;
444 break;
445 case SEEK_END:
446 file->off = file->stat.st_size;
447 break;
448 default:
449 printf("lseek(): invalid whence value %d\n", whence);
450 return -1;
453 return 0;
456 static int
457 bcpio_fstat(int fd, struct bootstat *buf)
459 const struct cpio_file *file;
461 file = find_open_file(fd);
462 if (file == NULL)
463 return -1;
465 *buf = file->stat;
467 return 0;
470 struct boot_fs_ops bcpio_ops = {
471 .fsw_name = "boot_cpio",
472 .fsw_mountroot = bcpio_mountroot,
473 .fsw_unmountroot = bcpio_unmountroot,
474 .fsw_open = bcpio_open,
475 .fsw_close = bcpio_close,
476 .fsw_closeall = bcpio_closeall,
477 .fsw_read = bcpio_read,
478 .fsw_lseek = bcpio_lseek,
479 .fsw_fstat = bcpio_fstat,