File Index
Files and directories
Sign in
FILE

gen_init_cpio.c

File preview with syntax highlighting.

linux-6.19.14/usr/gen_init_cpio.c C
  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 */
 32static char padding[PATH_MAX];
 33static unsigned int offset;
 34static unsigned int ino = 721;
 35static time_t default_mtime;
 36static bool do_file_mtime;
 37static bool do_csum = false;
 38static int outfd = STDOUT_FILENO;
 39static unsigned int dalign;
 40
 41struct file_handler {
 42	const char *type;
 43	int (*handler)(const char *line);
 44};
 45
 46static 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
 58static 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
 74static 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
 87static 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
121static 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
160static 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
178static 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
213enum generic_types {
214	GT_DIR,
215	GT_PIPE,
216	GT_SOCK
217};
218
219struct generic_type {
220	const char *type;
221	mode_t mode;
222};
223
224static 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
239static 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
258static int cpio_mkdir_line(const char *line)
259{
260	return cpio_mkgeneric_line(line, GT_DIR);
261}
262
263static int cpio_mkpipe_line(const char *line)
264{
265	return cpio_mkgeneric_line(line, GT_PIPE);
266}
267
268static int cpio_mksock_line(const char *line)
269{
270	return cpio_mkgeneric_line(line, GT_SOCK);
271}
272
273static 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
314static 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
335static 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
358static 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
495error:
496	if (file >= 0)
497		close(file);
498	return rc;
499}
500
501static 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
518static 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
570static 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
618static 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
645int 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}
~/ Read-only file preview.