Merge tag 'for-6.4/io_uring-2023-05-07' of git://git.kernel.dk/linux
[platform/kernel/linux-starfive.git] / usr / gen_init_cpio.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include <stdbool.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <time.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <ctype.h>
14 #include <limits.h>
15
16 /*
17  * Original work by Jeff Garzik
18  *
19  * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
20  * Hard link support by Luciano Rocha
21  */
22
23 #define xstr(s) #s
24 #define str(s) xstr(s)
25 #define MIN(a, b) ((a) < (b) ? (a) : (b))
26
27 static unsigned int offset;
28 static unsigned int ino = 721;
29 static time_t default_mtime;
30 static bool do_csum = false;
31
32 struct file_handler {
33         const char *type;
34         int (*handler)(const char *line);
35 };
36
37 static void push_string(const char *name)
38 {
39         unsigned int name_len = strlen(name) + 1;
40
41         fputs(name, stdout);
42         putchar(0);
43         offset += name_len;
44 }
45
46 static void push_pad (void)
47 {
48         while (offset & 3) {
49                 putchar(0);
50                 offset++;
51         }
52 }
53
54 static void push_rest(const char *name)
55 {
56         unsigned int name_len = strlen(name) + 1;
57         unsigned int tmp_ofs;
58
59         fputs(name, stdout);
60         putchar(0);
61         offset += name_len;
62
63         tmp_ofs = name_len + 110;
64         while (tmp_ofs & 3) {
65                 putchar(0);
66                 offset++;
67                 tmp_ofs++;
68         }
69 }
70
71 static void push_hdr(const char *s)
72 {
73         fputs(s, stdout);
74         offset += 110;
75 }
76
77 static void cpio_trailer(void)
78 {
79         char s[256];
80         const char name[] = "TRAILER!!!";
81
82         sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
83                "%08X%08X%08X%08X%08X%08X%08X",
84                 do_csum ? "070702" : "070701", /* magic */
85                 0,                      /* ino */
86                 0,                      /* mode */
87                 (long) 0,               /* uid */
88                 (long) 0,               /* gid */
89                 1,                      /* nlink */
90                 (long) 0,               /* mtime */
91                 0,                      /* filesize */
92                 0,                      /* major */
93                 0,                      /* minor */
94                 0,                      /* rmajor */
95                 0,                      /* rminor */
96                 (unsigned)strlen(name)+1, /* namesize */
97                 0);                     /* chksum */
98         push_hdr(s);
99         push_rest(name);
100
101         while (offset % 512) {
102                 putchar(0);
103                 offset++;
104         }
105 }
106
107 static int cpio_mkslink(const char *name, const char *target,
108                          unsigned int mode, uid_t uid, gid_t gid)
109 {
110         char s[256];
111
112         if (name[0] == '/')
113                 name++;
114         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
115                "%08X%08X%08X%08X%08X%08X%08X",
116                 do_csum ? "070702" : "070701", /* magic */
117                 ino++,                  /* ino */
118                 S_IFLNK | mode,         /* mode */
119                 (long) uid,             /* uid */
120                 (long) gid,             /* gid */
121                 1,                      /* nlink */
122                 (long) default_mtime,   /* mtime */
123                 (unsigned)strlen(target)+1, /* filesize */
124                 3,                      /* major */
125                 1,                      /* minor */
126                 0,                      /* rmajor */
127                 0,                      /* rminor */
128                 (unsigned)strlen(name) + 1,/* namesize */
129                 0);                     /* chksum */
130         push_hdr(s);
131         push_string(name);
132         push_pad();
133         push_string(target);
134         push_pad();
135         return 0;
136 }
137
138 static int cpio_mkslink_line(const char *line)
139 {
140         char name[PATH_MAX + 1];
141         char target[PATH_MAX + 1];
142         unsigned int mode;
143         int uid;
144         int gid;
145         int rc = -1;
146
147         if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
148                 fprintf(stderr, "Unrecognized dir format '%s'", line);
149                 goto fail;
150         }
151         rc = cpio_mkslink(name, target, mode, uid, gid);
152  fail:
153         return rc;
154 }
155
156 static int cpio_mkgeneric(const char *name, unsigned int mode,
157                        uid_t uid, gid_t gid)
158 {
159         char s[256];
160
161         if (name[0] == '/')
162                 name++;
163         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
164                "%08X%08X%08X%08X%08X%08X%08X",
165                 do_csum ? "070702" : "070701", /* magic */
166                 ino++,                  /* ino */
167                 mode,                   /* mode */
168                 (long) uid,             /* uid */
169                 (long) gid,             /* gid */
170                 2,                      /* nlink */
171                 (long) default_mtime,   /* mtime */
172                 0,                      /* filesize */
173                 3,                      /* major */
174                 1,                      /* minor */
175                 0,                      /* rmajor */
176                 0,                      /* rminor */
177                 (unsigned)strlen(name) + 1,/* namesize */
178                 0);                     /* chksum */
179         push_hdr(s);
180         push_rest(name);
181         return 0;
182 }
183
184 enum generic_types {
185         GT_DIR,
186         GT_PIPE,
187         GT_SOCK
188 };
189
190 struct generic_type {
191         const char *type;
192         mode_t mode;
193 };
194
195 static const struct generic_type generic_type_table[] = {
196         [GT_DIR] = {
197                 .type = "dir",
198                 .mode = S_IFDIR
199         },
200         [GT_PIPE] = {
201                 .type = "pipe",
202                 .mode = S_IFIFO
203         },
204         [GT_SOCK] = {
205                 .type = "sock",
206                 .mode = S_IFSOCK
207         }
208 };
209
210 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
211 {
212         char name[PATH_MAX + 1];
213         unsigned int mode;
214         int uid;
215         int gid;
216         int rc = -1;
217
218         if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
219                 fprintf(stderr, "Unrecognized %s format '%s'",
220                         line, generic_type_table[gt].type);
221                 goto fail;
222         }
223         mode |= generic_type_table[gt].mode;
224         rc = cpio_mkgeneric(name, mode, uid, gid);
225  fail:
226         return rc;
227 }
228
229 static int cpio_mkdir_line(const char *line)
230 {
231         return cpio_mkgeneric_line(line, GT_DIR);
232 }
233
234 static int cpio_mkpipe_line(const char *line)
235 {
236         return cpio_mkgeneric_line(line, GT_PIPE);
237 }
238
239 static int cpio_mksock_line(const char *line)
240 {
241         return cpio_mkgeneric_line(line, GT_SOCK);
242 }
243
244 static int cpio_mknod(const char *name, unsigned int mode,
245                        uid_t uid, gid_t gid, char dev_type,
246                        unsigned int maj, unsigned int min)
247 {
248         char s[256];
249
250         if (dev_type == 'b')
251                 mode |= S_IFBLK;
252         else
253                 mode |= S_IFCHR;
254
255         if (name[0] == '/')
256                 name++;
257         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
258                "%08X%08X%08X%08X%08X%08X%08X",
259                 do_csum ? "070702" : "070701", /* magic */
260                 ino++,                  /* ino */
261                 mode,                   /* mode */
262                 (long) uid,             /* uid */
263                 (long) gid,             /* gid */
264                 1,                      /* nlink */
265                 (long) default_mtime,   /* mtime */
266                 0,                      /* filesize */
267                 3,                      /* major */
268                 1,                      /* minor */
269                 maj,                    /* rmajor */
270                 min,                    /* rminor */
271                 (unsigned)strlen(name) + 1,/* namesize */
272                 0);                     /* chksum */
273         push_hdr(s);
274         push_rest(name);
275         return 0;
276 }
277
278 static int cpio_mknod_line(const char *line)
279 {
280         char name[PATH_MAX + 1];
281         unsigned int mode;
282         int uid;
283         int gid;
284         char dev_type;
285         unsigned int maj;
286         unsigned int min;
287         int rc = -1;
288
289         if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
290                          name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
291                 fprintf(stderr, "Unrecognized nod format '%s'", line);
292                 goto fail;
293         }
294         rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
295  fail:
296         return rc;
297 }
298
299 static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum)
300 {
301         while (size) {
302                 unsigned char filebuf[65536];
303                 ssize_t this_read;
304                 size_t i, this_size = MIN(size, sizeof(filebuf));
305
306                 this_read = read(fd, filebuf, this_size);
307                 if (this_read <= 0 || this_read > this_size)
308                         return -1;
309
310                 for (i = 0; i < this_read; i++)
311                         *csum += filebuf[i];
312
313                 size -= this_read;
314         }
315         /* seek back to the start for data segment I/O */
316         if (lseek(fd, 0, SEEK_SET) < 0)
317                 return -1;
318
319         return 0;
320 }
321
322 static int cpio_mkfile(const char *name, const char *location,
323                         unsigned int mode, uid_t uid, gid_t gid,
324                         unsigned int nlinks)
325 {
326         char s[256];
327         struct stat buf;
328         unsigned long size;
329         int file;
330         int retval;
331         int rc = -1;
332         int namesize;
333         unsigned int i;
334         uint32_t csum = 0;
335
336         mode |= S_IFREG;
337
338         file = open (location, O_RDONLY);
339         if (file < 0) {
340                 fprintf (stderr, "File %s could not be opened for reading\n", location);
341                 goto error;
342         }
343
344         retval = fstat(file, &buf);
345         if (retval) {
346                 fprintf(stderr, "File %s could not be stat()'ed\n", location);
347                 goto error;
348         }
349
350         if (buf.st_mtime > 0xffffffff) {
351                 fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
352                         location);
353                 buf.st_mtime = 0xffffffff;
354         }
355
356         if (buf.st_mtime < 0) {
357                 fprintf(stderr, "%s: Timestamp negative, clipping.\n",
358                         location);
359                 buf.st_mtime = 0;
360         }
361
362         if (buf.st_size > 0xffffffff) {
363                 fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",
364                         location);
365                 goto error;
366         }
367
368         if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {
369                 fprintf(stderr, "Failed to checksum file %s\n", location);
370                 goto error;
371         }
372
373         size = 0;
374         for (i = 1; i <= nlinks; i++) {
375                 /* data goes on last link */
376                 if (i == nlinks)
377                         size = buf.st_size;
378
379                 if (name[0] == '/')
380                         name++;
381                 namesize = strlen(name) + 1;
382                 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
383                        "%08lX%08X%08X%08X%08X%08X%08X",
384                         do_csum ? "070702" : "070701", /* magic */
385                         ino,                    /* ino */
386                         mode,                   /* mode */
387                         (long) uid,             /* uid */
388                         (long) gid,             /* gid */
389                         nlinks,                 /* nlink */
390                         (long) buf.st_mtime,    /* mtime */
391                         size,                   /* filesize */
392                         3,                      /* major */
393                         1,                      /* minor */
394                         0,                      /* rmajor */
395                         0,                      /* rminor */
396                         namesize,               /* namesize */
397                         size ? csum : 0);       /* chksum */
398                 push_hdr(s);
399                 push_string(name);
400                 push_pad();
401
402                 while (size) {
403                         unsigned char filebuf[65536];
404                         ssize_t this_read;
405                         size_t this_size = MIN(size, sizeof(filebuf));
406
407                         this_read = read(file, filebuf, this_size);
408                         if (this_read <= 0 || this_read > this_size) {
409                                 fprintf(stderr, "Can not read %s file\n", location);
410                                 goto error;
411                         }
412
413                         if (fwrite(filebuf, this_read, 1, stdout) != 1) {
414                                 fprintf(stderr, "writing filebuf failed\n");
415                                 goto error;
416                         }
417                         offset += this_read;
418                         size -= this_read;
419                 }
420                 push_pad();
421
422                 name += namesize;
423         }
424         ino++;
425         rc = 0;
426
427 error:
428         if (file >= 0)
429                 close(file);
430         return rc;
431 }
432
433 static char *cpio_replace_env(char *new_location)
434 {
435         char expanded[PATH_MAX + 1];
436         char *start, *end, *var;
437
438         while ((start = strstr(new_location, "${")) &&
439                (end = strchr(start + 2, '}'))) {
440                 *start = *end = 0;
441                 var = getenv(start + 2);
442                 snprintf(expanded, sizeof expanded, "%s%s%s",
443                          new_location, var ? var : "", end + 1);
444                 strcpy(new_location, expanded);
445         }
446
447         return new_location;
448 }
449
450 static int cpio_mkfile_line(const char *line)
451 {
452         char name[PATH_MAX + 1];
453         char *dname = NULL; /* malloc'ed buffer for hard links */
454         char location[PATH_MAX + 1];
455         unsigned int mode;
456         int uid;
457         int gid;
458         int nlinks = 1;
459         int end = 0, dname_len = 0;
460         int rc = -1;
461
462         if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
463                                 "s %o %d %d %n",
464                                 name, location, &mode, &uid, &gid, &end)) {
465                 fprintf(stderr, "Unrecognized file format '%s'", line);
466                 goto fail;
467         }
468         if (end && isgraph(line[end])) {
469                 int len;
470                 int nend;
471
472                 dname = malloc(strlen(line));
473                 if (!dname) {
474                         fprintf (stderr, "out of memory (%d)\n", dname_len);
475                         goto fail;
476                 }
477
478                 dname_len = strlen(name) + 1;
479                 memcpy(dname, name, dname_len);
480
481                 do {
482                         nend = 0;
483                         if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
484                                         name, &nend) < 1)
485                                 break;
486                         len = strlen(name) + 1;
487                         memcpy(dname + dname_len, name, len);
488                         dname_len += len;
489                         nlinks++;
490                         end += nend;
491                 } while (isgraph(line[end]));
492         } else {
493                 dname = name;
494         }
495         rc = cpio_mkfile(dname, cpio_replace_env(location),
496                          mode, uid, gid, nlinks);
497  fail:
498         if (dname_len) free(dname);
499         return rc;
500 }
501
502 static void usage(const char *prog)
503 {
504         fprintf(stderr, "Usage:\n"
505                 "\t%s [-t <timestamp>] [-c] <cpio_list>\n"
506                 "\n"
507                 "<cpio_list> is a file containing newline separated entries that\n"
508                 "describe the files to be included in the initramfs archive:\n"
509                 "\n"
510                 "# a comment\n"
511                 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
512                 "dir <name> <mode> <uid> <gid>\n"
513                 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
514                 "slink <name> <target> <mode> <uid> <gid>\n"
515                 "pipe <name> <mode> <uid> <gid>\n"
516                 "sock <name> <mode> <uid> <gid>\n"
517                 "\n"
518                 "<name>       name of the file/dir/nod/etc in the archive\n"
519                 "<location>   location of the file in the current filesystem\n"
520                 "             expands shell variables quoted with ${}\n"
521                 "<target>     link target\n"
522                 "<mode>       mode/permissions of the file\n"
523                 "<uid>        user id (0=root)\n"
524                 "<gid>        group id (0=root)\n"
525                 "<dev_type>   device type (b=block, c=character)\n"
526                 "<maj>        major number of nod\n"
527                 "<min>        minor number of nod\n"
528                 "<hard links> space separated list of other links to file\n"
529                 "\n"
530                 "example:\n"
531                 "# A simple initramfs\n"
532                 "dir /dev 0755 0 0\n"
533                 "nod /dev/console 0600 0 0 c 5 1\n"
534                 "dir /root 0700 0 0\n"
535                 "dir /sbin 0755 0 0\n"
536                 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
537                 "\n"
538                 "<timestamp> is time in seconds since Epoch that will be used\n"
539                 "as mtime for symlinks, special files and directories. The default\n"
540                 "is to use the current time for these entries.\n"
541                 "-c: calculate and store 32-bit checksums for file data.\n",
542                 prog);
543 }
544
545 static const struct file_handler file_handler_table[] = {
546         {
547                 .type    = "file",
548                 .handler = cpio_mkfile_line,
549         }, {
550                 .type    = "nod",
551                 .handler = cpio_mknod_line,
552         }, {
553                 .type    = "dir",
554                 .handler = cpio_mkdir_line,
555         }, {
556                 .type    = "slink",
557                 .handler = cpio_mkslink_line,
558         }, {
559                 .type    = "pipe",
560                 .handler = cpio_mkpipe_line,
561         }, {
562                 .type    = "sock",
563                 .handler = cpio_mksock_line,
564         }, {
565                 .type    = NULL,
566                 .handler = NULL,
567         }
568 };
569
570 #define LINE_SIZE (2 * PATH_MAX + 50)
571
572 int main (int argc, char *argv[])
573 {
574         FILE *cpio_list;
575         char line[LINE_SIZE];
576         char *args, *type;
577         int ec = 0;
578         int line_nr = 0;
579         const char *filename;
580
581         default_mtime = time(NULL);
582         while (1) {
583                 int opt = getopt(argc, argv, "t:ch");
584                 char *invalid;
585
586                 if (opt == -1)
587                         break;
588                 switch (opt) {
589                 case 't':
590                         default_mtime = strtol(optarg, &invalid, 10);
591                         if (!*optarg || *invalid) {
592                                 fprintf(stderr, "Invalid timestamp: %s\n",
593                                                 optarg);
594                                 usage(argv[0]);
595                                 exit(1);
596                         }
597                         break;
598                 case 'c':
599                         do_csum = true;
600                         break;
601                 case 'h':
602                 case '?':
603                         usage(argv[0]);
604                         exit(opt == 'h' ? 0 : 1);
605                 }
606         }
607
608         /*
609          * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
610          * representation that exceeds 8 chars and breaks the cpio header
611          * specification. Negative timestamps similarly exceed 8 chars.
612          */
613         if (default_mtime > 0xffffffff || default_mtime < 0) {
614                 fprintf(stderr, "ERROR: Timestamp out of range for cpio format\n");
615                 exit(1);
616         }
617
618         if (argc - optind != 1) {
619                 usage(argv[0]);
620                 exit(1);
621         }
622         filename = argv[optind];
623         if (!strcmp(filename, "-"))
624                 cpio_list = stdin;
625         else if (!(cpio_list = fopen(filename, "r"))) {
626                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
627                         filename, strerror(errno));
628                 usage(argv[0]);
629                 exit(1);
630         }
631
632         while (fgets(line, LINE_SIZE, cpio_list)) {
633                 int type_idx;
634                 size_t slen = strlen(line);
635
636                 line_nr++;
637
638                 if ('#' == *line) {
639                         /* comment - skip to next line */
640                         continue;
641                 }
642
643                 if (! (type = strtok(line, " \t"))) {
644                         fprintf(stderr,
645                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
646                                 line_nr, line);
647                         ec = -1;
648                         break;
649                 }
650
651                 if ('\n' == *type) {
652                         /* a blank line */
653                         continue;
654                 }
655
656                 if (slen == strlen(type)) {
657                         /* must be an empty line */
658                         continue;
659                 }
660
661                 if (! (args = strtok(NULL, "\n"))) {
662                         fprintf(stderr,
663                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
664                                 line_nr, line);
665                         ec = -1;
666                 }
667
668                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
669                         int rc;
670                         if (! strcmp(line, file_handler_table[type_idx].type)) {
671                                 if ((rc = file_handler_table[type_idx].handler(args))) {
672                                         ec = rc;
673                                         fprintf(stderr, " line %d\n", line_nr);
674                                 }
675                                 break;
676                         }
677                 }
678
679                 if (NULL == file_handler_table[type_idx].type) {
680                         fprintf(stderr, "unknown file type line %d: '%s'\n",
681                                 line_nr, line);
682                 }
683         }
684         if (ec == 0)
685                 cpio_trailer();
686
687         exit(ec);
688 }