1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <stdio.h> |
3 | #include <stdlib.h> |
4 | #include <stdint.h> |
5 | #include <stdbool.h> |
6 | #include <sys/types.h> |
7 | #include <sys/stat.h> |
8 | #include <string.h> |
9 | #include <unistd.h> |
10 | #include <time.h> |
11 | #include <fcntl.h> |
12 | #include <errno.h> |
13 | #include <ctype.h> |
14 | #include <limits.h> |
15 | |
16 | /* |
17 | * Original work by Jeff Garzik |
18 | * |
19 | * External file lists, symlink, pipe and fifo support by Thayne Harbaugh |
20 | * Hard link support by Luciano Rocha |
21 | */ |
22 | |
23 | #define xstr(s) #s |
24 | #define str(s) xstr(s) |
25 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) |
26 | |
27 | static unsigned int offset; |
28 | static unsigned int ino = 721; |
29 | static time_t default_mtime; |
30 | static bool do_file_mtime; |
31 | static bool do_csum = false; |
32 | |
33 | struct file_handler { |
34 | const char *type; |
35 | int (*handler)(const char *line); |
36 | }; |
37 | |
38 | static void push_string(const char *name) |
39 | { |
40 | unsigned int name_len = strlen(s: name) + 1; |
41 | |
42 | fputs(s: name, stdout); |
43 | putchar(c: 0); |
44 | offset += name_len; |
45 | } |
46 | |
47 | static void push_pad (void) |
48 | { |
49 | while (offset & 3) { |
50 | putchar(c: 0); |
51 | offset++; |
52 | } |
53 | } |
54 | |
55 | static void push_rest(const char *name) |
56 | { |
57 | unsigned int name_len = strlen(s: name) + 1; |
58 | unsigned int tmp_ofs; |
59 | |
60 | fputs(s: name, stdout); |
61 | putchar(c: 0); |
62 | offset += name_len; |
63 | |
64 | tmp_ofs = name_len + 110; |
65 | while (tmp_ofs & 3) { |
66 | putchar(c: 0); |
67 | offset++; |
68 | tmp_ofs++; |
69 | } |
70 | } |
71 | |
72 | static void push_hdr(const char *s) |
73 | { |
74 | fputs(s: s, stdout); |
75 | offset += 110; |
76 | } |
77 | |
78 | static void cpio_trailer(void) |
79 | { |
80 | char s[256]; |
81 | const char name[] = "TRAILER!!!" ; |
82 | |
83 | sprintf(s: s, format: "%s%08X%08X%08lX%08lX%08X%08lX" |
84 | "%08X%08X%08X%08X%08X%08X%08X" , |
85 | do_csum ? "070702" : "070701" , /* magic */ |
86 | 0, /* ino */ |
87 | 0, /* mode */ |
88 | (long) 0, /* uid */ |
89 | (long) 0, /* gid */ |
90 | 1, /* nlink */ |
91 | (long) 0, /* mtime */ |
92 | 0, /* filesize */ |
93 | 0, /* major */ |
94 | 0, /* minor */ |
95 | 0, /* rmajor */ |
96 | 0, /* rminor */ |
97 | (unsigned)strlen(s: name)+1, /* namesize */ |
98 | 0); /* chksum */ |
99 | push_hdr(s); |
100 | push_rest(name); |
101 | |
102 | while (offset % 512) { |
103 | putchar(c: 0); |
104 | offset++; |
105 | } |
106 | } |
107 | |
108 | static int cpio_mkslink(const char *name, const char *target, |
109 | unsigned int mode, uid_t uid, gid_t gid) |
110 | { |
111 | char s[256]; |
112 | |
113 | if (name[0] == '/') |
114 | name++; |
115 | sprintf(s: s,format: "%s%08X%08X%08lX%08lX%08X%08lX" |
116 | "%08X%08X%08X%08X%08X%08X%08X" , |
117 | do_csum ? "070702" : "070701" , /* magic */ |
118 | ino++, /* ino */ |
119 | S_IFLNK | mode, /* mode */ |
120 | (long) uid, /* uid */ |
121 | (long) gid, /* gid */ |
122 | 1, /* nlink */ |
123 | (long) default_mtime, /* mtime */ |
124 | (unsigned)strlen(s: target)+1, /* filesize */ |
125 | 3, /* major */ |
126 | 1, /* minor */ |
127 | 0, /* rmajor */ |
128 | 0, /* rminor */ |
129 | (unsigned)strlen(s: name) + 1,/* namesize */ |
130 | 0); /* chksum */ |
131 | push_hdr(s); |
132 | push_string(name); |
133 | push_pad(); |
134 | push_string(name: target); |
135 | push_pad(); |
136 | return 0; |
137 | } |
138 | |
139 | static int cpio_mkslink_line(const char *line) |
140 | { |
141 | char name[PATH_MAX + 1]; |
142 | char target[PATH_MAX + 1]; |
143 | unsigned int mode; |
144 | int uid; |
145 | int gid; |
146 | int rc = -1; |
147 | |
148 | if (5 != sscanf(s: line, format: "%" str(PATH_MAX) "s %" str(PATH_MAX) "s %o %d %d" , name, target, &mode, &uid, &gid)) { |
149 | fprintf(stderr, format: "Unrecognized dir format '%s'" , line); |
150 | goto fail; |
151 | } |
152 | rc = cpio_mkslink(name, target, mode, uid, gid); |
153 | fail: |
154 | return rc; |
155 | } |
156 | |
157 | static int cpio_mkgeneric(const char *name, unsigned int mode, |
158 | uid_t uid, gid_t gid) |
159 | { |
160 | char s[256]; |
161 | |
162 | if (name[0] == '/') |
163 | name++; |
164 | sprintf(s: s,format: "%s%08X%08X%08lX%08lX%08X%08lX" |
165 | "%08X%08X%08X%08X%08X%08X%08X" , |
166 | do_csum ? "070702" : "070701" , /* magic */ |
167 | ino++, /* ino */ |
168 | mode, /* mode */ |
169 | (long) uid, /* uid */ |
170 | (long) gid, /* gid */ |
171 | 2, /* nlink */ |
172 | (long) default_mtime, /* mtime */ |
173 | 0, /* filesize */ |
174 | 3, /* major */ |
175 | 1, /* minor */ |
176 | 0, /* rmajor */ |
177 | 0, /* rminor */ |
178 | (unsigned)strlen(s: name) + 1,/* namesize */ |
179 | 0); /* chksum */ |
180 | push_hdr(s); |
181 | push_rest(name); |
182 | return 0; |
183 | } |
184 | |
185 | enum generic_types { |
186 | GT_DIR, |
187 | GT_PIPE, |
188 | GT_SOCK |
189 | }; |
190 | |
191 | struct generic_type { |
192 | const char *type; |
193 | mode_t mode; |
194 | }; |
195 | |
196 | static const struct generic_type generic_type_table[] = { |
197 | [GT_DIR] = { |
198 | .type = "dir" , |
199 | .mode = S_IFDIR |
200 | }, |
201 | [GT_PIPE] = { |
202 | .type = "pipe" , |
203 | .mode = S_IFIFO |
204 | }, |
205 | [GT_SOCK] = { |
206 | .type = "sock" , |
207 | .mode = S_IFSOCK |
208 | } |
209 | }; |
210 | |
211 | static int cpio_mkgeneric_line(const char *line, enum generic_types gt) |
212 | { |
213 | char name[PATH_MAX + 1]; |
214 | unsigned int mode; |
215 | int uid; |
216 | int gid; |
217 | int rc = -1; |
218 | |
219 | if (4 != sscanf(s: line, format: "%" str(PATH_MAX) "s %o %d %d" , name, &mode, &uid, &gid)) { |
220 | fprintf(stderr, format: "Unrecognized %s format '%s'" , |
221 | line, generic_type_table[gt].type); |
222 | goto fail; |
223 | } |
224 | mode |= generic_type_table[gt].mode; |
225 | rc = cpio_mkgeneric(name, mode, uid, gid); |
226 | fail: |
227 | return rc; |
228 | } |
229 | |
230 | static int cpio_mkdir_line(const char *line) |
231 | { |
232 | return cpio_mkgeneric_line(line, gt: GT_DIR); |
233 | } |
234 | |
235 | static int cpio_mkpipe_line(const char *line) |
236 | { |
237 | return cpio_mkgeneric_line(line, gt: GT_PIPE); |
238 | } |
239 | |
240 | static int cpio_mksock_line(const char *line) |
241 | { |
242 | return cpio_mkgeneric_line(line, gt: GT_SOCK); |
243 | } |
244 | |
245 | static int cpio_mknod(const char *name, unsigned int mode, |
246 | uid_t uid, gid_t gid, char dev_type, |
247 | unsigned int maj, unsigned int min) |
248 | { |
249 | char s[256]; |
250 | |
251 | if (dev_type == 'b') |
252 | mode |= S_IFBLK; |
253 | else |
254 | mode |= S_IFCHR; |
255 | |
256 | if (name[0] == '/') |
257 | name++; |
258 | sprintf(s: s,format: "%s%08X%08X%08lX%08lX%08X%08lX" |
259 | "%08X%08X%08X%08X%08X%08X%08X" , |
260 | do_csum ? "070702" : "070701" , /* magic */ |
261 | ino++, /* ino */ |
262 | mode, /* mode */ |
263 | (long) uid, /* uid */ |
264 | (long) gid, /* gid */ |
265 | 1, /* nlink */ |
266 | (long) default_mtime, /* mtime */ |
267 | 0, /* filesize */ |
268 | 3, /* major */ |
269 | 1, /* minor */ |
270 | maj, /* rmajor */ |
271 | min, /* rminor */ |
272 | (unsigned)strlen(s: name) + 1,/* namesize */ |
273 | 0); /* chksum */ |
274 | push_hdr(s); |
275 | push_rest(name); |
276 | return 0; |
277 | } |
278 | |
279 | static int cpio_mknod_line(const char *line) |
280 | { |
281 | char name[PATH_MAX + 1]; |
282 | unsigned int mode; |
283 | int uid; |
284 | int gid; |
285 | char dev_type; |
286 | unsigned int maj; |
287 | unsigned int min; |
288 | int rc = -1; |
289 | |
290 | if (7 != sscanf(s: line, format: "%" str(PATH_MAX) "s %o %d %d %c %u %u" , |
291 | name, &mode, &uid, &gid, &dev_type, &maj, &min)) { |
292 | fprintf(stderr, format: "Unrecognized nod format '%s'" , line); |
293 | goto fail; |
294 | } |
295 | rc = cpio_mknod(name, mode, uid, gid, dev_type, maj, min); |
296 | fail: |
297 | return rc; |
298 | } |
299 | |
300 | static int cpio_mkfile_csum(int fd, unsigned long size, uint32_t *csum) |
301 | { |
302 | while (size) { |
303 | unsigned char filebuf[65536]; |
304 | ssize_t this_read; |
305 | size_t i, this_size = MIN(size, sizeof(filebuf)); |
306 | |
307 | this_read = read(fd: fd, buf: filebuf, nbytes: this_size); |
308 | if (this_read <= 0 || this_read > this_size) |
309 | return -1; |
310 | |
311 | for (i = 0; i < this_read; i++) |
312 | *csum += filebuf[i]; |
313 | |
314 | size -= this_read; |
315 | } |
316 | /* seek back to the start for data segment I/O */ |
317 | if (lseek(fd: fd, offset: 0, SEEK_SET) < 0) |
318 | return -1; |
319 | |
320 | return 0; |
321 | } |
322 | |
323 | static int cpio_mkfile(const char *name, const char *location, |
324 | unsigned int mode, uid_t uid, gid_t gid, |
325 | unsigned int nlinks) |
326 | { |
327 | char s[256]; |
328 | struct stat buf; |
329 | unsigned long size; |
330 | int file; |
331 | int retval; |
332 | int rc = -1; |
333 | time_t mtime; |
334 | int namesize; |
335 | unsigned int i; |
336 | uint32_t csum = 0; |
337 | |
338 | mode |= S_IFREG; |
339 | |
340 | file = open (file: location, O_RDONLY); |
341 | if (file < 0) { |
342 | fprintf (stderr, format: "File %s could not be opened for reading\n" , location); |
343 | goto error; |
344 | } |
345 | |
346 | retval = fstat(fd: file, buf: &buf); |
347 | if (retval) { |
348 | fprintf(stderr, format: "File %s could not be stat()'ed\n" , location); |
349 | goto error; |
350 | } |
351 | |
352 | if (do_file_mtime) { |
353 | mtime = default_mtime; |
354 | } else { |
355 | mtime = buf.st_mtime; |
356 | if (mtime > 0xffffffff) { |
357 | fprintf(stderr, format: "%s: Timestamp exceeds maximum cpio timestamp, clipping.\n" , |
358 | location); |
359 | mtime = 0xffffffff; |
360 | } |
361 | |
362 | if (mtime < 0) { |
363 | fprintf(stderr, format: "%s: Timestamp negative, clipping.\n" , |
364 | location); |
365 | mtime = 0; |
366 | } |
367 | } |
368 | |
369 | if (buf.st_size > 0xffffffff) { |
370 | fprintf(stderr, format: "%s: Size exceeds maximum cpio file size\n" , |
371 | location); |
372 | goto error; |
373 | } |
374 | |
375 | if (do_csum && cpio_mkfile_csum(fd: file, size: buf.st_size, csum: &csum) < 0) { |
376 | fprintf(stderr, format: "Failed to checksum file %s\n" , location); |
377 | goto error; |
378 | } |
379 | |
380 | size = 0; |
381 | for (i = 1; i <= nlinks; i++) { |
382 | /* data goes on last link */ |
383 | if (i == nlinks) |
384 | size = buf.st_size; |
385 | |
386 | if (name[0] == '/') |
387 | name++; |
388 | namesize = strlen(s: name) + 1; |
389 | sprintf(s: s,format: "%s%08X%08X%08lX%08lX%08X%08lX" |
390 | "%08lX%08X%08X%08X%08X%08X%08X" , |
391 | do_csum ? "070702" : "070701" , /* magic */ |
392 | ino, /* ino */ |
393 | mode, /* mode */ |
394 | (long) uid, /* uid */ |
395 | (long) gid, /* gid */ |
396 | nlinks, /* nlink */ |
397 | (long) mtime, /* mtime */ |
398 | size, /* filesize */ |
399 | 3, /* major */ |
400 | 1, /* minor */ |
401 | 0, /* rmajor */ |
402 | 0, /* rminor */ |
403 | namesize, /* namesize */ |
404 | size ? csum : 0); /* chksum */ |
405 | push_hdr(s); |
406 | push_string(name); |
407 | push_pad(); |
408 | |
409 | while (size) { |
410 | unsigned char filebuf[65536]; |
411 | ssize_t this_read; |
412 | size_t this_size = MIN(size, sizeof(filebuf)); |
413 | |
414 | this_read = read(fd: file, buf: filebuf, nbytes: this_size); |
415 | if (this_read <= 0 || this_read > this_size) { |
416 | fprintf(stderr, format: "Can not read %s file\n" , location); |
417 | goto error; |
418 | } |
419 | |
420 | if (fwrite(ptr: filebuf, size: this_read, n: 1, stdout) != 1) { |
421 | fprintf(stderr, format: "writing filebuf failed\n" ); |
422 | goto error; |
423 | } |
424 | offset += this_read; |
425 | size -= this_read; |
426 | } |
427 | push_pad(); |
428 | |
429 | name += namesize; |
430 | } |
431 | ino++; |
432 | rc = 0; |
433 | |
434 | error: |
435 | if (file >= 0) |
436 | close(fd: file); |
437 | return rc; |
438 | } |
439 | |
440 | static char *cpio_replace_env(char *new_location) |
441 | { |
442 | char expanded[PATH_MAX + 1]; |
443 | char *start, *end, *var; |
444 | |
445 | while ((start = strstr(haystack: new_location, needle: "${" )) && |
446 | (end = strchr(s: start + 2, c: '}'))) { |
447 | *start = *end = 0; |
448 | var = getenv(name: start + 2); |
449 | snprintf(s: expanded, maxlen: sizeof expanded, format: "%s%s%s" , |
450 | new_location, var ? var : "" , end + 1); |
451 | strcpy(dest: new_location, src: expanded); |
452 | } |
453 | |
454 | return new_location; |
455 | } |
456 | |
457 | static int cpio_mkfile_line(const char *line) |
458 | { |
459 | char name[PATH_MAX + 1]; |
460 | char *dname = NULL; /* malloc'ed buffer for hard links */ |
461 | char location[PATH_MAX + 1]; |
462 | unsigned int mode; |
463 | int uid; |
464 | int gid; |
465 | int nlinks = 1; |
466 | int end = 0, dname_len = 0; |
467 | int rc = -1; |
468 | |
469 | if (5 > sscanf(s: line, format: "%" str(PATH_MAX) "s %" str(PATH_MAX) |
470 | "s %o %d %d %n" , |
471 | name, location, &mode, &uid, &gid, &end)) { |
472 | fprintf(stderr, format: "Unrecognized file format '%s'" , line); |
473 | goto fail; |
474 | } |
475 | if (end && isgraph(line[end])) { |
476 | int len; |
477 | int nend; |
478 | |
479 | dname = malloc(size: strlen(s: line)); |
480 | if (!dname) { |
481 | fprintf (stderr, format: "out of memory (%d)\n" , dname_len); |
482 | goto fail; |
483 | } |
484 | |
485 | dname_len = strlen(s: name) + 1; |
486 | memcpy(dest: dname, src: name, n: dname_len); |
487 | |
488 | do { |
489 | nend = 0; |
490 | if (sscanf(s: line + end, format: "%" str(PATH_MAX) "s %n" , |
491 | name, &nend) < 1) |
492 | break; |
493 | len = strlen(s: name) + 1; |
494 | memcpy(dest: dname + dname_len, src: name, n: len); |
495 | dname_len += len; |
496 | nlinks++; |
497 | end += nend; |
498 | } while (isgraph(line[end])); |
499 | } else { |
500 | dname = name; |
501 | } |
502 | rc = cpio_mkfile(name: dname, location: cpio_replace_env(new_location: location), |
503 | mode, uid, gid, nlinks); |
504 | fail: |
505 | if (dname_len) free(ptr: dname); |
506 | return rc; |
507 | } |
508 | |
509 | static void usage(const char *prog) |
510 | { |
511 | fprintf(stderr, format: "Usage:\n" |
512 | "\t%s [-t <timestamp>] [-c] <cpio_list>\n" |
513 | "\n" |
514 | "<cpio_list> is a file containing newline separated entries that\n" |
515 | "describe the files to be included in the initramfs archive:\n" |
516 | "\n" |
517 | "# a comment\n" |
518 | "file <name> <location> <mode> <uid> <gid> [<hard links>]\n" |
519 | "dir <name> <mode> <uid> <gid>\n" |
520 | "nod <name> <mode> <uid> <gid> <dev_type> <maj> <min>\n" |
521 | "slink <name> <target> <mode> <uid> <gid>\n" |
522 | "pipe <name> <mode> <uid> <gid>\n" |
523 | "sock <name> <mode> <uid> <gid>\n" |
524 | "\n" |
525 | "<name> name of the file/dir/nod/etc in the archive\n" |
526 | "<location> location of the file in the current filesystem\n" |
527 | " expands shell variables quoted with ${}\n" |
528 | "<target> link target\n" |
529 | "<mode> mode/permissions of the file\n" |
530 | "<uid> user id (0=root)\n" |
531 | "<gid> group id (0=root)\n" |
532 | "<dev_type> device type (b=block, c=character)\n" |
533 | "<maj> major number of nod\n" |
534 | "<min> minor number of nod\n" |
535 | "<hard links> space separated list of other links to file\n" |
536 | "\n" |
537 | "example:\n" |
538 | "# A simple initramfs\n" |
539 | "dir /dev 0755 0 0\n" |
540 | "nod /dev/console 0600 0 0 c 5 1\n" |
541 | "dir /root 0700 0 0\n" |
542 | "dir /sbin 0755 0 0\n" |
543 | "file /sbin/kinit /usr/src/klibc/kinit/kinit 0755 0 0\n" |
544 | "\n" |
545 | "<timestamp> is time in seconds since Epoch that will be used\n" |
546 | "as mtime for symlinks, directories, regular and special files.\n" |
547 | "The default is to use the current time for all files, but\n" |
548 | "preserve modification time for regular files.\n" |
549 | "-c: calculate and store 32-bit checksums for file data.\n" , |
550 | prog); |
551 | } |
552 | |
553 | static const struct file_handler file_handler_table[] = { |
554 | { |
555 | .type = "file" , |
556 | .handler = cpio_mkfile_line, |
557 | }, { |
558 | .type = "nod" , |
559 | .handler = cpio_mknod_line, |
560 | }, { |
561 | .type = "dir" , |
562 | .handler = cpio_mkdir_line, |
563 | }, { |
564 | .type = "slink" , |
565 | .handler = cpio_mkslink_line, |
566 | }, { |
567 | .type = "pipe" , |
568 | .handler = cpio_mkpipe_line, |
569 | }, { |
570 | .type = "sock" , |
571 | .handler = cpio_mksock_line, |
572 | }, { |
573 | .type = NULL, |
574 | .handler = NULL, |
575 | } |
576 | }; |
577 | |
578 | #define LINE_SIZE (2 * PATH_MAX + 50) |
579 | |
580 | int main (int argc, char *argv[]) |
581 | { |
582 | FILE *cpio_list; |
583 | char line[LINE_SIZE]; |
584 | char *args, *type; |
585 | int ec = 0; |
586 | int line_nr = 0; |
587 | const char *filename; |
588 | |
589 | default_mtime = time(NULL); |
590 | while (1) { |
591 | int opt = getopt(argc: argc, argv: argv, shortopts: "t:ch" ); |
592 | char *invalid; |
593 | |
594 | if (opt == -1) |
595 | break; |
596 | switch (opt) { |
597 | case 't': |
598 | default_mtime = strtol(nptr: optarg, endptr: &invalid, base: 10); |
599 | if (!*optarg || *invalid) { |
600 | fprintf(stderr, format: "Invalid timestamp: %s\n" , |
601 | optarg); |
602 | usage(prog: argv[0]); |
603 | exit(status: 1); |
604 | } |
605 | do_file_mtime = true; |
606 | break; |
607 | case 'c': |
608 | do_csum = true; |
609 | break; |
610 | case 'h': |
611 | case '?': |
612 | usage(prog: argv[0]); |
613 | exit(status: opt == 'h' ? 0 : 1); |
614 | } |
615 | } |
616 | |
617 | /* |
618 | * Timestamps after 2106-02-07 06:28:15 UTC have an ascii hex time_t |
619 | * representation that exceeds 8 chars and breaks the cpio header |
620 | * specification. Negative timestamps similarly exceed 8 chars. |
621 | */ |
622 | if (default_mtime > 0xffffffff || default_mtime < 0) { |
623 | fprintf(stderr, format: "ERROR: Timestamp out of range for cpio format\n" ); |
624 | exit(status: 1); |
625 | } |
626 | |
627 | if (argc - optind != 1) { |
628 | usage(prog: argv[0]); |
629 | exit(status: 1); |
630 | } |
631 | filename = argv[optind]; |
632 | if (!strcmp(s1: filename, s2: "-" )) |
633 | cpio_list = stdin; |
634 | else if (!(cpio_list = fopen(filename: filename, modes: "r" ))) { |
635 | fprintf(stderr, format: "ERROR: unable to open '%s': %s\n\n" , |
636 | filename, strerror(errno)); |
637 | usage(prog: argv[0]); |
638 | exit(status: 1); |
639 | } |
640 | |
641 | while (fgets(s: line, LINE_SIZE, stream: cpio_list)) { |
642 | int type_idx; |
643 | size_t slen = strlen(s: line); |
644 | |
645 | line_nr++; |
646 | |
647 | if ('#' == *line) { |
648 | /* comment - skip to next line */ |
649 | continue; |
650 | } |
651 | |
652 | if (! (type = strtok(s: line, delim: " \t" ))) { |
653 | fprintf(stderr, |
654 | format: "ERROR: incorrect format, could not locate file type line %d: '%s'\n" , |
655 | line_nr, line); |
656 | ec = -1; |
657 | break; |
658 | } |
659 | |
660 | if ('\n' == *type) { |
661 | /* a blank line */ |
662 | continue; |
663 | } |
664 | |
665 | if (slen == strlen(s: type)) { |
666 | /* must be an empty line */ |
667 | continue; |
668 | } |
669 | |
670 | if (! (args = strtok(NULL, delim: "\n" ))) { |
671 | fprintf(stderr, |
672 | format: "ERROR: incorrect format, newline required line %d: '%s'\n" , |
673 | line_nr, line); |
674 | ec = -1; |
675 | } |
676 | |
677 | for (type_idx = 0; file_handler_table[type_idx].type; type_idx++) { |
678 | int rc; |
679 | if (! strcmp(s1: line, s2: file_handler_table[type_idx].type)) { |
680 | if ((rc = file_handler_table[type_idx].handler(args))) { |
681 | ec = rc; |
682 | fprintf(stderr, format: " line %d\n" , line_nr); |
683 | } |
684 | break; |
685 | } |
686 | } |
687 | |
688 | if (NULL == file_handler_table[type_idx].type) { |
689 | fprintf(stderr, format: "unknown file type line %d: '%s'\n" , |
690 | line_nr, line); |
691 | } |
692 | } |
693 | if (ec == 0) |
694 | cpio_trailer(); |
695 | |
696 | exit(status: ec); |
697 | } |
698 | |