~stewart/percona-xtrabackup/bug1213036

« back to all changes in this revision

Viewing changes to src/libarchive/tar/util.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/util.c,v 1.23 2008/12/15 06:00:25 kientzle Exp $");
 
28
 
 
29
#ifdef HAVE_SYS_STAT_H
 
30
#include <sys/stat.h>
 
31
#endif
 
32
#ifdef HAVE_SYS_TYPES_H
 
33
#include <sys/types.h>  /* Linux doesn't define mode_t, etc. in sys/stat.h. */
 
34
#endif
 
35
#include <ctype.h>
 
36
#ifdef HAVE_ERRNO_H
 
37
#include <errno.h>
 
38
#endif
 
39
#ifdef HAVE_IO_H
 
40
#include <io.h>
 
41
#endif
 
42
#ifdef HAVE_STDARG_H
 
43
#include <stdarg.h>
 
44
#endif
 
45
#ifdef HAVE_STDINT_H
 
46
#include <stdint.h>
 
47
#endif
 
48
#include <stdio.h>
 
49
#ifdef HAVE_STDLIB_H
 
50
#include <stdlib.h>
 
51
#endif
 
52
#ifdef HAVE_STRING_H
 
53
#include <string.h>
 
54
#endif
 
55
#ifdef HAVE_WCTYPE_H
 
56
#include <wctype.h>
 
57
#else
 
58
/* If we don't have wctype, we need to hack up some version of iswprint(). */
 
59
#define iswprint isprint
 
60
#endif
 
61
 
 
62
#include "bsdtar.h"
 
63
#include "err.h"
 
64
 
 
65
static size_t   bsdtar_expand_char(char *, size_t, char);
 
66
static const char *strip_components(const char *path, int elements);
 
67
 
 
68
#if defined(_WIN32) && !defined(__CYGWIN__)
 
69
#define read _read
 
70
#endif
 
71
 
 
72
/* TODO:  Hack up a version of mbtowc for platforms with no wide
 
73
 * character support at all.  I think the following might suffice,
 
74
 * but it needs careful testing.
 
75
 * #if !HAVE_MBTOWC
 
76
 * #define mbtowc(wcp, p, n) ((*wcp = *p), 1)
 
77
 * #endif
 
78
 */
 
79
 
 
80
/*
 
81
 * Print a string, taking care with any non-printable characters.
 
82
 *
 
83
 * Note that we use a stack-allocated buffer to receive the formatted
 
84
 * string if we can.  This is partly performance (avoiding a call to
 
85
 * malloc()), partly out of expedience (we have to call vsnprintf()
 
86
 * before malloc() anyway to find out how big a buffer we need; we may
 
87
 * as well point that first call at a small local buffer in case it
 
88
 * works), but mostly for safety (so we can use this to print messages
 
89
 * about out-of-memory conditions).
 
90
 */
 
91
 
 
92
void
 
93
safe_fprintf(FILE *f, const char *fmt, ...)
 
94
{
 
95
        char fmtbuff_stack[256]; /* Place to format the printf() string. */
 
96
        char outbuff[256]; /* Buffer for outgoing characters. */
 
97
        char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */
 
98
        char *fmtbuff;  /* Pointer to fmtbuff_stack or fmtbuff_heap. */
 
99
        int fmtbuff_length;
 
100
        int length, n;
 
101
        va_list ap;
 
102
        const char *p;
 
103
        unsigned i;
 
104
        wchar_t wc;
 
105
        char try_wc;
 
106
 
 
107
        /* Use a stack-allocated buffer if we can, for speed and safety. */
 
108
        fmtbuff_heap = NULL;
 
109
        fmtbuff_length = sizeof(fmtbuff_stack);
 
110
        fmtbuff = fmtbuff_stack;
 
111
 
 
112
        /* Try formatting into the stack buffer. */
 
113
        va_start(ap, fmt);
 
114
        length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap);
 
115
        va_end(ap);
 
116
 
 
117
        /* If the result was too large, allocate a buffer on the heap. */
 
118
        if (length >= fmtbuff_length) {
 
119
                fmtbuff_length = length+1;
 
120
                fmtbuff_heap = malloc(fmtbuff_length);
 
121
 
 
122
                /* Reformat the result into the heap buffer if we can. */
 
123
                if (fmtbuff_heap != NULL) {
 
124
                        fmtbuff = fmtbuff_heap;
 
125
                        va_start(ap, fmt);
 
126
                        length = vsnprintf(fmtbuff, fmtbuff_length, fmt, ap);
 
127
                        va_end(ap);
 
128
                } else {
 
129
                        /* Leave fmtbuff pointing to the truncated
 
130
                         * string in fmtbuff_stack. */
 
131
                        length = sizeof(fmtbuff_stack) - 1;
 
132
                }
 
133
        }
 
134
 
 
135
        /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit
 
136
         * more portable, so we use that here instead. */
 
137
        n = mbtowc(NULL, NULL, 1); /* Reset the shift state. */
 
138
 
 
139
        /* Write data, expanding unprintable characters. */
 
140
        p = fmtbuff;
 
141
        i = 0;
 
142
        try_wc = 1;
 
143
        while (*p != '\0') {
 
144
 
 
145
                /* Convert to wide char, test if the wide
 
146
                 * char is printable in the current locale. */
 
147
                if (try_wc && (n = mbtowc(&wc, p, length)) != -1) {
 
148
                        length -= n;
 
149
                        if (iswprint(wc) && wc != L'\\') {
 
150
                                /* Printable, copy the bytes through. */
 
151
                                while (n-- > 0)
 
152
                                        outbuff[i++] = *p++;
 
153
                        } else {
 
154
                                /* Not printable, format the bytes. */
 
155
                                while (n-- > 0)
 
156
                                        i += (unsigned)bsdtar_expand_char(
 
157
                                            outbuff, i, *p++);
 
158
                        }
 
159
                } else {
 
160
                        /* After any conversion failure, don't bother
 
161
                         * trying to convert the rest. */
 
162
                        i += (unsigned)bsdtar_expand_char(outbuff, i, *p++);
 
163
                        try_wc = 0;
 
164
                }
 
165
 
 
166
                /* If our output buffer is full, dump it and keep going. */
 
167
                if (i > (sizeof(outbuff) - 20)) {
 
168
                        outbuff[i] = '\0';
 
169
                        fprintf(f, "%s", outbuff);
 
170
                        i = 0;
 
171
                }
 
172
        }
 
173
        outbuff[i] = '\0';
 
174
        fprintf(f, "%s", outbuff);
 
175
 
 
176
        /* If we allocated a heap-based formatting buffer, free it now. */
 
177
        if (fmtbuff_heap != NULL)
 
178
                free(fmtbuff_heap);
 
179
}
 
180
 
 
181
/*
 
182
 * Render an arbitrary sequence of bytes into printable ASCII characters.
 
183
 */
 
184
static size_t
 
185
bsdtar_expand_char(char *buff, size_t offset, char c)
 
186
{
 
187
        size_t i = offset;
 
188
 
 
189
        if (isprint((unsigned char)c) && c != '\\')
 
190
                buff[i++] = c;
 
191
        else {
 
192
                buff[i++] = '\\';
 
193
                switch (c) {
 
194
                case '\a': buff[i++] = 'a'; break;
 
195
                case '\b': buff[i++] = 'b'; break;
 
196
                case '\f': buff[i++] = 'f'; break;
 
197
                case '\n': buff[i++] = 'n'; break;
 
198
#if '\r' != '\n'
 
199
                /* On some platforms, \n and \r are the same. */
 
200
                case '\r': buff[i++] = 'r'; break;
 
201
#endif
 
202
                case '\t': buff[i++] = 't'; break;
 
203
                case '\v': buff[i++] = 'v'; break;
 
204
                case '\\': buff[i++] = '\\'; break;
 
205
                default:
 
206
                        sprintf(buff + i, "%03o", 0xFF & (int)c);
 
207
                        i += 3;
 
208
                }
 
209
        }
 
210
 
 
211
        return (i - offset);
 
212
}
 
213
 
 
214
int
 
215
yes(const char *fmt, ...)
 
216
{
 
217
        char buff[32];
 
218
        char *p;
 
219
        ssize_t l;
 
220
 
 
221
        va_list ap;
 
222
        va_start(ap, fmt);
 
223
        vfprintf(stderr, fmt, ap);
 
224
        va_end(ap);
 
225
        fprintf(stderr, " (y/N)? ");
 
226
        fflush(stderr);
 
227
 
 
228
        l = read(2, buff, sizeof(buff) - 1);
 
229
        if (l <= 0)
 
230
                return (0);
 
231
        buff[l] = 0;
 
232
 
 
233
        for (p = buff; *p != '\0'; p++) {
 
234
                if (isspace((unsigned char)*p))
 
235
                        continue;
 
236
                switch(*p) {
 
237
                case 'y': case 'Y':
 
238
                        return (1);
 
239
                case 'n': case 'N':
 
240
                        return (0);
 
241
                default:
 
242
                        return (0);
 
243
                }
 
244
        }
 
245
 
 
246
        return (0);
 
247
}
 
248
 
 
249
/*-
 
250
 * The logic here for -C <dir> attempts to avoid
 
251
 * chdir() as long as possible.  For example:
 
252
 * "-C /foo -C /bar file"          needs chdir("/bar") but not chdir("/foo")
 
253
 * "-C /foo -C bar file"           needs chdir("/foo/bar")
 
254
 * "-C /foo -C bar /file1"         does not need chdir()
 
255
 * "-C /foo -C bar /file1 file2"   needs chdir("/foo/bar") before file2
 
256
 *
 
257
 * The only correct way to handle this is to record a "pending" chdir
 
258
 * request and combine multiple requests intelligently until we
 
259
 * need to process a non-absolute file.  set_chdir() adds the new dir
 
260
 * to the pending list; do_chdir() actually executes any pending chdir.
 
261
 *
 
262
 * This way, programs that build tar command lines don't have to worry
 
263
 * about -C with non-existent directories; such requests will only
 
264
 * fail if the directory must be accessed.
 
265
 *
 
266
 * TODO: Make this handle Windows paths correctly.
 
267
 */
 
268
void
 
269
set_chdir(struct bsdtar *bsdtar, const char *newdir)
 
270
{
 
271
        if (newdir[0] == '/') {
 
272
                /* The -C /foo -C /bar case; dump first one. */
 
273
                free(bsdtar->pending_chdir);
 
274
                bsdtar->pending_chdir = NULL;
 
275
        }
 
276
        if (bsdtar->pending_chdir == NULL)
 
277
                /* Easy case: no previously-saved dir. */
 
278
                bsdtar->pending_chdir = strdup(newdir);
 
279
        else {
 
280
                /* The -C /foo -C bar case; concatenate */
 
281
                char *old_pending = bsdtar->pending_chdir;
 
282
                size_t old_len = strlen(old_pending);
 
283
                bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2);
 
284
                if (old_pending[old_len - 1] == '/')
 
285
                        old_pending[old_len - 1] = '\0';
 
286
                if (bsdtar->pending_chdir != NULL)
 
287
                        sprintf(bsdtar->pending_chdir, "%s/%s",
 
288
                            old_pending, newdir);
 
289
                free(old_pending);
 
290
        }
 
291
        if (bsdtar->pending_chdir == NULL)
 
292
                lafe_errc(1, errno, "No memory");
 
293
}
 
294
 
 
295
void
 
296
do_chdir(struct bsdtar *bsdtar)
 
297
{
 
298
        if (bsdtar->pending_chdir == NULL)
 
299
                return;
 
300
 
 
301
        if (chdir(bsdtar->pending_chdir) != 0) {
 
302
                lafe_errc(1, 0, "could not chdir to '%s'\n",
 
303
                    bsdtar->pending_chdir);
 
304
        }
 
305
        free(bsdtar->pending_chdir);
 
306
        bsdtar->pending_chdir = NULL;
 
307
}
 
308
 
 
309
static const char *
 
310
strip_components(const char *p, int elements)
 
311
{
 
312
        /* Skip as many elements as necessary. */
 
313
        while (elements > 0) {
 
314
                switch (*p++) {
 
315
                case '/':
 
316
#if defined(_WIN32) && !defined(__CYGWIN__)
 
317
                case '\\': /* Support \ path sep on Windows ONLY. */
 
318
#endif
 
319
                        elements--;
 
320
                        break;
 
321
                case '\0':
 
322
                        /* Path is too short, skip it. */
 
323
                        return (NULL);
 
324
                }
 
325
        }
 
326
 
 
327
        /* Skip any / characters.  This handles short paths that have
 
328
         * additional / termination.  This also handles the case where
 
329
         * the logic above stops in the middle of a duplicate //
 
330
         * sequence (which would otherwise get converted to an
 
331
         * absolute path). */
 
332
        for (;;) {
 
333
                switch (*p) {
 
334
                case '/':
 
335
#if defined(_WIN32) && !defined(__CYGWIN__)
 
336
                case '\\': /* Support \ path sep on Windows ONLY. */
 
337
#endif
 
338
                        ++p;
 
339
                        break;
 
340
                case '\0':
 
341
                        return (NULL);
 
342
                default:
 
343
                        return (p);
 
344
                }
 
345
        }
 
346
}
 
347
 
 
348
/*
 
349
 * Handle --strip-components and any future path-rewriting options.
 
350
 * Returns non-zero if the pathname should not be extracted.
 
351
 *
 
352
 * TODO: Support pax-style regex path rewrites.
 
353
 */
 
354
int
 
355
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
 
356
{
 
357
        const char *name = archive_entry_pathname(entry);
 
358
#if HAVE_REGEX_H
 
359
        char *subst_name;
 
360
        int r;
 
361
#endif
 
362
 
 
363
#if HAVE_REGEX_H
 
364
        r = apply_substitution(bsdtar, name, &subst_name, 0);
 
365
        if (r == -1) {
 
366
                lafe_warnc(0, "Invalid substitution, skipping entry");
 
367
                return 1;
 
368
        }
 
369
        if (r == 1) {
 
370
                archive_entry_copy_pathname(entry, subst_name);
 
371
                if (*subst_name == '\0') {
 
372
                        free(subst_name);
 
373
                        return -1;
 
374
                } else
 
375
                        free(subst_name);
 
376
                name = archive_entry_pathname(entry);
 
377
        }
 
378
 
 
379
        if (archive_entry_hardlink(entry)) {
 
380
                r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 1);
 
381
                if (r == -1) {
 
382
                        lafe_warnc(0, "Invalid substitution, skipping entry");
 
383
                        return 1;
 
384
                }
 
385
                if (r == 1) {
 
386
                        archive_entry_copy_hardlink(entry, subst_name);
 
387
                        free(subst_name);
 
388
                }
 
389
        }
 
390
        if (archive_entry_symlink(entry) != NULL) {
 
391
                r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1);
 
392
                if (r == -1) {
 
393
                        lafe_warnc(0, "Invalid substitution, skipping entry");
 
394
                        return 1;
 
395
                }
 
396
                if (r == 1) {
 
397
                        archive_entry_copy_symlink(entry, subst_name);
 
398
                        free(subst_name);
 
399
                }
 
400
        }
 
401
#endif
 
402
 
 
403
        /* Strip leading dir names as per --strip-components option. */
 
404
        if (bsdtar->strip_components > 0) {
 
405
                const char *linkname = archive_entry_hardlink(entry);
 
406
 
 
407
                name = strip_components(name, bsdtar->strip_components);
 
408
                if (name == NULL)
 
409
                        return (1);
 
410
 
 
411
                if (linkname != NULL) {
 
412
                        linkname = strip_components(linkname,
 
413
                            bsdtar->strip_components);
 
414
                        if (linkname == NULL)
 
415
                                return (1);
 
416
                        archive_entry_copy_hardlink(entry, linkname);
 
417
                }
 
418
        }
 
419
 
 
420
        /* By default, don't write or restore absolute pathnames. */
 
421
        if (!bsdtar->option_absolute_paths) {
 
422
                const char *rp, *p = name;
 
423
                int slashonly = 1;
 
424
 
 
425
                /* Remove leading "//./" or "//?/" or "//?/UNC/"
 
426
                 * (absolute path prefixes used by Windows API) */
 
427
                if ((p[0] == '/' || p[0] == '\\') &&
 
428
                    (p[1] == '/' || p[1] == '\\') &&
 
429
                    (p[2] == '.' || p[2] == '?') &&
 
430
                    (p[3] == '/' || p[3] == '\\'))
 
431
                {
 
432
                        if (p[2] == '?' &&
 
433
                            (p[4] == 'U' || p[4] == 'u') &&
 
434
                            (p[5] == 'N' || p[5] == 'n') &&
 
435
                            (p[6] == 'C' || p[6] == 'c') &&
 
436
                            (p[7] == '/' || p[7] == '\\'))
 
437
                                p += 8;
 
438
                        else
 
439
                                p += 4;
 
440
                        slashonly = 0;
 
441
                }
 
442
                do {
 
443
                        rp = p;
 
444
                        /* Remove leading drive letter from archives created
 
445
                         * on Windows. */
 
446
                        if (((p[0] >= 'a' && p[0] <= 'z') ||
 
447
                             (p[0] >= 'A' && p[0] <= 'Z')) &&
 
448
                                 p[1] == ':') {
 
449
                                p += 2;
 
450
                                slashonly = 0;
 
451
                        }
 
452
                        /* Remove leading "/../", "//", etc. */
 
453
                        while (p[0] == '/' || p[0] == '\\') {
 
454
                                if (p[1] == '.' && p[2] == '.' &&
 
455
                                        (p[3] == '/' || p[3] == '\\')) {
 
456
                                        p += 3; /* Remove "/..", leave "/"
 
457
                                                         * for next pass. */
 
458
                                        slashonly = 0;
 
459
                                } else
 
460
                                        p += 1; /* Remove "/". */
 
461
                        }
 
462
                } while (rp != p);
 
463
 
 
464
                if (p != name && !bsdtar->warned_lead_slash) {
 
465
                        /* Generate a warning the first time this happens. */
 
466
                        if (slashonly)
 
467
                                lafe_warnc(0,
 
468
                                    "Removing leading '%c' from member names",
 
469
                                    name[0]);
 
470
                        else
 
471
                                lafe_warnc(0,
 
472
                                    "Removing leading drive letter from "
 
473
                                    "member names");
 
474
                        bsdtar->warned_lead_slash = 1;
 
475
                }
 
476
 
 
477
                /* Special case: Stripping everything yields ".". */
 
478
                if (*p == '\0')
 
479
                        name = ".";
 
480
                else
 
481
                        name = p;
 
482
        } else {
 
483
                /* Strip redundant leading '/' characters. */
 
484
                while (name[0] == '/' && name[1] == '/')
 
485
                        name++;
 
486
        }
 
487
 
 
488
        /* Safely replace name in archive_entry. */
 
489
        if (name != archive_entry_pathname(entry)) {
 
490
                char *q = strdup(name);
 
491
                archive_entry_copy_pathname(entry, q);
 
492
                free(q);
 
493
        }
 
494
        return (0);
 
495
}
 
496
 
 
497
/*
 
498
 * It would be nice to just use printf() for formatting large numbers,
 
499
 * but the compatibility problems are quite a headache.  Hence the
 
500
 * following simple utility function.
 
501
 */
 
502
const char *
 
503
tar_i64toa(int64_t n0)
 
504
{
 
505
        static char buff[24];
 
506
        int64_t n = n0 < 0 ? -n0 : n0;
 
507
        char *p = buff + sizeof(buff);
 
508
 
 
509
        *--p = '\0';
 
510
        do {
 
511
                *--p = '0' + (int)(n % 10);
 
512
                n /= 10;
 
513
        } while (n > 0);
 
514
        if (n0 < 0)
 
515
                *--p = '-';
 
516
        return p;
 
517
}
 
518
 
 
519
/*
 
520
 * Like strcmp(), but try to be a little more aware of the fact that
 
521
 * we're comparing two paths.  Right now, it just handles leading
 
522
 * "./" and trailing '/' specially, so that "a/b/" == "./a/b"
 
523
 *
 
524
 * TODO: Make this better, so that "./a//b/./c/" == "a/b/c"
 
525
 * TODO: After this works, push it down into libarchive.
 
526
 * TODO: Publish the path normalization routines in libarchive so
 
527
 * that bsdtar can normalize paths and use fast strcmp() instead
 
528
 * of this.
 
529
 *
 
530
 * Note: This is currently only used within write.c, so should
 
531
 * not handle \ path separators.
 
532
 */
 
533
 
 
534
int
 
535
pathcmp(const char *a, const char *b)
 
536
{
 
537
        /* Skip leading './' */
 
538
        if (a[0] == '.' && a[1] == '/' && a[2] != '\0')
 
539
                a += 2;
 
540
        if (b[0] == '.' && b[1] == '/' && b[2] != '\0')
 
541
                b += 2;
 
542
        /* Find the first difference, or return (0) if none. */
 
543
        while (*a == *b) {
 
544
                if (*a == '\0')
 
545
                        return (0);
 
546
                a++;
 
547
                b++;
 
548
        }
 
549
        /*
 
550
         * If one ends in '/' and the other one doesn't,
 
551
         * they're the same.
 
552
         */
 
553
        if (a[0] == '/' && a[1] == '\0' && b[0] == '\0')
 
554
                return (0);
 
555
        if (a[0] == '\0' && b[0] == '/' && b[1] == '\0')
 
556
                return (0);
 
557
        /* They're really different, return the correct sign. */
 
558
        return (*(const unsigned char *)a - *(const unsigned char *)b);
 
559
}