~akopytov/percona-xtrabackup/bug1166888-2.1

« back to all changes in this revision

Viewing changes to src/libarchive/tar/read.c

  • Committer: Alexey Kopytov
  • Date: 2012-02-10 20:05:56 UTC
  • mto: (391.1.5 staging)
  • mto: This revision was merged to the branch mainline in revision 390.
  • Revision ID: akopytov@gmail.com-20120210200556-6kx41z8wwrqfucro
Rebase of the parallel compression patch on new trunk + post-review
fixes.

Implementation of parallel compression and streaming for XtraBackup.

This revision implements the following changes:

* InnoDB files are now streamed by the xtrabackup binary rather than
innobackupex. As a result, integrity is now verified by xtrabackup and
thus tar4ibd is no longer needed, so it was removed.

* xtrabackup binary now accepts the new '--stream' option which has
exactly the same semantics as the '--stream' option in
innobackupex: it tells xtrabackup to stream all files to the standard
output in the specified format rather than storing them locally.

* The xtrabackup binary can now do parallel compression using the
quicklz library. Two new options were added to xtrabackup to support
this feature:

- '--compress' tells xtrabackup to compress all output data, including
the transaction log file and meta data files, using the specified
compression algorithm. The only currently supported algorithm is
'quicklz'. The resulting files have the qpress archive format,
i.e. every *.qp file produced by xtrabackup is essentially a one-file
qpress archive and can be extracted and uncompressed by the qpress
file archiver (http://www.quicklz.com/).

- '--compress-threads' specifies the number of worker threads used by
xtrabackup for parallel data compression. This option defaults to 1.

Parallel compression ('--compress-threads') can be used together with
parallel file copying ('--parallel'). For example, '--parallel=4
--compress --compress-threads=2' will create 4 IO threads that will
read the data and pipe it to 2 compression threads.

* To support simultaneous compression and streaming, a new custom
streaming format called 'xbstream' was introduced to XtraBackup in
addition to the 'tar' format. That was required to overcome some
limitations of traditional archive formats such as 'tar', 'cpio' and
others that do not allow streaming dynamically generated files, for
example dynamically compressed files.  Other advantages of xbstream over
traditional streaming/archive formats include ability to stream multiple
files concurrently (so it is possible to use streaming in the xbstream
format together with the --parallel option) and more compact data
storage.

* To allow streaming and extracting files to/from the xbstream format
produced by xtrabackup, a new utility aptly called 'xbstream' was
added to the XtraBackup distribution. This utility has a tar-like
interface:

- with the '-x' option it extracts files from the stream read from its
standard input to the current directory unless specified otherwise
with the '-C' option.

- with the '-c' option it streams files specified on the command line
to its standard output.

The utility also tries to minimize its impact on the OS page cache by
using the appropriate posix_fadvise() calls when available.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-
 
2
 * Copyright (c) 2003-2007 Tim Kientzle
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 * 1. Redistributions of source code must retain the above copyright
 
9
 *    notice, this list of conditions and the following disclaimer.
 
10
 * 2. Redistributions in binary form must reproduce the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer in the
 
12
 *    documentation and/or other materials provided with the distribution.
 
13
 *
 
14
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 
15
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
16
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
17
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 
18
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
19
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
20
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
21
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
22
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
23
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
24
 */
 
25
 
 
26
#include "bsdtar_platform.h"
 
27
__FBSDID("$FreeBSD: src/usr.bin/tar/read.c,v 1.40 2008/08/21 06:41:14 kientzle Exp $");
 
28
 
 
29
#ifdef HAVE_SYS_TYPES_H
 
30
#include <sys/types.h>
 
31
#endif
 
32
#ifdef HAVE_SYS_PARAM_H
 
33
#include <sys/param.h>
 
34
#endif
 
35
#ifdef HAVE_SYS_STAT_H
 
36
#include <sys/stat.h>
 
37
#endif
 
38
 
 
39
#ifdef HAVE_ERRNO_H
 
40
#include <errno.h>
 
41
#endif
 
42
#ifdef HAVE_GRP_H
 
43
#include <grp.h>
 
44
#endif
 
45
#ifdef HAVE_LIMITS_H
 
46
#include <limits.h>
 
47
#endif
 
48
#ifdef HAVE_PWD_H
 
49
#include <pwd.h>
 
50
#endif
 
51
#ifdef HAVE_STDINT_H
 
52
#include <stdint.h>
 
53
#endif
 
54
#include <stdio.h>
 
55
#ifdef HAVE_STDLIB_H
 
56
#include <stdlib.h>
 
57
#endif
 
58
#ifdef HAVE_STRING_H
 
59
#include <string.h>
 
60
#endif
 
61
#ifdef HAVE_TIME_H
 
62
#include <time.h>
 
63
#endif
 
64
#ifdef HAVE_UNISTD_H
 
65
#include <unistd.h>
 
66
#endif
 
67
 
 
68
#include "bsdtar.h"
 
69
#include "err.h"
 
70
 
 
71
struct progress_data {
 
72
        struct bsdtar *bsdtar;
 
73
        struct archive *archive;
 
74
        struct archive_entry *entry;
 
75
};
 
76
 
 
77
static void     list_item_verbose(struct bsdtar *, FILE *,
 
78
                    struct archive_entry *);
 
79
static void     read_archive(struct bsdtar *bsdtar, char mode);
 
80
 
 
81
void
 
82
tar_mode_t(struct bsdtar *bsdtar)
 
83
{
 
84
        read_archive(bsdtar, 't');
 
85
        if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
 
86
                bsdtar->return_value = 1;
 
87
}
 
88
 
 
89
void
 
90
tar_mode_x(struct bsdtar *bsdtar)
 
91
{
 
92
        read_archive(bsdtar, 'x');
 
93
 
 
94
        if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0)
 
95
                bsdtar->return_value = 1;
 
96
}
 
97
 
 
98
static void
 
99
progress_func(void *cookie)
 
100
{
 
101
        struct progress_data *progress_data = cookie;
 
102
        struct bsdtar *bsdtar = progress_data->bsdtar;
 
103
        struct archive *a = progress_data->archive;
 
104
        struct archive_entry *entry = progress_data->entry;
 
105
        uint64_t comp, uncomp;
 
106
 
 
107
        if (!need_report())
 
108
                return;
 
109
 
 
110
        if (bsdtar->verbose)
 
111
                fprintf(stderr, "\n");
 
112
        if (a != NULL) {
 
113
                comp = archive_position_compressed(a);
 
114
                uncomp = archive_position_uncompressed(a);
 
115
                fprintf(stderr,
 
116
                    "In: %s bytes, compression %d%%;",
 
117
                    tar_i64toa(comp), (int)((uncomp - comp) * 100 / uncomp));
 
118
                fprintf(stderr, "  Out: %d files, %s bytes\n",
 
119
                    archive_file_count(a), tar_i64toa(uncomp));
 
120
        }
 
121
        if (entry != NULL) {
 
122
                safe_fprintf(stderr, "Current: %s",
 
123
                    archive_entry_pathname(entry));
 
124
                fprintf(stderr, " (%s bytes)\n",
 
125
                    tar_i64toa(archive_entry_size(entry)));
 
126
        }
 
127
}
 
128
 
 
129
/*
 
130
 * Handle 'x' and 't' modes.
 
131
 */
 
132
static void
 
133
read_archive(struct bsdtar *bsdtar, char mode)
 
134
{
 
135
        struct progress_data    progress_data;
 
136
        FILE                     *out;
 
137
        struct archive           *a;
 
138
        struct archive_entry     *entry;
 
139
        const struct stat        *st;
 
140
        int                       r;
 
141
 
 
142
        while (*bsdtar->argv) {
 
143
                lafe_include(&bsdtar->matching, *bsdtar->argv);
 
144
                bsdtar->argv++;
 
145
        }
 
146
 
 
147
        if (bsdtar->names_from_file != NULL)
 
148
                lafe_include_from_file(&bsdtar->matching,
 
149
                    bsdtar->names_from_file, bsdtar->option_null);
 
150
 
 
151
        a = archive_read_new();
 
152
        if (bsdtar->compress_program != NULL)
 
153
                archive_read_support_compression_program(a, bsdtar->compress_program);
 
154
        else
 
155
                archive_read_support_compression_all(a);
 
156
        archive_read_support_format_all(a);
 
157
        if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options))
 
158
                lafe_errc(1, 0, "%s", archive_error_string(a));
 
159
        if (archive_read_open_file(a, bsdtar->filename,
 
160
            bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block :
 
161
            DEFAULT_BYTES_PER_BLOCK))
 
162
                lafe_errc(1, 0, "Error opening archive: %s",
 
163
                    archive_error_string(a));
 
164
 
 
165
        do_chdir(bsdtar);
 
166
 
 
167
        if (mode == 'x') {
 
168
                /* Set an extract callback so that we can handle SIGINFO. */
 
169
                progress_data.bsdtar = bsdtar;
 
170
                progress_data.archive = a;
 
171
                archive_read_extract_set_progress_callback(a, progress_func,
 
172
                    &progress_data);
 
173
        }
 
174
 
 
175
        if (mode == 'x' && bsdtar->option_chroot) {
 
176
#if HAVE_CHROOT
 
177
                if (chroot(".") != 0)
 
178
                        lafe_errc(1, errno, "Can't chroot to \".\"");
 
179
#else
 
180
                lafe_errc(1, 0,
 
181
                    "chroot isn't supported on this platform");
 
182
#endif
 
183
        }
 
184
 
 
185
        for (;;) {
 
186
                /* Support --fast-read option */
 
187
                if (bsdtar->option_fast_read &&
 
188
                    lafe_unmatched_inclusions(bsdtar->matching) == 0)
 
189
                        break;
 
190
 
 
191
                r = archive_read_next_header(a, &entry);
 
192
                progress_data.entry = entry;
 
193
                if (r == ARCHIVE_EOF)
 
194
                        break;
 
195
                if (r < ARCHIVE_OK)
 
196
                        lafe_warnc(0, "%s", archive_error_string(a));
 
197
                if (r <= ARCHIVE_WARN)
 
198
                        bsdtar->return_value = 1;
 
199
                if (r == ARCHIVE_RETRY) {
 
200
                        /* Retryable error: try again */
 
201
                        lafe_warnc(0, "Retrying...");
 
202
                        continue;
 
203
                }
 
204
                if (r == ARCHIVE_FATAL)
 
205
                        break;
 
206
 
 
207
                if (bsdtar->option_numeric_owner) {
 
208
                        archive_entry_set_uname(entry, NULL);
 
209
                        archive_entry_set_gname(entry, NULL);
 
210
                }
 
211
 
 
212
                /*
 
213
                 * Exclude entries that are too old.
 
214
                 */
 
215
                st = archive_entry_stat(entry);
 
216
                if (bsdtar->newer_ctime_sec > 0) {
 
217
                        if (st->st_ctime < bsdtar->newer_ctime_sec)
 
218
                                continue; /* Too old, skip it. */
 
219
                        if (st->st_ctime == bsdtar->newer_ctime_sec
 
220
                            && ARCHIVE_STAT_CTIME_NANOS(st)
 
221
                            <= bsdtar->newer_ctime_nsec)
 
222
                                continue; /* Too old, skip it. */
 
223
                }
 
224
                if (bsdtar->newer_mtime_sec > 0) {
 
225
                        if (st->st_mtime < bsdtar->newer_mtime_sec)
 
226
                                continue; /* Too old, skip it. */
 
227
                        if (st->st_mtime == bsdtar->newer_mtime_sec
 
228
                            && ARCHIVE_STAT_MTIME_NANOS(st)
 
229
                            <= bsdtar->newer_mtime_nsec)
 
230
                                continue; /* Too old, skip it. */
 
231
                }
 
232
 
 
233
                /*
 
234
                 * Note that pattern exclusions are checked before
 
235
                 * pathname rewrites are handled.  This gives more
 
236
                 * control over exclusions, since rewrites always lose
 
237
                 * information.  (For example, consider a rewrite
 
238
                 * s/foo[0-9]/foo/.  If we check exclusions after the
 
239
                 * rewrite, there would be no way to exclude foo1/bar
 
240
                 * while allowing foo2/bar.)
 
241
                 */
 
242
                if (lafe_excluded(bsdtar->matching, archive_entry_pathname(entry)))
 
243
                        continue; /* Excluded by a pattern test. */
 
244
 
 
245
                if (mode == 't') {
 
246
                        /* Perversely, gtar uses -O to mean "send to stderr"
 
247
                         * when used with -t. */
 
248
                        out = bsdtar->option_stdout ? stderr : stdout;
 
249
 
 
250
                        /*
 
251
                         * TODO: Provide some reasonable way to
 
252
                         * preview rewrites.  gtar always displays
 
253
                         * the unedited path in -t output, which means
 
254
                         * you cannot easily preview rewrites.
 
255
                         */
 
256
                        if (bsdtar->verbose < 2)
 
257
                                safe_fprintf(out, "%s",
 
258
                                    archive_entry_pathname(entry));
 
259
                        else
 
260
                                list_item_verbose(bsdtar, out, entry);
 
261
                        fflush(out);
 
262
                        r = archive_read_data_skip(a);
 
263
                        if (r == ARCHIVE_WARN) {
 
264
                                fprintf(out, "\n");
 
265
                                lafe_warnc(0, "%s",
 
266
                                    archive_error_string(a));
 
267
                        }
 
268
                        if (r == ARCHIVE_RETRY) {
 
269
                                fprintf(out, "\n");
 
270
                                lafe_warnc(0, "%s",
 
271
                                    archive_error_string(a));
 
272
                        }
 
273
                        if (r == ARCHIVE_FATAL) {
 
274
                                fprintf(out, "\n");
 
275
                                lafe_warnc(0, "%s",
 
276
                                    archive_error_string(a));
 
277
                                bsdtar->return_value = 1;
 
278
                                break;
 
279
                        }
 
280
                        fprintf(out, "\n");
 
281
                } else {
 
282
                        /* Note: some rewrite failures prevent extraction. */
 
283
                        if (edit_pathname(bsdtar, entry))
 
284
                                continue; /* Excluded by a rewrite failure. */
 
285
 
 
286
                        if (bsdtar->option_interactive &&
 
287
                            !yes("extract '%s'", archive_entry_pathname(entry)))
 
288
                                continue;
 
289
 
 
290
                        /*
 
291
                         * Format here is from SUSv2, including the
 
292
                         * deferred '\n'.
 
293
                         */
 
294
                        if (bsdtar->verbose) {
 
295
                                safe_fprintf(stderr, "x %s",
 
296
                                    archive_entry_pathname(entry));
 
297
                                fflush(stderr);
 
298
                        }
 
299
 
 
300
                        // TODO siginfo_printinfo(bsdtar, 0);
 
301
 
 
302
                        if (bsdtar->option_stdout)
 
303
                                r = archive_read_data_into_fd(a, 1);
 
304
                        else
 
305
                                r = archive_read_extract(a, entry,
 
306
                                    bsdtar->extract_flags);
 
307
                        if (r != ARCHIVE_OK) {
 
308
                                if (!bsdtar->verbose)
 
309
                                        safe_fprintf(stderr, "%s",
 
310
                                            archive_entry_pathname(entry));
 
311
                                safe_fprintf(stderr, ": %s",
 
312
                                    archive_error_string(a));
 
313
                                if (!bsdtar->verbose)
 
314
                                        fprintf(stderr, "\n");
 
315
                                bsdtar->return_value = 1;
 
316
                        }
 
317
                        if (bsdtar->verbose)
 
318
                                fprintf(stderr, "\n");
 
319
                        if (r == ARCHIVE_FATAL)
 
320
                                break;
 
321
                }
 
322
        }
 
323
 
 
324
 
 
325
        r = archive_read_close(a);
 
326
        if (r != ARCHIVE_OK)
 
327
                lafe_warnc(0, "%s", archive_error_string(a));
 
328
        if (r <= ARCHIVE_WARN)
 
329
                bsdtar->return_value = 1;
 
330
 
 
331
        if (bsdtar->verbose > 2)
 
332
                fprintf(stdout, "Archive Format: %s,  Compression: %s\n",
 
333
                    archive_format_name(a), archive_compression_name(a));
 
334
 
 
335
        archive_read_finish(a);
 
336
}
 
337
 
 
338
 
 
339
/*
 
340
 * Display information about the current file.
 
341
 *
 
342
 * The format here roughly duplicates the output of 'ls -l'.
 
343
 * This is based on SUSv2, where 'tar tv' is documented as
 
344
 * listing additional information in an "unspecified format,"
 
345
 * and 'pax -l' is documented as using the same format as 'ls -l'.
 
346
 */
 
347
static void
 
348
list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
 
349
{
 
350
        char                     tmp[100];
 
351
        size_t                   w;
 
352
        const char              *p;
 
353
        const char              *fmt;
 
354
        time_t                   tim;
 
355
        static time_t            now;
 
356
 
 
357
        /*
 
358
         * We avoid collecting the entire list in memory at once by
 
359
         * listing things as we see them.  However, that also means we can't
 
360
         * just pre-compute the field widths.  Instead, we start with guesses
 
361
         * and just widen them as necessary.  These numbers are completely
 
362
         * arbitrary.
 
363
         */
 
364
        if (!bsdtar->u_width) {
 
365
                bsdtar->u_width = 6;
 
366
                bsdtar->gs_width = 13;
 
367
        }
 
368
        if (!now)
 
369
                time(&now);
 
370
        fprintf(out, "%s %d ",
 
371
            archive_entry_strmode(entry),
 
372
            archive_entry_nlink(entry));
 
373
 
 
374
        /* Use uname if it's present, else uid. */
 
375
        p = archive_entry_uname(entry);
 
376
        if ((p == NULL) || (*p == '\0')) {
 
377
                sprintf(tmp, "%lu ",
 
378
                    (unsigned long)archive_entry_uid(entry));
 
379
                p = tmp;
 
380
        }
 
381
        w = strlen(p);
 
382
        if (w > bsdtar->u_width)
 
383
                bsdtar->u_width = w;
 
384
        fprintf(out, "%-*s ", (int)bsdtar->u_width, p);
 
385
 
 
386
        /* Use gname if it's present, else gid. */
 
387
        p = archive_entry_gname(entry);
 
388
        if (p != NULL && p[0] != '\0') {
 
389
                fprintf(out, "%s", p);
 
390
                w = strlen(p);
 
391
        } else {
 
392
                sprintf(tmp, "%lu",
 
393
                    (unsigned long)archive_entry_gid(entry));
 
394
                w = strlen(tmp);
 
395
                fprintf(out, "%s", tmp);
 
396
        }
 
397
 
 
398
        /*
 
399
         * Print device number or file size, right-aligned so as to make
 
400
         * total width of group and devnum/filesize fields be gs_width.
 
401
         * If gs_width is too small, grow it.
 
402
         */
 
403
        if (archive_entry_filetype(entry) == AE_IFCHR
 
404
            || archive_entry_filetype(entry) == AE_IFBLK) {
 
405
                sprintf(tmp, "%lu,%lu",
 
406
                    (unsigned long)archive_entry_rdevmajor(entry),
 
407
                    (unsigned long)archive_entry_rdevminor(entry));
 
408
        } else {
 
409
                strcpy(tmp, tar_i64toa(archive_entry_size(entry)));
 
410
        }
 
411
        if (w + strlen(tmp) >= bsdtar->gs_width)
 
412
                bsdtar->gs_width = w+strlen(tmp)+1;
 
413
        fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp);
 
414
 
 
415
        /* Format the time using 'ls -l' conventions. */
 
416
        tim = archive_entry_mtime(entry);
 
417
#define HALF_YEAR (time_t)365 * 86400 / 2
 
418
#if defined(_WIN32) && !defined(__CYGWIN__)
 
419
#define DAY_FMT  "%d"  /* Windows' strftime function does not support %e format. */
 
420
#else
 
421
#define DAY_FMT  "%e"  /* Day number without leading zeros */
 
422
#endif
 
423
        if (tim < now - HALF_YEAR || tim > now + HALF_YEAR)
 
424
                fmt = bsdtar->day_first ? DAY_FMT " %b  %Y" : "%b " DAY_FMT "  %Y";
 
425
        else
 
426
                fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M";
 
427
        strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
 
428
        fprintf(out, " %s ", tmp);
 
429
        safe_fprintf(out, "%s", archive_entry_pathname(entry));
 
430
 
 
431
        /* Extra information for links. */
 
432
        if (archive_entry_hardlink(entry)) /* Hard link */
 
433
                safe_fprintf(out, " link to %s",
 
434
                    archive_entry_hardlink(entry));
 
435
        else if (archive_entry_symlink(entry)) /* Symbolic link */
 
436
                safe_fprintf(out, " -> %s", archive_entry_symlink(entry));
 
437
}