]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/blob - usr/gen_init_cpio.c
Merge tag 'platform-drivers-x86-v4.15-1' of git://git.infradead.org/linux-platform...
[rpmsg/rpmsg.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>
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  */
21 #define xstr(s) #s
22 #define str(s) xstr(s)
24 static unsigned int offset;
25 static unsigned int ino = 721;
26 static time_t default_mtime;
28 struct file_handler {
29         const char *type;
30         int (*handler)(const char *line);
31 };
33 static void push_string(const char *name)
34 {
35         unsigned int name_len = strlen(name) + 1;
37         fputs(name, stdout);
38         putchar(0);
39         offset += name_len;
40 }
42 static void push_pad (void)
43 {
44         while (offset & 3) {
45                 putchar(0);
46                 offset++;
47         }
48 }
50 static void push_rest(const char *name)
51 {
52         unsigned int name_len = strlen(name) + 1;
53         unsigned int tmp_ofs;
55         fputs(name, stdout);
56         putchar(0);
57         offset += name_len;
59         tmp_ofs = name_len + 110;
60         while (tmp_ofs & 3) {
61                 putchar(0);
62                 offset++;
63                 tmp_ofs++;
64         }
65 }
67 static void push_hdr(const char *s)
68 {
69         fputs(s, stdout);
70         offset += 110;
71 }
73 static void cpio_trailer(void)
74 {
75         char s[256];
76         const char name[] = "TRAILER!!!";
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);
97         while (offset % 512) {
98                 putchar(0);
99                 offset++;
100         }
103 static int cpio_mkslink(const char *name, const char *target,
104                          unsigned int mode, uid_t uid, gid_t gid)
106         char s[256];
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;
134 static int cpio_mkslink_line(const char *line)
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;
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;
152 static int cpio_mkgeneric(const char *name, unsigned int mode,
153                        uid_t uid, gid_t gid)
155         char s[256];
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;
180 enum generic_types {
181         GT_DIR,
182         GT_PIPE,
183         GT_SOCK
184 };
186 struct generic_type {
187         const char *type;
188         mode_t mode;
189 };
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 };
206 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
208         char name[PATH_MAX + 1];
209         unsigned int mode;
210         int uid;
211         int gid;
212         int rc = -1;
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;
225 static int cpio_mkdir_line(const char *line)
227         return cpio_mkgeneric_line(line, GT_DIR);
230 static int cpio_mkpipe_line(const char *line)
232         return cpio_mkgeneric_line(line, GT_PIPE);
235 static int cpio_mksock_line(const char *line)
237         return cpio_mkgeneric_line(line, GT_SOCK);
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)
244         char s[256];
246         if (dev_type == 'b')
247                 mode |= S_IFBLK;
248         else
249                 mode |= S_IFCHR;
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;
274 static int cpio_mknod_line(const char *line)
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;
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;
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)
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;
309         mode |= S_IFREG;
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         }
317         retval = fstat(file, &buf);
318         if (retval) {
319                 fprintf(stderr, "File %s could not be stat()'ed\n", location);
320                 goto error;
321         }
323         filebuf = malloc(buf.st_size);
324         if (!filebuf) {
325                 fprintf (stderr, "out of memory\n");
326                 goto error;
327         }
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         }
335         size = 0;
336         for (i = 1; i <= nlinks; i++) {
337                 /* data goes on last link */
338                 if (i == nlinks) size = buf.st_size;
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();
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                 }
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;
383 static char *cpio_replace_env(char *new_location)
385         char expanded[PATH_MAX + 1];
386         char *start, *end, *var;
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         }
397         return new_location;
400 static int cpio_mkfile_line(const char *line)
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;
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;
422                 dname = malloc(strlen(line));
423                 if (!dname) {
424                         fprintf (stderr, "out of memory (%d)\n", dname_len);
425                         goto fail;
426                 }
428                 dname_len = strlen(name) + 1;
429                 memcpy(dname, name, dname_len);
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;
452 static void usage(const char *prog)
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);
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 };
519 #define LINE_SIZE (2 * PATH_MAX + 50)
521 int main (int argc, char *argv[])
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;
530         default_mtime = time(NULL);
531         while (1) {
532                 int opt = getopt(argc, argv, "t:h");
533                 char *invalid;
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         }
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         }
568         while (fgets(line, LINE_SIZE, cpio_list)) {
569                 int type_idx;
570                 size_t slen = strlen(line);
572                 line_nr++;
574                 if ('#' == *line) {
575                         /* comment - skip to next line */
576                         continue;
577                 }
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                 }
587                 if ('\n' == *type) {
588                         /* a blank line */
589                         continue;
590                 }
592                 if (slen == strlen(type)) {
593                         /* must be an empty line */
594                         continue;
595                 }
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                 }
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                 }
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();
623         exit(ec);