source: branches/2.2.9/mindi-busybox/archival/cpio.c @ 2725

Last change on this file since 2725 was 2725, checked in by Bruno Cornec, 9 years ago
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File size: 13.0 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 */
14#include "libbb.h"
15#include "archive.h"
16
17/* GNU cpio 2.9 --help (abridged):
18
19 Modes:
20  -t, --list                 List the archive
21  -i, --extract              Extract files from an archive
22  -o, --create               Create the archive
23  -p, --pass-through         Copy-pass mode [was ist das?!]
24
25 Options valid in any mode:
26      --block-size=SIZE      I/O block size = SIZE * 512 bytes
27  -B                         I/O block size = 5120 bytes
28  -c                         Use the old portable (ASCII) archive format
29  -C, --io-size=NUMBER       I/O block size in bytes
30  -f, --nonmatching          Only copy files that do not match given pattern
31  -F, --file=FILE            Use FILE instead of standard input or output
32  -H, --format=FORMAT        Use given archive FORMAT
33  -M, --message=STRING       Print STRING when the end of a volume of the
34                             backup media is reached
35  -n, --numeric-uid-gid      If -v, show numeric UID and GID
36      --quiet                Do not print the number of blocks copied
37      --rsh-command=COMMAND  Use remote COMMAND instead of rsh
38  -v, --verbose              Verbosely list the files processed
39  -V, --dot                  Print a "." for each file processed
40  -W, --warning=FLAG         Control warning display: 'none','truncate','all';
41                             multiple options accumulate
42
43 Options valid only in --extract mode:
44  -b, --swap                 Swap both halfwords of words and bytes of
45                             halfwords in the data (equivalent to -sS)
46  -r, --rename               Interactively rename files
47  -s, --swap-bytes           Swap the bytes of each halfword in the files
48  -S, --swap-halfwords       Swap the halfwords of each word (4 bytes)
49      --to-stdout            Extract files to standard output
50  -E, --pattern-file=FILE    Read additional patterns specifying filenames to
51                             extract or list from FILE
52      --only-verify-crc      Verify CRC's, don't actually extract the files
53
54 Options valid only in --create mode:
55  -A, --append               Append to an existing archive
56  -O FILE                    File to use instead of standard output
57
58 Options valid only in --pass-through mode:
59  -l, --link                 Link files instead of copying them, when possible
60
61 Options valid in --extract and --create modes:
62      --absolute-filenames   Do not strip file system prefix components from
63                             the file names
64      --no-absolute-filenames Create all files relative to the current dir
65
66 Options valid in --create and --pass-through modes:
67  -0, --null                 A list of filenames is terminated by a NUL
68  -a, --reset-access-time    Reset the access times of files after reading them
69  -I FILE                    File to use instead of standard input
70  -L, --dereference          Dereference symbolic links (copy the files
71                             that they point to instead of copying the links)
72  -R, --owner=[USER][:.][GROUP] Set owner of created files
73
74 Options valid in --extract and --pass-through modes:
75  -d, --make-directories     Create leading directories where needed
76  -m, --preserve-modification-time  Retain mtime when creating files
77      --no-preserve-owner    Do not change the ownership of the files
78      --sparse               Write files with blocks of zeros as sparse files
79  -u, --unconditional        Replace all files unconditionally
80 */
81enum {
82    CPIO_OPT_EXTRACT            = (1 << 0),
83    CPIO_OPT_TEST               = (1 << 1),
84    CPIO_OPT_NUL_TERMINATED     = (1 << 2),
85    CPIO_OPT_UNCONDITIONAL      = (1 << 3),
86    CPIO_OPT_VERBOSE            = (1 << 4),
87    CPIO_OPT_CREATE_LEADING_DIR = (1 << 5),
88    CPIO_OPT_PRESERVE_MTIME     = (1 << 6),
89    CPIO_OPT_DEREF              = (1 << 7),
90    CPIO_OPT_FILE               = (1 << 8),
91    OPTBIT_FILE = 8,
92    IF_FEATURE_CPIO_O(OPTBIT_CREATE     ,)
93    IF_FEATURE_CPIO_O(OPTBIT_FORMAT     ,)
94    IF_FEATURE_CPIO_P(OPTBIT_PASSTHROUGH,)
95    IF_LONG_OPTS(     OPTBIT_QUIET      ,)
96    IF_LONG_OPTS(     OPTBIT_2STDOUT    ,)
97    CPIO_OPT_CREATE             = IF_FEATURE_CPIO_O((1 << OPTBIT_CREATE     )) + 0,
98    CPIO_OPT_FORMAT             = IF_FEATURE_CPIO_O((1 << OPTBIT_FORMAT     )) + 0,
99    CPIO_OPT_PASSTHROUGH        = IF_FEATURE_CPIO_P((1 << OPTBIT_PASSTHROUGH)) + 0,
100    CPIO_OPT_QUIET              = IF_LONG_OPTS(     (1 << OPTBIT_QUIET      )) + 0,
101    CPIO_OPT_2STDOUT            = IF_LONG_OPTS(     (1 << OPTBIT_2STDOUT    )) + 0,
102};
103
104#define OPTION_STR "it0uvdmLF:"
105
106#if ENABLE_FEATURE_CPIO_O
107static off_t cpio_pad4(off_t size)
108{
109    int i;
110
111    i = (- size) & 3;
112    size += i;
113    while (--i >= 0)
114        bb_putchar('\0');
115    return size;
116}
117
118/* Return value will become exit code.
119 * It's ok to exit instead of return. */
120static NOINLINE int cpio_o(void)
121{
122    static const char trailer[] ALIGN1 = "TRAILER!!!";
123    struct name_s {
124        struct name_s *next;
125        char name[1];
126    };
127    struct inodes_s {
128        struct inodes_s *next;
129        struct name_s *names;
130        struct stat st;
131    };
132
133    struct inodes_s *links = NULL;
134    off_t bytes = 0; /* output bytes count */
135
136    while (1) {
137        const char *name;
138        char *line;
139        struct stat st;
140
141        line = (option_mask32 & CPIO_OPT_NUL_TERMINATED)
142                ? bb_get_chunk_from_file(stdin, NULL)
143                : xmalloc_fgetline(stdin);
144
145        if (line) {
146            /* Strip leading "./[./]..." from the filename */
147            name = line;
148            while (name[0] == '.' && name[1] == '/') {
149                while (*++name == '/')
150                    continue;
151            }
152            if (!*name) { /* line is empty */
153                free(line);
154                continue;
155            }
156            if ((option_mask32 & CPIO_OPT_DEREF)
157                    ? stat(name, &st)
158                    : lstat(name, &st)
159            ) {
160 abort_cpio_o:
161                bb_simple_perror_msg_and_die(name);
162            }
163
164            if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode)))
165                st.st_size = 0; /* paranoia */
166
167            /* Store hardlinks for later processing, dont output them */
168            if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) {
169                struct name_s *n;
170                struct inodes_s *l;
171
172                /* Do we have this hardlink remembered? */
173                l = links;
174                while (1) {
175                    if (l == NULL) {
176                        /* Not found: add new item to "links" list */
177                        l = xzalloc(sizeof(*l));
178                        l->st = st;
179                        l->next = links;
180                        links = l;
181                        break;
182                    }
183                    if (l->st.st_ino == st.st_ino) {
184                        /* found */
185                        break;
186                    }
187                    l = l->next;
188                }
189                /* Add new name to "l->names" list */
190                n = xmalloc(sizeof(*n) + strlen(name));
191                strcpy(n->name, name);
192                n->next = l->names;
193                l->names = n;
194
195                free(line);
196                continue;
197            }
198
199        } else { /* line == NULL: EOF */
200 next_link:
201            if (links) {
202                /* Output hardlink's data */
203                st = links->st;
204                name = links->names->name;
205                links->names = links->names->next;
206                /* GNU cpio is reported to emit file data
207                 * only for the last instance. Mimic that. */
208                if (links->names == NULL)
209                    links = links->next;
210                else
211                    st.st_size = 0;
212                /* NB: we leak links->names and/or links,
213                 * this is intended (we exit soon anyway) */
214            } else {
215                /* If no (more) hardlinks to output,
216                 * output "trailer" entry */
217                name = trailer;
218                /* st.st_size == 0 is a must, but for uniformity
219                 * in the output, we zero out everything */
220                memset(&st, 0, sizeof(st));
221                /* st.st_nlink = 1; - GNU cpio does this */
222            }
223        }
224
225        bytes += printf("070701"
226                        "%08X%08X%08X%08X%08X%08X%08X"
227                        "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */
228                /* strlen+1: */ "%08X"
229                /* chksum: */   "00000000" /* (only for "070702" files) */
230                /* name,NUL: */ "%s%c",
231                        (unsigned)(uint32_t) st.st_ino,
232                        (unsigned)(uint32_t) st.st_mode,
233                        (unsigned)(uint32_t) st.st_uid,
234                        (unsigned)(uint32_t) st.st_gid,
235                        (unsigned)(uint32_t) st.st_nlink,
236                        (unsigned)(uint32_t) st.st_mtime,
237                        (unsigned)(uint32_t) st.st_size,
238                        (unsigned)(uint32_t) major(st.st_dev),
239                        (unsigned)(uint32_t) minor(st.st_dev),
240                        (unsigned)(uint32_t) major(st.st_rdev),
241                        (unsigned)(uint32_t) minor(st.st_rdev),
242                        (unsigned)(strlen(name) + 1),
243                        name, '\0');
244        bytes = cpio_pad4(bytes);
245
246        if (st.st_size) {
247            if (S_ISLNK(st.st_mode)) {
248                char *lpath = xmalloc_readlink_or_warn(name);
249                if (!lpath)
250                    goto abort_cpio_o;
251                bytes += printf("%s", lpath);
252                free(lpath);
253            } else { /* S_ISREG */
254                int fd = xopen(name, O_RDONLY);
255                fflush_all();
256                /* We must abort if file got shorter too! */
257                bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size);
258                bytes += st.st_size;
259                close(fd);
260            }
261            bytes = cpio_pad4(bytes);
262        }
263
264        if (!line) {
265            if (name != trailer)
266                goto next_link;
267            /* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */
268            return EXIT_SUCCESS;
269        }
270
271        free(line);
272    } /* end of "while (1)" */
273}
274#endif
275
276int cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
277int cpio_main(int argc UNUSED_PARAM, char **argv)
278{
279    archive_handle_t *archive_handle;
280    char *cpio_filename;
281    IF_FEATURE_CPIO_O(const char *cpio_fmt = "";)
282    unsigned opt;
283
284#if ENABLE_LONG_OPTS
285    applet_long_options =
286        "extract\0"      No_argument       "i"
287        "list\0"         No_argument       "t"
288#if ENABLE_FEATURE_CPIO_O
289        "create\0"       No_argument       "o"
290        "format\0"       Required_argument "H"
291#if ENABLE_FEATURE_CPIO_P
292        "pass-through\0" No_argument       "p"
293#endif
294#endif
295        "verbose\0"      No_argument       "v"
296        "quiet\0"        No_argument       "\xff"
297        "to-stdout\0"    No_argument       "\xfe"
298        ;
299#endif
300
301    archive_handle = init_handle();
302    /* archive_handle->src_fd = STDIN_FILENO; - done by init_handle */
303    archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER;
304
305    /* As of now we do not enforce this: */
306    /* -i,-t,-o,-p are mutually exclusive */
307    /* -u,-d,-m make sense only with -i or -p */
308    /* -L makes sense only with -o or -p */
309
310#if !ENABLE_FEATURE_CPIO_O
311    opt = getopt32(argv, OPTION_STR, &cpio_filename);
312    argv += optind;
313    if (opt & CPIO_OPT_FILE) { /* -F */
314        xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
315    }
316#else
317    opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), &cpio_filename, &cpio_fmt);
318    argv += optind;
319    if ((opt & (CPIO_OPT_FILE|CPIO_OPT_CREATE)) == CPIO_OPT_FILE) { /* -F without -o */
320        xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
321    }
322    if (opt & CPIO_OPT_PASSTHROUGH) {
323        pid_t pid;
324        struct fd_pair pp;
325
326        if (argv[0] == NULL)
327            bb_show_usage();
328        if (opt & CPIO_OPT_CREATE_LEADING_DIR)
329            mkdir(argv[0], 0777);
330        /* Crude existence check:
331         * close(xopen(argv[0], O_RDONLY | O_DIRECTORY));
332         * We can also xopen, fstat, IS_DIR, later fchdir.
333         * This would check for existence earlier and cleaner.
334         * As it stands now, if we fail xchdir later,
335         * child dies on EPIPE, unless it caught
336         * a diffrerent problem earlier.
337         * This is good enough for now.
338         */
339#if !BB_MMU
340        pp.rd = 3;
341        pp.wr = 4;
342        if (!re_execed) {
343            close(3);
344            close(4);
345            xpiped_pair(pp);
346        }
347#else
348        xpiped_pair(pp);
349#endif
350        pid = fork_or_rexec(argv - optind);
351        if (pid == 0) { /* child */
352            close(pp.rd);
353            xmove_fd(pp.wr, STDOUT_FILENO);
354            goto dump;
355        }
356        /* parent */
357        xchdir(*argv++);
358        close(pp.wr);
359        xmove_fd(pp.rd, STDIN_FILENO);
360        //opt &= ~CPIO_OPT_PASSTHROUGH;
361        opt |= CPIO_OPT_EXTRACT;
362        goto skip;
363    }
364    /* -o */
365    if (opt & CPIO_OPT_CREATE) {
366        if (cpio_fmt[0] != 'n') /* we _require_ "-H newc" */
367            bb_show_usage();
368        if (opt & CPIO_OPT_FILE) {
369            xmove_fd(xopen(cpio_filename, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
370        }
371 dump:
372        return cpio_o();
373    }
374 skip:
375#endif
376
377    /* One of either extract or test options must be given */
378    if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) {
379        bb_show_usage();
380    }
381
382    if (opt & CPIO_OPT_TEST) {
383        /* if both extract and test options are given, ignore extract option */
384        opt &= ~CPIO_OPT_EXTRACT;
385        archive_handle->action_header = header_list;
386    }
387    if (opt & CPIO_OPT_EXTRACT) {
388        archive_handle->action_data = data_extract_all;
389        if (opt & CPIO_OPT_2STDOUT)
390            archive_handle->action_data = data_extract_to_stdout;
391    }
392    if (opt & CPIO_OPT_UNCONDITIONAL) {
393        archive_handle->ah_flags |= ARCHIVE_UNLINK_OLD;
394        archive_handle->ah_flags &= ~ARCHIVE_EXTRACT_NEWER;
395    }
396    if (opt & CPIO_OPT_VERBOSE) {
397        if (archive_handle->action_header == header_list) {
398            archive_handle->action_header = header_verbose_list;
399        } else {
400            archive_handle->action_header = header_list;
401        }
402    }
403    if (opt & CPIO_OPT_CREATE_LEADING_DIR) {
404        archive_handle->ah_flags |= ARCHIVE_CREATE_LEADING_DIRS;
405    }
406    if (opt & CPIO_OPT_PRESERVE_MTIME) {
407        archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
408    }
409
410    while (*argv) {
411        archive_handle->filter = filter_accept_list;
412        llist_add_to(&archive_handle->accept, *argv);
413        argv++;
414    }
415
416    /* see get_header_cpio */
417    archive_handle->cpio__blocks = (off_t)-1;
418    while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
419        continue;
420
421    if (archive_handle->cpio__blocks != (off_t)-1
422     && !(opt & CPIO_OPT_QUIET)
423    ) {
424        fprintf(stderr, "%"OFF_FMT"u blocks\n", archive_handle->cpio__blocks);
425    }
426
427    return EXIT_SUCCESS;
428}
Note: See TracBrowser for help on using the repository browser.