Merge tag 'fscrypt-for-linus' of git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt
[sfrench/cifs-2.6.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 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         filebuf = malloc(buf.st_size);
324         if (!filebuf) {
325                 fprintf (stderr, "out of memory\n");
326                 goto error;
327         }
328
329         retval = read (file, filebuf, buf.st_size);
330         if (retval < 0) {
331                 fprintf (stderr, "Can not read %s file\n", location);
332                 goto error;
333         }
334
335         size = 0;
336         for (i = 1; i <= nlinks; i++) {
337                 /* data goes on last link */
338                 if (i == nlinks) size = buf.st_size;
339
340                 if (name[0] == '/')
341                         name++;
342                 namesize = strlen(name) + 1;
343                 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
344                        "%08lX%08X%08X%08X%08X%08X%08X",
345                         "070701",               /* magic */
346                         ino,                    /* ino */
347                         mode,                   /* mode */
348                         (long) uid,             /* uid */
349                         (long) gid,             /* gid */
350                         nlinks,                 /* nlink */
351                         (long) buf.st_mtime,    /* mtime */
352                         size,                   /* filesize */
353                         3,                      /* major */
354                         1,                      /* minor */
355                         0,                      /* rmajor */
356                         0,                      /* rminor */
357                         namesize,               /* namesize */
358                         0);                     /* chksum */
359                 push_hdr(s);
360                 push_string(name);
361                 push_pad();
362
363                 if (size) {
364                         if (fwrite(filebuf, size, 1, stdout) != 1) {
365                                 fprintf(stderr, "writing filebuf failed\n");
366                                 goto error;
367                         }
368                         offset += size;
369                         push_pad();
370                 }
371
372                 name += namesize;
373         }
374         ino++;
375         rc = 0;
376         
377 error:
378         if (filebuf) free(filebuf);
379         if (file >= 0) close(file);
380         return rc;
381 }
382
383 static char *cpio_replace_env(char *new_location)
384 {
385         char expanded[PATH_MAX + 1];
386         char *start, *end, *var;
387
388         while ((start = strstr(new_location, "${")) &&
389                (end = strchr(start + 2, '}'))) {
390                 *start = *end = 0;
391                 var = getenv(start + 2);
392                 snprintf(expanded, sizeof expanded, "%s%s%s",
393                          new_location, var ? var : "", end + 1);
394                 strcpy(new_location, expanded);
395         }
396
397         return new_location;
398 }
399
400 static int cpio_mkfile_line(const char *line)
401 {
402         char name[PATH_MAX + 1];
403         char *dname = NULL; /* malloc'ed buffer for hard links */
404         char location[PATH_MAX + 1];
405         unsigned int mode;
406         int uid;
407         int gid;
408         int nlinks = 1;
409         int end = 0, dname_len = 0;
410         int rc = -1;
411
412         if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
413                                 "s %o %d %d %n",
414                                 name, location, &mode, &uid, &gid, &end)) {
415                 fprintf(stderr, "Unrecognized file format '%s'", line);
416                 goto fail;
417         }
418         if (end && isgraph(line[end])) {
419                 int len;
420                 int nend;
421
422                 dname = malloc(strlen(line));
423                 if (!dname) {
424                         fprintf (stderr, "out of memory (%d)\n", dname_len);
425                         goto fail;
426                 }
427
428                 dname_len = strlen(name) + 1;
429                 memcpy(dname, name, dname_len);
430
431                 do {
432                         nend = 0;
433                         if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
434                                         name, &nend) < 1)
435                                 break;
436                         len = strlen(name) + 1;
437                         memcpy(dname + dname_len, name, len);
438                         dname_len += len;
439                         nlinks++;
440                         end += nend;
441                 } while (isgraph(line[end]));
442         } else {
443                 dname = name;
444         }
445         rc = cpio_mkfile(dname, cpio_replace_env(location),
446                          mode, uid, gid, nlinks);
447  fail:
448         if (dname_len) free(dname);
449         return rc;
450 }
451
452 static void usage(const char *prog)
453 {
454         fprintf(stderr, "Usage:\n"
455                 "\t%s [-t <timestamp>] <cpio_list>\n"
456                 "\n"
457                 "<cpio_list> is a file containing newline separated entries that\n"
458                 "describe the files to be included in the initramfs archive:\n"
459                 "\n"
460                 "# a comment\n"
461                 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
462                 "dir <name> <mode> <uid> <gid>\n"
463                 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
464                 "slink <name> <target> <mode> <uid> <gid>\n"
465                 "pipe <name> <mode> <uid> <gid>\n"
466                 "sock <name> <mode> <uid> <gid>\n"
467                 "\n"
468                 "<name>       name of the file/dir/nod/etc in the archive\n"
469                 "<location>   location of the file in the current filesystem\n"
470                 "             expands shell variables quoted with ${}\n"
471                 "<target>     link target\n"
472                 "<mode>       mode/permissions of the file\n"
473                 "<uid>        user id (0=root)\n"
474                 "<gid>        group id (0=root)\n"
475                 "<dev_type>   device type (b=block, c=character)\n"
476                 "<maj>        major number of nod\n"
477                 "<min>        minor number of nod\n"
478                 "<hard links> space separated list of other links to file\n"
479                 "\n"
480                 "example:\n"
481                 "# A simple initramfs\n"
482                 "dir /dev 0755 0 0\n"
483                 "nod /dev/console 0600 0 0 c 5 1\n"
484                 "dir /root 0700 0 0\n"
485                 "dir /sbin 0755 0 0\n"
486                 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
487                 "\n"
488                 "<timestamp> is time in seconds since Epoch that will be used\n"
489                 "as mtime for symlinks, special files and directories. The default\n"
490                 "is to use the current time for these entries.\n",
491                 prog);
492 }
493
494 struct file_handler file_handler_table[] = {
495         {
496                 .type    = "file",
497                 .handler = cpio_mkfile_line,
498         }, {
499                 .type    = "nod",
500                 .handler = cpio_mknod_line,
501         }, {
502                 .type    = "dir",
503                 .handler = cpio_mkdir_line,
504         }, {
505                 .type    = "slink",
506                 .handler = cpio_mkslink_line,
507         }, {
508                 .type    = "pipe",
509                 .handler = cpio_mkpipe_line,
510         }, {
511                 .type    = "sock",
512                 .handler = cpio_mksock_line,
513         }, {
514                 .type    = NULL,
515                 .handler = NULL,
516         }
517 };
518
519 #define LINE_SIZE (2 * PATH_MAX + 50)
520
521 int main (int argc, char *argv[])
522 {
523         FILE *cpio_list;
524         char line[LINE_SIZE];
525         char *args, *type;
526         int ec = 0;
527         int line_nr = 0;
528         const char *filename;
529
530         default_mtime = time(NULL);
531         while (1) {
532                 int opt = getopt(argc, argv, "t:h");
533                 char *invalid;
534
535                 if (opt == -1)
536                         break;
537                 switch (opt) {
538                 case 't':
539                         default_mtime = strtol(optarg, &invalid, 10);
540                         if (!*optarg || *invalid) {
541                                 fprintf(stderr, "Invalid timestamp: %s\n",
542                                                 optarg);
543                                 usage(argv[0]);
544                                 exit(1);
545                         }
546                         break;
547                 case 'h':
548                 case '?':
549                         usage(argv[0]);
550                         exit(opt == 'h' ? 0 : 1);
551                 }
552         }
553
554         if (argc - optind != 1) {
555                 usage(argv[0]);
556                 exit(1);
557         }
558         filename = argv[optind];
559         if (!strcmp(filename, "-"))
560                 cpio_list = stdin;
561         else if (!(cpio_list = fopen(filename, "r"))) {
562                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
563                         filename, strerror(errno));
564                 usage(argv[0]);
565                 exit(1);
566         }
567
568         while (fgets(line, LINE_SIZE, cpio_list)) {
569                 int type_idx;
570                 size_t slen = strlen(line);
571
572                 line_nr++;
573
574                 if ('#' == *line) {
575                         /* comment - skip to next line */
576                         continue;
577                 }
578
579                 if (! (type = strtok(line, " \t"))) {
580                         fprintf(stderr,
581                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
582                                 line_nr, line);
583                         ec = -1;
584                         break;
585                 }
586
587                 if ('\n' == *type) {
588                         /* a blank line */
589                         continue;
590                 }
591
592                 if (slen == strlen(type)) {
593                         /* must be an empty line */
594                         continue;
595                 }
596
597                 if (! (args = strtok(NULL, "\n"))) {
598                         fprintf(stderr,
599                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
600                                 line_nr, line);
601                         ec = -1;
602                 }
603
604                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
605                         int rc;
606                         if (! strcmp(line, file_handler_table[type_idx].type)) {
607                                 if ((rc = file_handler_table[type_idx].handler(args))) {
608                                         ec = rc;
609                                         fprintf(stderr, " line %d\n", line_nr);
610                                 }
611                                 break;
612                         }
613                 }
614
615                 if (NULL == file_handler_table[type_idx].type) {
616                         fprintf(stderr, "unknown file type line %d: '%s'\n",
617                                 line_nr, line);
618                 }
619         }
620         if (ec == 0)
621                 cpio_trailer();
622
623         exit(ec);
624 }