Linux-2.6.12-rc2
[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  */
18
19 #define xstr(s) #s
20 #define str(s) xstr(s)
21
22 static unsigned int offset;
23 static unsigned int ino = 721;
24
25 struct file_handler {
26         const char *type;
27         int (*handler)(const char *line);
28 };
29
30 static void push_string(const char *name)
31 {
32         unsigned int name_len = strlen(name) + 1;
33
34         fputs(name, stdout);
35         putchar(0);
36         offset += name_len;
37 }
38
39 static void push_pad (void)
40 {
41         while (offset & 3) {
42                 putchar(0);
43                 offset++;
44         }
45 }
46
47 static void push_rest(const char *name)
48 {
49         unsigned int name_len = strlen(name) + 1;
50         unsigned int tmp_ofs;
51
52         fputs(name, stdout);
53         putchar(0);
54         offset += name_len;
55
56         tmp_ofs = name_len + 110;
57         while (tmp_ofs & 3) {
58                 putchar(0);
59                 offset++;
60                 tmp_ofs++;
61         }
62 }
63
64 static void push_hdr(const char *s)
65 {
66         fputs(s, stdout);
67         offset += 110;
68 }
69
70 static void cpio_trailer(void)
71 {
72         char s[256];
73         const char name[] = "TRAILER!!!";
74
75         sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
76                "%08X%08X%08X%08X%08X%08X%08X",
77                 "070701",               /* magic */
78                 0,                      /* ino */
79                 0,                      /* mode */
80                 (long) 0,               /* uid */
81                 (long) 0,               /* gid */
82                 1,                      /* nlink */
83                 (long) 0,               /* mtime */
84                 0,                      /* filesize */
85                 0,                      /* major */
86                 0,                      /* minor */
87                 0,                      /* rmajor */
88                 0,                      /* rminor */
89                 (unsigned)strlen(name)+1, /* namesize */
90                 0);                     /* chksum */
91         push_hdr(s);
92         push_rest(name);
93
94         while (offset % 512) {
95                 putchar(0);
96                 offset++;
97         }
98 }
99
100 static int cpio_mkslink(const char *name, const char *target,
101                          unsigned int mode, uid_t uid, gid_t gid)
102 {
103         char s[256];
104         time_t mtime = time(NULL);
105
106         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
107                "%08X%08X%08X%08X%08X%08X%08X",
108                 "070701",               /* magic */
109                 ino++,                  /* ino */
110                 S_IFLNK | mode,         /* mode */
111                 (long) uid,             /* uid */
112                 (long) gid,             /* gid */
113                 1,                      /* nlink */
114                 (long) mtime,           /* mtime */
115                 (unsigned)strlen(target)+1, /* filesize */
116                 3,                      /* major */
117                 1,                      /* minor */
118                 0,                      /* rmajor */
119                 0,                      /* rminor */
120                 (unsigned)strlen(name) + 1,/* namesize */
121                 0);                     /* chksum */
122         push_hdr(s);
123         push_string(name);
124         push_pad();
125         push_string(target);
126         push_pad();
127         return 0;
128 }
129
130 static int cpio_mkslink_line(const char *line)
131 {
132         char name[PATH_MAX + 1];
133         char target[PATH_MAX + 1];
134         unsigned int mode;
135         int uid;
136         int gid;
137         int rc = -1;
138
139         if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
140                 fprintf(stderr, "Unrecognized dir format '%s'", line);
141                 goto fail;
142         }
143         rc = cpio_mkslink(name, target, mode, uid, gid);
144  fail:
145         return rc;
146 }
147
148 static int cpio_mkgeneric(const char *name, unsigned int mode,
149                        uid_t uid, gid_t gid)
150 {
151         char s[256];
152         time_t mtime = time(NULL);
153
154         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
155                "%08X%08X%08X%08X%08X%08X%08X",
156                 "070701",               /* magic */
157                 ino++,                  /* ino */
158                 mode,                   /* mode */
159                 (long) uid,             /* uid */
160                 (long) gid,             /* gid */
161                 2,                      /* nlink */
162                 (long) mtime,           /* mtime */
163                 0,                      /* filesize */
164                 3,                      /* major */
165                 1,                      /* minor */
166                 0,                      /* rmajor */
167                 0,                      /* rminor */
168                 (unsigned)strlen(name) + 1,/* namesize */
169                 0);                     /* chksum */
170         push_hdr(s);
171         push_rest(name);
172         return 0;
173 }
174
175 enum generic_types {
176         GT_DIR,
177         GT_PIPE,
178         GT_SOCK
179 };
180
181 struct generic_type {
182         const char *type;
183         mode_t mode;
184 };
185
186 static struct generic_type generic_type_table[] = {
187         [GT_DIR] = {
188                 .type = "dir",
189                 .mode = S_IFDIR
190         },
191         [GT_PIPE] = {
192                 .type = "pipe",
193                 .mode = S_IFIFO
194         },
195         [GT_SOCK] = {
196                 .type = "sock",
197                 .mode = S_IFSOCK
198         }
199 };
200
201 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
202 {
203         char name[PATH_MAX + 1];
204         unsigned int mode;
205         int uid;
206         int gid;
207         int rc = -1;
208
209         if (4 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d", name, &mode, &uid, &gid)) {
210                 fprintf(stderr, "Unrecognized %s format '%s'",
211                         line, generic_type_table[gt].type);
212                 goto fail;
213         }
214         mode |= generic_type_table[gt].mode;
215         rc = cpio_mkgeneric(name, mode, uid, gid);
216  fail:
217         return rc;
218 }
219
220 static int cpio_mkdir_line(const char *line)
221 {
222         return cpio_mkgeneric_line(line, GT_DIR);
223 }
224
225 static int cpio_mkpipe_line(const char *line)
226 {
227         return cpio_mkgeneric_line(line, GT_PIPE);
228 }
229
230 static int cpio_mksock_line(const char *line)
231 {
232         return cpio_mkgeneric_line(line, GT_SOCK);
233 }
234
235 static int cpio_mknod(const char *name, unsigned int mode,
236                        uid_t uid, gid_t gid, char dev_type,
237                        unsigned int maj, unsigned int min)
238 {
239         char s[256];
240         time_t mtime = time(NULL);
241
242         if (dev_type == 'b')
243                 mode |= S_IFBLK;
244         else
245                 mode |= S_IFCHR;
246
247         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
248                "%08X%08X%08X%08X%08X%08X%08X",
249                 "070701",               /* magic */
250                 ino++,                  /* ino */
251                 mode,                   /* mode */
252                 (long) uid,             /* uid */
253                 (long) gid,             /* gid */
254                 1,                      /* nlink */
255                 (long) mtime,           /* mtime */
256                 0,                      /* filesize */
257                 3,                      /* major */
258                 1,                      /* minor */
259                 maj,                    /* rmajor */
260                 min,                    /* rminor */
261                 (unsigned)strlen(name) + 1,/* namesize */
262                 0);                     /* chksum */
263         push_hdr(s);
264         push_rest(name);
265         return 0;
266 }
267
268 static int cpio_mknod_line(const char *line)
269 {
270         char name[PATH_MAX + 1];
271         unsigned int mode;
272         int uid;
273         int gid;
274         char dev_type;
275         unsigned int maj;
276         unsigned int min;
277         int rc = -1;
278
279         if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
280                          name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
281                 fprintf(stderr, "Unrecognized nod format '%s'", line);
282                 goto fail;
283         }
284         rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
285  fail:
286         return rc;
287 }
288
289 /* Not marked static to keep the compiler quiet, as no one uses this yet... */
290 static int cpio_mkfile(const char *name, const char *location,
291                         unsigned int mode, uid_t uid, gid_t gid)
292 {
293         char s[256];
294         char *filebuf = NULL;
295         struct stat buf;
296         int file = -1;
297         int retval;
298         int rc = -1;
299
300         mode |= S_IFREG;
301
302         retval = stat (location, &buf);
303         if (retval) {
304                 fprintf (stderr, "File %s could not be located\n", location);
305                 goto error;
306         }
307
308         file = open (location, O_RDONLY);
309         if (file < 0) {
310                 fprintf (stderr, "File %s could not be opened for reading\n", location);
311                 goto error;
312         }
313
314         filebuf = malloc(buf.st_size);
315         if (!filebuf) {
316                 fprintf (stderr, "out of memory\n");
317                 goto error;
318         }
319
320         retval = read (file, filebuf, buf.st_size);
321         if (retval < 0) {
322                 fprintf (stderr, "Can not read %s file\n", location);
323                 goto error;
324         }
325
326         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
327                "%08X%08X%08X%08X%08X%08X%08X",
328                 "070701",               /* magic */
329                 ino++,                  /* ino */
330                 mode,                   /* mode */
331                 (long) uid,             /* uid */
332                 (long) gid,             /* gid */
333                 1,                      /* nlink */
334                 (long) buf.st_mtime,    /* mtime */
335                 (int) buf.st_size,      /* filesize */
336                 3,                      /* major */
337                 1,                      /* minor */
338                 0,                      /* rmajor */
339                 0,                      /* rminor */
340                 (unsigned)strlen(name) + 1,/* namesize */
341                 0);                     /* chksum */
342         push_hdr(s);
343         push_string(name);
344         push_pad();
345
346         fwrite(filebuf, buf.st_size, 1, stdout);
347         offset += buf.st_size;
348         push_pad();
349         rc = 0;
350         
351 error:
352         if (filebuf) free(filebuf);
353         if (file >= 0) close(file);
354         return rc;
355 }
356
357 static int cpio_mkfile_line(const char *line)
358 {
359         char name[PATH_MAX + 1];
360         char location[PATH_MAX + 1];
361         unsigned int mode;
362         int uid;
363         int gid;
364         int rc = -1;
365
366         if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, location, &mode, &uid, &gid)) {
367                 fprintf(stderr, "Unrecognized file format '%s'", line);
368                 goto fail;
369         }
370         rc = cpio_mkfile(name, location, mode, uid, gid);
371  fail:
372         return rc;
373 }
374
375 void usage(const char *prog)
376 {
377         fprintf(stderr, "Usage:\n"
378                 "\t%s <cpio_list>\n"
379                 "\n"
380                 "<cpio_list> is a file containing newline separated entries that\n"
381                 "describe the files to be included in the initramfs archive:\n"
382                 "\n"
383                 "# a comment\n"
384                 "file <name> <location> <mode> <uid> <gid>\n"
385                 "dir <name> <mode> <uid> <gid>\n"
386                 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
387                 "slink <name> <target> <mode> <uid> <gid>\n"
388                 "pipe <name> <mode> <uid> <gid>\n"
389                 "sock <name> <mode> <uid> <gid>\n"
390                 "\n"
391                 "<name>      name of the file/dir/nod/etc in the archive\n"
392                 "<location>  location of the file in the current filesystem\n"
393                 "<target>    link target\n"
394                 "<mode>      mode/permissions of the file\n"
395                 "<uid>       user id (0=root)\n"
396                 "<gid>       group id (0=root)\n"
397                 "<dev_type>  device type (b=block, c=character)\n"
398                 "<maj>       major number of nod\n"
399                 "<min>       minor number of nod\n"
400                 "\n"
401                 "example:\n"
402                 "# A simple initramfs\n"
403                 "dir /dev 0755 0 0\n"
404                 "nod /dev/console 0600 0 0 c 5 1\n"
405                 "dir /root 0700 0 0\n"
406                 "dir /sbin 0755 0 0\n"
407                 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n",
408                 prog);
409 }
410
411 struct file_handler file_handler_table[] = {
412         {
413                 .type    = "file",
414                 .handler = cpio_mkfile_line,
415         }, {
416                 .type    = "nod",
417                 .handler = cpio_mknod_line,
418         }, {
419                 .type    = "dir",
420                 .handler = cpio_mkdir_line,
421         }, {
422                 .type    = "slink",
423                 .handler = cpio_mkslink_line,
424         }, {
425                 .type    = "pipe",
426                 .handler = cpio_mkpipe_line,
427         }, {
428                 .type    = "sock",
429                 .handler = cpio_mksock_line,
430         }, {
431                 .type    = NULL,
432                 .handler = NULL,
433         }
434 };
435
436 #define LINE_SIZE (2 * PATH_MAX + 50)
437
438 int main (int argc, char *argv[])
439 {
440         FILE *cpio_list;
441         char line[LINE_SIZE];
442         char *args, *type;
443         int ec = 0;
444         int line_nr = 0;
445
446         if (2 != argc) {
447                 usage(argv[0]);
448                 exit(1);
449         }
450
451         if (! (cpio_list = fopen(argv[1], "r"))) {
452                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
453                         argv[1], strerror(errno));
454                 usage(argv[0]);
455                 exit(1);
456         }
457
458         while (fgets(line, LINE_SIZE, cpio_list)) {
459                 int type_idx;
460                 size_t slen = strlen(line);
461
462                 line_nr++;
463
464                 if ('#' == *line) {
465                         /* comment - skip to next line */
466                         continue;
467                 }
468
469                 if (! (type = strtok(line, " \t"))) {
470                         fprintf(stderr,
471                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
472                                 line_nr, line);
473                         ec = -1;
474                 }
475
476                 if ('\n' == *type) {
477                         /* a blank line */
478                         continue;
479                 }
480
481                 if (slen == strlen(type)) {
482                         /* must be an empty line */
483                         continue;
484                 }
485
486                 if (! (args = strtok(NULL, "\n"))) {
487                         fprintf(stderr,
488                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
489                                 line_nr, line);
490                         ec = -1;
491                 }
492
493                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
494                         int rc;
495                         if (! strcmp(line, file_handler_table[type_idx].type)) {
496                                 if ((rc = file_handler_table[type_idx].handler(args))) {
497                                         ec = rc;
498                                         fprintf(stderr, " line %d\n", line_nr);
499                                 }
500                                 break;
501                         }
502                 }
503
504                 if (NULL == file_handler_table[type_idx].type) {
505                         fprintf(stderr, "unknown file type line %d: '%s'\n",
506                                 line_nr, line);
507                 }
508         }
509         cpio_trailer();
510
511         exit(ec);
512 }