xref: /linux/usr/gen_init_cpio.c (revision 7c1f14f6e8e7f288350faec02b3fbc25971da289)
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