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