1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <stdint.h> 6 #include <stdbool.h> 7 #include <sys/types.h> 8 #include <sys/stat.h> 9 #include <string.h> 10 #include <unistd.h> 11 #include <time.h> 12 #include <fcntl.h> 13 #include <errno.h> 14 #include <ctype.h> 15 #include <limits.h> 16 17 /* 18 * Original work by Jeff Garzik 19 * 20 * External file lists, symlink, pipe and fifo support by Thayne Harbaugh 21 * Hard link support by Luciano Rocha 22 */ 23 24 #define xstr(s) #s 25 #define str(s) xstr(s) 26 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 27 #define CPIO_HDR_LEN 110 28 #define CPIO_TRAILER "TRAILER!!!" 29 #define padlen(_off, _align) (((_align) - ((_off) & ((_align) - 1))) % (_align)) 30 31 static char padding[512]; 32 static unsigned int offset; 33 static unsigned int ino = 721; 34 static time_t default_mtime; 35 static bool do_file_mtime; 36 static bool do_csum = false; 37 static int outfd = STDOUT_FILENO; 38 39 struct file_handler { 40 const char *type; 41 int (*handler)(const char *line); 42 }; 43 44 static int push_buf(const char *name, size_t name_len) 45 { 46 ssize_t len; 47 48 len = write(outfd, name, name_len); 49 if (len != name_len) 50 return -1; 51 52 offset += name_len; 53 return 0; 54 } 55 56 static int push_pad(size_t padlen) 57 { 58 ssize_t len = 0; 59 60 if (!padlen) 61 return 0; 62 63 if (padlen < sizeof(padding)) 64 len = write(outfd, padding, padlen); 65 if (len != padlen) 66 return -1; 67 68 offset += padlen; 69 return 0; 70 } 71 72 static int push_rest(const char *name, size_t name_len) 73 { 74 ssize_t len; 75 76 len = write(outfd, name, name_len); 77 if (len != name_len) 78 return -1; 79 80 offset += name_len; 81 82 return push_pad(padlen(name_len + CPIO_HDR_LEN, 4)); 83 } 84 85 static int cpio_trailer(void) 86 { 87 int len; 88 unsigned int namesize = sizeof(CPIO_TRAILER); 89 90 len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" 91 "%08X%08X%08X%08X%08X%08X%08X", 92 do_csum ? "070702" : "070701", /* magic */ 93 0, /* ino */ 94 0, /* mode */ 95 (long) 0, /* uid */ 96 (long) 0, /* gid */ 97 1, /* nlink */ 98 (long) 0, /* mtime */ 99 0, /* filesize */ 100 0, /* major */ 101 0, /* minor */ 102 0, /* rmajor */ 103 0, /* rminor */ 104 namesize, /* namesize */ 105 0); /* chksum */ 106 offset += len; 107 108 if (len != CPIO_HDR_LEN || 109 push_rest(CPIO_TRAILER, namesize) < 0 || 110 push_pad(padlen(offset, 512)) < 0) 111 return -1; 112 113 return fsync(outfd); 114 } 115 116 static int cpio_mkslink(const char *name, const char *target, 117 unsigned int mode, uid_t uid, gid_t gid) 118 { 119 int len; 120 unsigned int namesize, targetsize = strlen(target) + 1; 121 122 if (name[0] == '/') 123 name++; 124 namesize = strlen(name) + 1; 125 126 len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" 127 "%08X%08X%08X%08X%08X%08X%08X", 128 do_csum ? "070702" : "070701", /* magic */ 129 ino++, /* ino */ 130 S_IFLNK | mode, /* mode */ 131 (long) uid, /* uid */ 132 (long) gid, /* gid */ 133 1, /* nlink */ 134 (long) default_mtime, /* mtime */ 135 targetsize, /* filesize */ 136 3, /* major */ 137 1, /* minor */ 138 0, /* rmajor */ 139 0, /* rminor */ 140 namesize, /* namesize */ 141 0); /* chksum */ 142 offset += len; 143 144 if (len != CPIO_HDR_LEN || 145 push_buf(name, namesize) < 0 || 146 push_pad(padlen(offset, 4)) < 0 || 147 push_buf(target, targetsize) < 0 || 148 push_pad(padlen(offset, 4)) < 0) 149 return -1; 150 151 return 0; 152 153 } 154 155 static int cpio_mkslink_line(const char *line) 156 { 157 char name[PATH_MAX + 1]; 158 char target[PATH_MAX + 1]; 159 unsigned int mode; 160 int uid; 161 int gid; 162 int rc = -1; 163 164 if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) { 165 fprintf(stderr, "Unrecognized dir format '%s'", line); 166 goto fail; 167 } 168 rc = cpio_mkslink(name, target, mode, uid, gid); 169 fail: 170 return rc; 171 } 172 173 static int cpio_mkgeneric(const char *name, unsigned int mode, 174 uid_t uid, gid_t gid) 175 { 176 int len; 177 unsigned int namesize; 178 179 if (name[0] == '/') 180 name++; 181 namesize = strlen(name) + 1; 182 183 len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" 184 "%08X%08X%08X%08X%08X%08X%08X", 185 do_csum ? "070702" : "070701", /* magic */ 186 ino++, /* ino */ 187 mode, /* mode */ 188 (long) uid, /* uid */ 189 (long) gid, /* gid */ 190 2, /* nlink */ 191 (long) default_mtime, /* mtime */ 192 0, /* filesize */ 193 3, /* major */ 194 1, /* minor */ 195 0, /* rmajor */ 196 0, /* rminor */ 197 namesize, /* namesize */ 198 0); /* chksum */ 199 offset += len; 200 201 if (len != CPIO_HDR_LEN || 202 push_rest(name, namesize) < 0) 203 return -1; 204 205 return 0; 206 } 207 208 enum generic_types { 209 GT_DIR, 210 GT_PIPE, 211 GT_SOCK 212 }; 213 214 struct generic_type { 215 const char *type; 216 mode_t mode; 217 }; 218 219 static const struct generic_type generic_type_table[] = { 220 [GT_DIR] = { 221 .type = "dir", 222 .mode = S_IFDIR 223 }, 224 [GT_PIPE] = { 225 .type = "pipe", 226 .mode = S_IFIFO 227 }, 228 [GT_SOCK] = { 229 .type = "sock", 230 .mode = S_IFSOCK 231 } 232 }; 233 234 static int cpio_mkgeneric_line(const char *line, enum generic_types gt) 235 { 236 char name[PATH_MAX + 1]; 237 unsigned int mode; 238 int uid; 239 int gid; 240 int rc = -1; 241 242 if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) { 243 fprintf(stderr, "Unrecognized %s format '%s'", 244 line, generic_type_table[gt].type); 245 goto fail; 246 } 247 mode |= generic_type_table[gt].mode; 248 rc = cpio_mkgeneric(name, mode, uid, gid); 249 fail: 250 return rc; 251 } 252 253 static int cpio_mkdir_line(const char *line) 254 { 255 return cpio_mkgeneric_line(line, GT_DIR); 256 } 257 258 static int cpio_mkpipe_line(const char *line) 259 { 260 return cpio_mkgeneric_line(line, GT_PIPE); 261 } 262 263 static int cpio_mksock_line(const char *line) 264 { 265 return cpio_mkgeneric_line(line, GT_SOCK); 266 } 267 268 static int cpio_mknod(const char *name, unsigned int mode, 269 uid_t uid, gid_t gid, char dev_type, 270 unsigned int maj, unsigned int min) 271 { 272 int len; 273 unsigned int namesize; 274 275 if (dev_type == 'b') 276 mode |= S_IFBLK; 277 else 278 mode |= S_IFCHR; 279 280 if (name[0] == '/') 281 name++; 282 namesize = strlen(name) + 1; 283 284 len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" 285 "%08X%08X%08X%08X%08X%08X%08X", 286 do_csum ? "070702" : "070701", /* magic */ 287 ino++, /* ino */ 288 mode, /* mode */ 289 (long) uid, /* uid */ 290 (long) gid, /* gid */ 291 1, /* nlink */ 292 (long) default_mtime, /* mtime */ 293 0, /* filesize */ 294 3, /* major */ 295 1, /* minor */ 296 maj, /* rmajor */ 297 min, /* rminor */ 298 namesize, /* namesize */ 299 0); /* chksum */ 300 offset += len; 301 302 if (len != CPIO_HDR_LEN || 303 push_rest(name, namesize) < 0) 304 return -1; 305 306 return 0; 307 } 308 309 static int cpio_mknod_line(const char *line) 310 { 311 char name[PATH_MAX + 1]; 312 unsigned int mode; 313 int uid; 314 int gid; 315 char dev_type; 316 unsigned int maj; 317 unsigned int min; 318 int rc = -1; 319 320 if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u", 321 name, &mode, &uid, &gid, &dev_type, &maj, &min)) { 322 fprintf(stderr, "Unrecognized nod format '%s'", line); 323 goto fail; 324 } 325 rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min); 326 fail: 327 return rc; 328 } 329 330 static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum) 331 { 332 while (size) { 333 unsigned char filebuf[65536]; 334 ssize_t this_read; 335 size_t i, this_size = MIN(size, sizeof(filebuf)); 336 337 this_read = read(fd, filebuf, this_size); 338 if (this_read <= 0 || this_read > this_size) 339 return -1; 340 341 for (i = 0; i < this_read; i++) 342 *csum += filebuf[i]; 343 344 size -= this_read; 345 } 346 /* seek back to the start for data segment I/O */ 347 if (lseek(fd, 0, SEEK_SET) < 0) 348 return -1; 349 350 return 0; 351 } 352 353 static int cpio_mkfile(const char *name, const char *location, 354 unsigned int mode, uid_t uid, gid_t gid, 355 unsigned int nlinks) 356 { 357 struct stat buf; 358 unsigned long size; 359 int file, retval, len; 360 int rc = -1; 361 time_t mtime; 362 int namesize; 363 unsigned int i; 364 uint32_t csum = 0; 365 ssize_t this_read; 366 367 mode |= S_IFREG; 368 369 file = open (location, O_RDONLY); 370 if (file < 0) { 371 fprintf (stderr, "File %s could not be opened for reading\n", location); 372 goto error; 373 } 374 375 retval = fstat(file, &buf); 376 if (retval) { 377 fprintf(stderr, "File %s could not be stat()'ed\n", location); 378 goto error; 379 } 380 381 if (do_file_mtime) { 382 mtime = default_mtime; 383 } else { 384 mtime = buf.st_mtime; 385 if (mtime > 0xffffffff) { 386 fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n", 387 location); 388 mtime = 0xffffffff; 389 } 390 391 if (mtime < 0) { 392 fprintf(stderr, "%s: Timestamp negative, clipping.\n", 393 location); 394 mtime = 0; 395 } 396 } 397 398 if (buf.st_size > 0xffffffff) { 399 fprintf(stderr, "%s: Size exceeds maximum cpio file size\n", 400 location); 401 goto error; 402 } 403 404 if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) { 405 fprintf(stderr, "Failed to checksum file %s\n", location); 406 goto error; 407 } 408 409 size = 0; 410 for (i = 1; i <= nlinks; i++) { 411 /* data goes on last link */ 412 if (i == nlinks) 413 size = buf.st_size; 414 415 if (name[0] == '/') 416 name++; 417 namesize = strlen(name) + 1; 418 len = dprintf(outfd, "%s%08X%08X%08lX%08lX%08X%08lX" 419 "%08lX%08X%08X%08X%08X%08X%08X", 420 do_csum ? "070702" : "070701", /* magic */ 421 ino, /* ino */ 422 mode, /* mode */ 423 (long) uid, /* uid */ 424 (long) gid, /* gid */ 425 nlinks, /* nlink */ 426 (long) mtime, /* mtime */ 427 size, /* filesize */ 428 3, /* major */ 429 1, /* minor */ 430 0, /* rmajor */ 431 0, /* rminor */ 432 namesize, /* namesize */ 433 size ? csum : 0); /* chksum */ 434 offset += len; 435 436 if (len != CPIO_HDR_LEN || 437 push_buf(name, namesize) < 0 || 438 push_pad(padlen(offset, 4)) < 0) 439 goto error; 440 441 if (size) { 442 this_read = copy_file_range(file, NULL, outfd, NULL, size, 0); 443 if (this_read > 0) { 444 if (this_read > size) 445 goto error; 446 offset += this_read; 447 size -= this_read; 448 } 449 /* short or failed copy falls back to read/write... */ 450 } 451 452 while (size) { 453 unsigned char filebuf[65536]; 454 size_t this_size = MIN(size, sizeof(filebuf)); 455 456 this_read = read(file, filebuf, this_size); 457 if (this_read <= 0 || this_read > this_size) { 458 fprintf(stderr, "Can not read %s file\n", location); 459 goto error; 460 } 461 462 if (write(outfd, filebuf, this_read) != this_read) { 463 fprintf(stderr, "writing filebuf failed\n"); 464 goto error; 465 } 466 offset += this_read; 467 size -= this_read; 468 } 469 if (push_pad(padlen(offset, 4)) < 0) 470 goto error; 471 472 name += namesize; 473 } 474 ino++; 475 rc = 0; 476 477 error: 478 if (file >= 0) 479 close(file); 480 return rc; 481 } 482 483 static char *cpio_replace_env(char *new_location) 484 { 485 char expanded[PATH_MAX + 1]; 486 char *start, *end, *var; 487 488 while ((start = strstr(new_location, "${")) && 489 (end = strchr(start + 2, '}'))) { 490 *start = *end = 0; 491 var = getenv(start + 2); 492 snprintf(expanded, sizeof expanded, "%s%s%s", 493 new_location, var ? var : "", end + 1); 494 strcpy(new_location, expanded); 495 } 496 497 return new_location; 498 } 499 500 static int cpio_mkfile_line(const char *line) 501 { 502 char name[PATH_MAX + 1]; 503 char *dname = NULL; /* malloc'ed buffer for hard links */ 504 char location[PATH_MAX + 1]; 505 unsigned int mode; 506 int uid; 507 int gid; 508 int nlinks = 1; 509 int end = 0, dname_len = 0; 510 int rc = -1; 511 512 if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) 513 "s %o %d %d %n", 514 name, location, &mode, &uid, &gid, &end)) { 515 fprintf(stderr, "Unrecognized file format '%s'", line); 516 goto fail; 517 } 518 if (end && isgraph(line[end])) { 519 int len; 520 int nend; 521 522 dname = malloc(strlen(line)); 523 if (!dname) { 524 fprintf (stderr, "out of memory (%d)\n", dname_len); 525 goto fail; 526 } 527 528 dname_len = strlen(name) + 1; 529 memcpy(dname, name, dname_len); 530 531 do { 532 nend = 0; 533 if (sscanf(line + end, "%" str(PATH_MAX) "s %n", 534 name, &nend) < 1) 535 break; 536 len = strlen(name) + 1; 537 memcpy(dname + dname_len, name, len); 538 dname_len += len; 539 nlinks++; 540 end += nend; 541 } while (isgraph(line[end])); 542 } else { 543 dname = name; 544 } 545 rc = cpio_mkfile(dname, cpio_replace_env(location), 546 mode, uid, gid, nlinks); 547 fail: 548 if (dname_len) free(dname); 549 return rc; 550 } 551 552 static void usage(const char *prog) 553 { 554 fprintf(stderr, "Usage:\n" 555 "\t%s [-t <timestamp>] [-c] [-o <output_file>] <cpio_list>\n" 556 "\n" 557 "<cpio_list> is a file containing newline separated entries that\n" 558 "describe the files to be included in the initramfs archive:\n" 559 "\n" 560 "# a comment\n" 561 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n" 562 "dir <name> <mode> <uid> <gid>\n" 563 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n" 564 "slink <name> <target> <mode> <uid> <gid>\n" 565 "pipe <name> <mode> <uid> <gid>\n" 566 "sock <name> <mode> <uid> <gid>\n" 567 "\n" 568 "<name> name of the file/dir/nod/etc in the archive\n" 569 "<location> location of the file in the current filesystem\n" 570 " expands shell variables quoted with ${}\n" 571 "<target> link target\n" 572 "<mode> mode/permissions of the file\n" 573 "<uid> user id (0=root)\n" 574 "<gid> group id (0=root)\n" 575 "<dev_type> device type (b=block, c=character)\n" 576 "<maj> major number of nod\n" 577 "<min> minor number of nod\n" 578 "<hard links> space separated list of other links to file\n" 579 "\n" 580 "example:\n" 581 "# A simple initramfs\n" 582 "dir /dev 0755 0 0\n" 583 "nod /dev/console 0600 0 0 c 5 1\n" 584 "dir /root 0700 0 0\n" 585 "dir /sbin 0755 0 0\n" 586 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n" 587 "\n" 588 "<timestamp> is time in seconds since Epoch that will be used\n" 589 "as mtime for symlinks, directories, regular and special files.\n" 590 "The default is to use the current time for all files, but\n" 591 "preserve modification time for regular files.\n" 592 "-c: calculate and store 32-bit checksums for file data.\n" 593 "<output_file>: write cpio to this file instead of stdout\n", 594 prog); 595 } 596 597 static const struct file_handler file_handler_table[] = { 598 { 599 .type = "file", 600 .handler = cpio_mkfile_line, 601 }, { 602 .type = "nod", 603 .handler = cpio_mknod_line, 604 }, { 605 .type = "dir", 606 .handler = cpio_mkdir_line, 607 }, { 608 .type = "slink", 609 .handler = cpio_mkslink_line, 610 }, { 611 .type = "pipe", 612 .handler = cpio_mkpipe_line, 613 }, { 614 .type = "sock", 615 .handler = cpio_mksock_line, 616 }, { 617 .type = NULL, 618 .handler = NULL, 619 } 620 }; 621 622 #define LINE_SIZE (2 * PATH_MAX + 50) 623 624 int main (int argc, char *argv[]) 625 { 626 FILE *cpio_list; 627 char line[LINE_SIZE]; 628 char *args, *type; 629 int ec = 0; 630 int line_nr = 0; 631 const char *filename; 632 633 default_mtime = time(NULL); 634 while (1) { 635 int opt = getopt(argc, argv, "t:cho:"); 636 char *invalid; 637 638 if (opt == -1) 639 break; 640 switch (opt) { 641 case 't': 642 default_mtime = strtol(optarg, &invalid, 10); 643 if (!*optarg || *invalid) { 644 fprintf(stderr, "Invalid timestamp: %s\n", 645 optarg); 646 usage(argv[0]); 647 exit(1); 648 } 649 do_file_mtime = true; 650 break; 651 case 'c': 652 do_csum = true; 653 break; 654 case 'o': 655 outfd = open(optarg, 656 O_WRONLY | O_CREAT | O_LARGEFILE | O_TRUNC, 657 0600); 658 if (outfd < 0) { 659 fprintf(stderr, "failed to open %s\n", optarg); 660 usage(argv[0]); 661 exit(1); 662 } 663 break; 664 case 'h': 665 case '?': 666 usage(argv[0]); 667 exit(opt == 'h' ? 0 : 1); 668 } 669 } 670 671 /* 672 * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t 673 * representation that exceeds 8 chars and breaks the cpio header 674 * specification. Negative timestamps similarly exceed 8 chars. 675 */ 676 if (default_mtime > 0xffffffff || default_mtime < 0) { 677 fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n"); 678 exit(1); 679 } 680 681 if (argc - optind != 1) { 682 usage(argv[0]); 683 exit(1); 684 } 685 filename = argv[optind]; 686 if (!strcmp(filename, "-")) 687 cpio_list = stdin; 688 else if (!(cpio_list = fopen(filename, "r"))) { 689 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n", 690 filename, strerror(errno)); 691 usage(argv[0]); 692 exit(1); 693 } 694 695 while (fgets(line, LINE_SIZE, cpio_list)) { 696 int type_idx; 697 size_t slen = strlen(line); 698 699 line_nr++; 700 701 if ('#' == *line) { 702 /* comment - skip to next line */ 703 continue; 704 } 705 706 if (! (type = strtok(line, " \t"))) { 707 fprintf(stderr, 708 "ERROR: incorrect format, could not locate file type line %d: '%s'\n", 709 line_nr, line); 710 ec = -1; 711 break; 712 } 713 714 if ('\n' == *type) { 715 /* a blank line */ 716 continue; 717 } 718 719 if (slen == strlen(type)) { 720 /* must be an empty line */ 721 continue; 722 } 723 724 if (! (args = strtok(NULL, "\n"))) { 725 fprintf(stderr, 726 "ERROR: incorrect format, newline required line %d: '%s'\n", 727 line_nr, line); 728 ec = -1; 729 } 730 731 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) { 732 int rc; 733 if (! strcmp(line, file_handler_table[type_idx].type)) { 734 if ((rc = file_handler_table[type_idx].handler(args))) { 735 ec = rc; 736 fprintf(stderr, " line %d\n", line_nr); 737 } 738 break; 739 } 740 } 741 742 if (NULL == file_handler_table[type_idx].type) { 743 fprintf(stderr, "unknown file type line %d: '%s'\n", 744 line_nr, line); 745 } 746 } 747 if (ec == 0) 748 ec = cpio_trailer(); 749 750 exit(ec); 751 } 752