Merge tag 'drm-misc-fixes-2022-02-17' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-block.git] / usr / gen_init_cpio.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
1da177e4
LT
2#include <stdio.h>
3#include <stdlib.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <string.h>
7#include <unistd.h>
8#include <time.h>
9#include <fcntl.h>
10#include <errno.h>
11#include <ctype.h>
12#include <limits.h>
13
14/*
15 * Original work by Jeff Garzik
16 *
17 * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
24fa5096 18 * Hard link support by Luciano Rocha
1da177e4
LT
19 */
20
21#define xstr(s) #s
22#define str(s) xstr(s)
23
24static unsigned int offset;
25static unsigned int ino = 721;
a8b8017c 26static time_t default_mtime;
1da177e4
LT
27
28struct file_handler {
29 const char *type;
30 int (*handler)(const char *line);
31};
32
33static void push_string(const char *name)
34{
35 unsigned int name_len = strlen(name) + 1;
36
37 fputs(name, stdout);
38 putchar(0);
39 offset += name_len;
40}
41
42static void push_pad (void)
43{
44 while (offset & 3) {
45 putchar(0);
46 offset++;
47 }
48}
49
50static void push_rest(const char *name)
51{
52 unsigned int name_len = strlen(name) + 1;
53 unsigned int tmp_ofs;
54
55 fputs(name, stdout);
56 putchar(0);
57 offset += name_len;
58
59 tmp_ofs = name_len + 110;
60 while (tmp_ofs & 3) {
61 putchar(0);
62 offset++;
63 tmp_ofs++;
64 }
65}
66
67static void push_hdr(const char *s)
68{
69 fputs(s, stdout);
70 offset += 110;
71}
72
73static void cpio_trailer(void)
74{
75 char s[256];
76 const char name[] = "TRAILER!!!";
77
78 sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
79 "%08X%08X%08X%08X%08X%08X%08X",
80 "070701", /* magic */
81 0, /* ino */
82 0, /* mode */
83 (long) 0, /* uid */
84 (long) 0, /* gid */
85 1, /* nlink */
86 (long) 0, /* mtime */
87 0, /* filesize */
88 0, /* major */
89 0, /* minor */
90 0, /* rmajor */
91 0, /* rminor */
92 (unsigned)strlen(name)+1, /* namesize */
93 0); /* chksum */
94 push_hdr(s);
95 push_rest(name);
96
97 while (offset % 512) {
98 putchar(0);
99 offset++;
100 }
101}
102
103static int cpio_mkslink(const char *name, const char *target,
104 unsigned int mode, uid_t uid, gid_t gid)
105{
106 char s[256];
1da177e4 107
43f901fb
TC
108 if (name[0] == '/')
109 name++;
1da177e4
LT
110 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
111 "%08X%08X%08X%08X%08X%08X%08X",
112 "070701", /* magic */
113 ino++, /* ino */
114 S_IFLNK | mode, /* mode */
115 (long) uid, /* uid */
116 (long) gid, /* gid */
117 1, /* nlink */
a8b8017c 118 (long) default_mtime, /* mtime */
1da177e4
LT
119 (unsigned)strlen(target)+1, /* filesize */
120 3, /* major */
121 1, /* minor */
122 0, /* rmajor */
123 0, /* rminor */
124 (unsigned)strlen(name) + 1,/* namesize */
125 0); /* chksum */
126 push_hdr(s);
127 push_string(name);
128 push_pad();
129 push_string(target);
130 push_pad();
131 return 0;
132}
133
134static int cpio_mkslink_line(const char *line)
135{
136 char name[PATH_MAX + 1];
137 char target[PATH_MAX + 1];
138 unsigned int mode;
139 int uid;
140 int gid;
141 int rc = -1;
142
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);
145 goto fail;
146 }
147 rc = cpio_mkslink(name, target, mode, uid, gid);
148 fail:
149 return rc;
150}
151
152static int cpio_mkgeneric(const char *name, unsigned int mode,
153 uid_t uid, gid_t gid)
154{
155 char s[256];
1da177e4 156
43f901fb
TC
157 if (name[0] == '/')
158 name++;
1da177e4
LT
159 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
160 "%08X%08X%08X%08X%08X%08X%08X",
161 "070701", /* magic */
162 ino++, /* ino */
163 mode, /* mode */
164 (long) uid, /* uid */
165 (long) gid, /* gid */
166 2, /* nlink */
a8b8017c 167 (long) default_mtime, /* mtime */
1da177e4
LT
168 0, /* filesize */
169 3, /* major */
170 1, /* minor */
171 0, /* rmajor */
172 0, /* rminor */
173 (unsigned)strlen(name) + 1,/* namesize */
174 0); /* chksum */
175 push_hdr(s);
176 push_rest(name);
177 return 0;
178}
179
180enum generic_types {
181 GT_DIR,
182 GT_PIPE,
183 GT_SOCK
184};
185
186struct generic_type {
187 const char *type;
188 mode_t mode;
189};
190
3510c5cf 191static const struct generic_type generic_type_table[] = {
1da177e4
LT
192 [GT_DIR] = {
193 .type = "dir",
194 .mode = S_IFDIR
195 },
196 [GT_PIPE] = {
197 .type = "pipe",
198 .mode = S_IFIFO
199 },
200 [GT_SOCK] = {
201 .type = "sock",
202 .mode = S_IFSOCK
203 }
204};
205
206static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
207{
208 char name[PATH_MAX + 1];
209 unsigned int mode;
210 int uid;
211 int gid;
212 int rc = -1;
213
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);
217 goto fail;
218 }
219 mode |= generic_type_table[gt].mode;
220 rc = cpio_mkgeneric(name, mode, uid, gid);
221 fail:
222 return rc;
223}
224
225static int cpio_mkdir_line(const char *line)
226{
227 return cpio_mkgeneric_line(line, GT_DIR);
228}
229
230static int cpio_mkpipe_line(const char *line)
231{
232 return cpio_mkgeneric_line(line, GT_PIPE);
233}
234
235static int cpio_mksock_line(const char *line)
236{
237 return cpio_mkgeneric_line(line, GT_SOCK);
238}
239
240static 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)
243{
244 char s[256];
1da177e4
LT
245
246 if (dev_type == 'b')
247 mode |= S_IFBLK;
248 else
249 mode |= S_IFCHR;
250
43f901fb
TC
251 if (name[0] == '/')
252 name++;
1da177e4
LT
253 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
254 "%08X%08X%08X%08X%08X%08X%08X",
255 "070701", /* magic */
256 ino++, /* ino */
257 mode, /* mode */
258 (long) uid, /* uid */
259 (long) gid, /* gid */
260 1, /* nlink */
a8b8017c 261 (long) default_mtime, /* mtime */
1da177e4
LT
262 0, /* filesize */
263 3, /* major */
264 1, /* minor */
265 maj, /* rmajor */
266 min, /* rminor */
267 (unsigned)strlen(name) + 1,/* namesize */
268 0); /* chksum */
269 push_hdr(s);
270 push_rest(name);
271 return 0;
272}
273
274static int cpio_mknod_line(const char *line)
275{
276 char name[PATH_MAX + 1];
277 unsigned int mode;
278 int uid;
279 int gid;
280 char dev_type;
281 unsigned int maj;
282 unsigned int min;
283 int rc = -1;
284
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);
288 goto fail;
289 }
290 rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
291 fail:
292 return rc;
293}
294
1da177e4 295static int cpio_mkfile(const char *name, const char *location,
24fa5096
LR
296 unsigned int mode, uid_t uid, gid_t gid,
297 unsigned int nlinks)
1da177e4
LT
298{
299 char s[256];
300 char *filebuf = NULL;
301 struct stat buf;
24fa5096 302 long size;
1da177e4
LT
303 int file = -1;
304 int retval;
305 int rc = -1;
24fa5096 306 int namesize;
20f1de65 307 unsigned int i;
1da177e4
LT
308
309 mode |= S_IFREG;
310
1da177e4
LT
311 file = open (location, O_RDONLY);
312 if (file < 0) {
313 fprintf (stderr, "File %s could not be opened for reading\n", location);
314 goto error;
315 }
316
a3c888fc 317 retval = fstat(file, &buf);
96aebafa 318 if (retval) {
a3c888fc 319 fprintf(stderr, "File %s could not be stat()'ed\n", location);
96aebafa
JJ
320 goto error;
321 }
322
4c9d410f
NS
323 if (buf.st_mtime > 0xffffffff) {
324 fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
325 location);
326 buf.st_mtime = 0xffffffff;
327 }
328
1da177e4
LT
329 filebuf = malloc(buf.st_size);
330 if (!filebuf) {
331 fprintf (stderr, "out of memory\n");
332 goto error;
333 }
334
335 retval = read (file, filebuf, buf.st_size);
336 if (retval < 0) {
337 fprintf (stderr, "Can not read %s file\n", location);
338 goto error;
339 }
340
24fa5096
LR
341 size = 0;
342 for (i = 1; i <= nlinks; i++) {
343 /* data goes on last link */
344 if (i == nlinks) size = buf.st_size;
345
43f901fb
TC
346 if (name[0] == '/')
347 name++;
24fa5096
LR
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 */
352 ino, /* ino */
353 mode, /* mode */
354 (long) uid, /* uid */
355 (long) gid, /* gid */
356 nlinks, /* nlink */
357 (long) buf.st_mtime, /* mtime */
358 size, /* filesize */
359 3, /* major */
360 1, /* minor */
361 0, /* rmajor */
362 0, /* rminor */
363 namesize, /* namesize */
364 0); /* chksum */
365 push_hdr(s);
366 push_string(name);
367 push_pad();
368
369 if (size) {
6d87fea4
MF
370 if (fwrite(filebuf, size, 1, stdout) != 1) {
371 fprintf(stderr, "writing filebuf failed\n");
372 goto error;
373 }
24fa5096
LR
374 offset += size;
375 push_pad();
376 }
377
378 name += namesize;
379 }
380 ino++;
1da177e4
LT
381 rc = 0;
382
383error:
384 if (filebuf) free(filebuf);
385 if (file >= 0) close(file);
386 return rc;
387}
388
3b1ec9fb
SG
389static char *cpio_replace_env(char *new_location)
390{
20f1de65 391 char expanded[PATH_MAX + 1];
c725ee54
MN
392 char *start, *end, *var;
393
394 while ((start = strstr(new_location, "${")) &&
395 (end = strchr(start + 2, '}'))) {
396 *start = *end = 0;
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);
20f1de65
KC
401 }
402
403 return new_location;
3b1ec9fb
SG
404}
405
1da177e4
LT
406static int cpio_mkfile_line(const char *line)
407{
408 char name[PATH_MAX + 1];
24fa5096 409 char *dname = NULL; /* malloc'ed buffer for hard links */
1da177e4
LT
410 char location[PATH_MAX + 1];
411 unsigned int mode;
412 int uid;
413 int gid;
24fa5096
LR
414 int nlinks = 1;
415 int end = 0, dname_len = 0;
1da177e4
LT
416 int rc = -1;
417
24fa5096
LR
418 if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
419 "s %o %d %d %n",
420 name, location, &mode, &uid, &gid, &end)) {
1da177e4
LT
421 fprintf(stderr, "Unrecognized file format '%s'", line);
422 goto fail;
423 }
24fa5096
LR
424 if (end && isgraph(line[end])) {
425 int len;
426 int nend;
427
428 dname = malloc(strlen(line));
429 if (!dname) {
430 fprintf (stderr, "out of memory (%d)\n", dname_len);
431 goto fail;
432 }
433
434 dname_len = strlen(name) + 1;
435 memcpy(dname, name, dname_len);
436
437 do {
438 nend = 0;
439 if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
440 name, &nend) < 1)
441 break;
442 len = strlen(name) + 1;
443 memcpy(dname + dname_len, name, len);
444 dname_len += len;
445 nlinks++;
446 end += nend;
447 } while (isgraph(line[end]));
448 } else {
449 dname = name;
450 }
3b1ec9fb
SG
451 rc = cpio_mkfile(dname, cpio_replace_env(location),
452 mode, uid, gid, nlinks);
1da177e4 453 fail:
24fa5096 454 if (dname_len) free(dname);
1da177e4
LT
455 return rc;
456}
457
5c725138 458static void usage(const char *prog)
1da177e4
LT
459{
460 fprintf(stderr, "Usage:\n"
a8b8017c 461 "\t%s [-t <timestamp>] <cpio_list>\n"
1da177e4
LT
462 "\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"
465 "\n"
466 "# a comment\n"
24fa5096 467 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
1da177e4
LT
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"
473 "\n"
24fa5096
LR
474 "<name> name of the file/dir/nod/etc in the archive\n"
475 "<location> location of the file in the current filesystem\n"
3b1ec9fb 476 " expands shell variables quoted with ${}\n"
24fa5096
LR
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"
1da177e4
LT
485 "\n"
486 "example:\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"
a8b8017c
MM
492 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
493 "\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",
1da177e4
LT
497 prog);
498}
499
3510c5cf 500static const struct file_handler file_handler_table[] = {
1da177e4
LT
501 {
502 .type = "file",
503 .handler = cpio_mkfile_line,
504 }, {
505 .type = "nod",
506 .handler = cpio_mknod_line,
507 }, {
508 .type = "dir",
509 .handler = cpio_mkdir_line,
510 }, {
511 .type = "slink",
512 .handler = cpio_mkslink_line,
513 }, {
514 .type = "pipe",
515 .handler = cpio_mkpipe_line,
516 }, {
517 .type = "sock",
518 .handler = cpio_mksock_line,
519 }, {
520 .type = NULL,
521 .handler = NULL,
522 }
523};
524
525#define LINE_SIZE (2 * PATH_MAX + 50)
526
527int main (int argc, char *argv[])
528{
529 FILE *cpio_list;
530 char line[LINE_SIZE];
531 char *args, *type;
532 int ec = 0;
533 int line_nr = 0;
a8b8017c
MM
534 const char *filename;
535
536 default_mtime = time(NULL);
537 while (1) {
538 int opt = getopt(argc, argv, "t:h");
539 char *invalid;
540
541 if (opt == -1)
542 break;
543 switch (opt) {
544 case 't':
545 default_mtime = strtol(optarg, &invalid, 10);
546 if (!*optarg || *invalid) {
547 fprintf(stderr, "Invalid timestamp: %s\n",
548 optarg);
549 usage(argv[0]);
550 exit(1);
551 }
552 break;
553 case 'h':
554 case '?':
555 usage(argv[0]);
556 exit(opt == 'h' ? 0 : 1);
557 }
558 }
1da177e4 559
4c9d410f
NS
560 /*
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
563 * specification.
564 */
565 if (default_mtime > 0xffffffff) {
566 fprintf(stderr, "ERROR: Timestamp too large for cpio format\n");
567 exit(1);
568 }
569
a8b8017c 570 if (argc - optind != 1) {
1da177e4
LT
571 usage(argv[0]);
572 exit(1);
573 }
a8b8017c
MM
574 filename = argv[optind];
575 if (!strcmp(filename, "-"))
f2434ec1 576 cpio_list = stdin;
a8b8017c 577 else if (!(cpio_list = fopen(filename, "r"))) {
1da177e4 578 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
a8b8017c 579 filename, strerror(errno));
1da177e4
LT
580 usage(argv[0]);
581 exit(1);
582 }
583
584 while (fgets(line, LINE_SIZE, cpio_list)) {
585 int type_idx;
586 size_t slen = strlen(line);
587
588 line_nr++;
589
590 if ('#' == *line) {
591 /* comment - skip to next line */
592 continue;
593 }
594
595 if (! (type = strtok(line, " \t"))) {
596 fprintf(stderr,
597 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
598 line_nr, line);
599 ec = -1;
aa1e816f 600 break;
1da177e4
LT
601 }
602
603 if ('\n' == *type) {
604 /* a blank line */
605 continue;
606 }
607
608 if (slen == strlen(type)) {
609 /* must be an empty line */
610 continue;
611 }
612
613 if (! (args = strtok(NULL, "\n"))) {
614 fprintf(stderr,
615 "ERROR: incorrect format, newline required line %d: '%s'\n",
616 line_nr, line);
617 ec = -1;
618 }
619
620 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
621 int rc;
622 if (! strcmp(line, file_handler_table[type_idx].type)) {
623 if ((rc = file_handler_table[type_idx].handler(args))) {
624 ec = rc;
625 fprintf(stderr, " line %d\n", line_nr);
626 }
627 break;
628 }
629 }
630
631 if (NULL == file_handler_table[type_idx].type) {
632 fprintf(stderr, "unknown file type line %d: '%s'\n",
633 line_nr, line);
634 }
635 }
aa1e816f
JJ
636 if (ec == 0)
637 cpio_trailer();
1da177e4
LT
638
639 exit(ec);
640}