Merge branch 'for-2.6.38' of git://linux-nfs.org/~bfields/linux
[pandora-kernel.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         if (name[0] == '/')
108                 name++;
109         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
110                "%08X%08X%08X%08X%08X%08X%08X",
111                 "070701",               /* magic */
112                 ino++,                  /* ino */
113                 S_IFLNK | mode,         /* mode */
114                 (long) uid,             /* uid */
115                 (long) gid,             /* gid */
116                 1,                      /* nlink */
117                 (long) mtime,           /* mtime */
118                 (unsigned)strlen(target)+1, /* filesize */
119                 3,                      /* major */
120                 1,                      /* minor */
121                 0,                      /* rmajor */
122                 0,                      /* rminor */
123                 (unsigned)strlen(name) + 1,/* namesize */
124                 0);                     /* chksum */
125         push_hdr(s);
126         push_string(name);
127         push_pad();
128         push_string(target);
129         push_pad();
130         return 0;
131 }
132
133 static int cpio_mkslink_line(const char *line)
134 {
135         char name[PATH_MAX + 1];
136         char target[PATH_MAX + 1];
137         unsigned int mode;
138         int uid;
139         int gid;
140         int rc = -1;
141
142         if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
143                 fprintf(stderr, "Unrecognized dir format '%s'", line);
144                 goto fail;
145         }
146         rc = cpio_mkslink(name, target, mode, uid, gid);
147  fail:
148         return rc;
149 }
150
151 static int cpio_mkgeneric(const char *name, unsigned int mode,
152                        uid_t uid, gid_t gid)
153 {
154         char s[256];
155         time_t mtime = time(NULL);
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) 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         time_t mtime = time(NULL);
246
247         if (dev_type == 'b')
248                 mode |= S_IFBLK;
249         else
250                 mode |= S_IFCHR;
251
252         if (name[0] == '/')
253                 name++;
254         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
255                "%08X%08X%08X%08X%08X%08X%08X",
256                 "070701",               /* magic */
257                 ino++,                  /* ino */
258                 mode,                   /* mode */
259                 (long) uid,             /* uid */
260                 (long) gid,             /* gid */
261                 1,                      /* nlink */
262                 (long) mtime,           /* mtime */
263                 0,                      /* filesize */
264                 3,                      /* major */
265                 1,                      /* minor */
266                 maj,                    /* rmajor */
267                 min,                    /* rminor */
268                 (unsigned)strlen(name) + 1,/* namesize */
269                 0);                     /* chksum */
270         push_hdr(s);
271         push_rest(name);
272         return 0;
273 }
274
275 static int cpio_mknod_line(const char *line)
276 {
277         char name[PATH_MAX + 1];
278         unsigned int mode;
279         int uid;
280         int gid;
281         char dev_type;
282         unsigned int maj;
283         unsigned int min;
284         int rc = -1;
285
286         if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
287                          name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
288                 fprintf(stderr, "Unrecognized nod format '%s'", line);
289                 goto fail;
290         }
291         rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
292  fail:
293         return rc;
294 }
295
296 static int cpio_mkfile(const char *name, const char *location,
297                         unsigned int mode, uid_t uid, gid_t gid,
298                         unsigned int nlinks)
299 {
300         char s[256];
301         char *filebuf = NULL;
302         struct stat buf;
303         long size;
304         int file = -1;
305         int retval;
306         int rc = -1;
307         int namesize;
308         int i;
309
310         mode |= S_IFREG;
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         retval = fstat(file, &buf);
319         if (retval) {
320                 fprintf(stderr, "File %s could not be stat()'ed\n", location);
321                 goto error;
322         }
323
324         filebuf = malloc(buf.st_size);
325         if (!filebuf) {
326                 fprintf (stderr, "out of memory\n");
327                 goto error;
328         }
329
330         retval = read (file, filebuf, buf.st_size);
331         if (retval < 0) {
332                 fprintf (stderr, "Can not read %s file\n", location);
333                 goto error;
334         }
335
336         size = 0;
337         for (i = 1; i <= nlinks; i++) {
338                 /* data goes on last link */
339                 if (i == nlinks) size = buf.st_size;
340
341                 if (name[0] == '/')
342                         name++;
343                 namesize = strlen(name) + 1;
344                 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
345                        "%08lX%08X%08X%08X%08X%08X%08X",
346                         "070701",               /* magic */
347                         ino,                    /* ino */
348                         mode,                   /* mode */
349                         (long) uid,             /* uid */
350                         (long) gid,             /* gid */
351                         nlinks,                 /* nlink */
352                         (long) buf.st_mtime,    /* mtime */
353                         size,                   /* filesize */
354                         3,                      /* major */
355                         1,                      /* minor */
356                         0,                      /* rmajor */
357                         0,                      /* rminor */
358                         namesize,               /* namesize */
359                         0);                     /* chksum */
360                 push_hdr(s);
361                 push_string(name);
362                 push_pad();
363
364                 if (size) {
365                         if (fwrite(filebuf, size, 1, stdout) != 1) {
366                                 fprintf(stderr, "writing filebuf failed\n");
367                                 goto error;
368                         }
369                         offset += size;
370                         push_pad();
371                 }
372
373                 name += namesize;
374         }
375         ino++;
376         rc = 0;
377         
378 error:
379         if (filebuf) free(filebuf);
380         if (file >= 0) close(file);
381         return rc;
382 }
383
384 static char *cpio_replace_env(char *new_location)
385 {
386        char expanded[PATH_MAX + 1];
387        char env_var[PATH_MAX + 1];
388        char *start;
389        char *end;
390
391        for (start = NULL; (start = strstr(new_location, "${")); ) {
392                end = strchr(start, '}');
393                if (start < end) {
394                        *env_var = *expanded = '\0';
395                        strncat(env_var, start + 2, end - start - 2);
396                        strncat(expanded, new_location, start - new_location);
397                        strncat(expanded, getenv(env_var), PATH_MAX);
398                        strncat(expanded, end + 1, PATH_MAX);
399                        strncpy(new_location, expanded, PATH_MAX);
400                } else
401                        break;
402        }
403
404        return new_location;
405 }
406
407
408 static int cpio_mkfile_line(const char *line)
409 {
410         char name[PATH_MAX + 1];
411         char *dname = NULL; /* malloc'ed buffer for hard links */
412         char location[PATH_MAX + 1];
413         unsigned int mode;
414         int uid;
415         int gid;
416         int nlinks = 1;
417         int end = 0, dname_len = 0;
418         int rc = -1;
419
420         if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
421                                 "s %o %d %d %n",
422                                 name, location, &mode, &uid, &gid, &end)) {
423                 fprintf(stderr, "Unrecognized file format '%s'", line);
424                 goto fail;
425         }
426         if (end && isgraph(line[end])) {
427                 int len;
428                 int nend;
429
430                 dname = malloc(strlen(line));
431                 if (!dname) {
432                         fprintf (stderr, "out of memory (%d)\n", dname_len);
433                         goto fail;
434                 }
435
436                 dname_len = strlen(name) + 1;
437                 memcpy(dname, name, dname_len);
438
439                 do {
440                         nend = 0;
441                         if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
442                                         name, &nend) < 1)
443                                 break;
444                         len = strlen(name) + 1;
445                         memcpy(dname + dname_len, name, len);
446                         dname_len += len;
447                         nlinks++;
448                         end += nend;
449                 } while (isgraph(line[end]));
450         } else {
451                 dname = name;
452         }
453         rc = cpio_mkfile(dname, cpio_replace_env(location),
454                          mode, uid, gid, nlinks);
455  fail:
456         if (dname_len) free(dname);
457         return rc;
458 }
459
460 static void usage(const char *prog)
461 {
462         fprintf(stderr, "Usage:\n"
463                 "\t%s <cpio_list>\n"
464                 "\n"
465                 "<cpio_list> is a file containing newline separated entries that\n"
466                 "describe the files to be included in the initramfs archive:\n"
467                 "\n"
468                 "# a comment\n"
469                 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
470                 "dir <name> <mode> <uid> <gid>\n"
471                 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
472                 "slink <name> <target> <mode> <uid> <gid>\n"
473                 "pipe <name> <mode> <uid> <gid>\n"
474                 "sock <name> <mode> <uid> <gid>\n"
475                 "\n"
476                 "<name>       name of the file/dir/nod/etc in the archive\n"
477                 "<location>   location of the file in the current filesystem\n"
478                 "             expands shell variables quoted with ${}\n"
479                 "<target>     link target\n"
480                 "<mode>       mode/permissions of the file\n"
481                 "<uid>        user id (0=root)\n"
482                 "<gid>        group id (0=root)\n"
483                 "<dev_type>   device type (b=block, c=character)\n"
484                 "<maj>        major number of nod\n"
485                 "<min>        minor number of nod\n"
486                 "<hard links> space separated list of other links to file\n"
487                 "\n"
488                 "example:\n"
489                 "# A simple initramfs\n"
490                 "dir /dev 0755 0 0\n"
491                 "nod /dev/console 0600 0 0 c 5 1\n"
492                 "dir /root 0700 0 0\n"
493                 "dir /sbin 0755 0 0\n"
494                 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n",
495                 prog);
496 }
497
498 struct file_handler file_handler_table[] = {
499         {
500                 .type    = "file",
501                 .handler = cpio_mkfile_line,
502         }, {
503                 .type    = "nod",
504                 .handler = cpio_mknod_line,
505         }, {
506                 .type    = "dir",
507                 .handler = cpio_mkdir_line,
508         }, {
509                 .type    = "slink",
510                 .handler = cpio_mkslink_line,
511         }, {
512                 .type    = "pipe",
513                 .handler = cpio_mkpipe_line,
514         }, {
515                 .type    = "sock",
516                 .handler = cpio_mksock_line,
517         }, {
518                 .type    = NULL,
519                 .handler = NULL,
520         }
521 };
522
523 #define LINE_SIZE (2 * PATH_MAX + 50)
524
525 int main (int argc, char *argv[])
526 {
527         FILE *cpio_list;
528         char line[LINE_SIZE];
529         char *args, *type;
530         int ec = 0;
531         int line_nr = 0;
532
533         if (2 != argc) {
534                 usage(argv[0]);
535                 exit(1);
536         }
537
538         if (!strcmp(argv[1], "-"))
539                 cpio_list = stdin;
540         else if (! (cpio_list = fopen(argv[1], "r"))) {
541                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
542                         argv[1], strerror(errno));
543                 usage(argv[0]);
544                 exit(1);
545         }
546
547         while (fgets(line, LINE_SIZE, cpio_list)) {
548                 int type_idx;
549                 size_t slen = strlen(line);
550
551                 line_nr++;
552
553                 if ('#' == *line) {
554                         /* comment - skip to next line */
555                         continue;
556                 }
557
558                 if (! (type = strtok(line, " \t"))) {
559                         fprintf(stderr,
560                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
561                                 line_nr, line);
562                         ec = -1;
563                         break;
564                 }
565
566                 if ('\n' == *type) {
567                         /* a blank line */
568                         continue;
569                 }
570
571                 if (slen == strlen(type)) {
572                         /* must be an empty line */
573                         continue;
574                 }
575
576                 if (! (args = strtok(NULL, "\n"))) {
577                         fprintf(stderr,
578                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
579                                 line_nr, line);
580                         ec = -1;
581                 }
582
583                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
584                         int rc;
585                         if (! strcmp(line, file_handler_table[type_idx].type)) {
586                                 if ((rc = file_handler_table[type_idx].handler(args))) {
587                                         ec = rc;
588                                         fprintf(stderr, " line %d\n", line_nr);
589                                 }
590                                 break;
591                         }
592                 }
593
594                 if (NULL == file_handler_table[type_idx].type) {
595                         fprintf(stderr, "unknown file type line %d: '%s'\n",
596                                 line_nr, line);
597                 }
598         }
599         if (ec == 0)
600                 cpio_trailer();
601
602         exit(ec);
603 }