source: MondoRescue/branches/3.3/mindi-busybox/archival/cpio.c@ 3884

Last change on this file since 3884 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 15.5 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini cpio implementation for busybox
4 *
5 * Copyright (C) 2001 by Glenn McGrath
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 *
9 * Limitations:
10 * Doesn't check CRC's
11 * Only supports new ASCII and CRC formats
12 */
13#include "libbb.h"
14#include "common_bufsiz.h"
15#include "bb_archive.h"
16
17//config:config CPIO
18//config: bool "cpio"
19//config: default y
20//config: help
21//config: cpio is an archival utility program used to create, modify, and
22//config: extract contents from archives.
23//config: cpio has 110 bytes of overheads for every stored file.
24//config:
25//config: This implementation of cpio can extract cpio archives created in the
26//config: "newc" or "crc" format, it cannot create or modify them.
27//config:
28//config: Unless you have a specific application which requires cpio, you
29//config: should probably say N here.
30//config:
31//config:config FEATURE_CPIO_O
32//config: bool "Support for archive creation"
33//config: default y
34//config: depends on CPIO
35//config: help
36//config: This implementation of cpio can create cpio archives in the "newc"
37//config: format only.
38//config:
39//config:config FEATURE_CPIO_P
40//config: bool "Support for passthrough mode"
41//config: default y
42//config: depends on FEATURE_CPIO_O
43//config: help
44//config: Passthrough mode. Rarely used.
45
46//applet:IF_CPIO(APPLET(cpio, BB_DIR_BIN, BB_SUID_DROP))
47//kbuild:lib-$(CONFIG_CPIO) += cpio.o
48
49//usage:#define cpio_trivial_usage
50//usage: "[-dmvu] [-F FILE] [-R USER[:GRP]]" IF_FEATURE_CPIO_O(" [-H newc]")
51//usage: " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]")
52//usage: " [EXTR_FILE]..."
53//usage:#define cpio_full_usage "\n\n"
54//usage: "Extract or list files from a cpio archive"
55//usage: IF_FEATURE_CPIO_O(", or"
56//usage: "\ncreate an archive" IF_FEATURE_CPIO_P(" (-o) or copy files (-p)")
57//usage: " using file list on stdin"
58//usage: )
59//usage: "\n"
60//usage: "\nMain operation mode:"
61//usage: "\n -t List"
62//usage: "\n -i Extract EXTR_FILEs (or all)"
63//usage: IF_FEATURE_CPIO_O(
64//usage: "\n -o Create (requires -H newc)"
65//usage: )
66//usage: IF_FEATURE_CPIO_P(
67//usage: "\n -p DIR Copy files to DIR"
68//usage: )
69//usage: "\nOptions:"
70//usage: "\n -d Make leading directories"
71//usage: "\n -m Preserve mtime"
72//usage: "\n -v Verbose"
73//usage: "\n -u Overwrite"
74//usage: "\n -F FILE Input (-t,-i,-p) or output (-o) file"
75//usage: "\n -R USER[:GRP] Set owner of created files"
76//usage: IF_FEATURE_CPIO_O(
77//usage: "\n -H newc Archive format"
78//usage: )
79
80/* GNU cpio 2.9 --help (abridged):
81
82 Modes:
83 -t, --list List the archive
84 -i, --extract Extract files from an archive
85 -o, --create Create the archive
86 -p, --pass-through Copy-pass mode
87
88 Options valid in any mode:
89 --block-size=SIZE I/O block size = SIZE * 512 bytes
90 -B I/O block size = 5120 bytes
91 -c Use the old portable (ASCII) archive format
92 -C, --io-size=NUMBER I/O block size in bytes
93 -f, --nonmatching Only copy files that do not match given pattern
94 -F, --file=FILE Use FILE instead of standard input or output
95 -H, --format=FORMAT Use given archive FORMAT
96 -M, --message=STRING Print STRING when the end of a volume of the
97 backup media is reached
98 -n, --numeric-uid-gid If -v, show numeric UID and GID
99 --quiet Do not print the number of blocks copied
100 --rsh-command=COMMAND Use remote COMMAND instead of rsh
101 -v, --verbose Verbosely list the files processed
102 -V, --dot Print a "." for each file processed
103 -W, --warning=FLAG Control warning display: 'none','truncate','all';
104 multiple options accumulate
105
106 Options valid only in --extract mode:
107 -b, --swap Swap both halfwords of words and bytes of
108 halfwords in the data (equivalent to -sS)
109 -r, --rename Interactively rename files
110 -s, --swap-bytes Swap the bytes of each halfword in the files
111 -S, --swap-halfwords Swap the halfwords of each word (4 bytes)
112 --to-stdout Extract files to standard output
113 -E, --pattern-file=FILE Read additional patterns specifying filenames to
114 extract or list from FILE
115 --only-verify-crc Verify CRC's, don't actually extract the files
116
117 Options valid only in --create mode:
118 -A, --append Append to an existing archive
119 -O FILE File to use instead of standard output
120
121 Options valid only in --pass-through mode:
122 -l, --link Link files instead of copying them, when possible
123
124 Options valid in --extract and --create modes:
125 --absolute-filenames Do not strip file system prefix components from
126 the file names
127 --no-absolute-filenames Create all files relative to the current dir
128
129 Options valid in --create and --pass-through modes:
130 -0, --null A list of filenames is terminated by a NUL
131 -a, --reset-access-time Reset the access times of files after reading them
132 -I FILE File to use instead of standard input
133 -L, --dereference Dereference symbolic links (copy the files
134 that they point to instead of copying the links)
135 -R, --owner=[USER][:.][GRP] Set owner of created files
136
137 Options valid in --extract and --pass-through modes:
138 -d, --make-directories Create leading directories where needed
139 -m, --preserve-modification-time Retain mtime when creating files
140 --no-preserve-owner Do not change the ownership of the files
141 --sparse Write files with blocks of zeros as sparse files
142 -u, --unconditional Replace all files unconditionally
143 */
144
145enum {
146 OPT_EXTRACT = (1 << 0),
147 OPT_TEST = (1 << 1),
148 OPT_NUL_TERMINATED = (1 << 2),
149 OPT_UNCONDITIONAL = (1 << 3),
150 OPT_VERBOSE = (1 << 4),
151 OPT_CREATE_LEADING_DIR = (1 << 5),
152 OPT_PRESERVE_MTIME = (1 << 6),
153 OPT_DEREF = (1 << 7),
154 OPT_FILE = (1 << 8),
155 OPT_OWNER = (1 << 9),
156 OPTBIT_OWNER = 9,
157 IF_FEATURE_CPIO_O(OPTBIT_CREATE ,)
158 IF_FEATURE_CPIO_O(OPTBIT_FORMAT ,)
159 IF_FEATURE_CPIO_P(OPTBIT_PASSTHROUGH,)
160 IF_LONG_OPTS( OPTBIT_QUIET ,)
161 IF_LONG_OPTS( OPTBIT_2STDOUT ,)
162 OPT_CREATE = IF_FEATURE_CPIO_O((1 << OPTBIT_CREATE )) + 0,
163 OPT_FORMAT = IF_FEATURE_CPIO_O((1 << OPTBIT_FORMAT )) + 0,
164 OPT_PASSTHROUGH = IF_FEATURE_CPIO_P((1 << OPTBIT_PASSTHROUGH)) + 0,
165 OPT_QUIET = IF_LONG_OPTS( (1 << OPTBIT_QUIET )) + 0,
166 OPT_2STDOUT = IF_LONG_OPTS( (1 << OPTBIT_2STDOUT )) + 0,
167};
168
169#define OPTION_STR "it0uvdmLF:R:"
170
171struct globals {
172 struct bb_uidgid_t owner_ugid;
173} FIX_ALIASING;
174#define G (*(struct globals*)bb_common_bufsiz1)
175void BUG_cpio_globals_too_big(void);
176#define INIT_G() do { \
177 setup_common_bufsiz(); \
178 G.owner_ugid.uid = -1L; \
179 G.owner_ugid.gid = -1L; \
180} while (0)
181
182#if ENABLE_FEATURE_CPIO_O
183static off_t cpio_pad4(off_t size)
184{
185 int i;
186
187 i = (- size) & 3;
188 size += i;
189 while (--i >= 0)
190 bb_putchar('\0');
191 return size;
192}
193
194/* Return value will become exit code.
195 * It's ok to exit instead of return. */
196static NOINLINE int cpio_o(void)
197{
198 struct name_s {
199 struct name_s *next;
200 char name[1];
201 };
202 struct inodes_s {
203 struct inodes_s *next;
204 struct name_s *names;
205 struct stat st;
206 };
207
208 struct inodes_s *links = NULL;
209 off_t bytes = 0; /* output bytes count */
210
211 while (1) {
212 const char *name;
213 char *line;
214 struct stat st;
215
216 line = (option_mask32 & OPT_NUL_TERMINATED)
217 ? bb_get_chunk_from_file(stdin, NULL)
218 : xmalloc_fgetline(stdin);
219
220 if (line) {
221 /* Strip leading "./[./]..." from the filename */
222 name = line;
223 while (name[0] == '.' && name[1] == '/') {
224 while (*++name == '/')
225 continue;
226 }
227 if (!*name) { /* line is empty */
228 free(line);
229 continue;
230 }
231 if ((option_mask32 & OPT_DEREF)
232 ? stat(name, &st)
233 : lstat(name, &st)
234 ) {
235 abort_cpio_o:
236 bb_simple_perror_msg_and_die(name);
237 }
238
239 if (G.owner_ugid.uid != (uid_t)-1L)
240 st.st_uid = G.owner_ugid.uid;
241 if (G.owner_ugid.gid != (gid_t)-1L)
242 st.st_gid = G.owner_ugid.gid;
243
244 if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode)))
245 st.st_size = 0; /* paranoia */
246
247 /* Store hardlinks for later processing, dont output them */
248 if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) {
249 struct name_s *n;
250 struct inodes_s *l;
251
252 /* Do we have this hardlink remembered? */
253 l = links;
254 while (1) {
255 if (l == NULL) {
256 /* Not found: add new item to "links" list */
257 l = xzalloc(sizeof(*l));
258 l->st = st;
259 l->next = links;
260 links = l;
261 break;
262 }
263 if (l->st.st_ino == st.st_ino) {
264 /* found */
265 break;
266 }
267 l = l->next;
268 }
269 /* Add new name to "l->names" list */
270 n = xmalloc(sizeof(*n) + strlen(name));
271 strcpy(n->name, name);
272 n->next = l->names;
273 l->names = n;
274
275 free(line);
276 continue;
277 }
278 } else { /* line == NULL: EOF */
279 next_link:
280 if (links) {
281 /* Output hardlink's data */
282 st = links->st;
283 name = links->names->name;
284 links->names = links->names->next;
285 /* GNU cpio is reported to emit file data
286 * only for the last instance. Mimic that. */
287 if (links->names == NULL)
288 links = links->next;
289 else
290 st.st_size = 0;
291 /* NB: we leak links->names and/or links,
292 * this is intended (we exit soon anyway) */
293 } else {
294 /* If no (more) hardlinks to output,
295 * output "trailer" entry */
296 name = cpio_TRAILER;
297 /* st.st_size == 0 is a must, but for uniformity
298 * in the output, we zero out everything */
299 memset(&st, 0, sizeof(st));
300 /* st.st_nlink = 1; - GNU cpio does this */
301 }
302 }
303
304 bytes += printf("070701"
305 "%08X%08X%08X%08X%08X%08X%08X"
306 "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */
307 /* strlen+1: */ "%08X"
308 /* chksum: */ "00000000" /* (only for "070702" files) */
309 /* name,NUL: */ "%s%c",
310 (unsigned)(uint32_t) st.st_ino,
311 (unsigned)(uint32_t) st.st_mode,
312 (unsigned)(uint32_t) st.st_uid,
313 (unsigned)(uint32_t) st.st_gid,
314 (unsigned)(uint32_t) st.st_nlink,
315 (unsigned)(uint32_t) st.st_mtime,
316 (unsigned)(uint32_t) st.st_size,
317 (unsigned)(uint32_t) major(st.st_dev),
318 (unsigned)(uint32_t) minor(st.st_dev),
319 (unsigned)(uint32_t) major(st.st_rdev),
320 (unsigned)(uint32_t) minor(st.st_rdev),
321 (unsigned)(strlen(name) + 1),
322 name, '\0');
323 bytes = cpio_pad4(bytes);
324
325 if (st.st_size) {
326 if (S_ISLNK(st.st_mode)) {
327 char *lpath = xmalloc_readlink_or_warn(name);
328 if (!lpath)
329 goto abort_cpio_o;
330 bytes += printf("%s", lpath);
331 free(lpath);
332 } else { /* S_ISREG */
333 int fd = xopen(name, O_RDONLY);
334 fflush_all();
335 /* We must abort if file got shorter too! */
336 bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size);
337 bytes += st.st_size;
338 close(fd);
339 }
340 bytes = cpio_pad4(bytes);
341 }
342
343 if (!line) {
344 if (name != cpio_TRAILER)
345 goto next_link;
346 /* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */
347 return EXIT_SUCCESS;
348 }
349
350 free(line);
351 } /* end of "while (1)" */
352}
353#endif
354
355int cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
356int cpio_main(int argc UNUSED_PARAM, char **argv)
357{
358 archive_handle_t *archive_handle;
359 char *cpio_filename;
360 char *cpio_owner;
361 IF_FEATURE_CPIO_O(const char *cpio_fmt = "";)
362 unsigned opt;
363
364#if ENABLE_LONG_OPTS
365 applet_long_options =
366 "extract\0" No_argument "i"
367 "list\0" No_argument "t"
368#if ENABLE_FEATURE_CPIO_O
369 "create\0" No_argument "o"
370 "format\0" Required_argument "H"
371#if ENABLE_FEATURE_CPIO_P
372 "pass-through\0" No_argument "p"
373#endif
374#endif
375 "owner\0" Required_argument "R"
376 "verbose\0" No_argument "v"
377 "quiet\0" No_argument "\xff"
378 "to-stdout\0" No_argument "\xfe"
379 ;
380#endif
381
382 INIT_G();
383 archive_handle = init_handle();
384 /* archive_handle->src_fd = STDIN_FILENO; - done by init_handle */
385 archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER;
386
387 /* As of now we do not enforce this: */
388 /* -i,-t,-o,-p are mutually exclusive */
389 /* -u,-d,-m make sense only with -i or -p */
390 /* -L makes sense only with -o or -p */
391
392#if !ENABLE_FEATURE_CPIO_O
393 opt = getopt32(argv, OPTION_STR, &cpio_filename, &cpio_owner);
394#else
395 opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"),
396 &cpio_filename, &cpio_owner, &cpio_fmt);
397#endif
398 argv += optind;
399 if (opt & OPT_OWNER) { /* -R */
400 parse_chown_usergroup_or_die(&G.owner_ugid, cpio_owner);
401 archive_handle->cpio__owner = G.owner_ugid;
402 }
403#if !ENABLE_FEATURE_CPIO_O
404 if (opt & OPT_FILE) { /* -F */
405 xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
406 }
407#else
408 if ((opt & (OPT_FILE|OPT_CREATE)) == OPT_FILE) { /* -F without -o */
409 xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
410 }
411 if (opt & OPT_PASSTHROUGH) {
412 pid_t pid;
413 struct fd_pair pp;
414
415 if (argv[0] == NULL)
416 bb_show_usage();
417 if (opt & OPT_CREATE_LEADING_DIR)
418 mkdir(argv[0], 0777);
419 /* Crude existence check:
420 * close(xopen(argv[0], O_RDONLY | O_DIRECTORY));
421 * We can also xopen, fstat, IS_DIR, later fchdir.
422 * This would check for existence earlier and cleaner.
423 * As it stands now, if we fail xchdir later,
424 * child dies on EPIPE, unless it caught
425 * a diffrerent problem earlier.
426 * This is good enough for now.
427 */
428#if !BB_MMU
429 pp.rd = 3;
430 pp.wr = 4;
431 if (!re_execed) {
432 close(3);
433 close(4);
434 xpiped_pair(pp);
435 }
436#else
437 xpiped_pair(pp);
438#endif
439 pid = fork_or_rexec(argv - optind);
440 if (pid == 0) { /* child */
441 close(pp.rd);
442 xmove_fd(pp.wr, STDOUT_FILENO);
443 goto dump;
444 }
445 /* parent */
446 USE_FOR_NOMMU(argv[-optind][0] &= 0x7f); /* undo fork_or_rexec() damage */
447 xchdir(*argv++);
448 close(pp.wr);
449 xmove_fd(pp.rd, STDIN_FILENO);
450 //opt &= ~OPT_PASSTHROUGH;
451 opt |= OPT_EXTRACT;
452 goto skip;
453 }
454 /* -o */
455 if (opt & OPT_CREATE) {
456 if (cpio_fmt[0] != 'n') /* we _require_ "-H newc" */
457 bb_show_usage();
458 if (opt & OPT_FILE) {
459 xmove_fd(xopen(cpio_filename, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
460 }
461 dump:
462 return cpio_o();
463 }
464 skip:
465#endif
466
467 /* One of either extract or test options must be given */
468 if ((opt & (OPT_TEST | OPT_EXTRACT)) == 0) {
469 bb_show_usage();
470 }
471
472 if (opt & OPT_TEST) {
473 /* if both extract and test options are given, ignore extract option */
474 opt &= ~OPT_EXTRACT;
475 archive_handle->action_header = header_list;
476 }
477 if (opt & OPT_EXTRACT) {
478 archive_handle->action_data = data_extract_all;
479 if (opt & OPT_2STDOUT)
480 archive_handle->action_data = data_extract_to_stdout;
481 }
482 if (opt & OPT_UNCONDITIONAL) {
483 archive_handle->ah_flags |= ARCHIVE_UNLINK_OLD;
484 archive_handle->ah_flags &= ~ARCHIVE_EXTRACT_NEWER;
485 }
486 if (opt & OPT_VERBOSE) {
487 if (archive_handle->action_header == header_list) {
488 archive_handle->action_header = header_verbose_list;
489 } else {
490 archive_handle->action_header = header_list;
491 }
492 }
493 if (opt & OPT_CREATE_LEADING_DIR) {
494 archive_handle->ah_flags |= ARCHIVE_CREATE_LEADING_DIRS;
495 }
496 if (opt & OPT_PRESERVE_MTIME) {
497 archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
498 }
499
500 while (*argv) {
501 archive_handle->filter = filter_accept_list;
502 llist_add_to(&archive_handle->accept, *argv);
503 argv++;
504 }
505
506 /* see get_header_cpio */
507 archive_handle->cpio__blocks = (off_t)-1;
508 while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
509 continue;
510
511 if (archive_handle->cpio__blocks != (off_t)-1
512 && !(opt & OPT_QUIET)
513 ) {
514 fprintf(stderr, "%"OFF_FMT"u blocks\n", archive_handle->cpio__blocks);
515 }
516
517 return EXIT_SUCCESS;
518}
Note: See TracBrowser for help on using the repository browser.