md: make error_handler functions more uniform and correct.
[linux/fpc-iii.git] / usr / gen_init_cpio.c
blob7f06884ecd41b588d829064a53ac964395b0ae1e
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>
14 * Original work by Jeff Garzik
16 * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
17 * Hard link support by Luciano Rocha
20 #define xstr(s) #s
21 #define str(s) xstr(s)
23 static unsigned int offset;
24 static unsigned int ino = 721;
26 struct file_handler {
27 const char *type;
28 int (*handler)(const char *line);
31 static void push_string(const char *name)
33 unsigned int name_len = strlen(name) + 1;
35 fputs(name, stdout);
36 putchar(0);
37 offset += name_len;
40 static void push_pad (void)
42 while (offset & 3) {
43 putchar(0);
44 offset++;
48 static void push_rest(const char *name)
50 unsigned int name_len = strlen(name) + 1;
51 unsigned int tmp_ofs;
53 fputs(name, stdout);
54 putchar(0);
55 offset += name_len;
57 tmp_ofs = name_len + 110;
58 while (tmp_ofs & 3) {
59 putchar(0);
60 offset++;
61 tmp_ofs++;
65 static void push_hdr(const char *s)
67 fputs(s, stdout);
68 offset += 110;
71 static void cpio_trailer(void)
73 char s[256];
74 const char name[] = "TRAILER!!!";
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);
95 while (offset % 512) {
96 putchar(0);
97 offset++;
101 static int cpio_mkslink(const char *name, const char *target,
102 unsigned int mode, uid_t uid, gid_t gid)
104 char s[256];
105 time_t mtime = time(NULL);
107 if (name[0] == '/')
108 name++;
109 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;
133 static int cpio_mkslink_line(const char *line)
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;
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;
146 rc = cpio_mkslink(name, target, mode, uid, gid);
147 fail:
148 return rc;
151 static int cpio_mkgeneric(const char *name, unsigned int mode,
152 uid_t uid, gid_t gid)
154 char s[256];
155 time_t mtime = time(NULL);
157 if (name[0] == '/')
158 name++;
159 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;
180 enum generic_types {
181 GT_DIR,
182 GT_PIPE,
183 GT_SOCK
186 struct generic_type {
187 const char *type;
188 mode_t mode;
191 static struct generic_type generic_type_table[] = {
192 [GT_DIR] = {
193 .type = "dir",
194 .mode = S_IFDIR
196 [GT_PIPE] = {
197 .type = "pipe",
198 .mode = S_IFIFO
200 [GT_SOCK] = {
201 .type = "sock",
202 .mode = S_IFSOCK
206 static int cpio_mkgeneric_line(const char *line, enum generic_types gt)
208 char name[PATH_MAX + 1];
209 unsigned int mode;
210 int uid;
211 int gid;
212 int rc = -1;
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;
219 mode |= generic_type_table[gt].mode;
220 rc = cpio_mkgeneric(name, mode, uid, gid);
221 fail:
222 return rc;
225 static int cpio_mkdir_line(const char *line)
227 return cpio_mkgeneric_line(line, GT_DIR);
230 static int cpio_mkpipe_line(const char *line)
232 return cpio_mkgeneric_line(line, GT_PIPE);
235 static int cpio_mksock_line(const char *line)
237 return cpio_mkgeneric_line(line, GT_SOCK);
240 static 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)
244 char s[256];
245 time_t mtime = time(NULL);
247 if (dev_type == 'b')
248 mode |= S_IFBLK;
249 else
250 mode |= S_IFCHR;
252 if (name[0] == '/')
253 name++;
254 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;
275 static int cpio_mknod_line(const char *line)
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;
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;
291 rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
292 fail:
293 return rc;
296 static int cpio_mkfile(const char *name, const char *location,
297 unsigned int mode, uid_t uid, gid_t gid,
298 unsigned int nlinks)
300 char s[256];
301 char *filebuf = NULL;
302 struct stat buf;
303 long size;
304 int file = -1;
305 int retval;
306 int rc = -1;
307 int namesize;
308 int i;
310 mode |= S_IFREG;
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;
318 retval = fstat(file, &buf);
319 if (retval) {
320 fprintf(stderr, "File %s could not be stat()'ed\n", location);
321 goto error;
324 filebuf = malloc(buf.st_size);
325 if (!filebuf) {
326 fprintf (stderr, "out of memory\n");
327 goto error;
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;
336 size = 0;
337 for (i = 1; i <= nlinks; i++) {
338 /* data goes on last link */
339 if (i == nlinks) size = buf.st_size;
341 if (name[0] == '/')
342 name++;
343 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();
364 if (size) {
365 if (fwrite(filebuf, size, 1, stdout) != 1) {
366 fprintf(stderr, "writing filebuf failed\n");
367 goto error;
369 offset += size;
370 push_pad();
373 name += namesize;
375 ino++;
376 rc = 0;
378 error:
379 if (filebuf) free(filebuf);
380 if (file >= 0) close(file);
381 return rc;
384 static char *cpio_replace_env(char *new_location)
386 char expanded[PATH_MAX + 1];
387 char env_var[PATH_MAX + 1];
388 char *start;
389 char *end;
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;
404 return new_location;
408 static int cpio_mkfile_line(const char *line)
410 char name[PATH_MAX + 1];
411 char *dname = NULL; /* malloc'ed buffer for hard links */
412 char location[PATH_MAX + 1];
413 unsigned int mode;
414 int uid;
415 int gid;
416 int nlinks = 1;
417 int end = 0, dname_len = 0;
418 int rc = -1;
420 if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
421 "s %o %d %d %n",
422 name, location, &mode, &uid, &gid, &end)) {
423 fprintf(stderr, "Unrecognized file format '%s'", line);
424 goto fail;
426 if (end && isgraph(line[end])) {
427 int len;
428 int nend;
430 dname = malloc(strlen(line));
431 if (!dname) {
432 fprintf (stderr, "out of memory (%d)\n", dname_len);
433 goto fail;
436 dname_len = strlen(name) + 1;
437 memcpy(dname, name, dname_len);
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;
453 rc = cpio_mkfile(dname, cpio_replace_env(location),
454 mode, uid, gid, nlinks);
455 fail:
456 if (dname_len) free(dname);
457 return rc;
460 static void usage(const char *prog)
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"
469 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
470 "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"
476 "<name> name of the file/dir/nod/etc in the archive\n"
477 "<location> location of the file in the current filesystem\n"
478 " expands shell variables quoted with ${}\n"
479 "<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"
487 "\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);
498 struct file_handler file_handler_table[] = {
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,
523 #define LINE_SIZE (2 * PATH_MAX + 50)
525 int main (int argc, char *argv[])
527 FILE *cpio_list;
528 char line[LINE_SIZE];
529 char *args, *type;
530 int ec = 0;
531 int line_nr = 0;
533 if (2 != argc) {
534 usage(argv[0]);
535 exit(1);
538 if (!strcmp(argv[1], "-"))
539 cpio_list = stdin;
540 else if (! (cpio_list = fopen(argv[1], "r"))) {
541 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
542 argv[1], strerror(errno));
543 usage(argv[0]);
544 exit(1);
547 while (fgets(line, LINE_SIZE, cpio_list)) {
548 int type_idx;
549 size_t slen = strlen(line);
551 line_nr++;
553 if ('#' == *line) {
554 /* comment - skip to next line */
555 continue;
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;
563 break;
566 if ('\n' == *type) {
567 /* a blank line */
568 continue;
571 if (slen == strlen(type)) {
572 /* must be an empty line */
573 continue;
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;
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);
590 break;
594 if (NULL == file_handler_table[type_idx].type) {
595 fprintf(stderr, "unknown file type line %d: '%s'\n",
596 line_nr, line);
599 if (ec == 0)
600 cpio_trailer();
602 exit(ec);