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