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