Merge branch 'pm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspe...
[linux-2.6.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                         fwrite(filebuf, size, 1, stdout);
358                         offset += size;
359                         push_pad();
360                 }
361
362                 name += namesize;
363         }
364         ino++;
365         rc = 0;
366         
367 error:
368         if (filebuf) free(filebuf);
369         if (file >= 0) close(file);
370         return rc;
371 }
372
373 static char *cpio_replace_env(char *new_location)
374 {
375        char expanded[PATH_MAX + 1];
376        char env_var[PATH_MAX + 1];
377        char *start;
378        char *end;
379
380        for (start = NULL; (start = strstr(new_location, "${")); ) {
381                end = strchr(start, '}');
382                if (start < end) {
383                        *env_var = *expanded = '\0';
384                        strncat(env_var, start + 2, end - start - 2);
385                        strncat(expanded, new_location, start - new_location);
386                        strncat(expanded, getenv(env_var), PATH_MAX);
387                        strncat(expanded, end + 1, PATH_MAX);
388                        strncpy(new_location, expanded, PATH_MAX);
389                } else
390                        break;
391        }
392
393        return new_location;
394 }
395
396
397 static int cpio_mkfile_line(const char *line)
398 {
399         char name[PATH_MAX + 1];
400         char *dname = NULL; /* malloc'ed buffer for hard links */
401         char location[PATH_MAX + 1];
402         unsigned int mode;
403         int uid;
404         int gid;
405         int nlinks = 1;
406         int end = 0, dname_len = 0;
407         int rc = -1;
408
409         if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
410                                 "s %o %d %d %n",
411                                 name, location, &mode, &uid, &gid, &end)) {
412                 fprintf(stderr, "Unrecognized file format '%s'", line);
413                 goto fail;
414         }
415         if (end && isgraph(line[end])) {
416                 int len;
417                 int nend;
418
419                 dname = malloc(strlen(line));
420                 if (!dname) {
421                         fprintf (stderr, "out of memory (%d)\n", dname_len);
422                         goto fail;
423                 }
424
425                 dname_len = strlen(name) + 1;
426                 memcpy(dname, name, dname_len);
427
428                 do {
429                         nend = 0;
430                         if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
431                                         name, &nend) < 1)
432                                 break;
433                         len = strlen(name) + 1;
434                         memcpy(dname + dname_len, name, len);
435                         dname_len += len;
436                         nlinks++;
437                         end += nend;
438                 } while (isgraph(line[end]));
439         } else {
440                 dname = name;
441         }
442         rc = cpio_mkfile(dname, cpio_replace_env(location),
443                          mode, uid, gid, nlinks);
444  fail:
445         if (dname_len) free(dname);
446         return rc;
447 }
448
449 static void usage(const char *prog)
450 {
451         fprintf(stderr, "Usage:\n"
452                 "\t%s <cpio_list>\n"
453                 "\n"
454                 "<cpio_list> is a file containing newline separated entries that\n"
455                 "describe the files to be included in the initramfs archive:\n"
456                 "\n"
457                 "# a comment\n"
458                 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
459                 "dir <name> <mode> <uid> <gid>\n"
460                 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
461                 "slink <name> <target> <mode> <uid> <gid>\n"
462                 "pipe <name> <mode> <uid> <gid>\n"
463                 "sock <name> <mode> <uid> <gid>\n"
464                 "\n"
465                 "<name>       name of the file/dir/nod/etc in the archive\n"
466                 "<location>   location of the file in the current filesystem\n"
467                 "             expands shell variables quoted with ${}\n"
468                 "<target>     link target\n"
469                 "<mode>       mode/permissions of the file\n"
470                 "<uid>        user id (0=root)\n"
471                 "<gid>        group id (0=root)\n"
472                 "<dev_type>   device type (b=block, c=character)\n"
473                 "<maj>        major number of nod\n"
474                 "<min>        minor number of nod\n"
475                 "<hard links> space separated list of other links to file\n"
476                 "\n"
477                 "example:\n"
478                 "# A simple initramfs\n"
479                 "dir /dev 0755 0 0\n"
480                 "nod /dev/console 0600 0 0 c 5 1\n"
481                 "dir /root 0700 0 0\n"
482                 "dir /sbin 0755 0 0\n"
483                 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n",
484                 prog);
485 }
486
487 struct file_handler file_handler_table[] = {
488         {
489                 .type    = "file",
490                 .handler = cpio_mkfile_line,
491         }, {
492                 .type    = "nod",
493                 .handler = cpio_mknod_line,
494         }, {
495                 .type    = "dir",
496                 .handler = cpio_mkdir_line,
497         }, {
498                 .type    = "slink",
499                 .handler = cpio_mkslink_line,
500         }, {
501                 .type    = "pipe",
502                 .handler = cpio_mkpipe_line,
503         }, {
504                 .type    = "sock",
505                 .handler = cpio_mksock_line,
506         }, {
507                 .type    = NULL,
508                 .handler = NULL,
509         }
510 };
511
512 #define LINE_SIZE (2 * PATH_MAX + 50)
513
514 int main (int argc, char *argv[])
515 {
516         FILE *cpio_list;
517         char line[LINE_SIZE];
518         char *args, *type;
519         int ec = 0;
520         int line_nr = 0;
521
522         if (2 != argc) {
523                 usage(argv[0]);
524                 exit(1);
525         }
526
527         if (!strcmp(argv[1], "-"))
528                 cpio_list = stdin;
529         else if (! (cpio_list = fopen(argv[1], "r"))) {
530                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
531                         argv[1], strerror(errno));
532                 usage(argv[0]);
533                 exit(1);
534         }
535
536         while (fgets(line, LINE_SIZE, cpio_list)) {
537                 int type_idx;
538                 size_t slen = strlen(line);
539
540                 line_nr++;
541
542                 if ('#' == *line) {
543                         /* comment - skip to next line */
544                         continue;
545                 }
546
547                 if (! (type = strtok(line, " \t"))) {
548                         fprintf(stderr,
549                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
550                                 line_nr, line);
551                         ec = -1;
552                         break;
553                 }
554
555                 if ('\n' == *type) {
556                         /* a blank line */
557                         continue;
558                 }
559
560                 if (slen == strlen(type)) {
561                         /* must be an empty line */
562                         continue;
563                 }
564
565                 if (! (args = strtok(NULL, "\n"))) {
566                         fprintf(stderr,
567                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
568                                 line_nr, line);
569                         ec = -1;
570                 }
571
572                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
573                         int rc;
574                         if (! strcmp(line, file_handler_table[type_idx].type)) {
575                                 if ((rc = file_handler_table[type_idx].handler(args))) {
576                                         ec = rc;
577                                         fprintf(stderr, " line %d\n", line_nr);
578                                 }
579                                 break;
580                         }
581                 }
582
583                 if (NULL == file_handler_table[type_idx].type) {
584                         fprintf(stderr, "unknown file type line %d: '%s'\n",
585                                 line_nr, line);
586                 }
587         }
588         if (ec == 0)
589                 cpio_trailer();
590
591         exit(ec);
592 }