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