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