OSDN Git Service

dt-bindings: display: mediatek, mutex: Fix mediatek, gce-events type
[uclinux-h8/linux.git] / usr / gen_init_cpio.c
1 // SPDX-License-Identifier: GPL-2.0
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
18  * Hard link support by Luciano Rocha
19  */
20
21 #define xstr(s) #s
22 #define str(s) xstr(s)
23
24 static unsigned int offset;
25 static unsigned int ino = 721;
26 static time_t default_mtime;
27
28 struct file_handler {
29         const char *type;
30         int (*handler)(const char *line);
31 };
32
33 static 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
42 static void push_pad (void)
43 {
44         while (offset & 3) {
45                 putchar(0);
46                 offset++;
47         }
48 }
49
50 static 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
67 static void push_hdr(const char *s)
68 {
69         fputs(s, stdout);
70         offset += 110;
71 }
72
73 static 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
103 static int cpio_mkslink(const char *name, const char *target,
104                          unsigned int mode, uid_t uid, gid_t gid)
105 {
106         char s[256];
107
108         if (name[0] == '/')
109                 name++;
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 */
118                 (long) default_mtime,   /* mtime */
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
134 static 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
152 static int cpio_mkgeneric(const char *name, unsigned int mode,
153                        uid_t uid, gid_t gid)
154 {
155         char s[256];
156
157         if (name[0] == '/')
158                 name++;
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 */
167                 (long) default_mtime,   /* mtime */
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
180 enum generic_types {
181         GT_DIR,
182         GT_PIPE,
183         GT_SOCK
184 };
185
186 struct generic_type {
187         const char *type;
188         mode_t mode;
189 };
190
191 static const struct generic_type generic_type_table[] = {
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
206 static 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
225 static int cpio_mkdir_line(const char *line)
226 {
227         return cpio_mkgeneric_line(line, GT_DIR);
228 }
229
230 static int cpio_mkpipe_line(const char *line)
231 {
232         return cpio_mkgeneric_line(line, GT_PIPE);
233 }
234
235 static int cpio_mksock_line(const char *line)
236 {
237         return cpio_mkgeneric_line(line, GT_SOCK);
238 }
239
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)
243 {
244         char s[256];
245
246         if (dev_type == 'b')
247                 mode |= S_IFBLK;
248         else
249                 mode |= S_IFCHR;
250
251         if (name[0] == '/')
252                 name++;
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 */
261                 (long) default_mtime,   /* mtime */
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
274 static 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
295 static int cpio_mkfile(const char *name, const char *location,
296                         unsigned int mode, uid_t uid, gid_t gid,
297                         unsigned int nlinks)
298 {
299         char s[256];
300         char *filebuf = NULL;
301         struct stat buf;
302         long size;
303         int file = -1;
304         int retval;
305         int rc = -1;
306         int namesize;
307         unsigned int i;
308
309         mode |= S_IFREG;
310
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
317         retval = fstat(file, &buf);
318         if (retval) {
319                 fprintf(stderr, "File %s could not be stat()'ed\n", location);
320                 goto error;
321         }
322
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
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
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
346                 if (name[0] == '/')
347                         name++;
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) {
370                         if (fwrite(filebuf, size, 1, stdout) != 1) {
371                                 fprintf(stderr, "writing filebuf failed\n");
372                                 goto error;
373                         }
374                         offset += size;
375                         push_pad();
376                 }
377
378                 name += namesize;
379         }
380         ino++;
381         rc = 0;
382         
383 error:
384         if (filebuf) free(filebuf);
385         if (file >= 0) close(file);
386         return rc;
387 }
388
389 static char *cpio_replace_env(char *new_location)
390 {
391         char expanded[PATH_MAX + 1];
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);
401         }
402
403         return new_location;
404 }
405
406 static int cpio_mkfile_line(const char *line)
407 {
408         char name[PATH_MAX + 1];
409         char *dname = NULL; /* malloc'ed buffer for hard links */
410         char location[PATH_MAX + 1];
411         unsigned int mode;
412         int uid;
413         int gid;
414         int nlinks = 1;
415         int end = 0, dname_len = 0;
416         int rc = -1;
417
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)) {
421                 fprintf(stderr, "Unrecognized file format '%s'", line);
422                 goto fail;
423         }
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         }
451         rc = cpio_mkfile(dname, cpio_replace_env(location),
452                          mode, uid, gid, nlinks);
453  fail:
454         if (dname_len) free(dname);
455         return rc;
456 }
457
458 static void usage(const char *prog)
459 {
460         fprintf(stderr, "Usage:\n"
461                 "\t%s [-t <timestamp>] <cpio_list>\n"
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"
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"
473                 "\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"
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"
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",
497                 prog);
498 }
499
500 static const struct file_handler file_handler_table[] = {
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
527 int 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;
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         }
559
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
570         if (argc - optind != 1) {
571                 usage(argv[0]);
572                 exit(1);
573         }
574         filename = argv[optind];
575         if (!strcmp(filename, "-"))
576                 cpio_list = stdin;
577         else if (!(cpio_list = fopen(filename, "r"))) {
578                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
579                         filename, strerror(errno));
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;
600                         break;
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         }
636         if (ec == 0)
637                 cpio_trailer();
638
639         exit(ec);
640 }