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 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 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 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 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 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 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 156 add_open_file(struct cpio_file *file) 157 { 158 SLIST_INSERT_HEAD(&open_files, file, next); 159 } 160 161 static void 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 * 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 * 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 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 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 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 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 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 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 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 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 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 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 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