Linux 6.0-rc1
[linux-2.6-microblaze.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 = -1;
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_size > 0xffffffff) {
357                 fprintf(stderr, "%s: Size exceeds maximum cpio file size\n",
358                         location);
359                 goto error;
360         }
361
362         if (do_csum && cpio_mkfile_csum(file, buf.st_size, &csum) < 0) {
363                 fprintf(stderr, "Failed to checksum file %s\n", location);
364                 goto error;
365         }
366
367         size = 0;
368         for (i = 1; i <= nlinks; i++) {
369                 /* data goes on last link */
370                 if (i == nlinks)
371                         size = buf.st_size;
372
373                 if (name[0] == '/')
374                         name++;
375                 namesize = strlen(name) + 1;
376                 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
377                        "%08lX%08X%08X%08X%08X%08X%08X",
378                         do_csum ? "070702" : "070701", /* magic */
379                         ino,                    /* ino */
380                         mode,                   /* mode */
381                         (long) uid,             /* uid */
382                         (long) gid,             /* gid */
383                         nlinks,                 /* nlink */
384                         (long) buf.st_mtime,    /* mtime */
385                         size,                   /* filesize */
386                         3,                      /* major */
387                         1,                      /* minor */
388                         0,                      /* rmajor */
389                         0,                      /* rminor */
390                         namesize,               /* namesize */
391                         size ? csum : 0);       /* chksum */
392                 push_hdr(s);
393                 push_string(name);
394                 push_pad();
395
396                 while (size) {
397                         unsigned char filebuf[65536];
398                         ssize_t this_read;
399                         size_t this_size = MIN(size, sizeof(filebuf));
400
401                         this_read = read(file, filebuf, this_size);
402                         if (this_read <= 0 || this_read > this_size) {
403                                 fprintf(stderr, "Can not read %s file\n", location);
404                                 goto error;
405                         }
406
407                         if (fwrite(filebuf, this_read, 1, stdout) != 1) {
408                                 fprintf(stderr, "writing filebuf failed\n");
409                                 goto error;
410                         }
411                         offset += this_read;
412                         size -= this_read;
413                 }
414                 push_pad();
415
416                 name += namesize;
417         }
418         ino++;
419         rc = 0;
420
421 error:
422         if (file >= 0)
423                 close(file);
424         return rc;
425 }
426
427 static char *cpio_replace_env(char *new_location)
428 {
429         char expanded[PATH_MAX + 1];
430         char *start, *end, *var;
431
432         while ((start = strstr(new_location, "${")) &&
433                (end = strchr(start + 2, '}'))) {
434                 *start = *end = 0;
435                 var = getenv(start + 2);
436                 snprintf(expanded, sizeof expanded, "%s%s%s",
437                          new_location, var ? var : "", end + 1);
438                 strcpy(new_location, expanded);
439         }
440
441         return new_location;
442 }
443
444 static int cpio_mkfile_line(const char *line)
445 {
446         char name[PATH_MAX + 1];
447         char *dname = NULL; /* malloc'ed buffer for hard links */
448         char location[PATH_MAX + 1];
449         unsigned int mode;
450         int uid;
451         int gid;
452         int nlinks = 1;
453         int end = 0, dname_len = 0;
454         int rc = -1;
455
456         if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
457                                 "s %o %d %d %n",
458                                 name, location, &mode, &uid, &gid, &end)) {
459                 fprintf(stderr, "Unrecognized file format '%s'", line);
460                 goto fail;
461         }
462         if (end && isgraph(line[end])) {
463                 int len;
464                 int nend;
465
466                 dname = malloc(strlen(line));
467                 if (!dname) {
468                         fprintf (stderr, "out of memory (%d)\n", dname_len);
469                         goto fail;
470                 }
471
472                 dname_len = strlen(name) + 1;
473                 memcpy(dname, name, dname_len);
474
475                 do {
476                         nend = 0;
477                         if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
478                                         name, &nend) < 1)
479                                 break;
480                         len = strlen(name) + 1;
481                         memcpy(dname + dname_len, name, len);
482                         dname_len += len;
483                         nlinks++;
484                         end += nend;
485                 } while (isgraph(line[end]));
486         } else {
487                 dname = name;
488         }
489         rc = cpio_mkfile(dname, cpio_replace_env(location),
490                          mode, uid, gid, nlinks);
491  fail:
492         if (dname_len) free(dname);
493         return rc;
494 }
495
496 static void usage(const char *prog)
497 {
498         fprintf(stderr, "Usage:\n"
499                 "\t%s [-t <timestamp>] [-c] <cpio_list>\n"
500                 "\n"
501                 "<cpio_list> is a file containing newline separated entries that\n"
502                 "describe the files to be included in the initramfs archive:\n"
503                 "\n"
504                 "# a comment\n"
505                 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
506                 "dir <name> <mode> <uid> <gid>\n"
507                 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
508                 "slink <name> <target> <mode> <uid> <gid>\n"
509                 "pipe <name> <mode> <uid> <gid>\n"
510                 "sock <name> <mode> <uid> <gid>\n"
511                 "\n"
512                 "<name>       name of the file/dir/nod/etc in the archive\n"
513                 "<location>   location of the file in the current filesystem\n"
514                 "             expands shell variables quoted with ${}\n"
515                 "<target>     link target\n"
516                 "<mode>       mode/permissions of the file\n"
517                 "<uid>        user id (0=root)\n"
518                 "<gid>        group id (0=root)\n"
519                 "<dev_type>   device type (b=block, c=character)\n"
520                 "<maj>        major number of nod\n"
521                 "<min>        minor number of nod\n"
522                 "<hard links> space separated list of other links to file\n"
523                 "\n"
524                 "example:\n"
525                 "# A simple initramfs\n"
526                 "dir /dev 0755 0 0\n"
527                 "nod /dev/console 0600 0 0 c 5 1\n"
528                 "dir /root 0700 0 0\n"
529                 "dir /sbin 0755 0 0\n"
530                 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
531                 "\n"
532                 "<timestamp> is time in seconds since Epoch that will be used\n"
533                 "as mtime for symlinks, special files and directories. The default\n"
534                 "is to use the current time for these entries.\n"
535                 "-c: calculate and store 32-bit checksums for file data.\n",
536                 prog);
537 }
538
539 static const struct file_handler file_handler_table[] = {
540         {
541                 .type    = "file",
542                 .handler = cpio_mkfile_line,
543         }, {
544                 .type    = "nod",
545                 .handler = cpio_mknod_line,
546         }, {
547                 .type    = "dir",
548                 .handler = cpio_mkdir_line,
549         }, {
550                 .type    = "slink",
551                 .handler = cpio_mkslink_line,
552         }, {
553                 .type    = "pipe",
554                 .handler = cpio_mkpipe_line,
555         }, {
556                 .type    = "sock",
557                 .handler = cpio_mksock_line,
558         }, {
559                 .type    = NULL,
560                 .handler = NULL,
561         }
562 };
563
564 #define LINE_SIZE (2 * PATH_MAX + 50)
565
566 int main (int argc, char *argv[])
567 {
568         FILE *cpio_list;
569         char line[LINE_SIZE];
570         char *args, *type;
571         int ec = 0;
572         int line_nr = 0;
573         const char *filename;
574
575         default_mtime = time(NULL);
576         while (1) {
577                 int opt = getopt(argc, argv, "t:ch");
578                 char *invalid;
579
580                 if (opt == -1)
581                         break;
582                 switch (opt) {
583                 case 't':
584                         default_mtime = strtol(optarg, &invalid, 10);
585                         if (!*optarg || *invalid) {
586                                 fprintf(stderr, "Invalid timestamp: %s\n",
587                                                 optarg);
588                                 usage(argv[0]);
589                                 exit(1);
590                         }
591                         break;
592                 case 'c':
593                         do_csum = true;
594                         break;
595                 case 'h':
596                 case '?':
597                         usage(argv[0]);
598                         exit(opt == 'h' ? 0 : 1);
599                 }
600         }
601
602         /*
603          * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
604          * representation that exceeds 8 chars and breaks the cpio header
605          * specification.
606          */
607         if (default_mtime > 0xffffffff) {
608                 fprintf(stderr, "ERROR: Timestamp too large for cpio format\n");
609                 exit(1);
610         }
611
612         if (argc - optind != 1) {
613                 usage(argv[0]);
614                 exit(1);
615         }
616         filename = argv[optind];
617         if (!strcmp(filename, "-"))
618                 cpio_list = stdin;
619         else if (!(cpio_list = fopen(filename, "r"))) {
620                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
621                         filename, strerror(errno));
622                 usage(argv[0]);
623                 exit(1);
624         }
625
626         while (fgets(line, LINE_SIZE, cpio_list)) {
627                 int type_idx;
628                 size_t slen = strlen(line);
629
630                 line_nr++;
631
632                 if ('#' == *line) {
633                         /* comment - skip to next line */
634                         continue;
635                 }
636
637                 if (! (type = strtok(line, " \t"))) {
638                         fprintf(stderr,
639                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
640                                 line_nr, line);
641                         ec = -1;
642                         break;
643                 }
644
645                 if ('\n' == *type) {
646                         /* a blank line */
647                         continue;
648                 }
649
650                 if (slen == strlen(type)) {
651                         /* must be an empty line */
652                         continue;
653                 }
654
655                 if (! (args = strtok(NULL, "\n"))) {
656                         fprintf(stderr,
657                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
658                                 line_nr, line);
659                         ec = -1;
660                 }
661
662                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
663                         int rc;
664                         if (! strcmp(line, file_handler_table[type_idx].type)) {
665                                 if ((rc = file_handler_table[type_idx].handler(args))) {
666                                         ec = rc;
667                                         fprintf(stderr, " line %d\n", line_nr);
668                                 }
669                                 break;
670                         }
671                 }
672
673                 if (NULL == file_handler_table[type_idx].type) {
674                         fprintf(stderr, "unknown file type line %d: '%s'\n",
675                                 line_nr, line);
676                 }
677         }
678         if (ec == 0)
679                 cpio_trailer();
680
681         exit(ec);
682 }