~akopytov/percona-xtrabackup/bug1166888-2.1

« back to all changes in this revision

Viewing changes to src/libarchive/libarchive/archive_read_support_compression_uu.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) 2009 Michihiro NAKAJIMA
 
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 "archive_platform.h"
 
27
__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_compression_uu.c 201248 2009-12-30 06:12:03Z kientzle $");
 
28
 
 
29
#ifdef HAVE_ERRNO_H
 
30
#include <errno.h>
 
31
#endif
 
32
#ifdef HAVE_STDLIB_H
 
33
#include <stdlib.h>
 
34
#endif
 
35
#ifdef HAVE_STRING_H
 
36
#include <string.h>
 
37
#endif
 
38
 
 
39
#include "archive.h"
 
40
#include "archive_private.h"
 
41
#include "archive_read_private.h"
 
42
 
 
43
struct uudecode {
 
44
        int64_t          total;
 
45
        unsigned char   *in_buff;
 
46
#define IN_BUFF_SIZE    (1024)
 
47
        int              in_cnt;
 
48
        size_t           in_allocated;
 
49
        unsigned char   *out_buff;
 
50
#define OUT_BUFF_SIZE   (64 * 1024)
 
51
        int              state;
 
52
#define ST_FIND_HEAD    0
 
53
#define ST_READ_UU      1
 
54
#define ST_UUEND        2
 
55
#define ST_READ_BASE64  3
 
56
};
 
57
 
 
58
static int      uudecode_bidder_bid(struct archive_read_filter_bidder *,
 
59
                    struct archive_read_filter *filter);
 
60
static int      uudecode_bidder_init(struct archive_read_filter *);
 
61
 
 
62
static ssize_t  uudecode_filter_read(struct archive_read_filter *,
 
63
                    const void **);
 
64
static int      uudecode_filter_close(struct archive_read_filter *);
 
65
 
 
66
int
 
67
archive_read_support_compression_uu(struct archive *_a)
 
68
{
 
69
        struct archive_read *a = (struct archive_read *)_a;
 
70
        struct archive_read_filter_bidder *bidder;
 
71
 
 
72
        bidder = __archive_read_get_bidder(a);
 
73
        archive_clear_error(_a);
 
74
        if (bidder == NULL)
 
75
                return (ARCHIVE_FATAL);
 
76
 
 
77
        bidder->data = NULL;
 
78
        bidder->bid = uudecode_bidder_bid;
 
79
        bidder->init = uudecode_bidder_init;
 
80
        bidder->options = NULL;
 
81
        bidder->free = NULL;
 
82
        return (ARCHIVE_OK);
 
83
}
 
84
 
 
85
static const unsigned char ascii[256] = {
 
86
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
 
87
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
 
88
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
 
89
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
 
90
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
 
91
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
 
92
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
 
93
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
 
94
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
 
95
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
 
96
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
 
97
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
 
98
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
 
99
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
 
100
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
 
101
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
 
102
};
 
103
 
 
104
static const unsigned char uuchar[256] = {
 
105
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
 
106
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
 
107
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
 
108
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
 
109
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
 
110
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
 
111
        1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
 
112
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
 
113
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
 
114
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
 
115
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
 
116
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
 
117
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
 
118
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
 
119
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
 
120
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
 
121
};
 
122
 
 
123
static const unsigned char base64[256] = {
 
124
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
 
125
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
 
126
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
 
127
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
 
128
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
 
129
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
 
130
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
 
131
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
 
132
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
 
133
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
 
134
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
 
135
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
 
136
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
 
137
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
 
138
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
 
139
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
 
140
};
 
141
 
 
142
static const int base64num[128] = {
 
143
         0,  0,  0,  0,  0,  0,  0,  0,
 
144
         0,  0,  0,  0,  0,  0,  0,  0, /* 00 - 0F */
 
145
         0,  0,  0,  0,  0,  0,  0,  0,
 
146
         0,  0,  0,  0,  0,  0,  0,  0, /* 10 - 1F */
 
147
         0,  0,  0,  0,  0,  0,  0,  0,
 
148
         0,  0,  0, 62,  0,  0,  0, 63, /* 20 - 2F */
 
149
        52, 53, 54, 55, 56, 57, 58, 59,
 
150
        60, 61,  0,  0,  0,  0,  0,  0, /* 30 - 3F */
 
151
         0,  0,  1,  2,  3,  4,  5,  6,
 
152
         7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
 
153
        15, 16, 17, 18, 19, 20, 21, 22,
 
154
        23, 24, 25,  0,  0,  0,  0,  0, /* 50 - 5F */
 
155
         0, 26, 27, 28, 29, 30, 31, 32,
 
156
        33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
 
157
        41, 42, 43, 44, 45, 46, 47, 48,
 
158
        49, 50, 51,  0,  0,  0,  0,  0, /* 70 - 7F */
 
159
};
 
160
 
 
161
static ssize_t
 
162
get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
 
163
{
 
164
        ssize_t len;
 
165
 
 
166
        len = 0;
 
167
        while (len < avail) {
 
168
                switch (ascii[*b]) {
 
169
                case 0: /* Non-ascii character or control character. */
 
170
                        if (nlsize != NULL)
 
171
                                *nlsize = 0;
 
172
                        return (-1);
 
173
                case '\r':
 
174
                        if (avail-len > 1 && b[1] == '\n') {
 
175
                                if (nlsize != NULL)
 
176
                                        *nlsize = 2;
 
177
                                return (len+2);
 
178
                        }
 
179
                        /* FALL THROUGH */
 
180
                case '\n':
 
181
                        if (nlsize != NULL)
 
182
                                *nlsize = 1;
 
183
                        return (len+1);
 
184
                case 1:
 
185
                        b++;
 
186
                        len++;
 
187
                        break;
 
188
                }
 
189
        }
 
190
        if (nlsize != NULL)
 
191
                *nlsize = 0;
 
192
        return (avail);
 
193
}
 
194
 
 
195
static ssize_t
 
196
bid_get_line(struct archive_read_filter *filter,
 
197
    const unsigned char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
 
198
{
 
199
        ssize_t len;
 
200
        int quit;
 
201
        
 
202
        quit = 0;
 
203
        if (*avail == 0) {
 
204
                *nl = 0;
 
205
                len = 0;
 
206
        } else
 
207
                len = get_line(*b, *avail, nl);
 
208
        /*
 
209
         * Read bytes more while it does not reach the end of line.
 
210
         */
 
211
        while (*nl == 0 && len == *avail && !quit) {
 
212
                ssize_t diff = *ravail - *avail;
 
213
 
 
214
                *b = __archive_read_filter_ahead(filter, 160 + *ravail, avail);
 
215
                if (*b == NULL) {
 
216
                        if (*ravail >= *avail)
 
217
                                return (0);
 
218
                        /* Reading bytes reaches the end of file. */
 
219
                        *b = __archive_read_filter_ahead(filter, *avail, avail);
 
220
                        quit = 1;
 
221
                }
 
222
                *ravail = *avail;
 
223
                *b += diff;
 
224
                *avail -= diff;
 
225
                len = get_line(*b, *avail, nl);
 
226
        }
 
227
        return (len);
 
228
}
 
229
 
 
230
#define UUDECODE(c) (((c) - 0x20) & 0x3f)
 
231
 
 
232
static int
 
233
uudecode_bidder_bid(struct archive_read_filter_bidder *self,
 
234
    struct archive_read_filter *filter)
 
235
{
 
236
        const unsigned char *b;
 
237
        ssize_t avail, ravail;
 
238
        ssize_t len, nl;
 
239
        int l;
 
240
        int firstline;
 
241
 
 
242
        (void)self; /* UNUSED */
 
243
 
 
244
        b = __archive_read_filter_ahead(filter, 1, &avail);
 
245
        if (b == NULL)
 
246
                return (0);
 
247
 
 
248
        firstline = 20;
 
249
        ravail = avail;
 
250
        for (;;) {
 
251
                len = bid_get_line(filter, &b, &avail, &ravail, &nl);
 
252
                if (len < 0 || nl == 0)
 
253
                        return (0);/* Binary data. */
 
254
                if (memcmp(b, "begin ", 6) == 0 && len - nl >= 11)
 
255
                        l = 6;
 
256
                else if (memcmp(b, "begin-base64 ", 13) == 0 && len - nl >= 18)
 
257
                        l = 13;
 
258
                else
 
259
                        l = 0;
 
260
 
 
261
                if (l > 0 && (b[l] < '0' || b[l] > '7' ||
 
262
                    b[l+1] < '0' || b[l+1] > '7' ||
 
263
                    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
 
264
                        l = 0;
 
265
 
 
266
                b += len;
 
267
                avail -= len;
 
268
                if (l)
 
269
                        break;
 
270
                firstline = 0;
 
271
        }
 
272
        if (!avail)
 
273
                return (0);
 
274
        len = bid_get_line(filter, &b, &avail, &ravail, &nl);
 
275
        if (len < 0 || nl == 0)
 
276
                return (0);/* There are non-ascii characters. */
 
277
        avail -= len;
 
278
 
 
279
        if (l == 6) {
 
280
                if (!uuchar[*b])
 
281
                        return (0);
 
282
                /* Get a length of decoded bytes. */
 
283
                l = UUDECODE(*b++); len--;
 
284
                if (l > 45)
 
285
                        /* Normally, maximum length is 45(character 'M'). */
 
286
                        return (0);
 
287
                while (l && len-nl > 0) {
 
288
                        if (l > 0) {
 
289
                                if (!uuchar[*b++])
 
290
                                        return (0);
 
291
                                if (!uuchar[*b++])
 
292
                                        return (0);
 
293
                                len -= 2;
 
294
                                --l;
 
295
                        }
 
296
                        if (l > 0) {
 
297
                                if (!uuchar[*b++])
 
298
                                        return (0);
 
299
                                --len;
 
300
                                --l;
 
301
                        }
 
302
                        if (l > 0) {
 
303
                                if (!uuchar[*b++])
 
304
                                        return (0);
 
305
                                --len;
 
306
                                --l;
 
307
                        }
 
308
                }
 
309
                if (len-nl < 0)
 
310
                        return (0);
 
311
                if (len-nl == 1 &&
 
312
                    (uuchar[*b] ||               /* Check sum. */
 
313
                     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
 
314
                        ++b;
 
315
                        --len;
 
316
                }
 
317
                b += nl;
 
318
                if (avail && uuchar[*b])
 
319
                        return (firstline+30);
 
320
        }
 
321
        if (l == 13) {
 
322
                while (len-nl > 0) {
 
323
                        if (!base64[*b++])
 
324
                                return (0);
 
325
                        --len;
 
326
                }
 
327
                b += nl;
 
328
 
 
329
                if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
 
330
                        return (firstline+40);
 
331
                if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
 
332
                        return (firstline+40);
 
333
                if (avail > 0 && base64[*b])
 
334
                        return (firstline+30);
 
335
        }
 
336
 
 
337
        return (0);
 
338
}
 
339
 
 
340
static int
 
341
uudecode_bidder_init(struct archive_read_filter *self)
 
342
{
 
343
        struct uudecode   *uudecode;
 
344
        void *out_buff;
 
345
        void *in_buff;
 
346
 
 
347
        self->code = ARCHIVE_COMPRESSION_UU;
 
348
        self->name = "uu";
 
349
        self->read = uudecode_filter_read;
 
350
        self->skip = NULL; /* not supported */
 
351
        self->close = uudecode_filter_close;
 
352
 
 
353
        uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
 
354
        out_buff = malloc(OUT_BUFF_SIZE);
 
355
        in_buff = malloc(IN_BUFF_SIZE);
 
356
        if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
 
357
                archive_set_error(&self->archive->archive, ENOMEM,
 
358
                    "Can't allocate data for uudecode");
 
359
                free(uudecode);
 
360
                free(out_buff);
 
361
                free(in_buff);
 
362
                return (ARCHIVE_FATAL);
 
363
        }
 
364
 
 
365
        self->data = uudecode;
 
366
        uudecode->in_buff = in_buff;
 
367
        uudecode->in_cnt = 0;
 
368
        uudecode->in_allocated = IN_BUFF_SIZE;
 
369
        uudecode->out_buff = out_buff;
 
370
        uudecode->state = ST_FIND_HEAD;
 
371
 
 
372
        return (ARCHIVE_OK);
 
373
}
 
374
 
 
375
static int
 
376
ensure_in_buff_size(struct archive_read_filter *self,
 
377
    struct uudecode *uudecode, size_t size)
 
378
{
 
379
 
 
380
        if (size > uudecode->in_allocated) {
 
381
                unsigned char *ptr;
 
382
                size_t newsize;
 
383
 
 
384
                newsize = uudecode->in_allocated << 1;
 
385
                ptr = malloc(newsize);
 
386
                if (ptr == NULL ||
 
387
                    newsize < uudecode->in_allocated) {
 
388
                        free(ptr);
 
389
                        archive_set_error(&self->archive->archive,
 
390
                            ENOMEM,
 
391
                            "Can't allocate data for uudecode");
 
392
                        return (ARCHIVE_FATAL);
 
393
                }
 
394
                if (uudecode->in_cnt)
 
395
                        memmove(ptr, uudecode->in_buff,
 
396
                            uudecode->in_cnt);
 
397
                free(uudecode->in_buff);
 
398
                uudecode->in_buff = ptr;
 
399
                uudecode->in_allocated = newsize;
 
400
        }
 
401
        return (ARCHIVE_OK);
 
402
}
 
403
 
 
404
static ssize_t
 
405
uudecode_filter_read(struct archive_read_filter *self, const void **buff)
 
406
{
 
407
        struct uudecode *uudecode;
 
408
        const unsigned char *b, *d;
 
409
        unsigned char *out;
 
410
        ssize_t avail_in, ravail;
 
411
        ssize_t used;
 
412
        ssize_t total;
 
413
        ssize_t len, llen, nl;
 
414
 
 
415
        uudecode = (struct uudecode *)self->data;
 
416
 
 
417
read_more:
 
418
        d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
 
419
        if (d == NULL && avail_in < 0)
 
420
                return (ARCHIVE_FATAL);
 
421
        /* Quiet a code analyzer; make sure avail_in must be zero
 
422
         * when d is NULL. */
 
423
        if (d == NULL)
 
424
                avail_in = 0;
 
425
        used = 0;
 
426
        total = 0;
 
427
        out = uudecode->out_buff;
 
428
        ravail = avail_in;
 
429
        if (uudecode->in_cnt) {
 
430
                /*
 
431
                 * If there is remaining data which is saved by
 
432
                 * previous calling, use it first.
 
433
                 */
 
434
                if (ensure_in_buff_size(self, uudecode,
 
435
                    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
 
436
                        return (ARCHIVE_FATAL);
 
437
                memcpy(uudecode->in_buff + uudecode->in_cnt,
 
438
                    d, avail_in);
 
439
                d = uudecode->in_buff;
 
440
                avail_in += uudecode->in_cnt;
 
441
                uudecode->in_cnt = 0;
 
442
        }
 
443
        for (;used < avail_in; d += llen, used += llen) {
 
444
                int l, body;
 
445
 
 
446
                b = d;
 
447
                len = get_line(b, avail_in - used, &nl);
 
448
                if (len < 0) {
 
449
                        /* Non-ascii character is found. */
 
450
                        archive_set_error(&self->archive->archive,
 
451
                            ARCHIVE_ERRNO_MISC,
 
452
                            "Insufficient compressed data");
 
453
                        return (ARCHIVE_FATAL);
 
454
                }
 
455
                llen = len;
 
456
                if (nl == 0) {
 
457
                        /*
 
458
                         * Save remaining data which does not contain
 
459
                         * NL('\n','\r').
 
460
                         */
 
461
                        if (ensure_in_buff_size(self, uudecode, len)
 
462
                            != ARCHIVE_OK)
 
463
                                return (ARCHIVE_FATAL);
 
464
                        if (uudecode->in_buff != b)
 
465
                                memmove(uudecode->in_buff, b, len);
 
466
                        uudecode->in_cnt = len;
 
467
                        if (total == 0) {
 
468
                                /* Do not return 0; it means end-of-file.
 
469
                                 * We should try to read bytes more. */
 
470
                                __archive_read_filter_consume(
 
471
                                    self->upstream, ravail);
 
472
                                goto read_more;
 
473
                        }
 
474
                        break;
 
475
                }
 
476
                if (total + len * 2 > OUT_BUFF_SIZE)
 
477
                        break;
 
478
                switch (uudecode->state) {
 
479
                default:
 
480
                case ST_FIND_HEAD:
 
481
                        if (len - nl > 13 && memcmp(b, "begin ", 6) == 0)
 
482
                                l = 6;
 
483
                        else if (len - nl > 18 &&
 
484
                            memcmp(b, "begin-base64 ", 13) == 0)
 
485
                                l = 13;
 
486
                        else
 
487
                                l = 0;
 
488
                        if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
 
489
                            b[l+1] >= '0' && b[l+1] <= '7' &&
 
490
                            b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
 
491
                                if (l == 6)
 
492
                                        uudecode->state = ST_READ_UU;
 
493
                                else
 
494
                                        uudecode->state = ST_READ_BASE64;
 
495
                        }
 
496
                        break;
 
497
                case ST_READ_UU:
 
498
                        body = len - nl;
 
499
                        if (!uuchar[*b] || body <= 0) {
 
500
                                archive_set_error(&self->archive->archive,
 
501
                                    ARCHIVE_ERRNO_MISC,
 
502
                                    "Insufficient compressed data");
 
503
                                return (ARCHIVE_FATAL);
 
504
                        }
 
505
                        /* Get length of undecoded bytes of curent line. */
 
506
                        l = UUDECODE(*b++);
 
507
                        body--;
 
508
                        if (l > body) {
 
509
                                archive_set_error(&self->archive->archive,
 
510
                                    ARCHIVE_ERRNO_MISC,
 
511
                                    "Insufficient compressed data");
 
512
                                return (ARCHIVE_FATAL);
 
513
                        }
 
514
                        if (l == 0) {
 
515
                                uudecode->state = ST_UUEND;
 
516
                                break;
 
517
                        }
 
518
                        while (l > 0) {
 
519
                                int n = 0;
 
520
 
 
521
                                if (l > 0) {
 
522
                                        if (!uuchar[b[0]] || !uuchar[b[1]])
 
523
                                                break;
 
524
                                        n = UUDECODE(*b++) << 18;
 
525
                                        n |= UUDECODE(*b++) << 12;
 
526
                                        *out++ = n >> 16; total++;
 
527
                                        --l;
 
528
                                }
 
529
                                if (l > 0) {
 
530
                                        if (!uuchar[b[0]])
 
531
                                                break;
 
532
                                        n |= UUDECODE(*b++) << 6;
 
533
                                        *out++ = (n >> 8) & 0xFF; total++;
 
534
                                        --l;
 
535
                                }
 
536
                                if (l > 0) {
 
537
                                        if (!uuchar[b[0]])
 
538
                                                break;
 
539
                                        n |= UUDECODE(*b++);
 
540
                                        *out++ = n & 0xFF; total++;
 
541
                                        --l;
 
542
                                }
 
543
                        }
 
544
                        if (l) {
 
545
                                archive_set_error(&self->archive->archive,
 
546
                                    ARCHIVE_ERRNO_MISC,
 
547
                                    "Insufficient compressed data");
 
548
                                return (ARCHIVE_FATAL);
 
549
                        }
 
550
                        break;
 
551
                case ST_UUEND:
 
552
                        if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
 
553
                                uudecode->state = ST_FIND_HEAD;
 
554
                        else {
 
555
                                archive_set_error(&self->archive->archive,
 
556
                                    ARCHIVE_ERRNO_MISC,
 
557
                                    "Insufficient compressed data");
 
558
                                return (ARCHIVE_FATAL);
 
559
                        }
 
560
                        break;
 
561
                case ST_READ_BASE64:
 
562
                        l = len - nl;
 
563
                        if (l >= 3 && b[0] == '=' && b[1] == '=' &&
 
564
                            b[2] == '=') {
 
565
                                uudecode->state = ST_FIND_HEAD;
 
566
                                break;
 
567
                        }
 
568
                        while (l > 0) {
 
569
                                int n = 0;
 
570
 
 
571
                                if (l > 0) {
 
572
                                        if (!base64[b[0]] || !base64[b[1]])
 
573
                                                break;
 
574
                                        n = base64num[*b++] << 18;
 
575
                                        n |= base64num[*b++] << 12;
 
576
                                        *out++ = n >> 16; total++;
 
577
                                        l -= 2;
 
578
                                }
 
579
                                if (l > 0) {
 
580
                                        if (*b == '=')
 
581
                                                break;
 
582
                                        if (!base64[*b])
 
583
                                                break;
 
584
                                        n |= base64num[*b++] << 6;
 
585
                                        *out++ = (n >> 8) & 0xFF; total++;
 
586
                                        --l;
 
587
                                }
 
588
                                if (l > 0) {
 
589
                                        if (*b == '=')
 
590
                                                break;
 
591
                                        if (!base64[*b])
 
592
                                                break;
 
593
                                        n |= base64num[*b++];
 
594
                                        *out++ = n & 0xFF; total++;
 
595
                                        --l;
 
596
                                }
 
597
                        }
 
598
                        if (l && *b != '=') {
 
599
                                archive_set_error(&self->archive->archive,
 
600
                                    ARCHIVE_ERRNO_MISC,
 
601
                                    "Insufficient compressed data");
 
602
                                return (ARCHIVE_FATAL);
 
603
                        }
 
604
                        break;
 
605
                }
 
606
        }
 
607
 
 
608
        __archive_read_filter_consume(self->upstream, ravail);
 
609
 
 
610
        *buff = uudecode->out_buff;
 
611
        uudecode->total += total;
 
612
        return (total);
 
613
}
 
614
 
 
615
static int
 
616
uudecode_filter_close(struct archive_read_filter *self)
 
617
{
 
618
        struct uudecode *uudecode;
 
619
 
 
620
        uudecode = (struct uudecode *)self->data;
 
621
        free(uudecode->in_buff);
 
622
        free(uudecode->out_buff);
 
623
        free(uudecode);
 
624
 
 
625
        return (ARCHIVE_OK);
 
626
}
 
627