1 // SPDX-License-Identifier: GPL-2.0
15 * Original work by Jeff Garzik
17 * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
18 * Hard link support by Luciano Rocha
22 #define str(s) xstr(s)
24 static unsigned int offset;
25 static unsigned int ino = 721;
26 static time_t default_mtime;
30 int (*handler)(const char *line);
33 static void push_string(const char *name)
35 unsigned int name_len = strlen(name) + 1;
42 static void push_pad (void)
50 static void push_rest(const char *name)
52 unsigned int name_len = strlen(name) + 1;
59 tmp_ofs = name_len + 110;
67 static void push_hdr(const char *s)
73 static void cpio_trailer(void)
76 const char name[] = "TRAILER!!!";
78 sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
79 "%08X%08X%08X%08X%08X%08X%08X",
92 (unsigned)strlen(name)+1, /* namesize */
97 while (offset % 512) {
103 static int cpio_mkslink(const char *name, const char *target,
104 unsigned int mode, uid_t uid, gid_t gid)
110 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
111 "%08X%08X%08X%08X%08X%08X%08X",
112 "070701", /* magic */
114 S_IFLNK | mode, /* mode */
115 (long) uid, /* uid */
116 (long) gid, /* gid */
118 (long) default_mtime, /* mtime */
119 (unsigned)strlen(target)+1, /* filesize */
124 (unsigned)strlen(name) + 1,/* namesize */
134 static int cpio_mkslink_line(const char *line)
136 char name[PATH_MAX + 1];
137 char target[PATH_MAX + 1];
143 if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
144 fprintf(stderr, "Unrecognized dir format '%s'", line);
147 rc = cpio_mkslink(name, target, mode, uid, gid);
152 static int cpio_mkgeneric(const char *name, unsigned int mode,
153 uid_t uid, gid_t gid)
159 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
160 "%08X%08X%08X%08X%08X%08X%08X",
161 "070701", /* magic */
164 (long) uid, /* uid */
165 (long) gid, /* gid */
167 (long) default_mtime, /* mtime */
173 (unsigned)strlen(name) + 1,/* namesize */
186 struct generic_type {
191 static const struct generic_type generic_type_table[] = {
206 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
208 char name[PATH_MAX + 1];
214 if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
215 fprintf(stderr, "Unrecognized %s format '%s'",
216 line, generic_type_table[gt].type);
219 mode |= generic_type_table[gt].mode;
220 rc = cpio_mkgeneric(name, mode, uid, gid);
225 static int cpio_mkdir_line(const char *line)
227 return cpio_mkgeneric_line(line, GT_DIR);
230 static int cpio_mkpipe_line(const char *line)
232 return cpio_mkgeneric_line(line, GT_PIPE);
235 static int cpio_mksock_line(const char *line)
237 return cpio_mkgeneric_line(line, GT_SOCK);
240 static int cpio_mknod(const char *name, unsigned int mode,
241 uid_t uid, gid_t gid, char dev_type,
242 unsigned int maj, unsigned int min)
253 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
254 "%08X%08X%08X%08X%08X%08X%08X",
255 "070701", /* magic */
258 (long) uid, /* uid */
259 (long) gid, /* gid */
261 (long) default_mtime, /* mtime */
267 (unsigned)strlen(name) + 1,/* namesize */
274 static int cpio_mknod_line(const char *line)
276 char name[PATH_MAX + 1];
285 if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
286 name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
287 fprintf(stderr, "Unrecognized nod format '%s'", line);
290 rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
295 static int cpio_mkfile(const char *name, const char *location,
296 unsigned int mode, uid_t uid, gid_t gid,
300 char *filebuf = NULL;
311 file = open (location, O_RDONLY);
313 fprintf (stderr, "File %s could not be opened for reading\n", location);
317 retval = fstat(file, &buf);
319 fprintf(stderr, "File %s could not be stat()'ed\n", location);
323 if (buf.st_mtime > 0xffffffff) {
324 fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
326 buf.st_mtime = 0xffffffff;
329 filebuf = malloc(buf.st_size);
331 fprintf (stderr, "out of memory\n");
335 retval = read (file, filebuf, buf.st_size);
337 fprintf (stderr, "Can not read %s file\n", location);
342 for (i = 1; i <= nlinks; i++) {
343 /* data goes on last link */
344 if (i == nlinks) size = buf.st_size;
348 namesize = strlen(name) + 1;
349 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
350 "%08lX%08X%08X%08X%08X%08X%08X",
351 "070701", /* magic */
354 (long) uid, /* uid */
355 (long) gid, /* gid */
357 (long) buf.st_mtime, /* mtime */
363 namesize, /* namesize */
370 if (fwrite(filebuf, size, 1, stdout) != 1) {
371 fprintf(stderr, "writing filebuf failed\n");
384 if (filebuf) free(filebuf);
385 if (file >= 0) close(file);
389 static char *cpio_replace_env(char *new_location)
391 char expanded[PATH_MAX + 1];
392 char *start, *end, *var;
394 while ((start = strstr(new_location, "${")) &&
395 (end = strchr(start + 2, '}'))) {
397 var = getenv(start + 2);
398 snprintf(expanded, sizeof expanded, "%s%s%s",
399 new_location, var ? var : "", end + 1);
400 strcpy(new_location, expanded);
406 static int cpio_mkfile_line(const char *line)
408 char name[PATH_MAX + 1];
409 char *dname = NULL; /* malloc'ed buffer for hard links */
410 char location[PATH_MAX + 1];
415 int end = 0, dname_len = 0;
418 if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
420 name, location, &mode, &uid, &gid, &end)) {
421 fprintf(stderr, "Unrecognized file format '%s'", line);
424 if (end && isgraph(line[end])) {
428 dname = malloc(strlen(line));
430 fprintf (stderr, "out of memory (%d)\n", dname_len);
434 dname_len = strlen(name) + 1;
435 memcpy(dname, name, dname_len);
439 if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
442 len = strlen(name) + 1;
443 memcpy(dname + dname_len, name, len);
447 } while (isgraph(line[end]));
451 rc = cpio_mkfile(dname, cpio_replace_env(location),
452 mode, uid, gid, nlinks);
454 if (dname_len) free(dname);
458 static void usage(const char *prog)
460 fprintf(stderr, "Usage:\n"
461 "\t%s [-t <timestamp>] <cpio_list>\n"
463 "<cpio_list> is a file containing newline separated entries that\n"
464 "describe the files to be included in the initramfs archive:\n"
467 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
468 "dir <name> <mode> <uid> <gid>\n"
469 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
470 "slink <name> <target> <mode> <uid> <gid>\n"
471 "pipe <name> <mode> <uid> <gid>\n"
472 "sock <name> <mode> <uid> <gid>\n"
474 "<name> name of the file/dir/nod/etc in the archive\n"
475 "<location> location of the file in the current filesystem\n"
476 " expands shell variables quoted with ${}\n"
477 "<target> link target\n"
478 "<mode> mode/permissions of the file\n"
479 "<uid> user id (0=root)\n"
480 "<gid> group id (0=root)\n"
481 "<dev_type> device type (b=block, c=character)\n"
482 "<maj> major number of nod\n"
483 "<min> minor number of nod\n"
484 "<hard links> space separated list of other links to file\n"
487 "# A simple initramfs\n"
488 "dir /dev 0755 0 0\n"
489 "nod /dev/console 0600 0 0 c 5 1\n"
490 "dir /root 0700 0 0\n"
491 "dir /sbin 0755 0 0\n"
492 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
494 "<timestamp> is time in seconds since Epoch that will be used\n"
495 "as mtime for symlinks, special files and directories. The default\n"
496 "is to use the current time for these entries.\n",
500 static const struct file_handler file_handler_table[] = {
503 .handler = cpio_mkfile_line,
506 .handler = cpio_mknod_line,
509 .handler = cpio_mkdir_line,
512 .handler = cpio_mkslink_line,
515 .handler = cpio_mkpipe_line,
518 .handler = cpio_mksock_line,
525 #define LINE_SIZE (2 * PATH_MAX + 50)
527 int main (int argc, char *argv[])
530 char line[LINE_SIZE];
534 const char *filename;
536 default_mtime = time(NULL);
538 int opt = getopt(argc, argv, "t:h");
545 default_mtime = strtol(optarg, &invalid, 10);
546 if (!*optarg || *invalid) {
547 fprintf(stderr, "Invalid timestamp: %s\n",
556 exit(opt == 'h' ? 0 : 1);
561 * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
562 * representation that exceeds 8 chars and breaks the cpio header
565 if (default_mtime > 0xffffffff) {
566 fprintf(stderr, "ERROR: Timestamp too large for cpio format\n");
570 if (argc - optind != 1) {
574 filename = argv[optind];
575 if (!strcmp(filename, "-"))
577 else if (!(cpio_list = fopen(filename, "r"))) {
578 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
579 filename, strerror(errno));
584 while (fgets(line, LINE_SIZE, cpio_list)) {
586 size_t slen = strlen(line);
591 /* comment - skip to next line */
595 if (! (type = strtok(line, " \t"))) {
597 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
608 if (slen == strlen(type)) {
609 /* must be an empty line */
613 if (! (args = strtok(NULL, "\n"))) {
615 "ERROR: incorrect format, newline required line %d: '%s'\n",
620 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
622 if (! strcmp(line, file_handler_table[type_idx].type)) {
623 if ((rc = file_handler_table[type_idx].handler(args))) {
625 fprintf(stderr, " line %d\n", line_nr);
631 if (NULL == file_handler_table[type_idx].type) {
632 fprintf(stderr, "unknown file type line %d: '%s'\n",