busybox/archival/ar.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini ar implementation for busybox
   4 *
   5 * Copyright (C) 2000 by Glenn McGrath
   6 *
   7 * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
   8 *
   9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  10 *
  11 * Archive creation support:
  12 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
  13 * Written by Alexander Shishkin.
  14 *
  15 * There is no single standard to adhere to so ar may not portable
  16 * between different systems
  17 * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
  18 */
  19
  20//config:config AR
  21//config:       bool "ar"
  22//config:       default n  # needs to be improved to be able to replace binutils ar
  23//config:       help
  24//config:         ar is an archival utility program used to create, modify, and
  25//config:         extract contents from archives. An archive is a single file holding
  26//config:         a collection of other files in a structure that makes it possible to
  27//config:         retrieve the original individual files (called archive members).
  28//config:         The original files' contents, mode (permissions), timestamp, owner,
  29//config:         and group are preserved in the archive, and can be restored on
  30//config:         extraction.
  31//config:
  32//config:         The stored filename is limited to 15 characters. (for more information
  33//config:         see long filename support).
  34//config:         ar has 60 bytes of overheads for every stored file.
  35//config:
  36//config:         This implementation of ar can extract archives, it cannot create or
  37//config:         modify them.
  38//config:         On an x86 system, the ar applet adds about 1K.
  39//config:
  40//config:         Unless you have a specific application which requires ar, you should
  41//config:         probably say N here.
  42//config:
  43//config:config FEATURE_AR_LONG_FILENAMES
  44//config:       bool "Support for long filenames (not needed for debs)"
  45//config:       default y
  46//config:       depends on AR
  47//config:       help
  48//config:         By default the ar format can only store the first 15 characters
  49//config:         of the filename, this option removes that limitation.
  50//config:         It supports the GNU ar long filename method which moves multiple long
  51//config:         filenames into a the data section of a new ar entry.
  52//config:
  53//config:config FEATURE_AR_CREATE
  54//config:       bool "Support archive creation"
  55//config:       default y
  56//config:       depends on AR
  57//config:       help
  58//config:         This enables archive creation (-c and -r) with busybox ar.
  59
  60//applet:IF_AR(APPLET(ar, BB_DIR_USR_BIN, BB_SUID_DROP))
  61//kbuild:lib-$(CONFIG_AR) += ar.o
  62
  63//usage:#define ar_trivial_usage
  64//usage:       "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES"
  65//usage:#define ar_full_usage "\n\n"
  66//usage:       "Extract or list FILES from an ar archive\n"
  67//usage:     "\n        -o      Preserve original dates"
  68//usage:     "\n        -p      Extract to stdout"
  69//usage:     "\n        -t      List"
  70//usage:     "\n        -x      Extract"
  71//usage:     "\n        -v      Verbose"
  72
  73#include "libbb.h"
  74#include "bb_archive.h"
  75#include "ar.h"
  76
  77#if ENABLE_FEATURE_AR_CREATE
  78/* filter out entries with same names as specified on the command line */
  79static char FAST_FUNC filter_replaceable(archive_handle_t *handle)
  80{
  81        if (find_list_entry(handle->accept, handle->file_header->name))
  82                return EXIT_FAILURE;
  83
  84        return EXIT_SUCCESS;
  85}
  86
  87static void output_ar_header(archive_handle_t *handle)
  88{
  89        /* GNU ar 2.19.51.0.14 creates malformed archives
  90         * if input files are >10G. It also truncates files >4GB
  91         * (uses "size mod 4G"). We abort in this case:
  92         * We could add support for up to 10G files, but this is unlikely to be useful.
  93         * Note that unpacking side limits all fields to "unsigned int" data type,
  94         * and treats "all ones" as an error indicator. Thus max we allow here is UINT_MAX-1.
  95         */
  96        enum {
  97                /* for 2nd field: mtime */
  98                MAX11CHARS = UINT_MAX > 0xffffffff ? (unsigned)99999999999 : UINT_MAX-1,
  99                /* for last field: filesize */
 100                MAX10CHARS = UINT_MAX > 0xffffffff ? (unsigned)9999999999 : UINT_MAX-1,
 101        };
 102
 103        struct file_header_t *fh = handle->file_header;
 104
 105        if (handle->offset & 1) {
 106                xwrite(handle->src_fd, "\n", 1);
 107                handle->offset++;
 108        }
 109
 110        /* Careful! The widths should be exact. Fields must be separated */
 111        if (sizeof(off_t) > 4 && fh->size > (off_t)MAX10CHARS) {
 112                bb_error_msg_and_die("'%s' is bigger than ar can handle", fh->name);
 113        }
 114        fdprintf(handle->src_fd, "%-16.16s%-12lu%-6u%-6u%-8o%-10"OFF_FMT"u`\n",
 115                        fh->name,
 116                        (sizeof(time_t) > 4 && fh->mtime > MAX11CHARS) ? (long)0 : (long)fh->mtime,
 117                        fh->uid > 99999 ? 0 : (int)fh->uid,
 118                        fh->gid > 99999 ? 0 : (int)fh->gid,
 119                        (int)fh->mode & 07777777,
 120                        fh->size
 121        );
 122
 123        handle->offset += AR_HEADER_LEN;
 124}
 125
 126/*
 127 * when replacing files in an existing archive, copy from the
 128 * original archive those files that are to be left intact
 129 */
 130static void FAST_FUNC copy_data(archive_handle_t *handle)
 131{
 132        archive_handle_t *out_handle = handle->ar__out;
 133        struct file_header_t *fh = handle->file_header;
 134
 135        out_handle->file_header = fh;
 136        output_ar_header(out_handle);
 137
 138        bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size);
 139        out_handle->offset += fh->size;
 140}
 141
 142static int write_ar_header(archive_handle_t *handle)
 143{
 144        char *fn;
 145        char fn_h[17]; /* 15 + "/" + NUL */
 146        struct stat st;
 147        int fd;
 148
 149        fn = llist_pop(&handle->accept);
 150        if (!fn)
 151                return -1;
 152
 153        xstat(fn, &st);
 154
 155        handle->file_header->mtime = st.st_mtime;
 156        handle->file_header->uid = st.st_uid;
 157        handle->file_header->gid = st.st_gid;
 158        handle->file_header->mode = st.st_mode;
 159        handle->file_header->size = st.st_size;
 160        handle->file_header->name = fn_h;
 161//TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES...
 162        sprintf(fn_h, "%.15s/", bb_basename(fn));
 163
 164        output_ar_header(handle);
 165
 166        fd = xopen(fn, O_RDONLY);
 167        bb_copyfd_exact_size(fd, handle->src_fd, st.st_size);
 168        close(fd);
 169        handle->offset += st.st_size;
 170
 171        return 0;
 172}
 173
 174static int write_ar_archive(archive_handle_t *handle)
 175{
 176        struct stat st;
 177        archive_handle_t *out_handle;
 178
 179        xfstat(handle->src_fd, &st, handle->ar__name);
 180
 181        /* if archive exists, create a new handle for output.
 182         * we create it in place of the old one.
 183         */
 184        if (st.st_size != 0) {
 185                out_handle = init_handle();
 186                xunlink(handle->ar__name);
 187                out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC);
 188                out_handle->accept = handle->accept;
 189        } else {
 190                out_handle = handle;
 191        }
 192
 193        handle->ar__out = out_handle;
 194
 195        xwrite(out_handle->src_fd, AR_MAGIC "\n", AR_MAGIC_LEN + 1);
 196        out_handle->offset += AR_MAGIC_LEN + 1;
 197
 198        /* skip to the end of the archive if we have to append stuff */
 199        if (st.st_size != 0) {
 200                handle->filter = filter_replaceable;
 201                handle->action_data = copy_data;
 202                unpack_ar_archive(handle);
 203        }
 204
 205        while (write_ar_header(out_handle) == 0)
 206                continue;
 207
 208        /* optional, since we exit right after we return */
 209        if (ENABLE_FEATURE_CLEAN_UP) {
 210                close(handle->src_fd);
 211                if (out_handle->src_fd != handle->src_fd)
 212                        close(out_handle->src_fd);
 213        }
 214
 215        return EXIT_SUCCESS;
 216}
 217#endif /* FEATURE_AR_CREATE */
 218
 219static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header)
 220{
 221        const char *mode = bb_mode_string(file_header->mode);
 222        char *mtime;
 223
 224        mtime = ctime(&file_header->mtime);
 225        mtime[16] = ' ';
 226        memmove(&mtime[17], &mtime[20], 4);
 227        mtime[21] = '\0';
 228        printf("%s %u/%u%7"OFF_FMT"u %s %s\n", &mode[1],
 229                        (int)file_header->uid, (int)file_header->gid,
 230                        file_header->size,
 231                        &mtime[4], file_header->name
 232        );
 233}
 234
 235#define AR_OPT_VERBOSE          (1 << 0)
 236#define AR_OPT_PRESERVE_DATE    (1 << 1)
 237/* "ar r" implies create, but warns about it. c suppresses warning.
 238 * bbox accepts but ignores it: */
 239#define AR_OPT_CREATE           (1 << 2)
 240
 241#define AR_CMD_PRINT            (1 << 3)
 242#define FIRST_CMD               AR_CMD_PRINT
 243#define AR_CMD_LIST             (1 << 4)
 244#define AR_CMD_EXTRACT          (1 << 5)
 245#define AR_CMD_INSERT           (1 << 6)
 246
 247int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 248int ar_main(int argc UNUSED_PARAM, char **argv)
 249{
 250        archive_handle_t *archive_handle;
 251        unsigned opt, t;
 252
 253        archive_handle = init_handle();
 254
 255        /* --: prepend '-' to the first argument if required */
 256        /* -1: at least one param is reqd */
 257        /* one of p,t,x[,r] is required */
 258        opt_complementary = "--:-1:p:t:x"IF_FEATURE_AR_CREATE(":r");
 259        opt = getopt32(argv, "voc""ptx"IF_FEATURE_AR_CREATE("r"));
 260        argv += optind;
 261
 262        t = opt / FIRST_CMD;
 263        if (t & (t-1)) /* more than one of p,t,x[,r] are specified */
 264                bb_show_usage();
 265
 266        if (opt & AR_CMD_PRINT) {
 267                archive_handle->action_data = data_extract_to_stdout;
 268        }
 269        if (opt & AR_CMD_LIST) {
 270                archive_handle->action_header = header_list;
 271        }
 272        if (opt & AR_CMD_EXTRACT) {
 273                archive_handle->action_data = data_extract_all;
 274        }
 275        if (opt & AR_OPT_PRESERVE_DATE) {
 276                archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
 277        }
 278        if (opt & AR_OPT_VERBOSE) {
 279                archive_handle->action_header = header_verbose_list_ar;
 280        }
 281#if ENABLE_FEATURE_AR_CREATE
 282        archive_handle->ar__name = *argv;
 283#endif
 284        archive_handle->src_fd = xopen(*argv++,
 285                        (opt & AR_CMD_INSERT)
 286                                ? O_RDWR | O_CREAT
 287                                : O_RDONLY
 288        );
 289
 290        if (*argv)
 291                archive_handle->filter = filter_accept_list;
 292        while (*argv) {
 293                llist_add_to_end(&archive_handle->accept, *argv++);
 294        }
 295
 296#if ENABLE_FEATURE_AR_CREATE
 297        if (opt & AR_CMD_INSERT)
 298                return write_ar_archive(archive_handle);
 299#endif
 300
 301        unpack_ar_archive(archive_handle);
 302
 303        return EXIT_SUCCESS;
 304}
 305