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 tarball for details.
  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#include "libbb.h"
  21#include "unarchive.h"
  22#include "ar.h"
  23
  24#if ENABLE_FEATURE_AR_CREATE
  25/* filter out entries with same names as specified on the command line */
  26static char FAST_FUNC filter_replaceable(archive_handle_t *handle)
  27{
  28        if (find_list_entry(handle->accept, handle->file_header->name))
  29                return EXIT_FAILURE;
  30
  31        return EXIT_SUCCESS;
  32}
  33
  34static void output_ar_header(archive_handle_t *handle)
  35{
  36        /* GNU ar 2.19.51.0.14 creates malformed archives
  37         * if input files are >10G. It also truncates files >4GB
  38         * (uses "size mod 4G"). We abort in this case:
  39         * We could add support for up to 10G files, but this is unlikely to be useful.
  40         * Note that unpacking side limits all fields to "unsigned int" data type,
  41         * and treats "all ones" as an error indicator. Thus max we allow here is UINT_MAX-1.
  42         */
  43        enum {
  44                /* for 2nd field: mtime */
  45                MAX11CHARS = UINT_MAX > 0xffffffff ? (unsigned)99999999999 : UINT_MAX-1,
  46                /* for last field: filesize */
  47                MAX10CHARS = UINT_MAX > 0xffffffff ? (unsigned)9999999999 : UINT_MAX-1,
  48        };
  49
  50        struct file_header_t *fh = handle->file_header;
  51
  52        if (handle->offset & 1) {
  53                xwrite(handle->src_fd, "\n", 1);
  54                handle->offset++;
  55        }
  56
  57        /* Careful! The widths should be exact. Fields must be separated */
  58        if (sizeof(off_t) > 4 && fh->size > (off_t)MAX10CHARS) {
  59                bb_error_msg_and_die("'%s' is bigger than ar can handle", fh->name);
  60        }
  61        fdprintf(handle->src_fd, "%-16.16s%-12lu%-6u%-6u%-8o%-10"OFF_FMT"u`\n",
  62                        fh->name,
  63                        (sizeof(time_t) > 4 && fh->mtime > MAX11CHARS) ? (long)0 : (long)fh->mtime,
  64                        fh->uid > 99999 ? 0 : (int)fh->uid,
  65                        fh->gid > 99999 ? 0 : (int)fh->gid,
  66                        (int)fh->mode & 07777777,
  67                        fh->size
  68        );
  69
  70        handle->offset += AR_HEADER_LEN;
  71}
  72
  73/*
  74 * when replacing files in an existing archive, copy from the the
  75 * original archive those files that are to be left intact
  76 */
  77static void FAST_FUNC copy_data(archive_handle_t *handle)
  78{
  79        archive_handle_t *out_handle = handle->ar__out;
  80        struct file_header_t *fh = handle->file_header;
  81
  82        out_handle->file_header = fh;
  83        output_ar_header(out_handle);
  84
  85        bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size);
  86        out_handle->offset += fh->size;
  87}
  88
  89static int write_ar_header(archive_handle_t *handle)
  90{
  91        char *fn;
  92        char fn_h[17]; /* 15 + "/" + NUL */
  93        struct stat st;
  94        int fd;
  95
  96        fn = llist_pop(&handle->accept);
  97        if (!fn)
  98                return -1;
  99
 100        xstat(fn, &st);
 101
 102        handle->file_header->mtime = st.st_mtime;
 103        handle->file_header->uid = st.st_uid;
 104        handle->file_header->gid = st.st_gid;
 105        handle->file_header->mode = st.st_mode;
 106        handle->file_header->size = st.st_size;
 107        handle->file_header->name = fn_h;
 108//TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES...
 109        sprintf(fn_h, "%.15s/", bb_basename(fn));
 110
 111        output_ar_header(handle);
 112
 113        fd = xopen(fn, O_RDONLY);
 114        bb_copyfd_exact_size(fd, handle->src_fd, st.st_size);
 115        close(fd);
 116        handle->offset += st.st_size;
 117
 118        return 0;
 119}
 120
 121static int write_ar_archive(archive_handle_t *handle)
 122{
 123        struct stat st;
 124        archive_handle_t *out_handle;
 125
 126        if (fstat(handle->src_fd, &st) == -1)
 127                bb_simple_perror_msg_and_die(handle->ar__name);
 128
 129        /* if archive exists, create a new handle for output.
 130         * we create it in place of the old one.
 131         */
 132        if (st.st_size != 0) {
 133                out_handle = init_handle();
 134                xunlink(handle->ar__name);
 135                out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC);
 136                out_handle->accept = handle->accept;
 137        } else {
 138                out_handle = handle;
 139        }
 140
 141        handle->ar__out = out_handle;
 142
 143        xwrite(out_handle->src_fd, AR_MAGIC "\n", AR_MAGIC_LEN + 1);
 144        out_handle->offset += AR_MAGIC_LEN + 1;
 145
 146        /* skip to the end of the archive if we have to append stuff */
 147        if (st.st_size != 0) {
 148                handle->filter = filter_replaceable;
 149                handle->action_data = copy_data;
 150                unpack_ar_archive(handle);
 151        }
 152
 153        while (write_ar_header(out_handle) == 0)
 154                continue;
 155
 156        /* optional, since we exit right after we return */
 157        if (ENABLE_FEATURE_CLEAN_UP) {
 158                close(handle->src_fd);
 159                if (out_handle->src_fd != handle->src_fd)
 160                        close(out_handle->src_fd);
 161        }
 162
 163        return EXIT_SUCCESS;
 164}
 165#endif /* FEATURE_AR_CREATE */
 166
 167static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header)
 168{
 169        const char *mode = bb_mode_string(file_header->mode);
 170        char *mtime;
 171
 172        mtime = ctime(&file_header->mtime);
 173        mtime[16] = ' ';
 174        memmove(&mtime[17], &mtime[20], 4);
 175        mtime[21] = '\0';
 176        printf("%s %u/%u%7"OFF_FMT"u %s %s\n", &mode[1],
 177                        (int)file_header->uid, (int)file_header->gid,
 178                        file_header->size,
 179                        &mtime[4], file_header->name
 180        );
 181}
 182
 183#define AR_OPT_VERBOSE          (1 << 0)
 184#define AR_OPT_PRESERVE_DATE    (1 << 1)
 185/* "ar r" implies create, but warns about it. c suppresses warning.
 186 * bbox accepts but ignores it: */
 187#define AR_OPT_CREATE           (1 << 2)
 188
 189#define AR_CMD_PRINT            (1 << 3)
 190#define FIRST_CMD AR_CMD_PRINT
 191#define AR_CMD_LIST             (1 << 4)
 192#define AR_CMD_EXTRACT          (1 << 5)
 193#define AR_CMD_INSERT           (1 << 6)
 194
 195int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 196int ar_main(int argc UNUSED_PARAM, char **argv)
 197{
 198        archive_handle_t *archive_handle;
 199        unsigned opt, t;
 200
 201        archive_handle = init_handle();
 202
 203        /* --: prepend '-' to the first argument if required */
 204        /* -1: at least one param is reqd */
 205        /* one of p,t,x[,r] is required */
 206        opt_complementary = "--:-1:p:t:x"IF_FEATURE_AR_CREATE(":r");
 207        opt = getopt32(argv, "voc""ptx"IF_FEATURE_AR_CREATE("r"));
 208        argv += optind;
 209
 210        t = opt / FIRST_CMD;
 211        if (t & (t-1)) /* more than one of p,t,x[,r] are specified */
 212                bb_show_usage();
 213
 214        if (opt & AR_CMD_PRINT) {
 215                archive_handle->action_data = data_extract_to_stdout;
 216        }
 217        if (opt & AR_CMD_LIST) {
 218                archive_handle->action_header = header_list;
 219        }
 220        if (opt & AR_CMD_EXTRACT) {
 221                archive_handle->action_data = data_extract_all;
 222        }
 223        if (opt & AR_OPT_PRESERVE_DATE) {
 224                archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
 225        }
 226        if (opt & AR_OPT_VERBOSE) {
 227                archive_handle->action_header = header_verbose_list_ar;
 228        }
 229#if ENABLE_FEATURE_AR_CREATE
 230        archive_handle->ar__name = *argv;
 231#endif
 232        archive_handle->src_fd = xopen(*argv++,
 233                        (opt & AR_CMD_INSERT)
 234                                ? O_RDWR | O_CREAT
 235                                : O_RDONLY
 236        );
 237
 238        if (*argv)
 239                archive_handle->filter = filter_accept_list;
 240        while (*argv) {
 241                llist_add_to_end(&archive_handle->accept, *argv++);
 242        }
 243
 244#if ENABLE_FEATURE_AR_CREATE
 245        if (opt & AR_CMD_INSERT)
 246                return write_ar_archive(archive_handle);
 247#endif
 248
 249        unpack_ar_archive(archive_handle);
 250
 251        return EXIT_SUCCESS;
 252}
 253