blob: 0e2c8a5838b142ae14c7ed25fde5613b4a06f89c [file] [log] [blame]
Greg Kroah-Hartmanb2441312017-11-01 15:07:57 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002#include <stdio.h>
3#include <stdlib.h>
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <string.h>
7#include <unistd.h>
8#include <time.h>
9#include <fcntl.h>
10#include <errno.h>
11#include <ctype.h>
12#include <limits.h>
13
14/*
15 * Original work by Jeff Garzik
16 *
17 * External file lists, symlink, pipe and fifo support by Thayne Harbaugh
Luciano Rocha24fa5092007-02-10 01:44:45 -080018 * Hard link support by Luciano Rocha
Linus Torvalds1da177e2005-04-16 15:20:36 -070019 */
20
21#define xstr(s) #s
22#define str(s) xstr(s)
23
24static unsigned int offset;
25static unsigned int ino = 721;
Michal Mareka8b80172011-03-31 23:16:42 +020026static time_t default_mtime;
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
28struct file_handler {
29 const char *type;
30 int (*handler)(const char *line);
31};
32
33static void push_string(const char *name)
34{
35 unsigned int name_len = strlen(name) + 1;
36
37 fputs(name, stdout);
38 putchar(0);
39 offset += name_len;
40}
41
42static void push_pad (void)
43{
44 while (offset & 3) {
45 putchar(0);
46 offset++;
47 }
48}
49
50static void push_rest(const char *name)
51{
52 unsigned int name_len = strlen(name) + 1;
53 unsigned int tmp_ofs;
54
55 fputs(name, stdout);
56 putchar(0);
57 offset += name_len;
58
59 tmp_ofs = name_len + 110;
60 while (tmp_ofs & 3) {
61 putchar(0);
62 offset++;
63 tmp_ofs++;
64 }
65}
66
67static void push_hdr(const char *s)
68{
69 fputs(s, stdout);
70 offset += 110;
71}
72
73static void cpio_trailer(void)
74{
75 char s[256];
76 const char name[] = "TRAILER!!!";
77
78 sprintf(s, "%s%08X%08X%08lX%08lX%08X%08lX"
79 "%08X%08X%08X%08X%08X%08X%08X",
80 "070701", /* magic */
81 0, /* ino */
82 0, /* mode */
83 (long) 0, /* uid */
84 (long) 0, /* gid */
85 1, /* nlink */
86 (long) 0, /* mtime */
87 0, /* filesize */
88 0, /* major */
89 0, /* minor */
90 0, /* rmajor */
91 0, /* rminor */
92 (unsigned)strlen(name)+1, /* namesize */
93 0); /* chksum */
94 push_hdr(s);
95 push_rest(name);
96
97 while (offset % 512) {
98 putchar(0);
99 offset++;
100 }
101}
102
103static int cpio_mkslink(const char *name, const char *target,
104 unsigned int mode, uid_t uid, gid_t gid)
105{
106 char s[256];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Thomas Chou43f901f2010-10-06 15:13:53 +0800108 if (name[0] == '/')
109 name++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
111 "%08X%08X%08X%08X%08X%08X%08X",
112 "070701", /* magic */
113 ino++, /* ino */
114 S_IFLNK | mode, /* mode */
115 (long) uid, /* uid */
116 (long) gid, /* gid */
117 1, /* nlink */
Michal Mareka8b80172011-03-31 23:16:42 +0200118 (long) default_mtime, /* mtime */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 (unsigned)strlen(target)+1, /* filesize */
120 3, /* major */
121 1, /* minor */
122 0, /* rmajor */
123 0, /* rminor */
124 (unsigned)strlen(name) + 1,/* namesize */
125 0); /* chksum */
126 push_hdr(s);
127 push_string(name);
128 push_pad();
129 push_string(target);
130 push_pad();
131 return 0;
132}
133
134static int cpio_mkslink_line(const char *line)
135{
136 char name[PATH_MAX + 1];
137 char target[PATH_MAX + 1];
138 unsigned int mode;
139 int uid;
140 int gid;
141 int rc = -1;
142
143 if (5 != sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d", name, target, &mode, &uid, &gid)) {
144 fprintf(stderr, "Unrecognized dir format '%s'", line);
145 goto fail;
146 }
147 rc = cpio_mkslink(name, target, mode, uid, gid);
148 fail:
149 return rc;
150}
151
152static int cpio_mkgeneric(const char *name, unsigned int mode,
153 uid_t uid, gid_t gid)
154{
155 char s[256];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
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 */
Michal Mareka8b80172011-03-31 23:16:42 +0200167 (long) default_mtime, /* mtime */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 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
Masahiro Yamada3510c5c2021-10-12 11:55:14 +0900191static const struct generic_type generic_type_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 [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];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
246 if (dev_type == 'b')
247 mode |= S_IFBLK;
248 else
249 mode |= S_IFCHR;
250
Thomas Chou43f901f2010-10-06 15:13:53 +0800251 if (name[0] == '/')
252 name++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
254 "%08X%08X%08X%08X%08X%08X%08X",
255 "070701", /* magic */
256 ino++, /* ino */
257 mode, /* mode */
258 (long) uid, /* uid */
259 (long) gid, /* gid */
260 1, /* nlink */
Michal Mareka8b80172011-03-31 23:16:42 +0200261 (long) default_mtime, /* mtime */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 0, /* filesize */
263 3, /* major */
264 1, /* minor */
265 maj, /* rmajor */
266 min, /* rminor */
267 (unsigned)strlen(name) + 1,/* namesize */
268 0); /* chksum */
269 push_hdr(s);
270 push_rest(name);
271 return 0;
272}
273
274static int cpio_mknod_line(const char *line)
275{
276 char name[PATH_MAX + 1];
277 unsigned int mode;
278 int uid;
279 int gid;
280 char dev_type;
281 unsigned int maj;
282 unsigned int min;
283 int rc = -1;
284
285 if (7 != sscanf(line, "%" str(PATH_MAX) "s %o %d %d %c %u %u",
286 name, &mode, &uid, &gid, &dev_type, &maj, &min)) {
287 fprintf(stderr, "Unrecognized nod format '%s'", line);
288 goto fail;
289 }
290 rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min);
291 fail:
292 return rc;
293}
294
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295static int cpio_mkfile(const char *name, const char *location,
Luciano Rocha24fa5092007-02-10 01:44:45 -0800296 unsigned int mode, uid_t uid, gid_t gid,
297 unsigned int nlinks)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298{
299 char s[256];
300 char *filebuf = NULL;
301 struct stat buf;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800302 long size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 int file = -1;
304 int retval;
305 int rc = -1;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800306 int namesize;
Kees Cook20f1de62012-10-25 13:38:14 -0700307 unsigned int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 mode |= S_IFREG;
310
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 file = open (location, O_RDONLY);
312 if (file < 0) {
313 fprintf (stderr, "File %s could not be opened for reading\n", location);
314 goto error;
315 }
316
Andrew Mortona3c888f2011-01-05 23:49:53 +0100317 retval = fstat(file, &buf);
Jesper Juhl96aebaf2010-12-24 21:28:56 +0100318 if (retval) {
Andrew Mortona3c888f2011-01-05 23:49:53 +0100319 fprintf(stderr, "File %s could not be stat()'ed\n", location);
Jesper Juhl96aebaf2010-12-24 21:28:56 +0100320 goto error;
321 }
322
Nicolas Schier4c9d4102021-10-12 20:12:20 +0000323 if (buf.st_mtime > 0xffffffff) {
324 fprintf(stderr, "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n",
325 location);
326 buf.st_mtime = 0xffffffff;
327 }
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 filebuf = malloc(buf.st_size);
330 if (!filebuf) {
331 fprintf (stderr, "out of memory\n");
332 goto error;
333 }
334
335 retval = read (file, filebuf, buf.st_size);
336 if (retval < 0) {
337 fprintf (stderr, "Can not read %s file\n", location);
338 goto error;
339 }
340
Luciano Rocha24fa5092007-02-10 01:44:45 -0800341 size = 0;
342 for (i = 1; i <= nlinks; i++) {
343 /* data goes on last link */
344 if (i == nlinks) size = buf.st_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
Thomas Chou43f901f2010-10-06 15:13:53 +0800346 if (name[0] == '/')
347 name++;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800348 namesize = strlen(name) + 1;
349 sprintf(s,"%s%08X%08X%08lX%08lX%08X%08lX"
350 "%08lX%08X%08X%08X%08X%08X%08X",
351 "070701", /* magic */
352 ino, /* ino */
353 mode, /* mode */
354 (long) uid, /* uid */
355 (long) gid, /* gid */
356 nlinks, /* nlink */
357 (long) buf.st_mtime, /* mtime */
358 size, /* filesize */
359 3, /* major */
360 1, /* minor */
361 0, /* rmajor */
362 0, /* rminor */
363 namesize, /* namesize */
364 0); /* chksum */
365 push_hdr(s);
366 push_string(name);
367 push_pad();
368
369 if (size) {
Mike Frysinger6d87fea2009-12-09 06:55:19 -0500370 if (fwrite(filebuf, size, 1, stdout) != 1) {
371 fprintf(stderr, "writing filebuf failed\n");
372 goto error;
373 }
Luciano Rocha24fa5092007-02-10 01:44:45 -0800374 offset += size;
375 push_pad();
376 }
377
378 name += namesize;
379 }
380 ino++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 rc = 0;
382
383error:
384 if (filebuf) free(filebuf);
385 if (file >= 0) close(file);
386 return rc;
387}
388
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400389static char *cpio_replace_env(char *new_location)
390{
Kees Cook20f1de62012-10-25 13:38:14 -0700391 char expanded[PATH_MAX + 1];
Michal Nazarewiczc725ee542013-11-12 15:08:41 -0800392 char *start, *end, *var;
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400393
Michal Nazarewiczc725ee542013-11-12 15:08:41 -0800394 while ((start = strstr(new_location, "${")) &&
395 (end = strchr(start + 2, '}'))) {
396 *start = *end = 0;
397 var = getenv(start + 2);
398 snprintf(expanded, sizeof expanded, "%s%s%s",
399 new_location, var ? var : "", end + 1);
400 strcpy(new_location, expanded);
Kees Cook20f1de62012-10-25 13:38:14 -0700401 }
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400402
Kees Cook20f1de62012-10-25 13:38:14 -0700403 return new_location;
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400404}
405
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406static int cpio_mkfile_line(const char *line)
407{
408 char name[PATH_MAX + 1];
Luciano Rocha24fa5092007-02-10 01:44:45 -0800409 char *dname = NULL; /* malloc'ed buffer for hard links */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 char location[PATH_MAX + 1];
411 unsigned int mode;
412 int uid;
413 int gid;
Luciano Rocha24fa5092007-02-10 01:44:45 -0800414 int nlinks = 1;
415 int end = 0, dname_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 int rc = -1;
417
Luciano Rocha24fa5092007-02-10 01:44:45 -0800418 if (5 > sscanf(line, "%" str(PATH_MAX) "s %" str(PATH_MAX)
419 "s %o %d %d %n",
420 name, location, &mode, &uid, &gid, &end)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 fprintf(stderr, "Unrecognized file format '%s'", line);
422 goto fail;
423 }
Luciano Rocha24fa5092007-02-10 01:44:45 -0800424 if (end && isgraph(line[end])) {
425 int len;
426 int nend;
427
428 dname = malloc(strlen(line));
429 if (!dname) {
430 fprintf (stderr, "out of memory (%d)\n", dname_len);
431 goto fail;
432 }
433
434 dname_len = strlen(name) + 1;
435 memcpy(dname, name, dname_len);
436
437 do {
438 nend = 0;
439 if (sscanf(line + end, "%" str(PATH_MAX) "s %n",
440 name, &nend) < 1)
441 break;
442 len = strlen(name) + 1;
443 memcpy(dname + dname_len, name, len);
444 dname_len += len;
445 nlinks++;
446 end += nend;
447 } while (isgraph(line[end]));
448 } else {
449 dname = name;
450 }
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400451 rc = cpio_mkfile(dname, cpio_replace_env(location),
452 mode, uid, gid, nlinks);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 fail:
Luciano Rocha24fa5092007-02-10 01:44:45 -0800454 if (dname_len) free(dname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 return rc;
456}
457
Trevor Keith5c725132009-09-22 16:43:38 -0700458static void usage(const char *prog)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459{
460 fprintf(stderr, "Usage:\n"
Michal Mareka8b80172011-03-31 23:16:42 +0200461 "\t%s [-t <timestamp>] <cpio_list>\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 "\n"
463 "<cpio_list> is a file containing newline separated entries that\n"
464 "describe the files to be included in the initramfs archive:\n"
465 "\n"
466 "# a comment\n"
Luciano Rocha24fa5092007-02-10 01:44:45 -0800467 "file <name> <location> <mode> <uid> <gid> [<hard links>]\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 "dir <name> <mode> <uid> <gid>\n"
469 "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n"
470 "slink <name> <target> <mode> <uid> <gid>\n"
471 "pipe <name> <mode> <uid> <gid>\n"
472 "sock <name> <mode> <uid> <gid>\n"
473 "\n"
Luciano Rocha24fa5092007-02-10 01:44:45 -0800474 "<name> name of the file/dir/nod/etc in the archive\n"
475 "<location> location of the file in the current filesystem\n"
Sally, Gene3b1ec9f2008-10-29 09:54:17 -0400476 " expands shell variables quoted with ${}\n"
Luciano Rocha24fa5092007-02-10 01:44:45 -0800477 "<target> link target\n"
478 "<mode> mode/permissions of the file\n"
479 "<uid> user id (0=root)\n"
480 "<gid> group id (0=root)\n"
481 "<dev_type> device type (b=block, c=character)\n"
482 "<maj> major number of nod\n"
483 "<min> minor number of nod\n"
484 "<hard links> space separated list of other links to file\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 "\n"
486 "example:\n"
487 "# A simple initramfs\n"
488 "dir /dev 0755 0 0\n"
489 "nod /dev/console 0600 0 0 c 5 1\n"
490 "dir /root 0700 0 0\n"
491 "dir /sbin 0755 0 0\n"
Michal Mareka8b80172011-03-31 23:16:42 +0200492 "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n"
493 "\n"
494 "<timestamp> is time in seconds since Epoch that will be used\n"
495 "as mtime for symlinks, special files and directories. The default\n"
496 "is to use the current time for these entries.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 prog);
498}
499
Masahiro Yamada3510c5c2021-10-12 11:55:14 +0900500static const struct file_handler file_handler_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 {
502 .type = "file",
503 .handler = cpio_mkfile_line,
504 }, {
505 .type = "nod",
506 .handler = cpio_mknod_line,
507 }, {
508 .type = "dir",
509 .handler = cpio_mkdir_line,
510 }, {
511 .type = "slink",
512 .handler = cpio_mkslink_line,
513 }, {
514 .type = "pipe",
515 .handler = cpio_mkpipe_line,
516 }, {
517 .type = "sock",
518 .handler = cpio_mksock_line,
519 }, {
520 .type = NULL,
521 .handler = NULL,
522 }
523};
524
525#define LINE_SIZE (2 * PATH_MAX + 50)
526
527int main (int argc, char *argv[])
528{
529 FILE *cpio_list;
530 char line[LINE_SIZE];
531 char *args, *type;
532 int ec = 0;
533 int line_nr = 0;
Michal Mareka8b80172011-03-31 23:16:42 +0200534 const char *filename;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
Michal Mareka8b80172011-03-31 23:16:42 +0200536 default_mtime = time(NULL);
537 while (1) {
538 int opt = getopt(argc, argv, "t:h");
539 char *invalid;
540
541 if (opt == -1)
542 break;
543 switch (opt) {
544 case 't':
545 default_mtime = strtol(optarg, &invalid, 10);
546 if (!*optarg || *invalid) {
547 fprintf(stderr, "Invalid timestamp: %s\n",
548 optarg);
549 usage(argv[0]);
550 exit(1);
551 }
552 break;
553 case 'h':
554 case '?':
555 usage(argv[0]);
556 exit(opt == 'h' ? 0 : 1);
557 }
558 }
559
Nicolas Schier4c9d4102021-10-12 20:12:20 +0000560 /*
561 * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t
562 * representation that exceeds 8 chars and breaks the cpio header
563 * specification.
564 */
565 if (default_mtime > 0xffffffff) {
566 fprintf(stderr, "ERROR: Timestamp too large for cpio format\n");
567 exit(1);
568 }
569
Michal Mareka8b80172011-03-31 23:16:42 +0200570 if (argc - optind != 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 usage(argv[0]);
572 exit(1);
573 }
Michal Mareka8b80172011-03-31 23:16:42 +0200574 filename = argv[optind];
575 if (!strcmp(filename, "-"))
Mike Frysingerf2434ec2007-05-10 22:44:28 -0700576 cpio_list = stdin;
Michal Mareka8b80172011-03-31 23:16:42 +0200577 else if (!(cpio_list = fopen(filename, "r"))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 fprintf(stderr, "ERROR: unable to open '%s': %s\n\n",
Michal Mareka8b80172011-03-31 23:16:42 +0200579 filename, strerror(errno));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 usage(argv[0]);
581 exit(1);
582 }
583
584 while (fgets(line, LINE_SIZE, cpio_list)) {
585 int type_idx;
586 size_t slen = strlen(line);
587
588 line_nr++;
589
590 if ('#' == *line) {
591 /* comment - skip to next line */
592 continue;
593 }
594
595 if (! (type = strtok(line, " \t"))) {
596 fprintf(stderr,
597 "ERROR: incorrect format, could not locate file type line %d: '%s'\n",
598 line_nr, line);
599 ec = -1;
Jesper Juhlaa1e8162006-04-18 22:21:54 -0700600 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 }
602
603 if ('\n' == *type) {
604 /* a blank line */
605 continue;
606 }
607
608 if (slen == strlen(type)) {
609 /* must be an empty line */
610 continue;
611 }
612
613 if (! (args = strtok(NULL, "\n"))) {
614 fprintf(stderr,
615 "ERROR: incorrect format, newline required line %d: '%s'\n",
616 line_nr, line);
617 ec = -1;
618 }
619
620 for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) {
621 int rc;
622 if (! strcmp(line, file_handler_table[type_idx].type)) {
623 if ((rc = file_handler_table[type_idx].handler(args))) {
624 ec = rc;
625 fprintf(stderr, " line %d\n", line_nr);
626 }
627 break;
628 }
629 }
630
631 if (NULL == file_handler_table[type_idx].type) {
632 fprintf(stderr, "unknown file type line %d: '%s'\n",
633 line_nr, line);
634 }
635 }
Jesper Juhlaa1e8162006-04-18 22:21:54 -0700636 if (ec == 0)
637 cpio_trailer();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
639 exit(ec);
640}