~akopytov/percona-xtrabackup/bug1166888-2.0

« back to all changes in this revision

Viewing changes to src/libarchive/libarchive/archive_read_support_format_mtree.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
 * 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_read_support_format_mtree.c 201165 2009-12-29 05:52:13Z kientzle $");
 
29
 
 
30
#ifdef HAVE_SYS_STAT_H
 
31
#include <sys/stat.h>
 
32
#endif
 
33
#ifdef HAVE_ERRNO_H
 
34
#include <errno.h>
 
35
#endif
 
36
#ifdef HAVE_FCNTL_H
 
37
#include <fcntl.h>
 
38
#endif
 
39
#include <stddef.h>
 
40
/* #include <stdint.h> */ /* See archive_platform.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 "archive.h"
 
49
#include "archive_entry.h"
 
50
#include "archive_private.h"
 
51
#include "archive_read_private.h"
 
52
#include "archive_string.h"
 
53
 
 
54
#ifndef O_BINARY
 
55
#define O_BINARY 0
 
56
#endif
 
57
 
 
58
#define MTREE_HAS_DEVICE        0x0001
 
59
#define MTREE_HAS_FFLAGS        0x0002
 
60
#define MTREE_HAS_GID           0x0004
 
61
#define MTREE_HAS_GNAME         0x0008
 
62
#define MTREE_HAS_MTIME         0x0010
 
63
#define MTREE_HAS_NLINK         0x0020
 
64
#define MTREE_HAS_PERM          0x0040
 
65
#define MTREE_HAS_SIZE          0x0080
 
66
#define MTREE_HAS_TYPE          0x0100
 
67
#define MTREE_HAS_UID           0x0200
 
68
#define MTREE_HAS_UNAME         0x0400
 
69
 
 
70
#define MTREE_HAS_OPTIONAL      0x0800
 
71
 
 
72
struct mtree_option {
 
73
        struct mtree_option *next;
 
74
        char *value;
 
75
};
 
76
 
 
77
struct mtree_entry {
 
78
        struct mtree_entry *next;
 
79
        struct mtree_option *options;
 
80
        char *name;
 
81
        char full;
 
82
        char used;
 
83
};
 
84
 
 
85
struct mtree {
 
86
        struct archive_string    line;
 
87
        size_t                   buffsize;
 
88
        char                    *buff;
 
89
        off_t                    offset;
 
90
        int                      fd;
 
91
        int                      filetype;
 
92
        int                      archive_format;
 
93
        const char              *archive_format_name;
 
94
        struct mtree_entry      *entries;
 
95
        struct mtree_entry      *this_entry;
 
96
        struct archive_string    current_dir;
 
97
        struct archive_string    contents_name;
 
98
 
 
99
        struct archive_entry_linkresolver *resolver;
 
100
 
 
101
        off_t                    cur_size, cur_offset;
 
102
};
 
103
 
 
104
static int      cleanup(struct archive_read *);
 
105
static int      mtree_bid(struct archive_read *);
 
106
static int      parse_file(struct archive_read *, struct archive_entry *,
 
107
                    struct mtree *, struct mtree_entry *, int *);
 
108
static void     parse_escapes(char *, struct mtree_entry *);
 
109
static int      parse_line(struct archive_read *, struct archive_entry *,
 
110
                    struct mtree *, struct mtree_entry *, int *);
 
111
static int      parse_keyword(struct archive_read *, struct mtree *,
 
112
                    struct archive_entry *, struct mtree_option *, int *);
 
113
static int      read_data(struct archive_read *a,
 
114
                    const void **buff, size_t *size, off_t *offset);
 
115
static ssize_t  readline(struct archive_read *, struct mtree *, char **, ssize_t);
 
116
static int      skip(struct archive_read *a);
 
117
static int      read_header(struct archive_read *,
 
118
                    struct archive_entry *);
 
119
static int64_t  mtree_atol10(char **);
 
120
static int64_t  mtree_atol8(char **);
 
121
static int64_t  mtree_atol(char **);
 
122
 
 
123
static void
 
124
free_options(struct mtree_option *head)
 
125
{
 
126
        struct mtree_option *next;
 
127
 
 
128
        for (; head != NULL; head = next) {
 
129
                next = head->next;
 
130
                free(head->value);
 
131
                free(head);
 
132
        }
 
133
}
 
134
 
 
135
int
 
136
archive_read_support_format_mtree(struct archive *_a)
 
137
{
 
138
        struct archive_read *a = (struct archive_read *)_a;
 
139
        struct mtree *mtree;
 
140
        int r;
 
141
 
 
142
        mtree = (struct mtree *)malloc(sizeof(*mtree));
 
143
        if (mtree == NULL) {
 
144
                archive_set_error(&a->archive, ENOMEM,
 
145
                    "Can't allocate mtree data");
 
146
                return (ARCHIVE_FATAL);
 
147
        }
 
148
        memset(mtree, 0, sizeof(*mtree));
 
149
        mtree->fd = -1;
 
150
 
 
151
        r = __archive_read_register_format(a, mtree, "mtree",
 
152
            mtree_bid, NULL, read_header, read_data, skip, cleanup);
 
153
 
 
154
        if (r != ARCHIVE_OK)
 
155
                free(mtree);
 
156
        return (ARCHIVE_OK);
 
157
}
 
158
 
 
159
static int
 
160
cleanup(struct archive_read *a)
 
161
{
 
162
        struct mtree *mtree;
 
163
        struct mtree_entry *p, *q;
 
164
 
 
165
        mtree = (struct mtree *)(a->format->data);
 
166
 
 
167
        p = mtree->entries;
 
168
        while (p != NULL) {
 
169
                q = p->next;
 
170
                free(p->name);
 
171
                free_options(p->options);
 
172
                free(p);
 
173
                p = q;
 
174
        }
 
175
        archive_string_free(&mtree->line);
 
176
        archive_string_free(&mtree->current_dir);
 
177
        archive_string_free(&mtree->contents_name);
 
178
        archive_entry_linkresolver_free(mtree->resolver);
 
179
 
 
180
        free(mtree->buff);
 
181
        free(mtree);
 
182
        (a->format->data) = NULL;
 
183
        return (ARCHIVE_OK);
 
184
}
 
185
 
 
186
 
 
187
static int
 
188
mtree_bid(struct archive_read *a)
 
189
{
 
190
        const char *signature = "#mtree";
 
191
        const char *p;
 
192
 
 
193
        /* Now let's look at the actual header and see if it matches. */
 
194
        p = __archive_read_ahead(a, strlen(signature), NULL);
 
195
        if (p == NULL)
 
196
                return (-1);
 
197
 
 
198
        if (strncmp(p, signature, strlen(signature)) == 0)
 
199
                return (8 * (int)strlen(signature));
 
200
        return (0);
 
201
}
 
202
 
 
203
/*
 
204
 * The extended mtree format permits multiple lines specifying
 
205
 * attributes for each file.  For those entries, only the last line
 
206
 * is actually used.  Practically speaking, that means we have
 
207
 * to read the entire mtree file into memory up front.
 
208
 *
 
209
 * The parsing is done in two steps.  First, it is decided if a line
 
210
 * changes the global defaults and if it is, processed accordingly.
 
211
 * Otherwise, the options of the line are merged with the current
 
212
 * global options.
 
213
 */
 
214
static int
 
215
add_option(struct archive_read *a, struct mtree_option **global,
 
216
    const char *value, size_t len)
 
217
{
 
218
        struct mtree_option *option;
 
219
 
 
220
        if ((option = malloc(sizeof(*option))) == NULL) {
 
221
                archive_set_error(&a->archive, errno, "Can't allocate memory");
 
222
                return (ARCHIVE_FATAL);
 
223
        }
 
224
        if ((option->value = malloc(len + 1)) == NULL) {
 
225
                free(option);
 
226
                archive_set_error(&a->archive, errno, "Can't allocate memory");
 
227
                return (ARCHIVE_FATAL);
 
228
        }
 
229
        memcpy(option->value, value, len);
 
230
        option->value[len] = '\0';
 
231
        option->next = *global;
 
232
        *global = option;
 
233
        return (ARCHIVE_OK);
 
234
}
 
235
 
 
236
static void
 
237
remove_option(struct mtree_option **global, const char *value, size_t len)
 
238
{
 
239
        struct mtree_option *iter, *last;
 
240
 
 
241
        last = NULL;
 
242
        for (iter = *global; iter != NULL; last = iter, iter = iter->next) {
 
243
                if (strncmp(iter->value, value, len) == 0 &&
 
244
                    (iter->value[len] == '\0' ||
 
245
                     iter->value[len] == '='))
 
246
                        break;
 
247
        }
 
248
        if (iter == NULL)
 
249
                return;
 
250
        if (last == NULL)
 
251
                *global = iter->next;
 
252
        else
 
253
                last->next = iter->next;
 
254
 
 
255
        free(iter->value);
 
256
        free(iter);
 
257
}
 
258
 
 
259
static int
 
260
process_global_set(struct archive_read *a,
 
261
    struct mtree_option **global, const char *line)
 
262
{
 
263
        const char *next, *eq;
 
264
        size_t len;
 
265
        int r;
 
266
 
 
267
        line += 4;
 
268
        for (;;) {
 
269
                next = line + strspn(line, " \t\r\n");
 
270
                if (*next == '\0')
 
271
                        return (ARCHIVE_OK);
 
272
                line = next;
 
273
                next = line + strcspn(line, " \t\r\n");
 
274
                eq = strchr(line, '=');
 
275
                if (eq > next)
 
276
                        len = next - line;
 
277
                else
 
278
                        len = eq - line;
 
279
 
 
280
                remove_option(global, line, len);
 
281
                r = add_option(a, global, line, next - line);
 
282
                if (r != ARCHIVE_OK)
 
283
                        return (r);
 
284
                line = next;
 
285
        }
 
286
}
 
287
 
 
288
static int
 
289
process_global_unset(struct archive_read *a,
 
290
    struct mtree_option **global, const char *line)
 
291
{
 
292
        const char *next;
 
293
        size_t len;
 
294
 
 
295
        line += 6;
 
296
        if (strchr(line, '=') != NULL) {
 
297
                archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 
298
                    "/unset shall not contain `='");
 
299
                return ARCHIVE_FATAL;
 
300
        }
 
301
 
 
302
        for (;;) {
 
303
                next = line + strspn(line, " \t\r\n");
 
304
                if (*next == '\0')
 
305
                        return (ARCHIVE_OK);
 
306
                line = next;
 
307
                len = strcspn(line, " \t\r\n");
 
308
 
 
309
                if (len == 3 && strncmp(line, "all", 3) == 0) {
 
310
                        free_options(*global);
 
311
                        *global = NULL;
 
312
                } else {
 
313
                        remove_option(global, line, len);
 
314
                }
 
315
 
 
316
                line += len;
 
317
        }
 
318
}
 
319
 
 
320
static int
 
321
process_add_entry(struct archive_read *a, struct mtree *mtree,
 
322
    struct mtree_option **global, const char *line,
 
323
    struct mtree_entry **last_entry)
 
324
{
 
325
        struct mtree_entry *entry;
 
326
        struct mtree_option *iter;
 
327
        const char *next, *eq;
 
328
        size_t len;
 
329
        int r;
 
330
 
 
331
        if ((entry = malloc(sizeof(*entry))) == NULL) {
 
332
                archive_set_error(&a->archive, errno, "Can't allocate memory");
 
333
                return (ARCHIVE_FATAL);
 
334
        }
 
335
        entry->next = NULL;
 
336
        entry->options = NULL;
 
337
        entry->name = NULL;
 
338
        entry->used = 0;
 
339
        entry->full = 0;
 
340
 
 
341
        /* Add this entry to list. */
 
342
        if (*last_entry == NULL)
 
343
                mtree->entries = entry;
 
344
        else
 
345
                (*last_entry)->next = entry;
 
346
        *last_entry = entry;
 
347
 
 
348
        len = strcspn(line, " \t\r\n");
 
349
        if ((entry->name = malloc(len + 1)) == NULL) {
 
350
                archive_set_error(&a->archive, errno, "Can't allocate memory");
 
351
                return (ARCHIVE_FATAL);
 
352
        }
 
353
 
 
354
        memcpy(entry->name, line, len);
 
355
        entry->name[len] = '\0';
 
356
        parse_escapes(entry->name, entry);
 
357
 
 
358
        line += len;
 
359
        for (iter = *global; iter != NULL; iter = iter->next) {
 
360
                r = add_option(a, &entry->options, iter->value,
 
361
                    strlen(iter->value));
 
362
                if (r != ARCHIVE_OK)
 
363
                        return (r);
 
364
        }
 
365
 
 
366
        for (;;) {
 
367
                next = line + strspn(line, " \t\r\n");
 
368
                if (*next == '\0')
 
369
                        return (ARCHIVE_OK);
 
370
                line = next;
 
371
                next = line + strcspn(line, " \t\r\n");
 
372
                eq = strchr(line, '=');
 
373
                if (eq == NULL || eq > next)
 
374
                        len = next - line;
 
375
                else
 
376
                        len = eq - line;
 
377
 
 
378
                remove_option(&entry->options, line, len);
 
379
                r = add_option(a, &entry->options, line, next - line);
 
380
                if (r != ARCHIVE_OK)
 
381
                        return (r);
 
382
                line = next;
 
383
        }
 
384
}
 
385
 
 
386
static int
 
387
read_mtree(struct archive_read *a, struct mtree *mtree)
 
388
{
 
389
        ssize_t len;
 
390
        uintmax_t counter;
 
391
        char *p;
 
392
        struct mtree_option *global;
 
393
        struct mtree_entry *last_entry;
 
394
        int r;
 
395
 
 
396
        mtree->archive_format = ARCHIVE_FORMAT_MTREE;
 
397
        mtree->archive_format_name = "mtree";
 
398
 
 
399
        global = NULL;
 
400
        last_entry = NULL;
 
401
 
 
402
        for (counter = 1; ; ++counter) {
 
403
                len = readline(a, mtree, &p, 256);
 
404
                if (len == 0) {
 
405
                        mtree->this_entry = mtree->entries;
 
406
                        free_options(global);
 
407
                        return (ARCHIVE_OK);
 
408
                }
 
409
                if (len < 0) {
 
410
                        free_options(global);
 
411
                        return (len);
 
412
                }
 
413
                /* Leading whitespace is never significant, ignore it. */
 
414
                while (*p == ' ' || *p == '\t') {
 
415
                        ++p;
 
416
                        --len;
 
417
                }
 
418
                /* Skip content lines and blank lines. */
 
419
                if (*p == '#')
 
420
                        continue;
 
421
                if (*p == '\r' || *p == '\n' || *p == '\0')
 
422
                        continue;
 
423
                if (*p != '/') {
 
424
                        r = process_add_entry(a, mtree, &global, p,
 
425
                            &last_entry);
 
426
                } else if (strncmp(p, "/set", 4) == 0) {
 
427
                        if (p[4] != ' ' && p[4] != '\t')
 
428
                                break;
 
429
                        r = process_global_set(a, &global, p);
 
430
                } else if (strncmp(p, "/unset", 6) == 0) {
 
431
                        if (p[6] != ' ' && p[6] != '\t')
 
432
                                break;
 
433
                        r = process_global_unset(a, &global, p);
 
434
                } else
 
435
                        break;
 
436
 
 
437
                if (r != ARCHIVE_OK) {
 
438
                        free_options(global);
 
439
                        return r;
 
440
                }
 
441
        }
 
442
 
 
443
        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 
444
            "Can't parse line %ju", counter);
 
445
        free_options(global);
 
446
        return (ARCHIVE_FATAL);
 
447
}
 
448
 
 
449
/*
 
450
 * Read in the entire mtree file into memory on the first request.
 
451
 * Then use the next unused file to satisfy each header request.
 
452
 */
 
453
static int
 
454
read_header(struct archive_read *a, struct archive_entry *entry)
 
455
{
 
456
        struct mtree *mtree;
 
457
        char *p;
 
458
        int r, use_next;
 
459
 
 
460
        mtree = (struct mtree *)(a->format->data);
 
461
 
 
462
        if (mtree->fd >= 0) {
 
463
                close(mtree->fd);
 
464
                mtree->fd = -1;
 
465
        }
 
466
 
 
467
        if (mtree->entries == NULL) {
 
468
                mtree->resolver = archive_entry_linkresolver_new();
 
469
                if (mtree->resolver == NULL)
 
470
                        return ARCHIVE_FATAL;
 
471
                archive_entry_linkresolver_set_strategy(mtree->resolver,
 
472
                    ARCHIVE_FORMAT_MTREE);
 
473
                r = read_mtree(a, mtree);
 
474
                if (r != ARCHIVE_OK)
 
475
                        return (r);
 
476
        }
 
477
 
 
478
        a->archive.archive_format = mtree->archive_format;
 
479
        a->archive.archive_format_name = mtree->archive_format_name;
 
480
 
 
481
        for (;;) {
 
482
                if (mtree->this_entry == NULL)
 
483
                        return (ARCHIVE_EOF);
 
484
                if (strcmp(mtree->this_entry->name, "..") == 0) {
 
485
                        mtree->this_entry->used = 1;
 
486
                        if (archive_strlen(&mtree->current_dir) > 0) {
 
487
                                /* Roll back current path. */
 
488
                                p = mtree->current_dir.s
 
489
                                    + mtree->current_dir.length - 1;
 
490
                                while (p >= mtree->current_dir.s && *p != '/')
 
491
                                        --p;
 
492
                                if (p >= mtree->current_dir.s)
 
493
                                        --p;
 
494
                                mtree->current_dir.length
 
495
                                    = p - mtree->current_dir.s + 1;
 
496
                        }
 
497
                }
 
498
                if (!mtree->this_entry->used) {
 
499
                        use_next = 0;
 
500
                        r = parse_file(a, entry, mtree, mtree->this_entry, &use_next);
 
501
                        if (use_next == 0)
 
502
                                return (r);
 
503
                }
 
504
                mtree->this_entry = mtree->this_entry->next;
 
505
        }
 
506
}
 
507
 
 
508
/*
 
509
 * A single file can have multiple lines contribute specifications.
 
510
 * Parse as many lines as necessary, then pull additional information
 
511
 * from a backing file on disk as necessary.
 
512
 */
 
513
static int
 
514
parse_file(struct archive_read *a, struct archive_entry *entry,
 
515
    struct mtree *mtree, struct mtree_entry *mentry, int *use_next)
 
516
{
 
517
        const char *path;
 
518
        struct stat st_storage, *st;
 
519
        struct mtree_entry *mp;
 
520
        struct archive_entry *sparse_entry;
 
521
        int r = ARCHIVE_OK, r1, parsed_kws, mismatched_type;
 
522
 
 
523
        mentry->used = 1;
 
524
 
 
525
        /* Initialize reasonable defaults. */
 
526
        mtree->filetype = AE_IFREG;
 
527
        archive_entry_set_size(entry, 0);
 
528
 
 
529
        /* Parse options from this line. */
 
530
        parsed_kws = 0;
 
531
        r = parse_line(a, entry, mtree, mentry, &parsed_kws);
 
532
 
 
533
        if (mentry->full) {
 
534
                archive_entry_copy_pathname(entry, mentry->name);
 
535
                /*
 
536
                 * "Full" entries are allowed to have multiple lines
 
537
                 * and those lines aren't required to be adjacent.  We
 
538
                 * don't support multiple lines for "relative" entries
 
539
                 * nor do we make any attempt to merge data from
 
540
                 * separate "relative" and "full" entries.  (Merging
 
541
                 * "relative" and "full" entries would require dealing
 
542
                 * with pathname canonicalization, which is a very
 
543
                 * tricky subject.)
 
544
                 */
 
545
                for (mp = mentry->next; mp != NULL; mp = mp->next) {
 
546
                        if (mp->full && !mp->used
 
547
                            && strcmp(mentry->name, mp->name) == 0) {
 
548
                                /* Later lines override earlier ones. */
 
549
                                mp->used = 1;
 
550
                                r1 = parse_line(a, entry, mtree, mp,
 
551
                                    &parsed_kws);
 
552
                                if (r1 < r)
 
553
                                        r = r1;
 
554
                        }
 
555
                }
 
556
        } else {
 
557
                /*
 
558
                 * Relative entries require us to construct
 
559
                 * the full path and possibly update the
 
560
                 * current directory.
 
561
                 */
 
562
                size_t n = archive_strlen(&mtree->current_dir);
 
563
                if (n > 0)
 
564
                        archive_strcat(&mtree->current_dir, "/");
 
565
                archive_strcat(&mtree->current_dir, mentry->name);
 
566
                archive_entry_copy_pathname(entry, mtree->current_dir.s);
 
567
                if (archive_entry_filetype(entry) != AE_IFDIR)
 
568
                        mtree->current_dir.length = n;
 
569
        }
 
570
 
 
571
        /*
 
572
         * Try to open and stat the file to get the real size
 
573
         * and other file info.  It would be nice to avoid
 
574
         * this here so that getting a listing of an mtree
 
575
         * wouldn't require opening every referenced contents
 
576
         * file.  But then we wouldn't know the actual
 
577
         * contents size, so I don't see a really viable way
 
578
         * around this.  (Also, we may want to someday pull
 
579
         * other unspecified info from the contents file on
 
580
         * disk.)
 
581
         */
 
582
        mtree->fd = -1;
 
583
        if (archive_strlen(&mtree->contents_name) > 0)
 
584
                path = mtree->contents_name.s;
 
585
        else
 
586
                path = archive_entry_pathname(entry);
 
587
 
 
588
        if (archive_entry_filetype(entry) == AE_IFREG ||
 
589
            archive_entry_filetype(entry) == AE_IFDIR) {
 
590
                mtree->fd = open(path, O_RDONLY | O_BINARY);
 
591
                if (mtree->fd == -1 &&
 
592
                    (errno != ENOENT ||
 
593
                     archive_strlen(&mtree->contents_name) > 0)) {
 
594
                        archive_set_error(&a->archive, errno,
 
595
                            "Can't open %s", path);
 
596
                        r = ARCHIVE_WARN;
 
597
                }
 
598
        }
 
599
 
 
600
        st = &st_storage;
 
601
        if (mtree->fd >= 0) {
 
602
                if (fstat(mtree->fd, st) == -1) {
 
603
                        archive_set_error(&a->archive, errno,
 
604
                            "Could not fstat %s", path);
 
605
                        r = ARCHIVE_WARN;
 
606
                        /* If we can't stat it, don't keep it open. */
 
607
                        close(mtree->fd);
 
608
                        mtree->fd = -1;
 
609
                        st = NULL;
 
610
                }
 
611
        } else if (lstat(path, st) == -1) {
 
612
                st = NULL;
 
613
        }
 
614
 
 
615
        /*
 
616
         * If there is a contents file on disk, use that size;
 
617
         * otherwise leave it as-is (it might have been set from
 
618
         * the mtree size= keyword).
 
619
         */
 
620
        if (st != NULL) {
 
621
                mismatched_type = 0;
 
622
                if ((st->st_mode & S_IFMT) == S_IFREG &&
 
623
                    archive_entry_filetype(entry) != AE_IFREG)
 
624
                        mismatched_type = 1;
 
625
                if ((st->st_mode & S_IFMT) == S_IFLNK &&
 
626
                    archive_entry_filetype(entry) != AE_IFLNK)
 
627
                        mismatched_type = 1;
 
628
                if ((st->st_mode & S_IFSOCK) == S_IFSOCK &&
 
629
                    archive_entry_filetype(entry) != AE_IFSOCK)
 
630
                        mismatched_type = 1;
 
631
                if ((st->st_mode & S_IFMT) == S_IFCHR &&
 
632
                    archive_entry_filetype(entry) != AE_IFCHR)
 
633
                        mismatched_type = 1;
 
634
                if ((st->st_mode & S_IFMT) == S_IFBLK &&
 
635
                    archive_entry_filetype(entry) != AE_IFBLK)
 
636
                        mismatched_type = 1;
 
637
                if ((st->st_mode & S_IFMT) == S_IFDIR &&
 
638
                    archive_entry_filetype(entry) != AE_IFDIR)
 
639
                        mismatched_type = 1;
 
640
                if ((st->st_mode & S_IFMT) == S_IFIFO &&
 
641
                    archive_entry_filetype(entry) != AE_IFIFO)
 
642
                        mismatched_type = 1;
 
643
 
 
644
                if (mismatched_type) {
 
645
                        if ((parsed_kws & MTREE_HAS_OPTIONAL) == 0) {
 
646
                                archive_set_error(&a->archive,
 
647
                                    ARCHIVE_ERRNO_MISC,
 
648
                                    "mtree specification has different type for %s",
 
649
                                    archive_entry_pathname(entry));
 
650
                                r = ARCHIVE_WARN;
 
651
                        } else {
 
652
                                *use_next = 1;
 
653
                        }
 
654
                        /* Don't hold a non-regular file open. */
 
655
                        if (mtree->fd >= 0)
 
656
                                close(mtree->fd);
 
657
                        mtree->fd = -1;
 
658
                        st = NULL;
 
659
                        return r;
 
660
                }
 
661
        }
 
662
 
 
663
        if (st != NULL) {
 
664
                if ((parsed_kws & MTREE_HAS_DEVICE) == 0 &&
 
665
                    (archive_entry_filetype(entry) == AE_IFCHR ||
 
666
                     archive_entry_filetype(entry) == AE_IFBLK))
 
667
                        archive_entry_set_rdev(entry, st->st_rdev);
 
668
                if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0)
 
669
                        archive_entry_set_gid(entry, st->st_gid);
 
670
                if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0)
 
671
                        archive_entry_set_uid(entry, st->st_uid);
 
672
                if ((parsed_kws & MTREE_HAS_MTIME) == 0) {
 
673
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
 
674
                        archive_entry_set_mtime(entry, st->st_mtime,
 
675
                            st->st_mtimespec.tv_nsec);
 
676
#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
 
677
                        archive_entry_set_mtime(entry, st->st_mtime,
 
678
                            st->st_mtim.tv_nsec);
 
679
#elif HAVE_STRUCT_STAT_ST_MTIME_N
 
680
                        archive_entry_set_mtime(entry, st->st_mtime,
 
681
                            st->st_mtime_n);
 
682
#elif HAVE_STRUCT_STAT_ST_UMTIME
 
683
                        archive_entry_set_mtime(entry, st->st_mtime,
 
684
                            st->st_umtime*1000);
 
685
#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
 
686
                        archive_entry_set_mtime(entry, st->st_mtime,
 
687
                            st->st_mtime_usec*1000);
 
688
#else
 
689
                        archive_entry_set_mtime(entry, st->st_mtime, 0);
 
690
#endif
 
691
                }
 
692
                if ((parsed_kws & MTREE_HAS_NLINK) == 0)
 
693
                        archive_entry_set_nlink(entry, st->st_nlink);
 
694
                if ((parsed_kws & MTREE_HAS_PERM) == 0)
 
695
                        archive_entry_set_perm(entry, st->st_mode);
 
696
                if ((parsed_kws & MTREE_HAS_SIZE) == 0)
 
697
                        archive_entry_set_size(entry, st->st_size);
 
698
                archive_entry_set_ino(entry, st->st_ino);
 
699
                archive_entry_set_dev(entry, st->st_dev);
 
700
 
 
701
                archive_entry_linkify(mtree->resolver, &entry, &sparse_entry);
 
702
        } else if (parsed_kws & MTREE_HAS_OPTIONAL) {
 
703
                /*
 
704
                 * Couldn't open the entry, stat it or the on-disk type
 
705
                 * didn't match.  If this entry is optional, just ignore it
 
706
                 * and read the next header entry.
 
707
                 */
 
708
                *use_next = 1;
 
709
                return ARCHIVE_OK;
 
710
        }
 
711
 
 
712
        mtree->cur_size = archive_entry_size(entry);
 
713
        mtree->offset = 0;
 
714
 
 
715
        return r;
 
716
}
 
717
 
 
718
/*
 
719
 * Each line contains a sequence of keywords.
 
720
 */
 
721
static int
 
722
parse_line(struct archive_read *a, struct archive_entry *entry,
 
723
    struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws)
 
724
{
 
725
        struct mtree_option *iter;
 
726
        int r = ARCHIVE_OK, r1;
 
727
 
 
728
        for (iter = mp->options; iter != NULL; iter = iter->next) {
 
729
                r1 = parse_keyword(a, mtree, entry, iter, parsed_kws);
 
730
                if (r1 < r)
 
731
                        r = r1;
 
732
        }
 
733
        if ((*parsed_kws & MTREE_HAS_TYPE) == 0) {
 
734
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 
735
                    "Missing type keyword in mtree specification");
 
736
                return (ARCHIVE_WARN);
 
737
        }
 
738
        return (r);
 
739
}
 
740
 
 
741
/*
 
742
 * Device entries have one of the following forms:
 
743
 * raw dev_t
 
744
 * format,major,minor[,subdevice]
 
745
 *
 
746
 * Just use major and minor, no translation etc is done
 
747
 * between formats.
 
748
 */
 
749
static int
 
750
parse_device(struct archive *a, struct archive_entry *entry, char *val)
 
751
{
 
752
        char *comma1, *comma2;
 
753
 
 
754
        comma1 = strchr(val, ',');
 
755
        if (comma1 == NULL) {
 
756
                archive_entry_set_dev(entry, mtree_atol10(&val));
 
757
                return (ARCHIVE_OK);
 
758
        }
 
759
        ++comma1;
 
760
        comma2 = strchr(comma1, ',');
 
761
        if (comma2 == NULL) {
 
762
                archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
 
763
                    "Malformed device attribute");
 
764
                return (ARCHIVE_WARN);
 
765
        }
 
766
        ++comma2;
 
767
        archive_entry_set_rdevmajor(entry, mtree_atol(&comma1));
 
768
        archive_entry_set_rdevminor(entry, mtree_atol(&comma2));
 
769
        return (ARCHIVE_OK);
 
770
}
 
771
 
 
772
/*
 
773
 * Parse a single keyword and its value.
 
774
 */
 
775
static int
 
776
parse_keyword(struct archive_read *a, struct mtree *mtree,
 
777
    struct archive_entry *entry, struct mtree_option *option, int *parsed_kws)
 
778
{
 
779
        char *val, *key;
 
780
 
 
781
        key = option->value;
 
782
 
 
783
        if (*key == '\0')
 
784
                return (ARCHIVE_OK);
 
785
 
 
786
        if (strcmp(key, "optional") == 0) {
 
787
                *parsed_kws |= MTREE_HAS_OPTIONAL;
 
788
                return (ARCHIVE_OK);
 
789
        }
 
790
        if (strcmp(key, "ignore") == 0) {
 
791
                /*
 
792
                 * The mtree processing is not recursive, so
 
793
                 * recursion will only happen for explicitly listed
 
794
                 * entries.
 
795
                 */
 
796
                return (ARCHIVE_OK);
 
797
        }
 
798
 
 
799
        val = strchr(key, '=');
 
800
        if (val == NULL) {
 
801
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 
802
                    "Malformed attribute \"%s\" (%d)", key, key[0]);
 
803
                return (ARCHIVE_WARN);
 
804
        }
 
805
 
 
806
        *val = '\0';
 
807
        ++val;
 
808
 
 
809
        switch (key[0]) {
 
810
        case 'c':
 
811
                if (strcmp(key, "content") == 0
 
812
                    || strcmp(key, "contents") == 0) {
 
813
                        parse_escapes(val, NULL);
 
814
                        archive_strcpy(&mtree->contents_name, val);
 
815
                        break;
 
816
                }
 
817
                if (strcmp(key, "cksum") == 0)
 
818
                        break;
 
819
        case 'd':
 
820
                if (strcmp(key, "device") == 0) {
 
821
                        *parsed_kws |= MTREE_HAS_DEVICE;
 
822
                        return parse_device(&a->archive, entry, val);
 
823
                }
 
824
        case 'f':
 
825
                if (strcmp(key, "flags") == 0) {
 
826
                        *parsed_kws |= MTREE_HAS_FFLAGS;
 
827
                        archive_entry_copy_fflags_text(entry, val);
 
828
                        break;
 
829
                }
 
830
        case 'g':
 
831
                if (strcmp(key, "gid") == 0) {
 
832
                        *parsed_kws |= MTREE_HAS_GID;
 
833
                        archive_entry_set_gid(entry, mtree_atol10(&val));
 
834
                        break;
 
835
                }
 
836
                if (strcmp(key, "gname") == 0) {
 
837
                        *parsed_kws |= MTREE_HAS_GNAME;
 
838
                        archive_entry_copy_gname(entry, val);
 
839
                        break;
 
840
                }
 
841
        case 'l':
 
842
                if (strcmp(key, "link") == 0) {
 
843
                        archive_entry_copy_symlink(entry, val);
 
844
                        break;
 
845
                }
 
846
        case 'm':
 
847
                if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0)
 
848
                        break;
 
849
                if (strcmp(key, "mode") == 0) {
 
850
                        if (val[0] >= '0' && val[0] <= '9') {
 
851
                                *parsed_kws |= MTREE_HAS_PERM;
 
852
                                archive_entry_set_perm(entry,
 
853
                                    mtree_atol8(&val));
 
854
                        } else {
 
855
                                archive_set_error(&a->archive,
 
856
                                    ARCHIVE_ERRNO_FILE_FORMAT,
 
857
                                    "Symbolic mode \"%s\" unsupported", val);
 
858
                                return ARCHIVE_WARN;
 
859
                        }
 
860
                        break;
 
861
                }
 
862
        case 'n':
 
863
                if (strcmp(key, "nlink") == 0) {
 
864
                        *parsed_kws |= MTREE_HAS_NLINK;
 
865
                        archive_entry_set_nlink(entry, mtree_atol10(&val));
 
866
                        break;
 
867
                }
 
868
        case 'r':
 
869
                if (strcmp(key, "rmd160") == 0 ||
 
870
                    strcmp(key, "rmd160digest") == 0)
 
871
                        break;
 
872
        case 's':
 
873
                if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0)
 
874
                        break;
 
875
                if (strcmp(key, "sha256") == 0 ||
 
876
                    strcmp(key, "sha256digest") == 0)
 
877
                        break;
 
878
                if (strcmp(key, "sha384") == 0 ||
 
879
                    strcmp(key, "sha384digest") == 0)
 
880
                        break;
 
881
                if (strcmp(key, "sha512") == 0 ||
 
882
                    strcmp(key, "sha512digest") == 0)
 
883
                        break;
 
884
                if (strcmp(key, "size") == 0) {
 
885
                        archive_entry_set_size(entry, mtree_atol10(&val));
 
886
                        break;
 
887
                }
 
888
        case 't':
 
889
                if (strcmp(key, "tags") == 0) {
 
890
                        /*
 
891
                         * Comma delimited list of tags.
 
892
                         * Ignore the tags for now, but the interface
 
893
                         * should be extended to allow inclusion/exclusion.
 
894
                         */
 
895
                        break;
 
896
                }
 
897
                if (strcmp(key, "time") == 0) {
 
898
                        time_t m;
 
899
                        long ns;
 
900
 
 
901
                        *parsed_kws |= MTREE_HAS_MTIME;
 
902
                        m = (time_t)mtree_atol10(&val);
 
903
                        if (*val == '.') {
 
904
                                ++val;
 
905
                                ns = (long)mtree_atol10(&val);
 
906
                        } else
 
907
                                ns = 0;
 
908
                        archive_entry_set_mtime(entry, m, ns);
 
909
                        break;
 
910
                }
 
911
                if (strcmp(key, "type") == 0) {
 
912
                        *parsed_kws |= MTREE_HAS_TYPE;
 
913
                        switch (val[0]) {
 
914
                        case 'b':
 
915
                                if (strcmp(val, "block") == 0) {
 
916
                                        mtree->filetype = AE_IFBLK;
 
917
                                        break;
 
918
                                }
 
919
                        case 'c':
 
920
                                if (strcmp(val, "char") == 0) {
 
921
                                        mtree->filetype = AE_IFCHR;
 
922
                                        break;
 
923
                                }
 
924
                        case 'd':
 
925
                                if (strcmp(val, "dir") == 0) {
 
926
                                        mtree->filetype = AE_IFDIR;
 
927
                                        break;
 
928
                                }
 
929
                        case 'f':
 
930
                                if (strcmp(val, "fifo") == 0) {
 
931
                                        mtree->filetype = AE_IFIFO;
 
932
                                        break;
 
933
                                }
 
934
                                if (strcmp(val, "file") == 0) {
 
935
                                        mtree->filetype = AE_IFREG;
 
936
                                        break;
 
937
                                }
 
938
                        case 'l':
 
939
                                if (strcmp(val, "link") == 0) {
 
940
                                        mtree->filetype = AE_IFLNK;
 
941
                                        break;
 
942
                                }
 
943
                        default:
 
944
                                archive_set_error(&a->archive,
 
945
                                    ARCHIVE_ERRNO_FILE_FORMAT,
 
946
                                    "Unrecognized file type \"%s\"", val);
 
947
                                return (ARCHIVE_WARN);
 
948
                        }
 
949
                        archive_entry_set_filetype(entry, mtree->filetype);
 
950
                        break;
 
951
                }
 
952
        case 'u':
 
953
                if (strcmp(key, "uid") == 0) {
 
954
                        *parsed_kws |= MTREE_HAS_UID;
 
955
                        archive_entry_set_uid(entry, mtree_atol10(&val));
 
956
                        break;
 
957
                }
 
958
                if (strcmp(key, "uname") == 0) {
 
959
                        *parsed_kws |= MTREE_HAS_UNAME;
 
960
                        archive_entry_copy_uname(entry, val);
 
961
                        break;
 
962
                }
 
963
        default:
 
964
                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
 
965
                    "Unrecognized key %s=%s", key, val);
 
966
                return (ARCHIVE_WARN);
 
967
        }
 
968
        return (ARCHIVE_OK);
 
969
}
 
970
 
 
971
static int
 
972
read_data(struct archive_read *a, const void **buff, size_t *size, off_t *offset)
 
973
{
 
974
        size_t bytes_to_read;
 
975
        ssize_t bytes_read;
 
976
        struct mtree *mtree;
 
977
 
 
978
        mtree = (struct mtree *)(a->format->data);
 
979
        if (mtree->fd < 0) {
 
980
                *buff = NULL;
 
981
                *offset = 0;
 
982
                *size = 0;
 
983
                return (ARCHIVE_EOF);
 
984
        }
 
985
        if (mtree->buff == NULL) {
 
986
                mtree->buffsize = 64 * 1024;
 
987
                mtree->buff = malloc(mtree->buffsize);
 
988
                if (mtree->buff == NULL) {
 
989
                        archive_set_error(&a->archive, ENOMEM,
 
990
                            "Can't allocate memory");
 
991
                        return (ARCHIVE_FATAL);
 
992
                }
 
993
        }
 
994
 
 
995
        *buff = mtree->buff;
 
996
        *offset = mtree->offset;
 
997
        if ((off_t)mtree->buffsize > mtree->cur_size - mtree->offset)
 
998
                bytes_to_read = mtree->cur_size - mtree->offset;
 
999
        else
 
1000
                bytes_to_read = mtree->buffsize;
 
1001
        bytes_read = read(mtree->fd, mtree->buff, bytes_to_read);
 
1002
        if (bytes_read < 0) {
 
1003
                archive_set_error(&a->archive, errno, "Can't read");
 
1004
                return (ARCHIVE_WARN);
 
1005
        }
 
1006
        if (bytes_read == 0) {
 
1007
                *size = 0;
 
1008
                return (ARCHIVE_EOF);
 
1009
        }
 
1010
        mtree->offset += bytes_read;
 
1011
        *size = bytes_read;
 
1012
        return (ARCHIVE_OK);
 
1013
}
 
1014
 
 
1015
/* Skip does nothing except possibly close the contents file. */
 
1016
static int
 
1017
skip(struct archive_read *a)
 
1018
{
 
1019
        struct mtree *mtree;
 
1020
 
 
1021
        mtree = (struct mtree *)(a->format->data);
 
1022
        if (mtree->fd >= 0) {
 
1023
                close(mtree->fd);
 
1024
                mtree->fd = -1;
 
1025
        }
 
1026
        return (ARCHIVE_OK);
 
1027
}
 
1028
 
 
1029
/*
 
1030
 * Since parsing backslash sequences always makes strings shorter,
 
1031
 * we can always do this conversion in-place.
 
1032
 */
 
1033
static void
 
1034
parse_escapes(char *src, struct mtree_entry *mentry)
 
1035
{
 
1036
        char *dest = src;
 
1037
        char c;
 
1038
 
 
1039
        if (mentry != NULL && strcmp(src, ".") == 0)
 
1040
                mentry->full = 1;
 
1041
 
 
1042
        while (*src != '\0') {
 
1043
                c = *src++;
 
1044
                if (c == '/' && mentry != NULL)
 
1045
                        mentry->full = 1;
 
1046
                if (c == '\\') {
 
1047
                        switch (src[0]) {
 
1048
                        case '0':
 
1049
                                if (src[1] < '0' || src[1] > '7') {
 
1050
                                        c = 0;
 
1051
                                        ++src;
 
1052
                                        break;
 
1053
                                }
 
1054
                                /* FALLTHROUGH */
 
1055
                        case '1':
 
1056
                        case '2':
 
1057
                        case '3':
 
1058
                                if (src[1] >= '0' && src[1] <= '7' &&
 
1059
                                    src[2] >= '0' && src[2] <= '7') {
 
1060
                                        c = (src[0] - '0') << 6;
 
1061
                                        c |= (src[1] - '0') << 3;
 
1062
                                        c |= (src[2] - '0');
 
1063
                                        src += 3;
 
1064
                                }
 
1065
                                break;
 
1066
                        case 'a':
 
1067
                                c = '\a';
 
1068
                                ++src;
 
1069
                                break;
 
1070
                        case 'b':
 
1071
                                c = '\b';
 
1072
                                ++src;
 
1073
                                break;
 
1074
                        case 'f':
 
1075
                                c = '\f';
 
1076
                                ++src;
 
1077
                                break;
 
1078
                        case 'n':
 
1079
                                c = '\n';
 
1080
                                ++src;
 
1081
                                break;
 
1082
                        case 'r':
 
1083
                                c = '\r';
 
1084
                                ++src;
 
1085
                                break;
 
1086
                        case 's':
 
1087
                                c = ' ';
 
1088
                                ++src;
 
1089
                                break;
 
1090
                        case 't':
 
1091
                                c = '\t';
 
1092
                                ++src;
 
1093
                                break;
 
1094
                        case 'v':
 
1095
                                c = '\v';
 
1096
                                ++src;
 
1097
                                break;
 
1098
                        }
 
1099
                }
 
1100
                *dest++ = c;
 
1101
        }
 
1102
        *dest = '\0';
 
1103
}
 
1104
 
 
1105
/*
 
1106
 * Note that this implementation does not (and should not!) obey
 
1107
 * locale settings; you cannot simply substitute strtol here, since
 
1108
 * it does obey locale.
 
1109
 */
 
1110
static int64_t
 
1111
mtree_atol8(char **p)
 
1112
{
 
1113
        int64_t l, limit, last_digit_limit;
 
1114
        int digit, base;
 
1115
 
 
1116
        base = 8;
 
1117
        limit = INT64_MAX / base;
 
1118
        last_digit_limit = INT64_MAX % base;
 
1119
 
 
1120
        l = 0;
 
1121
        digit = **p - '0';
 
1122
        while (digit >= 0 && digit < base) {
 
1123
                if (l>limit || (l == limit && digit > last_digit_limit)) {
 
1124
                        l = INT64_MAX; /* Truncate on overflow. */
 
1125
                        break;
 
1126
                }
 
1127
                l = (l * base) + digit;
 
1128
                digit = *++(*p) - '0';
 
1129
        }
 
1130
        return (l);
 
1131
}
 
1132
 
 
1133
/*
 
1134
 * Note that this implementation does not (and should not!) obey
 
1135
 * locale settings; you cannot simply substitute strtol here, since
 
1136
 * it does obey locale.
 
1137
 */
 
1138
static int64_t
 
1139
mtree_atol10(char **p)
 
1140
{
 
1141
        int64_t l, limit, last_digit_limit;
 
1142
        int base, digit, sign;
 
1143
 
 
1144
        base = 10;
 
1145
        limit = INT64_MAX / base;
 
1146
        last_digit_limit = INT64_MAX % base;
 
1147
 
 
1148
        if (**p == '-') {
 
1149
                sign = -1;
 
1150
                ++(*p);
 
1151
        } else
 
1152
                sign = 1;
 
1153
 
 
1154
        l = 0;
 
1155
        digit = **p - '0';
 
1156
        while (digit >= 0 && digit < base) {
 
1157
                if (l > limit || (l == limit && digit > last_digit_limit)) {
 
1158
                        l = INT64_MAX; /* Truncate on overflow. */
 
1159
                        break;
 
1160
                }
 
1161
                l = (l * base) + digit;
 
1162
                digit = *++(*p) - '0';
 
1163
        }
 
1164
        return (sign < 0) ? -l : l;
 
1165
}
 
1166
 
 
1167
/*
 
1168
 * Note that this implementation does not (and should not!) obey
 
1169
 * locale settings; you cannot simply substitute strtol here, since
 
1170
 * it does obey locale.
 
1171
 */
 
1172
static int64_t
 
1173
mtree_atol16(char **p)
 
1174
{
 
1175
        int64_t l, limit, last_digit_limit;
 
1176
        int base, digit, sign;
 
1177
 
 
1178
        base = 16;
 
1179
        limit = INT64_MAX / base;
 
1180
        last_digit_limit = INT64_MAX % base;
 
1181
 
 
1182
        if (**p == '-') {
 
1183
                sign = -1;
 
1184
                ++(*p);
 
1185
        } else
 
1186
                sign = 1;
 
1187
 
 
1188
        l = 0;
 
1189
        if (**p >= '0' && **p <= '9')
 
1190
                digit = **p - '0';
 
1191
        else if (**p >= 'a' && **p <= 'f')
 
1192
                digit = **p - 'a' + 10;
 
1193
        else if (**p >= 'A' && **p <= 'F')
 
1194
                digit = **p - 'A' + 10;
 
1195
        else
 
1196
                digit = -1;
 
1197
        while (digit >= 0 && digit < base) {
 
1198
                if (l > limit || (l == limit && digit > last_digit_limit)) {
 
1199
                        l = INT64_MAX; /* Truncate on overflow. */
 
1200
                        break;
 
1201
                }
 
1202
                l = (l * base) + digit;
 
1203
                if (**p >= '0' && **p <= '9')
 
1204
                        digit = **p - '0';
 
1205
                else if (**p >= 'a' && **p <= 'f')
 
1206
                        digit = **p - 'a' + 10;
 
1207
                else if (**p >= 'A' && **p <= 'F')
 
1208
                        digit = **p - 'A' + 10;
 
1209
                else
 
1210
                        digit = -1;
 
1211
        }
 
1212
        return (sign < 0) ? -l : l;
 
1213
}
 
1214
 
 
1215
static int64_t
 
1216
mtree_atol(char **p)
 
1217
{
 
1218
        if (**p != '0')
 
1219
                return mtree_atol10(p);
 
1220
        if ((*p)[1] == 'x' || (*p)[1] == 'X') {
 
1221
                *p += 2;
 
1222
                return mtree_atol16(p);
 
1223
        }
 
1224
        return mtree_atol8(p);
 
1225
}
 
1226
 
 
1227
/*
 
1228
 * Returns length of line (including trailing newline)
 
1229
 * or negative on error.  'start' argument is updated to
 
1230
 * point to first character of line.
 
1231
 */
 
1232
static ssize_t
 
1233
readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit)
 
1234
{
 
1235
        ssize_t bytes_read;
 
1236
        ssize_t total_size = 0;
 
1237
        ssize_t find_off = 0;
 
1238
        const void *t;
 
1239
        const char *s;
 
1240
        void *p;
 
1241
        char *u;
 
1242
 
 
1243
        /* Accumulate line in a line buffer. */
 
1244
        for (;;) {
 
1245
                /* Read some more. */
 
1246
                t = __archive_read_ahead(a, 1, &bytes_read);
 
1247
                if (t == NULL)
 
1248
                        return (0);
 
1249
                if (bytes_read < 0)
 
1250
                        return (ARCHIVE_FATAL);
 
1251
                s = t;  /* Start of line? */
 
1252
                p = memchr(t, '\n', bytes_read);
 
1253
                /* If we found '\n', trim the read. */
 
1254
                if (p != NULL) {
 
1255
                        bytes_read = 1 + ((const char *)p) - s;
 
1256
                }
 
1257
                if (total_size + bytes_read + 1 > limit) {
 
1258
                        archive_set_error(&a->archive,
 
1259
                            ARCHIVE_ERRNO_FILE_FORMAT,
 
1260
                            "Line too long");
 
1261
                        return (ARCHIVE_FATAL);
 
1262
                }
 
1263
                if (archive_string_ensure(&mtree->line,
 
1264
                        total_size + bytes_read + 1) == NULL) {
 
1265
                        archive_set_error(&a->archive, ENOMEM,
 
1266
                            "Can't allocate working buffer");
 
1267
                        return (ARCHIVE_FATAL);
 
1268
                }
 
1269
                memcpy(mtree->line.s + total_size, t, bytes_read);
 
1270
                __archive_read_consume(a, bytes_read);
 
1271
                total_size += bytes_read;
 
1272
                /* Null terminate. */
 
1273
                mtree->line.s[total_size] = '\0';
 
1274
                /* If we found an unescaped '\n', clean up and return. */
 
1275
                for (u = mtree->line.s + find_off; *u; ++u) {
 
1276
                        if (u[0] == '\n') {
 
1277
                                *start = mtree->line.s;
 
1278
                                return total_size;
 
1279
                        }
 
1280
                        if (u[0] == '#') {
 
1281
                                if (p == NULL)
 
1282
                                        break;
 
1283
                                *start = mtree->line.s;
 
1284
                                return total_size;
 
1285
                        }
 
1286
                        if (u[0] != '\\')
 
1287
                                continue;
 
1288
                        if (u[1] == '\\') {
 
1289
                                ++u;
 
1290
                                continue;
 
1291
                        }
 
1292
                        if (u[1] == '\n') {
 
1293
                                memmove(u, u + 1,
 
1294
                                    total_size - (u - mtree->line.s) + 1);
 
1295
                                --total_size;
 
1296
                                ++u;
 
1297
                                break;
 
1298
                        }
 
1299
                        if (u[1] == '\0')
 
1300
                                break;
 
1301
                }
 
1302
                find_off = u - mtree->line.s;
 
1303
        }
 
1304
}