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