Merge master.kernel.org:/home/rmk/linux-2.6-arm
[linux-3.10.git] / usr / gen_init_cpio.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <time.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <ctype.h>
11 #include <limits.h>
12
13 /*
14  * Original work by Jeff Garzik
15  *
16  * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
17  * Hard link support by Luciano Rocha
18  */
19
20 #define xstr(s) #s
21 #define str(s) xstr(s)
22
23 static unsigned int offset;
24 static unsigned int ino = 721;
25
26 struct file_handler {
27         const char *type;
28         int (*handler)(const char *line);
29 };
30
31 static void push_string(const char *name)
32 {
33         unsigned int name_len = strlen(name) + 1;
34
35         fputs(name, stdout);
36         putchar(0);
37         offset += name_len;
38 }
39
40 static void push_pad (void)
41 {
42         while (offset & 3) {
43                 putchar(0);
44                 offset++;
45         }
46 }
47
48 static void push_rest(const char *name)
49 {
50         unsigned int name_len = strlen(name) + 1;
51         unsigned int tmp_ofs;
52
53         fputs(name, stdout);
54         putchar(0);
55         offset += name_len;
56
57         tmp_ofs = name_len + 110;
58         while (tmp_ofs & 3) {
59                 putchar(0);
60                 offset++;
61                 tmp_ofs++;
62         }
63 }
64
65 static void push_hdr(const char *s)
66 {
67         fputs(s, stdout);
68         offset += 110;
69 }
70
71 static void cpio_trailer(void)
72 {
73         char s[256];
74         const char name[] = "TRAILER!!!";
75
76         sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
77                "%08X%08X%08X%08X%08X%08X%08X",
78                 "070701",               /* magic */
79                 0,                      /* ino */
80                 0,                      /* mode */
81                 (long) 0,               /* uid */
82                 (long) 0,               /* gid */
83                 1,                      /* nlink */
84                 (long) 0,               /* mtime */
85                 0,                      /* filesize */
86                 0,                      /* major */
87                 0,                      /* minor */
88                 0,                      /* rmajor */
89                 0,                      /* rminor */
90                 (unsigned)strlen(name)+1, /* namesize */
91                 0);                     /* chksum */
92         push_hdr(s);
93         push_rest(name);
94
95         while (offset % 512) {
96                 putchar(0);
97                 offset++;
98         }
99 }
100
101 static int cpio_mkslink(const char *name, const char *target,
102                          unsigned int mode, uid_t uid, gid_t gid)
103 {
104         char s[256];
105         time_t mtime = time(NULL);
106
107         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
108                "%08X%08X%08X%08X%08X%08X%08X",
109                 "070701",               /* magic */
110                 ino++,                  /* ino */
111                 S_IFLNK | mode,         /* mode */
112                 (long) uid,             /* uid */
113                 (long) gid,             /* gid */
114                 1,                      /* nlink */
115                 (long) mtime,           /* mtime */
116                 (unsigned)strlen(target)+1, /* filesize */
117                 3,                      /* major */
118                 1,                      /* minor */
119                 0,                      /* rmajor */
120                 0,                      /* rminor */
121                 (unsigned)strlen(name) + 1,/* namesize */
122                 0);                     /* chksum */
123         push_hdr(s);
124         push_string(name);
125         push_pad();
126         push_string(target);
127         push_pad();
128         return 0;
129 }
130
131 static int cpio_mkslink_line(const char *line)
132 {
133         char name[PATH_MAX + 1];
134         char target[PATH_MAX + 1];
135         unsigned int mode;
136         int uid;
137         int gid;
138         int rc = -1;
139
140         if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
141                 fprintf(stderr, "Unrecognized dir format '%s'", line);
142                 goto fail;
143         }
144         rc = cpio_mkslink(name, target, mode, uid, gid);
145  fail:
146         return rc;
147 }
148
149 static int cpio_mkgeneric(const char *name, unsigned int mode,
150                        uid_t uid, gid_t gid)
151 {
152         char s[256];
153         time_t mtime = time(NULL);
154
155         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
156                "%08X%08X%08X%08X%08X%08X%08X",
157                 "070701",               /* magic */
158                 ino++,                  /* ino */
159                 mode,                   /* mode */
160                 (long) uid,             /* uid */
161                 (long) gid,             /* gid */
162                 2,                      /* nlink */
163                 (long) mtime,           /* mtime */
164                 0,                      /* filesize */
165                 3,                      /* major */
166                 1,                      /* minor */
167                 0,                      /* rmajor */
168                 0,                      /* rminor */
169                 (unsigned)strlen(name) + 1,/* namesize */
170                 0);                     /* chksum */
171         push_hdr(s);
172         push_rest(name);
173         return 0;
174 }
175
176 enum generic_types {
177         GT_DIR,
178         GT_PIPE,
179         GT_SOCK
180 };
181
182 struct generic_type {
183         const char *type;
184         mode_t mode;
185 };
186
187 static struct generic_type generic_type_table[] = {
188         [GT_DIR] = {
189                 .type = "dir",
190                 .mode = S_IFDIR
191         },
192         [GT_PIPE] = {
193                 .type = "pipe",
194                 .mode = S_IFIFO
195         },
196         [GT_SOCK] = {
197                 .type = "sock",
198                 .mode = S_IFSOCK
199         }
200 };
201
202 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
203 {
204         char name[PATH_MAX + 1];
205         unsigned int mode;
206         int uid;
207         int gid;
208         int rc = -1;
209
210         if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
211                 fprintf(stderr, "Unrecognized %s format '%s'",
212                         line, generic_type_table[gt].type);
213                 goto fail;
214         }
215         mode |= generic_type_table[gt].mode;
216         rc = cpio_mkgeneric(name, mode, uid, gid);
217  fail:
218         return rc;
219 }
220
221 static int cpio_mkdir_line(const char *line)
222 {
223         return cpio_mkgeneric_line(line, GT_DIR);
224 }
225
226 static int cpio_mkpipe_line(const char *line)
227 {
228         return cpio_mkgeneric_line(line, GT_PIPE);
229 }
230
231 static int cpio_mksock_line(const char *line)
232 {
233         return cpio_mkgeneric_line(line, GT_SOCK);
234 }
235
236 static int cpio_mknod(const char *name, unsigned int mode,
237                        uid_t uid, gid_t gid, char dev_type,
238                        unsigned int maj, unsigned int min)
239 {
240         char s[256];
241         time_t mtime = time(NULL);
242
243         if (dev_type == 'b')
244                 mode |= S_IFBLK;
245         else
246                 mode |= S_IFCHR;
247
248         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
249                "%08X%08X%08X%08X%08X%08X%08X",
250                 "070701",               /* magic */
251                 ino++,                  /* ino */
252                 mode,                   /* mode */
253                 (long) uid,             /* uid */
254                 (long) gid,             /* gid */
255                 1,                      /* nlink */
256                 (long) mtime,           /* mtime */
257                 0,                      /* filesize */
258                 3,                      /* major */
259                 1,                      /* minor */
260                 maj,                    /* rmajor */
261                 min,                    /* rminor */
262                 (unsigned)strlen(name) + 1,/* namesize */
263                 0);                     /* chksum */
264         push_hdr(s);
265         push_rest(name);
266         return 0;
267 }
268
269 static int cpio_mknod_line(const char *line)
270 {
271         char name[PATH_MAX + 1];
272         unsigned int mode;
273         int uid;
274         int gid;
275         char dev_type;
276         unsigned int maj;
277         unsigned int min;
278         int rc = -1;
279
280         if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
281                          name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
282                 fprintf(stderr, "Unrecognized nod format '%s'", line);
283                 goto fail;
284         }
285         rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
286  fail:
287         return rc;
288 }
289
290 static int cpio_mkfile(const char *name, const char *location,
291                         unsigned int mode, uid_t uid, gid_t gid,
292                         unsigned int nlinks)
293 {
294         char s[256];
295         char *filebuf = NULL;
296         struct stat buf;
297         long size;
298         int file = -1;
299         int retval;
300         int rc = -1;
301         int namesize;
302         int i;
303
304         mode |= S_IFREG;
305
306         retval = stat (location, &buf);
307         if (retval) {
308                 fprintf (stderr, "File %s could not be located\n", location);
309                 goto error;
310         }
311
312         file = open (location, O_RDONLY);
313         if (file < 0) {
314                 fprintf (stderr, "File %s could not be opened for reading\n", location);
315                 goto error;
316         }
317
318         filebuf = malloc(buf.st_size);
319         if (!filebuf) {
320                 fprintf (stderr, "out of memory\n");
321                 goto error;
322         }
323
324         retval = read (file, filebuf, buf.st_size);
325         if (retval < 0) {
326                 fprintf (stderr, "Can not read %s file\n", location);
327                 goto error;
328         }
329
330         size = 0;
331         for (i = 1; i <= nlinks; i++) {
332                 /* data goes on last link */
333                 if (i == nlinks) size = buf.st_size;
334
335                 namesize = strlen(name) + 1;
336                 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
337                        "%08lX%08X%08X%08X%08X%08X%08X",
338                         "070701",               /* magic */
339                         ino,                    /* ino */
340                         mode,                   /* mode */
341                         (long) uid,             /* uid */
342                         (long) gid,             /* gid */
343                         nlinks,                 /* nlink */
344                         (long) buf.st_mtime,    /* mtime */
345                         size,                   /* filesize */
346                         3,                      /* major */
347                         1,                      /* minor */
348                         0,                      /* rmajor */
349                         0,                      /* rminor */
350                         namesize,               /* namesize */
351                         0);                     /* chksum */
352                 push_hdr(s);
353                 push_string(name);
354                 push_pad();
355
356                 if (size) {
357                         if (fwrite(filebuf, size, 1, stdout) != 1) {
358                                 fprintf(stderr, "writing filebuf failed\n");
359                                 goto error;
360                         }
361                         offset += size;
362                         push_pad();
363                 }
364
365                 name += namesize;
366         }
367         ino++;
368         rc = 0;
369         
370 error:
371         if (filebuf) free(filebuf);
372         if (file >= 0) close(file);
373         return rc;
374 }
375
376 static char *cpio_replace_env(char *new_location)
377 {
378        char expanded[PATH_MAX + 1];
379        char env_var[PATH_MAX + 1];
380        char *start;
381        char *end;
382
383        for (start = NULL; (start = strstr(new_location, "${")); ) {
384                end = strchr(start, '}');
385                if (start < end) {
386                        *env_var = *expanded = '\0';
387                        strncat(env_var, start + 2, end - start - 2);
388                        strncat(expanded, new_location, start - new_location);
389                        strncat(expanded, getenv(env_var), PATH_MAX);
390                        strncat(expanded, end + 1, PATH_MAX);
391                        strncpy(new_location, expanded, PATH_MAX);
392                } else
393                        break;
394        }
395
396        return new_location;
397 }
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 <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                 prog);
488 }
489
490 struct file_handler file_handler_table[] = {
491         {
492                 .type    = "file",
493                 .handler = cpio_mkfile_line,
494         }, {
495                 .type    = "nod",
496                 .handler = cpio_mknod_line,
497         }, {
498                 .type    = "dir",
499                 .handler = cpio_mkdir_line,
500         }, {
501                 .type    = "slink",
502                 .handler = cpio_mkslink_line,
503         }, {
504                 .type    = "pipe",
505                 .handler = cpio_mkpipe_line,
506         }, {
507                 .type    = "sock",
508                 .handler = cpio_mksock_line,
509         }, {
510                 .type    = NULL,
511                 .handler = NULL,
512         }
513 };
514
515 #define LINE_SIZE (2 * PATH_MAX + 50)
516
517 int main (int argc, char *argv[])
518 {
519         FILE *cpio_list;
520         char line[LINE_SIZE];
521         char *args, *type;
522         int ec = 0;
523         int line_nr = 0;
524
525         if (2 != argc) {
526                 usage(argv[0]);
527                 exit(1);
528         }
529
530         if (!strcmp(argv[1], "-"))
531                 cpio_list = stdin;
532         else if (! (cpio_list = fopen(argv[1], "r"))) {
533                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
534                         argv[1], strerror(errno));
535                 usage(argv[0]);
536                 exit(1);
537         }
538
539         while (fgets(line, LINE_SIZE, cpio_list)) {
540                 int type_idx;
541                 size_t slen = strlen(line);
542
543                 line_nr++;
544
545                 if ('#' == *line) {
546                         /* comment - skip to next line */
547                         continue;
548                 }
549
550                 if (! (type = strtok(line, " \t"))) {
551                         fprintf(stderr,
552                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
553                                 line_nr, line);
554                         ec = -1;
555                         break;
556                 }
557
558                 if ('\n' == *type) {
559                         /* a blank line */
560                         continue;
561                 }
562
563                 if (slen == strlen(type)) {
564                         /* must be an empty line */
565                         continue;
566                 }
567
568                 if (! (args = strtok(NULL, "\n"))) {
569                         fprintf(stderr,
570                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
571                                 line_nr, line);
572                         ec = -1;
573                 }
574
575                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
576                         int rc;
577                         if (! strcmp(line, file_handler_table[type_idx].type)) {
578                                 if ((rc = file_handler_table[type_idx].handler(args))) {
579                                         ec = rc;
580                                         fprintf(stderr, " line %d\n", line_nr);
581                                 }
582                                 break;
583                         }
584                 }
585
586                 if (NULL == file_handler_table[type_idx].type) {
587                         fprintf(stderr, "unknown file type line %d: '%s'\n",
588                                 line_nr, line);
589                 }
590         }
591         if (ec == 0)
592                 cpio_trailer();
593
594         exit(ec);
595 }