10bb8f8895196b476a959115135609099fac6768
[linux-flexiantxendom0-3.2.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 i;
299         int rc = -1;
300
301         mode |= S_IFREG;
302
303         retval = stat (location, &buf);
304         if (retval) {
305                 fprintf (stderr, "File %s could not be located\n", location);
306                 goto error;
307         }
308
309         file = open (location, O_RDONLY);
310         if (file < 0) {
311                 fprintf (stderr, "File %s could not be opened for reading\n", location);
312                 goto error;
313         }
314
315         filebuf = malloc(buf.st_size);
316         if (!filebuf) {
317                 fprintf (stderr, "out of memory\n");
318                 goto error;
319         }
320
321         retval = read (file, filebuf, buf.st_size);
322         if (retval < 0) {
323                 fprintf (stderr, "Can not read %s file\n", location);
324                 goto error;
325         }
326
327         sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
328                "%08X%08X%08X%08X%08X%08X%08X",
329                 "070701",               /* magic */
330                 ino++,                  /* ino */
331                 mode,                   /* mode */
332                 (long) uid,             /* uid */
333                 (long) gid,             /* gid */
334                 1,                      /* nlink */
335                 (long) buf.st_mtime,    /* mtime */
336                 (int) buf.st_size,      /* filesize */
337                 3,                      /* major */
338                 1,                      /* minor */
339                 0,                      /* rmajor */
340                 0,                      /* rminor */
341                 (unsigned)strlen(name) + 1,/* namesize */
342                 0);                     /* chksum */
343         push_hdr(s);
344         push_string(name);
345         push_pad();
346
347         for (i = 0; i < buf.st_size; ++i)
348                 fputc(filebuf[i], stdout);
349         offset += buf.st_size;
350         push_pad();
351         rc = 0;
352         
353 error:
354         if (filebuf) free(filebuf);
355         if (file >= 0) close(file);
356         return rc;
357 }
358
359 static int cpio_mkfile_line(const char *line)
360 {
361         char name[PATH_MAX + 1];
362         char location[PATH_MAX + 1];
363         unsigned int mode;
364         int uid;
365         int gid;
366         int rc = -1;
367
368         if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, location, &mode, &uid, &gid)) {
369                 fprintf(stderr, "Unrecognized file format '%s'", line);
370                 goto fail;
371         }
372         rc = cpio_mkfile(name, location, mode, uid, gid);
373  fail:
374         return rc;
375 }
376
377 void usage(const char *prog)
378 {
379         fprintf(stderr, "Usage:\n"
380                 "\t%s <cpio_list>\n"
381                 "\n"
382                 "<cpio_list> is a file containing newline separated entries that\n"
383                 "describe the files to be included in the initramfs archive:\n"
384                 "\n"
385                 "# a comment\n"
386                 "file <name> <location> <mode> <uid> <gid>\n"
387                 "dir <name> <mode> <uid> <gid>\n"
388                 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
389                 "slink <name> <target> <mode> <uid> <gid>\n"
390                 "pipe <name> <mode> <uid> <gid>\n"
391                 "sock <name> <mode> <uid> <gid>\n"
392                 "\n"
393                 "<name>      name of the file/dir/nod/etc in the archive\n"
394                 "<location>  location of the file in the current filesystem\n"
395                 "<target>    link target\n"
396                 "<mode>      mode/permissions of the file\n"
397                 "<uid>       user id (0=root)\n"
398                 "<gid>       group id (0=root)\n"
399                 "<dev_type>  device type (b=block, c=character)\n"
400                 "<maj>       major number of nod\n"
401                 "<min>       minor number of nod\n"
402                 "\n"
403                 "example:\n"
404                 "# A simple initramfs\n"
405                 "dir /dev 0755 0 0\n"
406                 "nod /dev/console 0600 0 0 c 5 1\n"
407                 "dir /root 0700 0 0\n"
408                 "dir /sbin 0755 0 0\n"
409                 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n",
410                 prog);
411 }
412
413 struct file_handler file_handler_table[] = {
414         {
415                 .type    = "file",
416                 .handler = cpio_mkfile_line,
417         }, {
418                 .type    = "nod",
419                 .handler = cpio_mknod_line,
420         }, {
421                 .type    = "dir",
422                 .handler = cpio_mkdir_line,
423         }, {
424                 .type    = "slink",
425                 .handler = cpio_mkslink_line,
426         }, {
427                 .type    = "pipe",
428                 .handler = cpio_mkpipe_line,
429         }, {
430                 .type    = "sock",
431                 .handler = cpio_mksock_line,
432         }, {
433                 .type    = NULL,
434                 .handler = NULL,
435         }
436 };
437
438 #define LINE_SIZE (2 * PATH_MAX + 50)
439
440 int main (int argc, char *argv[])
441 {
442         FILE *cpio_list;
443         char line[LINE_SIZE];
444         char *args, *type;
445         int ec = 0;
446         int line_nr = 0;
447
448         if (2 != argc) {
449                 usage(argv[0]);
450                 exit(1);
451         }
452
453         if (! (cpio_list = fopen(argv[1], "r"))) {
454                 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
455                         argv[1], strerror(errno));
456                 usage(argv[0]);
457                 exit(1);
458         }
459
460         while (fgets(line, LINE_SIZE, cpio_list)) {
461                 int type_idx;
462                 size_t slen = strlen(line);
463
464                 line_nr++;
465
466                 if ('#' == *line) {
467                         /* comment - skip to next line */
468                         continue;
469                 }
470
471                 if (! (type = strtok(line, " \t"))) {
472                         fprintf(stderr,
473                                 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
474                                 line_nr, line);
475                         ec = -1;
476                 }
477
478                 if ('\n' == *type) {
479                         /* a blank line */
480                         continue;
481                 }
482
483                 if (slen == strlen(type)) {
484                         /* must be an empty line */
485                         continue;
486                 }
487
488                 if (! (args = strtok(NULL, "\n"))) {
489                         fprintf(stderr,
490                                 "ERROR: incorrect format, newline required line %d: '%s'\n",
491                                 line_nr, line);
492                         ec = -1;
493                 }
494
495                 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
496                         int rc;
497                         if (! strcmp(line, file_handler_table[type_idx].type)) {
498                                 if ((rc = file_handler_table[type_idx].handler(args))) {
499                                         ec = rc;
500                                         fprintf(stderr, " line %d\n", line_nr);
501                                 }
502                                 break;
503                         }
504                 }
505
506                 if (NULL == file_handler_table[type_idx].type) {
507                         fprintf(stderr, "unknown file type line %d: '%s'\n",
508                                 line_nr, line);
509                 }
510         }
511         cpio_trailer();
512
513         exit(ec);
514 }