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
push_buf(const char * name,size_t name_len)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
push_pad(size_t padlen)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
push_rest(const char * name,size_t name_len)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
cpio_trailer(void)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
cpio_mkslink(const char * name,const char * target,unsigned int mode,uid_t uid,gid_t gid)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
cpio_mkslink_line(const char * line)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
cpio_mkgeneric(const char * name,unsigned int mode,uid_t uid,gid_t gid)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
cpio_mkgeneric_line(const char * line,enum generic_types gt)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
cpio_mkdir_line(const char * line)258 static int cpio_mkdir_line(const char *line)
259 {
260 return cpio_mkgeneric_line(line, GT_DIR);
261 }
262
cpio_mkpipe_line(const char * line)263 static int cpio_mkpipe_line(const char *line)
264 {
265 return cpio_mkgeneric_line(line, GT_PIPE);
266 }
267
cpio_mksock_line(const char * line)268 static int cpio_mksock_line(const char *line)
269 {
270 return cpio_mkgeneric_line(line, GT_SOCK);
271 }
272
cpio_mknod(const char * name,unsigned int mode,uid_t uid,gid_t gid,char dev_type,unsigned int maj,unsigned int min)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
cpio_mknod_line(const char * line)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
cpio_mkfile_csum(int fd,unsigned long size,uint32_t * csum)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
cpio_mkfile(const char * name,const char * location,unsigned int mode,uid_t uid,gid_t gid,unsigned int nlinks)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
cpio_replace_env(char * new_location)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
cpio_mkfile_line(const char * line)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
usage(const char * prog)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
main(int argc,char * argv[])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