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