blob: 7f06884ecd41b588d829064a53ac964395b0ae1e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001#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
Luciano Rocha24fa5092007-02-10 01:44:45 -080017 * Hard link support by Luciano Rocha
Linus Torvalds1da177e2005-04-16 15:20:36 -070018 */
19
20#define xstr(s) #s
21#define str(s) xstr(s)
22
23static unsigned int offset;
24static unsigned int ino = 721;
25
26struct file_handler {
27 const char *type;
28 int (*handler)(const char *line);
29};
30
31static 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
40static void push_pad (void)
41{
42 while (offset & 3) {
43 putchar(0);
44 offset++;
45 }
46}
47
48static 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
65static void push_hdr(const char *s)
66{
67 fputs(s, stdout);
68 offset += 110;
69}
70
71static 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
101static 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
Thomas Chou43f901f2010-10-06 15:13:53 +0800107 if (name[0] == '/')
108 name++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 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
133static 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
151static 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
Thomas Chou43f901f2010-10-06 15:13:53 +0800157 if (name[0] == '/')
158 name++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 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
180enum generic_types {
181 GT_DIR,
182 GT_PIPE,
183 GT_SOCK
184};
185
186struct generic_type {
187 const char *type;
188 mode_t mode;
189};
190
191static 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
206static 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
225static int cpio_mkdir_line(const char *line)
226{
227 return cpio_mkgeneric_line(line, GT_DIR);
228}
229
230static int cpio_mkpipe_line(const char *line)
231{
232 return cpio_mkgeneric_line(line, GT_PIPE);
233}
234
235static int cpio_mksock_line(const char *line)
236{
237 return cpio_mkgeneric_line(line, GT_SOCK);
238}
239
240static 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
Thomas Chou43f901f2010-10-06 15:13:53 +0800252 if (name[0] == '/')
253 name++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 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
275static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296static int cpio_mkfile(const char *name, const char *location,
Luciano Rocha24fa5092007-02-10 01:44:45 -0800297 unsigned int mode, uid_t uid, gid_t gid,
298 unsigned int nlinks)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
300 char s[256];
301 char *filebuf = NULL;
302 struct stat buf;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800303 long size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 int file = -1;
305 int retval;
306 int rc = -1;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800307 int namesize;
308 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 mode |= S_IFREG;
311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 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
Andrew Mortona3c888f2011-01-05 23:49:53 +0100318 retval = fstat(file, &buf);
Jesper Juhl96aebaf2010-12-24 21:28:56 +0100319 if (retval) {
Andrew Mortona3c888f2011-01-05 23:49:53 +0100320 fprintf(stderr, "File %s could not be stat()'ed\n", location);
Jesper Juhl96aebaf2010-12-24 21:28:56 +0100321 goto error;
322 }
323
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 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
Luciano Rocha24fa5092007-02-10 01:44:45 -0800336 size = 0;
337 for (i = 1; i <= nlinks; i++) {
338 /* data goes on last link */
339 if (i == nlinks) size = buf.st_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
Thomas Chou43f901f2010-10-06 15:13:53 +0800341 if (name[0] == '/')
342 name++;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800343 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) {
Mike Frysinger6d87fea2009-12-09 06:55:19 -0500365 if (fwrite(filebuf, size, 1, stdout) != 1) {
366 fprintf(stderr, "writing filebuf failed\n");
367 goto error;
368 }
Luciano Rocha24fa5092007-02-10 01:44:45 -0800369 offset += size;
370 push_pad();
371 }
372
373 name += namesize;
374 }
375 ino++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 rc = 0;
377
378error:
379 if (filebuf) free(filebuf);
380 if (file >= 0) close(file);
381 return rc;
382}
383
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400384static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408static int cpio_mkfile_line(const char *line)
409{
410 char name[PATH_MAX + 1];
Luciano Rocha24fa5092007-02-10 01:44:45 -0800411 char *dname = NULL; /* malloc'ed buffer for hard links */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 char location[PATH_MAX + 1];
413 unsigned int mode;
414 int uid;
415 int gid;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800416 int nlinks = 1;
417 int end = 0, dname_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 int rc = -1;
419
Luciano Rocha24fa5092007-02-10 01:44:45 -0800420 if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
421 "s %o %d %d %n",
422 name, location, &mode, &uid, &gid, &end)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 fprintf(stderr, "Unrecognized file format '%s'", line);
424 goto fail;
425 }
Luciano Rocha24fa5092007-02-10 01:44:45 -0800426 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 }
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400453 rc = cpio_mkfile(dname, cpio_replace_env(location),
454 mode, uid, gid, nlinks);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 fail:
Luciano Rocha24fa5092007-02-10 01:44:45 -0800456 if (dname_len) free(dname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 return rc;
458}
459
Trevor Keith5c725132009-09-22 16:43:38 -0700460static void usage(const char *prog)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{
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"
Luciano Rocha24fa5092007-02-10 01:44:45 -0800469 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 "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"
Luciano Rocha24fa5092007-02-10 01:44:45 -0800476 "<name> name of the file/dir/nod/etc in the archive\n"
477 "<location> location of the file in the current filesystem\n"
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400478 " expands shell variables quoted with ${}\n"
Luciano Rocha24fa5092007-02-10 01:44:45 -0800479 "<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"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 "\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
498struct 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
525int 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
Mike Frysingerf2434ec2007-05-10 22:44:28 -0700538 if (!strcmp(argv[1], "-"))
539 cpio_list = stdin;
540 else if (! (cpio_list = fopen(argv[1], "r"))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 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;
Jesper Juhlaa1e8162006-04-18 22:21:54 -0700563 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 }
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 }
Jesper Juhlaa1e8162006-04-18 22:21:54 -0700599 if (ec == 0)
600 cpio_trailer();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 exit(ec);
603}