~akopytov/percona-xtrabackup/bug1166888-2.0

« back to all changes in this revision

Viewing changes to src/libarchive/cpio/cmdline.c

  • Committer: Alexey Kopytov
  • Date: 2012-02-10 20:05:56 UTC
  • 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
 *    in this position and unchanged.
 
11
 * 2. Redistributions in binary form must reproduce the above copyright
 
12
 *    notice, this list of conditions and the following disclaimer in the
 
13
 *    documentation and/or other materials provided with the distribution.
 
14
 *
 
15
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 
16
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
17
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
18
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 
19
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
20
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
21
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
22
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
24
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
25
 */
 
26
 
 
27
 
 
28
#include "cpio_platform.h"
 
29
__FBSDID("$FreeBSD: src/usr.bin/cpio/cmdline.c,v 1.5 2008/12/06 07:30:40 kientzle Exp $");
 
30
 
 
31
#ifdef HAVE_ERRNO_H
 
32
#include <errno.h>
 
33
#endif
 
34
#ifdef HAVE_GRP_H
 
35
#include <grp.h>
 
36
#endif
 
37
#ifdef HAVE_PWD_H
 
38
#include <pwd.h>
 
39
#endif
 
40
#include <stdio.h>
 
41
#ifdef HAVE_STDLIB_H
 
42
#include <stdlib.h>
 
43
#endif
 
44
#ifdef HAVE_STRING_H
 
45
#include <string.h>
 
46
#endif
 
47
 
 
48
#include "cpio.h"
 
49
#include "err.h"
 
50
 
 
51
/*
 
52
 * Short options for cpio.  Please keep this sorted.
 
53
 */
 
54
static const char *short_options = "0AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuvW:yZz";
 
55
 
 
56
/*
 
57
 * Long options for cpio.  Please keep this sorted.
 
58
 */
 
59
static const struct option {
 
60
        const char *name;
 
61
        int required;   /* 1 if this option requires an argument */
 
62
        int equivalent; /* Equivalent short option. */
 
63
} cpio_longopts[] = {
 
64
        { "create",                     0, 'o' },
 
65
        { "extract",                    0, 'i' },
 
66
        { "file",                       1, 'F' },
 
67
        { "format",                     1, 'H' },
 
68
        { "help",                       0, 'h' },
 
69
        { "insecure",                   0, OPTION_INSECURE },
 
70
        { "link",                       0, 'l' },
 
71
        { "list",                       0, 't' },
 
72
        { "lzma",                       0, OPTION_LZMA },
 
73
        { "make-directories",           0, 'd' },
 
74
        { "no-preserve-owner",          0, OPTION_NO_PRESERVE_OWNER },
 
75
        { "null",                       0, '0' },
 
76
        { "numeric-uid-gid",            0, 'n' },
 
77
        { "owner",                      1, 'R' },
 
78
        { "pass-through",               0, 'p' },
 
79
        { "preserve-modification-time", 0, 'm' },
 
80
        { "preserve-owner",             0, OPTION_PRESERVE_OWNER },
 
81
        { "quiet",                      0, OPTION_QUIET },
 
82
        { "unconditional",              0, 'u' },
 
83
        { "verbose",                    0, 'v' },
 
84
        { "version",                    0, OPTION_VERSION },
 
85
        { "xz",                         0, 'J' },
 
86
        { NULL, 0, 0 }
 
87
};
 
88
 
 
89
/*
 
90
 * I used to try to select platform-provided getopt() or
 
91
 * getopt_long(), but that caused a lot of headaches.  In particular,
 
92
 * I couldn't consistently use long options in the test harness
 
93
 * because not all platforms have getopt_long().  That in turn led to
 
94
 * overuse of the -W hack in the test harness, which made it rough to
 
95
 * run the test harness against GNU cpio.  (I periodically run the
 
96
 * test harness here against GNU cpio as a sanity-check.  Yes,
 
97
 * I've found a couple of bugs in GNU cpio that way.)
 
98
 */
 
99
int
 
100
cpio_getopt(struct cpio *cpio)
 
101
{
 
102
        enum { state_start = 0, state_next_word, state_short, state_long };
 
103
        static int state = state_start;
 
104
        static char *opt_word;
 
105
 
 
106
        const struct option *popt, *match = NULL, *match2 = NULL;
 
107
        const char *p, *long_prefix = "--";
 
108
        size_t optlength;
 
109
        int opt = '?';
 
110
        int required = 0;
 
111
 
 
112
        cpio->optarg = NULL;
 
113
 
 
114
        /* First time through, initialize everything. */
 
115
        if (state == state_start) {
 
116
                /* Skip program name. */
 
117
                ++cpio->argv;
 
118
                --cpio->argc;
 
119
                state = state_next_word;
 
120
        }
 
121
 
 
122
        /*
 
123
         * We're ready to look at the next word in argv.
 
124
         */
 
125
        if (state == state_next_word) {
 
126
                /* No more arguments, so no more options. */
 
127
                if (cpio->argv[0] == NULL)
 
128
                        return (-1);
 
129
                /* Doesn't start with '-', so no more options. */
 
130
                if (cpio->argv[0][0] != '-')
 
131
                        return (-1);
 
132
                /* "--" marks end of options; consume it and return. */
 
133
                if (strcmp(cpio->argv[0], "--") == 0) {
 
134
                        ++cpio->argv;
 
135
                        --cpio->argc;
 
136
                        return (-1);
 
137
                }
 
138
                /* Get next word for parsing. */
 
139
                opt_word = *cpio->argv++;
 
140
                --cpio->argc;
 
141
                if (opt_word[1] == '-') {
 
142
                        /* Set up long option parser. */
 
143
                        state = state_long;
 
144
                        opt_word += 2; /* Skip leading '--' */
 
145
                } else {
 
146
                        /* Set up short option parser. */
 
147
                        state = state_short;
 
148
                        ++opt_word;  /* Skip leading '-' */
 
149
                }
 
150
        }
 
151
 
 
152
        /*
 
153
         * We're parsing a group of POSIX-style single-character options.
 
154
         */
 
155
        if (state == state_short) {
 
156
                /* Peel next option off of a group of short options. */
 
157
                opt = *opt_word++;
 
158
                if (opt == '\0') {
 
159
                        /* End of this group; recurse to get next option. */
 
160
                        state = state_next_word;
 
161
                        return cpio_getopt(cpio);
 
162
                }
 
163
 
 
164
                /* Does this option take an argument? */
 
165
                p = strchr(short_options, opt);
 
166
                if (p == NULL)
 
167
                        return ('?');
 
168
                if (p[1] == ':')
 
169
                        required = 1;
 
170
 
 
171
                /* If it takes an argument, parse that. */
 
172
                if (required) {
 
173
                        /* If arg is run-in, opt_word already points to it. */
 
174
                        if (opt_word[0] == '\0') {
 
175
                                /* Otherwise, pick up the next word. */
 
176
                                opt_word = *cpio->argv;
 
177
                                if (opt_word == NULL) {
 
178
                                        lafe_warnc(0,
 
179
                                            "Option -%c requires an argument",
 
180
                                            opt);
 
181
                                        return ('?');
 
182
                                }
 
183
                                ++cpio->argv;
 
184
                                --cpio->argc;
 
185
                        }
 
186
                        if (opt == 'W') {
 
187
                                state = state_long;
 
188
                                long_prefix = "-W "; /* For clearer errors. */
 
189
                        } else {
 
190
                                state = state_next_word;
 
191
                                cpio->optarg = opt_word;
 
192
                        }
 
193
                }
 
194
        }
 
195
 
 
196
        /* We're reading a long option, including -W long=arg convention. */
 
197
        if (state == state_long) {
 
198
                /* After this long option, we'll be starting a new word. */
 
199
                state = state_next_word;
 
200
 
 
201
                /* Option name ends at '=' if there is one. */
 
202
                p = strchr(opt_word, '=');
 
203
                if (p != NULL) {
 
204
                        optlength = (size_t)(p - opt_word);
 
205
                        cpio->optarg = (char *)(uintptr_t)(p + 1);
 
206
                } else {
 
207
                        optlength = strlen(opt_word);
 
208
                }
 
209
 
 
210
                /* Search the table for an unambiguous match. */
 
211
                for (popt = cpio_longopts; popt->name != NULL; popt++) {
 
212
                        /* Short-circuit if first chars don't match. */
 
213
                        if (popt->name[0] != opt_word[0])
 
214
                                continue;
 
215
                        /* If option is a prefix of name in table, record it.*/
 
216
                        if (strncmp(opt_word, popt->name, optlength) == 0) {
 
217
                                match2 = match; /* Record up to two matches. */
 
218
                                match = popt;
 
219
                                /* If it's an exact match, we're done. */
 
220
                                if (strlen(popt->name) == optlength) {
 
221
                                        match2 = NULL; /* Forget the others. */
 
222
                                        break;
 
223
                                }
 
224
                        }
 
225
                }
 
226
 
 
227
                /* Fail if there wasn't a unique match. */
 
228
                if (match == NULL) {
 
229
                        lafe_warnc(0,
 
230
                            "Option %s%s is not supported",
 
231
                            long_prefix, opt_word);
 
232
                        return ('?');
 
233
                }
 
234
                if (match2 != NULL) {
 
235
                        lafe_warnc(0,
 
236
                            "Ambiguous option %s%s (matches --%s and --%s)",
 
237
                            long_prefix, opt_word, match->name, match2->name);
 
238
                        return ('?');
 
239
                }
 
240
 
 
241
                /* We've found a unique match; does it need an argument? */
 
242
                if (match->required) {
 
243
                        /* Argument required: get next word if necessary. */
 
244
                        if (cpio->optarg == NULL) {
 
245
                                cpio->optarg = *cpio->argv;
 
246
                                if (cpio->optarg == NULL) {
 
247
                                        lafe_warnc(0,
 
248
                                            "Option %s%s requires an argument",
 
249
                                            long_prefix, match->name);
 
250
                                        return ('?');
 
251
                                }
 
252
                                ++cpio->argv;
 
253
                                --cpio->argc;
 
254
                        }
 
255
                } else {
 
256
                        /* Argument forbidden: fail if there is one. */
 
257
                        if (cpio->optarg != NULL) {
 
258
                                lafe_warnc(0,
 
259
                                    "Option %s%s does not allow an argument",
 
260
                                    long_prefix, match->name);
 
261
                                return ('?');
 
262
                        }
 
263
                }
 
264
                return (match->equivalent);
 
265
        }
 
266
 
 
267
        return (opt);
 
268
}
 
269
 
 
270
 
 
271
/*
 
272
 * Parse the argument to the -R or --owner flag.
 
273
 *
 
274
 * The format is one of the following:
 
275
 *   <username|uid>    - Override user but not group
 
276
 *   <username>:   - Override both, group is user's default group
 
277
 *   <uid>:    - Override user but not group
 
278
 *   <username|uid>:<groupname|gid> - Override both
 
279
 *   :<groupname|gid>  - Override group but not user
 
280
 *
 
281
 * Where uid/gid are decimal representations and groupname/username
 
282
 * are names to be looked up in system database.  Note that we try
 
283
 * to look up an argument as a name first, then try numeric parsing.
 
284
 *
 
285
 * A period can be used instead of the colon.
 
286
 *
 
287
 * Sets uid/gid return as appropriate, -1 indicates uid/gid not specified.
 
288
 *
 
289
 * Returns NULL if no error, otherwise returns error string for display.
 
290
 *
 
291
 */
 
292
const char *
 
293
owner_parse(const char *spec, int *uid, int *gid)
 
294
{
 
295
        static char errbuff[128];
 
296
        const char *u, *ue, *g;
 
297
 
 
298
        *uid = -1;
 
299
        *gid = -1;
 
300
 
 
301
        if (spec[0] == '\0')
 
302
                return ("Invalid empty user/group spec");
 
303
 
 
304
        /*
 
305
         * Split spec into [user][:.][group]
 
306
         *  u -> first char of username, NULL if no username
 
307
         *  ue -> first char after username (colon, period, or \0)
 
308
         *  g -> first char of group name
 
309
         */
 
310
        if (*spec == ':' || *spec == '.') {
 
311
                /* If spec starts with ':' or '.', then just group. */
 
312
                ue = u = NULL;
 
313
                g = spec + 1;
 
314
        } else {
 
315
                /* Otherwise, [user] or [user][:] or [user][:][group] */
 
316
                ue = u = spec;
 
317
                while (*ue != ':' && *ue != '.' && *ue != '\0')
 
318
                        ++ue;
 
319
                g = ue;
 
320
                if (*g != '\0') /* Skip : or . to find first char of group. */
 
321
                        ++g;
 
322
        }
 
323
 
 
324
        if (u != NULL) {
 
325
                /* Look up user: ue is first char after end of user. */
 
326
                char *user;
 
327
                struct passwd *pwent;
 
328
 
 
329
                user = (char *)malloc(ue - u + 1);
 
330
                if (user == NULL)
 
331
                        return ("Couldn't allocate memory");
 
332
                memcpy(user, u, ue - u);
 
333
                user[ue - u] = '\0';
 
334
                if ((pwent = getpwnam(user)) != NULL) {
 
335
                        *uid = pwent->pw_uid;
 
336
                        if (*ue != '\0')
 
337
                                *gid = pwent->pw_gid;
 
338
                } else {
 
339
                        char *end;
 
340
                        errno = 0;
 
341
                        *uid = strtoul(user, &end, 10);
 
342
                        if (errno || *end != '\0') {
 
343
                                snprintf(errbuff, sizeof(errbuff),
 
344
                                    "Couldn't lookup user ``%s''", user);
 
345
                                errbuff[sizeof(errbuff) - 1] = '\0';
 
346
                                return (errbuff);
 
347
                        }
 
348
                }
 
349
                free(user);
 
350
        }
 
351
 
 
352
        if (*g != '\0') {
 
353
                struct group *grp;
 
354
                if ((grp = getgrnam(g)) != NULL) {
 
355
                        *gid = grp->gr_gid;
 
356
                } else {
 
357
                        char *end;
 
358
                        errno = 0;
 
359
                        *gid = strtoul(g, &end, 10);
 
360
                        if (errno || *end != '\0') {
 
361
                                snprintf(errbuff, sizeof(errbuff),
 
362
                                    "Couldn't lookup group ``%s''", g);
 
363
                                errbuff[sizeof(errbuff) - 1] = '\0';
 
364
                                return (errbuff);
 
365
                        }
 
366
                }
 
367
        }
 
368
        return (NULL);
 
369
}