~gl-az/percona-xtrabackup/2.1-io-block-size

« back to all changes in this revision

Viewing changes to src/libarchive/libarchive/archive_write_set_format_shar.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
 * Copyright (c) 2008 Joerg Sonnenberger
 
4
 * All rights reserved.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 * 1. Redistributions of source code must retain the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer.
 
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
#include "archive_platform.h"
 
28
__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_shar.c 189438 2009-03-06 05:58:56Z kientzle $");
 
29
 
 
30
#ifdef HAVE_ERRNO_H
 
31
#include <errno.h>
 
32
#endif
 
33
#include <stdio.h>
 
34
#ifdef HAVE_STDLIB_H
 
35
#include <stdlib.h>
 
36
#endif
 
37
#ifdef HAVE_STRING_H
 
38
#include <string.h>
 
39
#endif
 
40
 
 
41
#include "archive.h"
 
42
#include "archive_entry.h"
 
43
#include "archive_private.h"
 
44
#include "archive_write_private.h"
 
45
 
 
46
struct shar {
 
47
        int                      dump;
 
48
        int                      end_of_line;
 
49
        struct archive_entry    *entry;
 
50
        int                      has_data;
 
51
        char                    *last_dir;
 
52
 
 
53
        /* Line buffer for uuencoded dump format */
 
54
        char                     outbuff[45];
 
55
        size_t                   outpos;
 
56
 
 
57
        int                      wrote_header;
 
58
        struct archive_string    work;
 
59
        struct archive_string    quoted_name;
 
60
};
 
61
 
 
62
static int      archive_write_shar_finish(struct archive_write *);
 
63
static int      archive_write_shar_destroy(struct archive_write *);
 
64
static int      archive_write_shar_header(struct archive_write *,
 
65
                    struct archive_entry *);
 
66
static ssize_t  archive_write_shar_data_sed(struct archive_write *,
 
67
                    const void * buff, size_t);
 
68
static ssize_t  archive_write_shar_data_uuencode(struct archive_write *,
 
69
                    const void * buff, size_t);
 
70
static int      archive_write_shar_finish_entry(struct archive_write *);
 
71
 
 
72
/*
 
73
 * Copy the given string to the buffer, quoting all shell meta characters
 
74
 * found.
 
75
 */
 
76
static void
 
77
shar_quote(struct archive_string *buf, const char *str, int in_shell)
 
78
{
 
79
        static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
 
80
        size_t len;
 
81
 
 
82
        while (*str != '\0') {
 
83
                if ((len = strcspn(str, meta)) != 0) {
 
84
                        archive_strncat(buf, str, len);
 
85
                        str += len;
 
86
                } else if (*str == '\n') {
 
87
                        if (in_shell)
 
88
                                archive_strcat(buf, "\"\n\"");
 
89
                        else
 
90
                                archive_strcat(buf, "\\n");
 
91
                        ++str;
 
92
                } else {
 
93
                        archive_strappend_char(buf, '\\');
 
94
                        archive_strappend_char(buf, *str);
 
95
                        ++str;
 
96
                }
 
97
        }
 
98
}
 
99
 
 
100
/*
 
101
 * Set output format to 'shar' format.
 
102
 */
 
103
int
 
104
archive_write_set_format_shar(struct archive *_a)
 
105
{
 
106
        struct archive_write *a = (struct archive_write *)_a;
 
107
        struct shar *shar;
 
108
 
 
109
        /* If someone else was already registered, unregister them. */
 
110
        if (a->format_destroy != NULL)
 
111
                (a->format_destroy)(a);
 
112
 
 
113
        shar = (struct shar *)malloc(sizeof(*shar));
 
114
        if (shar == NULL) {
 
115
                archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
 
116
                return (ARCHIVE_FATAL);
 
117
        }
 
118
        memset(shar, 0, sizeof(*shar));
 
119
        archive_string_init(&shar->work);
 
120
        archive_string_init(&shar->quoted_name);
 
121
        a->format_data = shar;
 
122
 
 
123
        a->pad_uncompressed = 0;
 
124
        a->format_name = "shar";
 
125
        a->format_write_header = archive_write_shar_header;
 
126
        a->format_finish = archive_write_shar_finish;
 
127
        a->format_destroy = archive_write_shar_destroy;
 
128
        a->format_write_data = archive_write_shar_data_sed;
 
129
        a->format_finish_entry = archive_write_shar_finish_entry;
 
130
        a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
 
131
        a->archive.archive_format_name = "shar";
 
132
        return (ARCHIVE_OK);
 
133
}
 
134
 
 
135
/*
 
136
 * An alternate 'shar' that uses uudecode instead of 'sed' to encode
 
137
 * file contents and can therefore be used to archive binary files.
 
138
 * In addition, this variant also attempts to restore ownership, file modes,
 
139
 * and other extended file information.
 
140
 */
 
141
int
 
142
archive_write_set_format_shar_dump(struct archive *_a)
 
143
{
 
144
        struct archive_write *a = (struct archive_write *)_a;
 
145
        struct shar *shar;
 
146
 
 
147
        archive_write_set_format_shar(&a->archive);
 
148
        shar = (struct shar *)a->format_data;
 
149
        shar->dump = 1;
 
150
        a->format_write_data = archive_write_shar_data_uuencode;
 
151
        a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
 
152
        a->archive.archive_format_name = "shar dump";
 
153
        return (ARCHIVE_OK);
 
154
}
 
155
 
 
156
static int
 
157
archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
 
158
{
 
159
        const char *linkname;
 
160
        const char *name;
 
161
        char *p, *pp;
 
162
        struct shar *shar;
 
163
 
 
164
        shar = (struct shar *)a->format_data;
 
165
        if (!shar->wrote_header) {
 
166
                archive_strcat(&shar->work, "#!/bin/sh\n");
 
167
                archive_strcat(&shar->work, "# This is a shell archive\n");
 
168
                shar->wrote_header = 1;
 
169
        }
 
170
 
 
171
        /* Save the entry for the closing. */
 
172
        if (shar->entry)
 
173
                archive_entry_free(shar->entry);
 
174
        shar->entry = archive_entry_clone(entry);
 
175
        name = archive_entry_pathname(entry);
 
176
 
 
177
        /* Handle some preparatory issues. */
 
178
        switch(archive_entry_filetype(entry)) {
 
179
        case AE_IFREG:
 
180
                /* Only regular files have non-zero size. */
 
181
                break;
 
182
        case AE_IFDIR:
 
183
                archive_entry_set_size(entry, 0);
 
184
                /* Don't bother trying to recreate '.' */
 
185
                if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)
 
186
                        return (ARCHIVE_OK);
 
187
                break;
 
188
        case AE_IFIFO:
 
189
        case AE_IFCHR:
 
190
        case AE_IFBLK:
 
191
                /* All other file types have zero size in the archive. */
 
192
                archive_entry_set_size(entry, 0);
 
193
                break;
 
194
        default:
 
195
                archive_entry_set_size(entry, 0);
 
196
                if (archive_entry_hardlink(entry) == NULL &&
 
197
                    archive_entry_symlink(entry) == NULL) {
 
198
                        archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 
199
                            "shar format cannot archive this");
 
200
                        return (ARCHIVE_WARN);
 
201
                }
 
202
        }
 
203
 
 
204
        archive_string_empty(&shar->quoted_name);
 
205
        shar_quote(&shar->quoted_name, name, 1);
 
206
 
 
207
        /* Stock preparation for all file types. */
 
208
        archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);
 
209
 
 
210
        if (archive_entry_filetype(entry) != AE_IFDIR) {
 
211
                /* Try to create the dir. */
 
212
                p = strdup(name);
 
213
                pp = strrchr(p, '/');
 
214
                /* If there is a / character, try to create the dir. */
 
215
                if (pp != NULL) {
 
216
                        *pp = '\0';
 
217
 
 
218
                        /* Try to avoid a lot of redundant mkdir commands. */
 
219
                        if (strcmp(p, ".") == 0) {
 
220
                                /* Don't try to "mkdir ." */
 
221
                                free(p);
 
222
                        } else if (shar->last_dir == NULL) {
 
223
                                archive_strcat(&shar->work, "mkdir -p ");
 
224
                                shar_quote(&shar->work, p, 1);
 
225
                                archive_strcat(&shar->work,
 
226
                                    " > /dev/null 2>&1\n");
 
227
                                shar->last_dir = p;
 
228
                        } else if (strcmp(p, shar->last_dir) == 0) {
 
229
                                /* We've already created this exact dir. */
 
230
                                free(p);
 
231
                        } else if (strlen(p) < strlen(shar->last_dir) &&
 
232
                            strncmp(p, shar->last_dir, strlen(p)) == 0) {
 
233
                                /* We've already created a subdir. */
 
234
                                free(p);
 
235
                        } else {
 
236
                                archive_strcat(&shar->work, "mkdir -p ");
 
237
                                shar_quote(&shar->work, p, 1);
 
238
                                archive_strcat(&shar->work,
 
239
                                    " > /dev/null 2>&1\n");
 
240
                                shar->last_dir = p;
 
241
                        }
 
242
                } else {
 
243
                        free(p);
 
244
                }
 
245
        }
 
246
 
 
247
        /* Handle file-type specific issues. */
 
248
        shar->has_data = 0;
 
249
        if ((linkname = archive_entry_hardlink(entry)) != NULL) {
 
250
                archive_strcat(&shar->work, "ln -f ");
 
251
                shar_quote(&shar->work, linkname, 1);
 
252
                archive_string_sprintf(&shar->work, " %s\n",
 
253
                    shar->quoted_name.s);
 
254
        } else if ((linkname = archive_entry_symlink(entry)) != NULL) {
 
255
                archive_strcat(&shar->work, "ln -fs ");
 
256
                shar_quote(&shar->work, linkname, 1);
 
257
                archive_string_sprintf(&shar->work, " %s\n",
 
258
                    shar->quoted_name.s);
 
259
        } else {
 
260
                switch(archive_entry_filetype(entry)) {
 
261
                case AE_IFREG:
 
262
                        if (archive_entry_size(entry) == 0) {
 
263
                                /* More portable than "touch." */
 
264
                                archive_string_sprintf(&shar->work,
 
265
                                    "test -e \"%s\" || :> \"%s\"\n",
 
266
                                    shar->quoted_name.s, shar->quoted_name.s);
 
267
                        } else {
 
268
                                if (shar->dump) {
 
269
                                        archive_string_sprintf(&shar->work,
 
270
                                            "uudecode -p > %s << 'SHAR_END'\n",
 
271
                                            shar->quoted_name.s);
 
272
                                        archive_string_sprintf(&shar->work,
 
273
                                            "begin %o ",
 
274
                                            archive_entry_mode(entry) & 0777);
 
275
                                        shar_quote(&shar->work, name, 0);
 
276
                                        archive_strcat(&shar->work, "\n");
 
277
                                } else {
 
278
                                        archive_string_sprintf(&shar->work,
 
279
                                            "sed 's/^X//' > %s << 'SHAR_END'\n",
 
280
                                            shar->quoted_name.s);
 
281
                                }
 
282
                                shar->has_data = 1;
 
283
                                shar->end_of_line = 1;
 
284
                                shar->outpos = 0;
 
285
                        }
 
286
                        break;
 
287
                case AE_IFDIR:
 
288
                        archive_string_sprintf(&shar->work,
 
289
                            "mkdir -p %s > /dev/null 2>&1\n",
 
290
                            shar->quoted_name.s);
 
291
                        /* Record that we just created this directory. */
 
292
                        if (shar->last_dir != NULL)
 
293
                                free(shar->last_dir);
 
294
 
 
295
                        shar->last_dir = strdup(name);
 
296
                        /* Trim a trailing '/'. */
 
297
                        pp = strrchr(shar->last_dir, '/');
 
298
                        if (pp != NULL && pp[1] == '\0')
 
299
                                *pp = '\0';
 
300
                        /*
 
301
                         * TODO: Put dir name/mode on a list to be fixed
 
302
                         * up at end of archive.
 
303
                         */
 
304
                        break;
 
305
                case AE_IFIFO:
 
306
                        archive_string_sprintf(&shar->work,
 
307
                            "mkfifo %s\n", shar->quoted_name.s);
 
308
                        break;
 
309
                case AE_IFCHR:
 
310
                        archive_string_sprintf(&shar->work,
 
311
                            "mknod %s c %d %d\n", shar->quoted_name.s,
 
312
                            archive_entry_rdevmajor(entry),
 
313
                            archive_entry_rdevminor(entry));
 
314
                        break;
 
315
                case AE_IFBLK:
 
316
                        archive_string_sprintf(&shar->work,
 
317
                            "mknod %s b %d %d\n", shar->quoted_name.s,
 
318
                            archive_entry_rdevmajor(entry),
 
319
                            archive_entry_rdevminor(entry));
 
320
                        break;
 
321
                default:
 
322
                        return (ARCHIVE_WARN);
 
323
                }
 
324
        }
 
325
 
 
326
        return (ARCHIVE_OK);
 
327
}
 
328
 
 
329
static ssize_t
 
330
archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
 
331
{
 
332
        static const size_t ensured = 65533;
 
333
        struct shar *shar;
 
334
        const char *src;
 
335
        char *buf, *buf_end;
 
336
        int ret;
 
337
        size_t written = n;
 
338
 
 
339
        shar = (struct shar *)a->format_data;
 
340
        if (!shar->has_data || n == 0)
 
341
                return (0);
 
342
 
 
343
        src = (const char *)buff;
 
344
 
 
345
        /*
 
346
         * ensure is the number of bytes in buffer before expanding the
 
347
         * current character.  Each operation writes the current character
 
348
         * and optionally the start-of-new-line marker.  This can happen
 
349
         * twice before entering the loop, so make sure three additional
 
350
         * bytes can be written.
 
351
         */
 
352
        if (archive_string_ensure(&shar->work, ensured + 3) == NULL)
 
353
                __archive_errx(1, "Out of memory");
 
354
 
 
355
        if (shar->work.length > ensured) {
 
356
                ret = (*a->compressor.write)(a, shar->work.s,
 
357
                    shar->work.length);
 
358
                if (ret != ARCHIVE_OK)
 
359
                        return (ARCHIVE_FATAL);
 
360
                archive_string_empty(&shar->work);
 
361
        }
 
362
        buf = shar->work.s + shar->work.length;
 
363
        buf_end = shar->work.s + ensured;
 
364
 
 
365
        if (shar->end_of_line) {
 
366
                *buf++ = 'X';
 
367
                shar->end_of_line = 0;
 
368
        }
 
369
 
 
370
        while (n-- != 0) {
 
371
                if ((*buf++ = *src++) == '\n') {
 
372
                        if (n == 0)
 
373
                                shar->end_of_line = 1;
 
374
                        else
 
375
                                *buf++ = 'X';
 
376
                }
 
377
 
 
378
                if (buf >= buf_end) {
 
379
                        shar->work.length = buf - shar->work.s;
 
380
                        ret = (*a->compressor.write)(a, shar->work.s,
 
381
                            shar->work.length);
 
382
                        if (ret != ARCHIVE_OK)
 
383
                                return (ARCHIVE_FATAL);
 
384
                        archive_string_empty(&shar->work);
 
385
                        buf = shar->work.s;
 
386
                }
 
387
        }
 
388
 
 
389
        shar->work.length = buf - shar->work.s;
 
390
 
 
391
        return (written);
 
392
}
 
393
 
 
394
#define UUENC(c)        (((c)!=0) ? ((c) & 077) + ' ': '`')
 
395
 
 
396
static void
 
397
uuencode_group(const char _in[3], char out[4])
 
398
{
 
399
        const unsigned char *in = (const unsigned char *)_in;
 
400
        int t;
 
401
 
 
402
        t = (in[0] << 16) | (in[1] << 8) | in[2];
 
403
        out[0] = UUENC( 0x3f & (t >> 18) );
 
404
        out[1] = UUENC( 0x3f & (t >> 12) );
 
405
        out[2] = UUENC( 0x3f & (t >> 6) );
 
406
        out[3] = UUENC( 0x3f & t );
 
407
}
 
408
 
 
409
static void
 
410
uuencode_line(struct shar *shar, const char *inbuf, size_t len)
 
411
{
 
412
        char tmp_buf[3], *buf;
 
413
        size_t alloc_len;
 
414
 
 
415
        /* len <= 45 -> expanded to 60 + len byte + new line */
 
416
        alloc_len = shar->work.length + 62;
 
417
        if (archive_string_ensure(&shar->work, alloc_len) == NULL)
 
418
                __archive_errx(1, "Out of memory");
 
419
 
 
420
        buf = shar->work.s + shar->work.length;
 
421
        *buf++ = UUENC(len);
 
422
        while (len >= 3) {
 
423
                uuencode_group(inbuf, buf);
 
424
                len -= 3;
 
425
                inbuf += 3;
 
426
                buf += 4;
 
427
        }
 
428
        if (len != 0) {
 
429
                tmp_buf[0] = inbuf[0];
 
430
                if (len == 1)
 
431
                        tmp_buf[1] = '\0';
 
432
                else
 
433
                        tmp_buf[1] = inbuf[1];
 
434
                tmp_buf[2] = '\0';
 
435
                uuencode_group(tmp_buf, buf);
 
436
                buf += 4;
 
437
        }
 
438
        *buf++ = '\n';
 
439
        if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62))
 
440
                __archive_errx(1, "Buffer overflow");
 
441
        shar->work.length = buf - shar->work.s;
 
442
}
 
443
 
 
444
static ssize_t
 
445
archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
 
446
    size_t length)
 
447
{
 
448
        struct shar *shar;
 
449
        const char *src;
 
450
        size_t n;
 
451
        int ret;
 
452
 
 
453
        shar = (struct shar *)a->format_data;
 
454
        if (!shar->has_data)
 
455
                return (ARCHIVE_OK);
 
456
        src = (const char *)buff;
 
457
 
 
458
        if (shar->outpos != 0) {
 
459
                n = 45 - shar->outpos;
 
460
                if (n > length)
 
461
                        n = length;
 
462
                memcpy(shar->outbuff + shar->outpos, src, n);
 
463
                if (shar->outpos + n < 45) {
 
464
                        shar->outpos += n;
 
465
                        return length;
 
466
                }
 
467
                uuencode_line(shar, shar->outbuff, 45);
 
468
                src += n;
 
469
                n = length - n;
 
470
        } else {
 
471
                n = length;
 
472
        }
 
473
 
 
474
        while (n >= 45) {
 
475
                uuencode_line(shar, src, 45);
 
476
                src += 45;
 
477
                n -= 45;
 
478
 
 
479
                if (shar->work.length < 65536)
 
480
                        continue;
 
481
                ret = (*a->compressor.write)(a, shar->work.s,
 
482
                    shar->work.length);
 
483
                if (ret != ARCHIVE_OK)
 
484
                        return (ARCHIVE_FATAL);
 
485
                archive_string_empty(&shar->work);
 
486
        }
 
487
        if (n != 0) {
 
488
                memcpy(shar->outbuff, src, n);
 
489
                shar->outpos = n;
 
490
        }
 
491
        return (length);
 
492
}
 
493
 
 
494
static int
 
495
archive_write_shar_finish_entry(struct archive_write *a)
 
496
{
 
497
        const char *g, *p, *u;
 
498
        struct shar *shar;
 
499
        int ret;
 
500
 
 
501
        shar = (struct shar *)a->format_data;
 
502
        if (shar->entry == NULL)
 
503
                return (0);
 
504
 
 
505
        if (shar->dump) {
 
506
                /* Finish uuencoded data. */
 
507
                if (shar->has_data) {
 
508
                        if (shar->outpos > 0)
 
509
                                uuencode_line(shar, shar->outbuff,
 
510
                                    shar->outpos);
 
511
                        archive_strcat(&shar->work, "`\nend\n");
 
512
                        archive_strcat(&shar->work, "SHAR_END\n");
 
513
                }
 
514
                /* Restore file mode, owner, flags. */
 
515
                /*
 
516
                 * TODO: Don't immediately restore mode for
 
517
                 * directories; defer that to end of script.
 
518
                 */
 
519
                archive_string_sprintf(&shar->work, "chmod %o ",
 
520
                    archive_entry_mode(shar->entry) & 07777);
 
521
                shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
 
522
                archive_strcat(&shar->work, "\n");
 
523
 
 
524
                u = archive_entry_uname(shar->entry);
 
525
                g = archive_entry_gname(shar->entry);
 
526
                if (u != NULL || g != NULL) {
 
527
                        archive_strcat(&shar->work, "chown ");
 
528
                        if (u != NULL)
 
529
                                shar_quote(&shar->work, u, 1);
 
530
                        if (g != NULL) {
 
531
                                archive_strcat(&shar->work, ":");
 
532
                                shar_quote(&shar->work, g, 1);
 
533
                        }
 
534
                        shar_quote(&shar->work,
 
535
                            archive_entry_pathname(shar->entry), 1);
 
536
                        archive_strcat(&shar->work, "\n");
 
537
                }
 
538
 
 
539
                if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
 
540
                        archive_string_sprintf(&shar->work, "chflags %s ",
 
541
                            p, archive_entry_pathname(shar->entry));
 
542
                        shar_quote(&shar->work,
 
543
                            archive_entry_pathname(shar->entry), 1);
 
544
                        archive_strcat(&shar->work, "\n");
 
545
                }
 
546
 
 
547
                /* TODO: restore ACLs */
 
548
 
 
549
        } else {
 
550
                if (shar->has_data) {
 
551
                        /* Finish sed-encoded data:  ensure last line ends. */
 
552
                        if (!shar->end_of_line)
 
553
                                archive_strappend_char(&shar->work, '\n');
 
554
                        archive_strcat(&shar->work, "SHAR_END\n");
 
555
                }
 
556
        }
 
557
 
 
558
        archive_entry_free(shar->entry);
 
559
        shar->entry = NULL;
 
560
 
 
561
        if (shar->work.length < 65536)
 
562
                return (ARCHIVE_OK);
 
563
 
 
564
        ret = (*a->compressor.write)(a, shar->work.s, shar->work.length);
 
565
        if (ret != ARCHIVE_OK)
 
566
                return (ARCHIVE_FATAL);
 
567
        archive_string_empty(&shar->work);
 
568
 
 
569
        return (ARCHIVE_OK);
 
570
}
 
571
 
 
572
static int
 
573
archive_write_shar_finish(struct archive_write *a)
 
574
{
 
575
        struct shar *shar;
 
576
        int ret;
 
577
 
 
578
        /*
 
579
         * TODO: Accumulate list of directory names/modes and
 
580
         * fix them all up at end-of-archive.
 
581
         */
 
582
 
 
583
        shar = (struct shar *)a->format_data;
 
584
 
 
585
        /*
 
586
         * Only write the end-of-archive markers if the archive was
 
587
         * actually started.  This avoids problems if someone sets
 
588
         * shar format, then sets another format (which would invoke
 
589
         * shar_finish to free the format-specific data).
 
590
         */
 
591
        if (shar->wrote_header == 0)
 
592
                return (ARCHIVE_OK);
 
593
 
 
594
        archive_strcat(&shar->work, "exit\n");
 
595
 
 
596
        ret = (*a->compressor.write)(a, shar->work.s, shar->work.length);
 
597
        if (ret != ARCHIVE_OK)
 
598
                return (ARCHIVE_FATAL);
 
599
 
 
600
        /* Shar output is never padded. */
 
601
        archive_write_set_bytes_in_last_block(&a->archive, 1);
 
602
        /*
 
603
         * TODO: shar should also suppress padding of
 
604
         * uncompressed data within gzip/bzip2 streams.
 
605
         */
 
606
 
 
607
        return (ARCHIVE_OK);
 
608
}
 
609
 
 
610
static int
 
611
archive_write_shar_destroy(struct archive_write *a)
 
612
{
 
613
        struct shar *shar;
 
614
 
 
615
        shar = (struct shar *)a->format_data;
 
616
        if (shar == NULL)
 
617
                return (ARCHIVE_OK);
 
618
 
 
619
        archive_entry_free(shar->entry);
 
620
        free(shar->last_dir);
 
621
        archive_string_free(&(shar->work));
 
622
        archive_string_free(&(shar->quoted_name));
 
623
        free(shar);
 
624
        a->format_data = NULL;
 
625
        return (ARCHIVE_OK);
 
626
}