1228753Smm/*- 2228753Smm * Copyright (c) 2003-2007 Tim Kientzle 3228753Smm * Copyright (c) 2008 Joerg Sonnenberger 4248616Smm * Copyright (c) 2011-2012 Michihiro NAKAJIMA 5228753Smm * All rights reserved. 6228753Smm * 7228753Smm * Redistribution and use in source and binary forms, with or without 8228753Smm * modification, are permitted provided that the following conditions 9228753Smm * are met: 10228753Smm * 1. Redistributions of source code must retain the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer. 12228753Smm * 2. Redistributions in binary form must reproduce the above copyright 13228753Smm * notice, this list of conditions and the following disclaimer in the 14228753Smm * documentation and/or other materials provided with the distribution. 15228753Smm * 16228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26228753Smm */ 27228753Smm 28228753Smm#include "archive_platform.h" 29228763Smm__FBSDID("$FreeBSD$"); 30228753Smm 31228753Smm#ifdef HAVE_SYS_STAT_H 32228753Smm#include <sys/stat.h> 33228753Smm#endif 34228753Smm#ifdef HAVE_ERRNO_H 35228753Smm#include <errno.h> 36228753Smm#endif 37228753Smm#ifdef HAVE_FCNTL_H 38228753Smm#include <fcntl.h> 39228753Smm#endif 40228753Smm#include <stddef.h> 41228753Smm/* #include <stdint.h> */ /* See archive_platform.h */ 42228753Smm#ifdef HAVE_STDLIB_H 43228753Smm#include <stdlib.h> 44228753Smm#endif 45228753Smm#ifdef HAVE_STRING_H 46228753Smm#include <string.h> 47228753Smm#endif 48228753Smm 49228753Smm#include "archive.h" 50228753Smm#include "archive_entry.h" 51228753Smm#include "archive_private.h" 52228753Smm#include "archive_read_private.h" 53228753Smm#include "archive_string.h" 54228753Smm 55228753Smm#ifndef O_BINARY 56228753Smm#define O_BINARY 0 57228753Smm#endif 58248616Smm#ifndef O_CLOEXEC 59248616Smm#define O_CLOEXEC 0 60248616Smm#endif 61228753Smm 62228753Smm#define MTREE_HAS_DEVICE 0x0001 63228753Smm#define MTREE_HAS_FFLAGS 0x0002 64228753Smm#define MTREE_HAS_GID 0x0004 65228753Smm#define MTREE_HAS_GNAME 0x0008 66228753Smm#define MTREE_HAS_MTIME 0x0010 67228753Smm#define MTREE_HAS_NLINK 0x0020 68228753Smm#define MTREE_HAS_PERM 0x0040 69228753Smm#define MTREE_HAS_SIZE 0x0080 70228753Smm#define MTREE_HAS_TYPE 0x0100 71228753Smm#define MTREE_HAS_UID 0x0200 72228753Smm#define MTREE_HAS_UNAME 0x0400 73228753Smm 74228753Smm#define MTREE_HAS_OPTIONAL 0x0800 75248616Smm#define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ 76228753Smm 77228753Smmstruct mtree_option { 78228753Smm struct mtree_option *next; 79228753Smm char *value; 80228753Smm}; 81228753Smm 82228753Smmstruct mtree_entry { 83228753Smm struct mtree_entry *next; 84228753Smm struct mtree_option *options; 85228753Smm char *name; 86228753Smm char full; 87228753Smm char used; 88228753Smm}; 89228753Smm 90228753Smmstruct mtree { 91228753Smm struct archive_string line; 92228753Smm size_t buffsize; 93228753Smm char *buff; 94232153Smm int64_t offset; 95228753Smm int fd; 96228753Smm int archive_format; 97228753Smm const char *archive_format_name; 98228753Smm struct mtree_entry *entries; 99228753Smm struct mtree_entry *this_entry; 100228753Smm struct archive_string current_dir; 101228753Smm struct archive_string contents_name; 102228753Smm 103228753Smm struct archive_entry_linkresolver *resolver; 104228753Smm 105232153Smm int64_t cur_size; 106228753Smm}; 107228753Smm 108232153Smmstatic int bid_keycmp(const char *, const char *, ssize_t); 109228753Smmstatic int cleanup(struct archive_read *); 110248616Smmstatic int detect_form(struct archive_read *, int *); 111232153Smmstatic int mtree_bid(struct archive_read *, int); 112228753Smmstatic int parse_file(struct archive_read *, struct archive_entry *, 113228753Smm struct mtree *, struct mtree_entry *, int *); 114228753Smmstatic void parse_escapes(char *, struct mtree_entry *); 115228753Smmstatic int parse_line(struct archive_read *, struct archive_entry *, 116228753Smm struct mtree *, struct mtree_entry *, int *); 117228753Smmstatic int parse_keyword(struct archive_read *, struct mtree *, 118228753Smm struct archive_entry *, struct mtree_option *, int *); 119228753Smmstatic int read_data(struct archive_read *a, 120232153Smm const void **buff, size_t *size, int64_t *offset); 121228753Smmstatic ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t); 122228753Smmstatic int skip(struct archive_read *a); 123228753Smmstatic int read_header(struct archive_read *, 124228753Smm struct archive_entry *); 125228753Smmstatic int64_t mtree_atol10(char **); 126228753Smmstatic int64_t mtree_atol8(char **); 127228753Smmstatic int64_t mtree_atol(char **); 128228753Smm 129232153Smm/* 130232153Smm * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them 131232153Smm * here. TODO: Move this to configure time, but be careful 132232153Smm * about cross-compile environments. 133232153Smm */ 134232153Smmstatic int64_t 135232153Smmget_time_t_max(void) 136232153Smm{ 137232153Smm#if defined(TIME_T_MAX) 138232153Smm return TIME_T_MAX; 139232153Smm#else 140232153Smm static time_t t; 141232153Smm time_t a; 142232153Smm if (t == 0) { 143232153Smm a = 1; 144232153Smm while (a > t) { 145232153Smm t = a; 146232153Smm a = a * 2 + 1; 147232153Smm } 148232153Smm } 149232153Smm return t; 150232153Smm#endif 151232153Smm} 152232153Smm 153232153Smmstatic int64_t 154232153Smmget_time_t_min(void) 155232153Smm{ 156232153Smm#if defined(TIME_T_MIN) 157232153Smm return TIME_T_MIN; 158232153Smm#else 159232153Smm /* 't' will hold the minimum value, which will be zero (if 160232153Smm * time_t is unsigned) or -2^n (if time_t is signed). */ 161232153Smm static int computed; 162232153Smm static time_t t; 163232153Smm time_t a; 164232153Smm if (computed == 0) { 165232153Smm a = (time_t)-1; 166232153Smm while (a < t) { 167232153Smm t = a; 168232153Smm a = a * 2; 169232153Smm } 170232153Smm computed = 1; 171232153Smm } 172232153Smm return t; 173232153Smm#endif 174232153Smm} 175232153Smm 176228753Smmstatic void 177228753Smmfree_options(struct mtree_option *head) 178228753Smm{ 179228753Smm struct mtree_option *next; 180228753Smm 181228753Smm for (; head != NULL; head = next) { 182228753Smm next = head->next; 183228753Smm free(head->value); 184228753Smm free(head); 185228753Smm } 186228753Smm} 187228753Smm 188228753Smmint 189228753Smmarchive_read_support_format_mtree(struct archive *_a) 190228753Smm{ 191228753Smm struct archive_read *a = (struct archive_read *)_a; 192228753Smm struct mtree *mtree; 193228753Smm int r; 194228753Smm 195232153Smm archive_check_magic(_a, ARCHIVE_READ_MAGIC, 196232153Smm ARCHIVE_STATE_NEW, "archive_read_support_format_mtree"); 197232153Smm 198228753Smm mtree = (struct mtree *)malloc(sizeof(*mtree)); 199228753Smm if (mtree == NULL) { 200228753Smm archive_set_error(&a->archive, ENOMEM, 201228753Smm "Can't allocate mtree data"); 202228753Smm return (ARCHIVE_FATAL); 203228753Smm } 204228753Smm memset(mtree, 0, sizeof(*mtree)); 205228753Smm mtree->fd = -1; 206228753Smm 207228753Smm r = __archive_read_register_format(a, mtree, "mtree", 208248616Smm mtree_bid, NULL, read_header, read_data, skip, NULL, cleanup); 209228753Smm 210228753Smm if (r != ARCHIVE_OK) 211228753Smm free(mtree); 212228753Smm return (ARCHIVE_OK); 213228753Smm} 214228753Smm 215228753Smmstatic int 216228753Smmcleanup(struct archive_read *a) 217228753Smm{ 218228753Smm struct mtree *mtree; 219228753Smm struct mtree_entry *p, *q; 220228753Smm 221228753Smm mtree = (struct mtree *)(a->format->data); 222228753Smm 223228753Smm p = mtree->entries; 224228753Smm while (p != NULL) { 225228753Smm q = p->next; 226228753Smm free(p->name); 227228753Smm free_options(p->options); 228228753Smm free(p); 229228753Smm p = q; 230228753Smm } 231228753Smm archive_string_free(&mtree->line); 232228753Smm archive_string_free(&mtree->current_dir); 233228753Smm archive_string_free(&mtree->contents_name); 234228753Smm archive_entry_linkresolver_free(mtree->resolver); 235228753Smm 236228753Smm free(mtree->buff); 237228753Smm free(mtree); 238228753Smm (a->format->data) = NULL; 239228753Smm return (ARCHIVE_OK); 240228753Smm} 241228753Smm 242232153Smmstatic ssize_t 243232153Smmget_line_size(const char *b, ssize_t avail, ssize_t *nlsize) 244232153Smm{ 245232153Smm ssize_t len; 246228753Smm 247232153Smm len = 0; 248232153Smm while (len < avail) { 249232153Smm switch (*b) { 250232153Smm case '\0':/* Non-ascii character or control character. */ 251232153Smm if (nlsize != NULL) 252232153Smm *nlsize = 0; 253232153Smm return (-1); 254232153Smm case '\r': 255232153Smm if (avail-len > 1 && b[1] == '\n') { 256232153Smm if (nlsize != NULL) 257232153Smm *nlsize = 2; 258232153Smm return (len+2); 259232153Smm } 260232153Smm /* FALL THROUGH */ 261232153Smm case '\n': 262232153Smm if (nlsize != NULL) 263232153Smm *nlsize = 1; 264232153Smm return (len+1); 265232153Smm default: 266232153Smm b++; 267232153Smm len++; 268232153Smm break; 269232153Smm } 270232153Smm } 271232153Smm if (nlsize != NULL) 272232153Smm *nlsize = 0; 273232153Smm return (avail); 274232153Smm} 275232153Smm 276232153Smmstatic ssize_t 277232153Smmnext_line(struct archive_read *a, 278232153Smm const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl) 279232153Smm{ 280232153Smm ssize_t len; 281232153Smm int quit; 282232153Smm 283232153Smm quit = 0; 284232153Smm if (*avail == 0) { 285232153Smm *nl = 0; 286232153Smm len = 0; 287232153Smm } else 288232153Smm len = get_line_size(*b, *avail, nl); 289232153Smm /* 290232153Smm * Read bytes more while it does not reach the end of line. 291232153Smm */ 292232153Smm while (*nl == 0 && len == *avail && !quit) { 293232153Smm ssize_t diff = *ravail - *avail; 294232153Smm size_t nbytes_req = (*ravail+1023) & ~1023U; 295232153Smm ssize_t tested; 296232153Smm 297232153Smm /* Increase reading bytes if it is not enough to at least 298232153Smm * new two lines. */ 299232153Smm if (nbytes_req < (size_t)*ravail + 160) 300232153Smm nbytes_req <<= 1; 301232153Smm 302232153Smm *b = __archive_read_ahead(a, nbytes_req, avail); 303232153Smm if (*b == NULL) { 304232153Smm if (*ravail >= *avail) 305232153Smm return (0); 306232153Smm /* Reading bytes reaches the end of file. */ 307232153Smm *b = __archive_read_ahead(a, *avail, avail); 308232153Smm quit = 1; 309232153Smm } 310232153Smm *ravail = *avail; 311232153Smm *b += diff; 312232153Smm *avail -= diff; 313232153Smm tested = len;/* Skip some bytes we already determinated. */ 314232153Smm len = get_line_size(*b, *avail, nl); 315232153Smm if (len >= 0) 316232153Smm len += tested; 317232153Smm } 318232153Smm return (len); 319232153Smm} 320232153Smm 321232153Smm/* 322232153Smm * Compare characters with a mtree keyword. 323232153Smm * Returns the length of a mtree keyword if matched. 324232153Smm * Returns 0 if not matched. 325232153Smm */ 326228753Smmstatic int 327232153Smmbid_keycmp(const char *p, const char *key, ssize_t len) 328228753Smm{ 329232153Smm int match_len = 0; 330232153Smm 331232153Smm while (len > 0 && *p && *key) { 332232153Smm if (*p == *key) { 333232153Smm --len; 334232153Smm ++p; 335232153Smm ++key; 336232153Smm ++match_len; 337232153Smm continue; 338232153Smm } 339232153Smm return (0);/* Not match */ 340232153Smm } 341232153Smm if (*key != '\0') 342232153Smm return (0);/* Not match */ 343232153Smm 344232153Smm /* A following character should be specified characters */ 345232153Smm if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' || 346232153Smm p[0] == '\n' || p[0] == '\r' || 347232153Smm (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))) 348232153Smm return (match_len); 349232153Smm return (0);/* Not match */ 350232153Smm} 351232153Smm 352232153Smm/* 353232153Smm * Test whether the characters 'p' has is mtree keyword. 354232153Smm * Returns the length of a detected keyword. 355232153Smm * Returns 0 if any keywords were not found. 356232153Smm */ 357248616Smmstatic int 358232153Smmbid_keyword(const char *p, ssize_t len) 359232153Smm{ 360232153Smm static const char *keys_c[] = { 361232153Smm "content", "contents", "cksum", NULL 362232153Smm }; 363232153Smm static const char *keys_df[] = { 364232153Smm "device", "flags", NULL 365232153Smm }; 366232153Smm static const char *keys_g[] = { 367232153Smm "gid", "gname", NULL 368232153Smm }; 369232153Smm static const char *keys_il[] = { 370232153Smm "ignore", "link", NULL 371232153Smm }; 372232153Smm static const char *keys_m[] = { 373232153Smm "md5", "md5digest", "mode", NULL 374232153Smm }; 375232153Smm static const char *keys_no[] = { 376248616Smm "nlink", "nochange", "optional", NULL 377232153Smm }; 378232153Smm static const char *keys_r[] = { 379232153Smm "rmd160", "rmd160digest", NULL 380232153Smm }; 381232153Smm static const char *keys_s[] = { 382232153Smm "sha1", "sha1digest", 383232153Smm "sha256", "sha256digest", 384232153Smm "sha384", "sha384digest", 385232153Smm "sha512", "sha512digest", 386232153Smm "size", NULL 387232153Smm }; 388232153Smm static const char *keys_t[] = { 389232153Smm "tags", "time", "type", NULL 390232153Smm }; 391232153Smm static const char *keys_u[] = { 392232153Smm "uid", "uname", NULL 393232153Smm }; 394232153Smm const char **keys; 395232153Smm int i; 396232153Smm 397232153Smm switch (*p) { 398232153Smm case 'c': keys = keys_c; break; 399232153Smm case 'd': case 'f': keys = keys_df; break; 400232153Smm case 'g': keys = keys_g; break; 401232153Smm case 'i': case 'l': keys = keys_il; break; 402232153Smm case 'm': keys = keys_m; break; 403232153Smm case 'n': case 'o': keys = keys_no; break; 404232153Smm case 'r': keys = keys_r; break; 405232153Smm case 's': keys = keys_s; break; 406232153Smm case 't': keys = keys_t; break; 407232153Smm case 'u': keys = keys_u; break; 408232153Smm default: return (0);/* Unknown key */ 409232153Smm } 410232153Smm 411232153Smm for (i = 0; keys[i] != NULL; i++) { 412232153Smm int l = bid_keycmp(p, keys[i], len); 413232153Smm if (l > 0) 414232153Smm return (l); 415232153Smm } 416232153Smm return (0);/* Unknown key */ 417232153Smm} 418232153Smm 419232153Smm/* 420232153Smm * Test whether there is a set of mtree keywords. 421232153Smm * Returns the number of keyword. 422232153Smm * Returns -1 if we got incorrect sequence. 423232153Smm * This function expects a set of "<space characters>keyword=value". 424232153Smm * When "unset" is specified, expects a set of "<space characters>keyword". 425232153Smm */ 426232153Smmstatic int 427248616Smmbid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path) 428232153Smm{ 429232153Smm int l; 430232153Smm int keycnt = 0; 431232153Smm 432232153Smm while (len > 0 && *p) { 433232153Smm int blank = 0; 434232153Smm 435232153Smm /* Test whether there are blank characters in the line. */ 436232153Smm while (len >0 && (*p == ' ' || *p == '\t')) { 437232153Smm ++p; 438232153Smm --len; 439232153Smm blank = 1; 440232153Smm } 441232153Smm if (*p == '\n' || *p == '\r') 442232153Smm break; 443232153Smm if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')) 444232153Smm break; 445248616Smm if (!blank && !last_is_path) /* No blank character. */ 446232153Smm return (-1); 447248616Smm if (last_is_path && len == 0) 448248616Smm return (keycnt); 449232153Smm 450232153Smm if (unset) { 451232153Smm l = bid_keycmp(p, "all", len); 452232153Smm if (l > 0) 453232153Smm return (1); 454232153Smm } 455232153Smm /* Test whether there is a correct key in the line. */ 456232153Smm l = bid_keyword(p, len); 457232153Smm if (l == 0) 458232153Smm return (-1);/* Unknown keyword was found. */ 459232153Smm p += l; 460232153Smm len -= l; 461232153Smm keycnt++; 462232153Smm 463232153Smm /* Skip value */ 464232153Smm if (*p == '=') { 465232153Smm int value = 0; 466232153Smm ++p; 467232153Smm --len; 468232153Smm while (len > 0 && *p != ' ' && *p != '\t') { 469232153Smm ++p; 470232153Smm --len; 471232153Smm value = 1; 472232153Smm } 473232153Smm /* A keyword should have a its value unless 474232153Smm * "/unset" operation. */ 475232153Smm if (!unset && value == 0) 476232153Smm return (-1); 477232153Smm } 478232153Smm } 479232153Smm return (keycnt); 480232153Smm} 481232153Smm 482232153Smmstatic int 483248616Smmbid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path) 484232153Smm{ 485232153Smm int f = 0; 486232153Smm static const unsigned char safe_char[256] = { 487232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 488232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ 489232153Smm /* !"$%&'()*+,-./ EXCLUSION:( )(#) */ 490232153Smm 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ 491232153Smm /* 0123456789:;<>? EXCLUSION:(=) */ 492232153Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ 493232153Smm /* @ABCDEFGHIJKLMNO */ 494232153Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ 495232153Smm /* PQRSTUVWXYZ[\]^_ */ 496232153Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ 497232153Smm /* `abcdefghijklmno */ 498232153Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ 499232153Smm /* pqrstuvwxyz{|}~ */ 500232153Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */ 501232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 502232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 503232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 504232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 505232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 506232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 507232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 508232153Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ 509232153Smm }; 510248616Smm ssize_t ll = len; 511248616Smm const char *pp = p; 512232153Smm 513248616Smm *last_is_path = 0; 514232153Smm /* 515232153Smm * Skip the path-name which is quoted. 516232153Smm */ 517248616Smm while (ll > 0 && *pp != ' ' &&*pp != '\t' && *pp != '\r' && 518248616Smm *pp != '\n') { 519248616Smm if (!safe_char[*(const unsigned char *)pp]) { 520248616Smm f = 0; 521248616Smm break; 522248616Smm } 523248616Smm ++pp; 524248616Smm --ll; 525232153Smm ++f; 526232153Smm } 527248616Smm /* If a path-name was not found at the first, try to check 528248616Smm * a mtree format ``NetBSD's mtree -D'' creates, which 529248616Smm * places the path-name at the last. */ 530248616Smm if (f == 0) { 531248616Smm const char *pb = p + len - nl; 532248616Smm int name_len = 0; 533248616Smm int slash; 534232153Smm 535248616Smm /* Do not accept multi lines for form D. */ 536248616Smm if (pb-2 >= p && 537248616Smm pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t')) 538248616Smm return (-1); 539248616Smm if (pb-1 >= p && pb[-1] == '\\') 540248616Smm return (-1); 541248616Smm 542248616Smm slash = 0; 543248616Smm while (p <= --pb && *pb != ' ' && *pb != '\t') { 544248616Smm if (!safe_char[*(const unsigned char *)pb]) 545248616Smm return (-1); 546248616Smm name_len++; 547248616Smm /* The pathname should have a slash in this 548248616Smm * format. */ 549248616Smm if (*pb == '/') 550248616Smm slash = 1; 551248616Smm } 552248616Smm if (name_len == 0 || slash == 0) 553248616Smm return (-1); 554248616Smm /* If '/' is placed at the first in this field, this is not 555248616Smm * a valid filename. */ 556248616Smm if (pb[1] == '/') 557248616Smm return (-1); 558248616Smm ll = len - nl - name_len; 559248616Smm pp = p; 560248616Smm *last_is_path = 1; 561248616Smm } 562248616Smm 563248616Smm return (bid_keyword_list(pp, ll, 0, *last_is_path)); 564232153Smm} 565232153Smm 566232153Smm#define MAX_BID_ENTRY 3 567232153Smm 568232153Smmstatic int 569232153Smmmtree_bid(struct archive_read *a, int best_bid) 570232153Smm{ 571228753Smm const char *signature = "#mtree"; 572228753Smm const char *p; 573228753Smm 574232153Smm (void)best_bid; /* UNUSED */ 575232153Smm 576228753Smm /* Now let's look at the actual header and see if it matches. */ 577248616Smm p = __archive_read_ahead(a, strlen(signature), NULL); 578228753Smm if (p == NULL) 579228753Smm return (-1); 580228753Smm 581232153Smm if (memcmp(p, signature, strlen(signature)) == 0) 582228753Smm return (8 * (int)strlen(signature)); 583232153Smm 584232153Smm /* 585232153Smm * There is not a mtree signature. Let's try to detect mtree format. 586232153Smm */ 587248616Smm return (detect_form(a, NULL)); 588248616Smm} 589248616Smm 590248616Smmstatic int 591248616Smmdetect_form(struct archive_read *a, int *is_form_d) 592248616Smm{ 593248616Smm const char *p; 594248616Smm ssize_t avail, ravail; 595248616Smm ssize_t detected_bytes = 0, len, nl; 596248616Smm int entry_cnt = 0, multiline = 0; 597248616Smm int form_D = 0;/* The archive is generated by `NetBSD mtree -D' 598248616Smm * (In this source we call it `form D') . */ 599248616Smm 600248616Smm if (is_form_d != NULL) 601248616Smm *is_form_d = 0; 602248616Smm p = __archive_read_ahead(a, 1, &avail); 603248616Smm if (p == NULL) 604248616Smm return (-1); 605232153Smm ravail = avail; 606232153Smm for (;;) { 607232153Smm len = next_line(a, &p, &avail, &ravail, &nl); 608232153Smm /* The terminal character of the line should be 609232153Smm * a new line character, '\r\n' or '\n'. */ 610232153Smm if (len <= 0 || nl == 0) 611232153Smm break; 612232153Smm if (!multiline) { 613232153Smm /* Leading whitespace is never significant, 614232153Smm * ignore it. */ 615232153Smm while (len > 0 && (*p == ' ' || *p == '\t')) { 616232153Smm ++p; 617232153Smm --avail; 618232153Smm --len; 619232153Smm } 620232153Smm /* Skip comment or empty line. */ 621232153Smm if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') { 622232153Smm p += len; 623232153Smm avail -= len; 624232153Smm continue; 625232153Smm } 626232153Smm } else { 627232153Smm /* A continuance line; the terminal 628232153Smm * character of previous line was '\' character. */ 629248616Smm if (bid_keyword_list(p, len, 0, 0) <= 0) 630232153Smm break; 631232153Smm if (multiline == 1) 632232153Smm detected_bytes += len; 633232153Smm if (p[len-nl-1] != '\\') { 634232153Smm if (multiline == 1 && 635232153Smm ++entry_cnt >= MAX_BID_ENTRY) 636232153Smm break; 637232153Smm multiline = 0; 638232153Smm } 639232153Smm p += len; 640232153Smm avail -= len; 641232153Smm continue; 642232153Smm } 643232153Smm if (p[0] != '/') { 644248616Smm int last_is_path, keywords; 645248616Smm 646248616Smm keywords = bid_entry(p, len, nl, &last_is_path); 647248616Smm if (keywords >= 0) { 648232153Smm detected_bytes += len; 649248616Smm if (form_D == 0) { 650248616Smm if (last_is_path) 651248616Smm form_D = 1; 652248616Smm else if (keywords > 0) 653248616Smm /* This line is not `form D'. */ 654248616Smm form_D = -1; 655248616Smm } else if (form_D == 1) { 656248616Smm if (!last_is_path && keywords > 0) 657248616Smm /* This this is not `form D' 658248616Smm * and We cannot accept mixed 659248616Smm * format. */ 660248616Smm break; 661248616Smm } 662248616Smm if (!last_is_path && p[len-nl-1] == '\\') 663232153Smm /* This line continues. */ 664232153Smm multiline = 1; 665232153Smm else { 666232153Smm /* We've got plenty of correct lines 667232153Smm * to assume that this file is a mtree 668232153Smm * format. */ 669232153Smm if (++entry_cnt >= MAX_BID_ENTRY) 670232153Smm break; 671232153Smm } 672232153Smm } else 673232153Smm break; 674232153Smm } else if (strncmp(p, "/set", 4) == 0) { 675248616Smm if (bid_keyword_list(p+4, len-4, 0, 0) <= 0) 676232153Smm break; 677232153Smm /* This line continues. */ 678232153Smm if (p[len-nl-1] == '\\') 679232153Smm multiline = 2; 680232153Smm } else if (strncmp(p, "/unset", 6) == 0) { 681248616Smm if (bid_keyword_list(p+6, len-6, 1, 0) <= 0) 682232153Smm break; 683232153Smm /* This line continues. */ 684232153Smm if (p[len-nl-1] == '\\') 685232153Smm multiline = 2; 686232153Smm } else 687232153Smm break; 688232153Smm 689232153Smm /* Test next line. */ 690232153Smm p += len; 691232153Smm avail -= len; 692232153Smm } 693248616Smm if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) { 694248616Smm if (is_form_d != NULL) { 695248616Smm if (form_D == 1) 696248616Smm *is_form_d = 1; 697248616Smm } 698232153Smm return (32); 699248616Smm } 700232153Smm 701228753Smm return (0); 702228753Smm} 703228753Smm 704228753Smm/* 705228753Smm * The extended mtree format permits multiple lines specifying 706228753Smm * attributes for each file. For those entries, only the last line 707228753Smm * is actually used. Practically speaking, that means we have 708228753Smm * to read the entire mtree file into memory up front. 709228753Smm * 710228753Smm * The parsing is done in two steps. First, it is decided if a line 711228753Smm * changes the global defaults and if it is, processed accordingly. 712228753Smm * Otherwise, the options of the line are merged with the current 713228753Smm * global options. 714228753Smm */ 715228753Smmstatic int 716228753Smmadd_option(struct archive_read *a, struct mtree_option **global, 717228753Smm const char *value, size_t len) 718228753Smm{ 719232153Smm struct mtree_option *opt; 720228753Smm 721232153Smm if ((opt = malloc(sizeof(*opt))) == NULL) { 722228753Smm archive_set_error(&a->archive, errno, "Can't allocate memory"); 723228753Smm return (ARCHIVE_FATAL); 724228753Smm } 725232153Smm if ((opt->value = malloc(len + 1)) == NULL) { 726232153Smm free(opt); 727228753Smm archive_set_error(&a->archive, errno, "Can't allocate memory"); 728228753Smm return (ARCHIVE_FATAL); 729228753Smm } 730232153Smm memcpy(opt->value, value, len); 731232153Smm opt->value[len] = '\0'; 732232153Smm opt->next = *global; 733232153Smm *global = opt; 734228753Smm return (ARCHIVE_OK); 735228753Smm} 736228753Smm 737228753Smmstatic void 738228753Smmremove_option(struct mtree_option **global, const char *value, size_t len) 739228753Smm{ 740228753Smm struct mtree_option *iter, *last; 741228753Smm 742228753Smm last = NULL; 743228753Smm for (iter = *global; iter != NULL; last = iter, iter = iter->next) { 744228753Smm if (strncmp(iter->value, value, len) == 0 && 745228753Smm (iter->value[len] == '\0' || 746228753Smm iter->value[len] == '=')) 747228753Smm break; 748228753Smm } 749228753Smm if (iter == NULL) 750228753Smm return; 751228753Smm if (last == NULL) 752228753Smm *global = iter->next; 753228753Smm else 754228753Smm last->next = iter->next; 755228753Smm 756228753Smm free(iter->value); 757228753Smm free(iter); 758228753Smm} 759228753Smm 760228753Smmstatic int 761228753Smmprocess_global_set(struct archive_read *a, 762228753Smm struct mtree_option **global, const char *line) 763228753Smm{ 764228753Smm const char *next, *eq; 765228753Smm size_t len; 766228753Smm int r; 767228753Smm 768228753Smm line += 4; 769228753Smm for (;;) { 770228753Smm next = line + strspn(line, " \t\r\n"); 771228753Smm if (*next == '\0') 772228753Smm return (ARCHIVE_OK); 773228753Smm line = next; 774228753Smm next = line + strcspn(line, " \t\r\n"); 775228753Smm eq = strchr(line, '='); 776228753Smm if (eq > next) 777228753Smm len = next - line; 778228753Smm else 779228753Smm len = eq - line; 780228753Smm 781228753Smm remove_option(global, line, len); 782228753Smm r = add_option(a, global, line, next - line); 783228753Smm if (r != ARCHIVE_OK) 784228753Smm return (r); 785228753Smm line = next; 786228753Smm } 787228753Smm} 788228753Smm 789228753Smmstatic int 790228753Smmprocess_global_unset(struct archive_read *a, 791228753Smm struct mtree_option **global, const char *line) 792228753Smm{ 793228753Smm const char *next; 794228753Smm size_t len; 795228753Smm 796228753Smm line += 6; 797228753Smm if (strchr(line, '=') != NULL) { 798228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, 799228753Smm "/unset shall not contain `='"); 800228753Smm return ARCHIVE_FATAL; 801228753Smm } 802228753Smm 803228753Smm for (;;) { 804228753Smm next = line + strspn(line, " \t\r\n"); 805228753Smm if (*next == '\0') 806228753Smm return (ARCHIVE_OK); 807228753Smm line = next; 808228753Smm len = strcspn(line, " \t\r\n"); 809228753Smm 810228753Smm if (len == 3 && strncmp(line, "all", 3) == 0) { 811228753Smm free_options(*global); 812228753Smm *global = NULL; 813228753Smm } else { 814228753Smm remove_option(global, line, len); 815228753Smm } 816228753Smm 817228753Smm line += len; 818228753Smm } 819228753Smm} 820228753Smm 821228753Smmstatic int 822228753Smmprocess_add_entry(struct archive_read *a, struct mtree *mtree, 823248616Smm struct mtree_option **global, const char *line, ssize_t line_len, 824248616Smm struct mtree_entry **last_entry, int is_form_d) 825228753Smm{ 826228753Smm struct mtree_entry *entry; 827228753Smm struct mtree_option *iter; 828248616Smm const char *next, *eq, *name, *end; 829228753Smm size_t len; 830228753Smm int r; 831228753Smm 832228753Smm if ((entry = malloc(sizeof(*entry))) == NULL) { 833228753Smm archive_set_error(&a->archive, errno, "Can't allocate memory"); 834228753Smm return (ARCHIVE_FATAL); 835228753Smm } 836228753Smm entry->next = NULL; 837228753Smm entry->options = NULL; 838228753Smm entry->name = NULL; 839228753Smm entry->used = 0; 840228753Smm entry->full = 0; 841228753Smm 842228753Smm /* Add this entry to list. */ 843228753Smm if (*last_entry == NULL) 844228753Smm mtree->entries = entry; 845228753Smm else 846228753Smm (*last_entry)->next = entry; 847228753Smm *last_entry = entry; 848228753Smm 849248616Smm if (is_form_d) { 850248616Smm /* 851248616Smm * This form places the file name as last parameter. 852248616Smm */ 853248616Smm name = line + line_len -1; 854248616Smm while (line_len > 0) { 855248616Smm if (*name != '\r' && *name != '\n' && 856248616Smm *name != '\t' && *name != ' ') 857248616Smm break; 858248616Smm name--; 859248616Smm line_len--; 860248616Smm } 861248616Smm len = 0; 862248616Smm while (line_len > 0) { 863248616Smm if (*name == '\r' || *name == '\n' || 864248616Smm *name == '\t' || *name == ' ') { 865248616Smm name++; 866248616Smm break; 867248616Smm } 868248616Smm name--; 869248616Smm line_len--; 870248616Smm len++; 871248616Smm } 872248616Smm end = name; 873248616Smm } else { 874248616Smm len = strcspn(line, " \t\r\n"); 875248616Smm name = line; 876248616Smm line += len; 877248616Smm end = line + line_len; 878248616Smm } 879248616Smm 880228753Smm if ((entry->name = malloc(len + 1)) == NULL) { 881228753Smm archive_set_error(&a->archive, errno, "Can't allocate memory"); 882228753Smm return (ARCHIVE_FATAL); 883228753Smm } 884228753Smm 885248616Smm memcpy(entry->name, name, len); 886228753Smm entry->name[len] = '\0'; 887228753Smm parse_escapes(entry->name, entry); 888228753Smm 889228753Smm for (iter = *global; iter != NULL; iter = iter->next) { 890228753Smm r = add_option(a, &entry->options, iter->value, 891228753Smm strlen(iter->value)); 892228753Smm if (r != ARCHIVE_OK) 893228753Smm return (r); 894228753Smm } 895228753Smm 896228753Smm for (;;) { 897228753Smm next = line + strspn(line, " \t\r\n"); 898228753Smm if (*next == '\0') 899228753Smm return (ARCHIVE_OK); 900248616Smm if (next >= end) 901248616Smm return (ARCHIVE_OK); 902228753Smm line = next; 903228753Smm next = line + strcspn(line, " \t\r\n"); 904228753Smm eq = strchr(line, '='); 905228753Smm if (eq == NULL || eq > next) 906228753Smm len = next - line; 907228753Smm else 908228753Smm len = eq - line; 909228753Smm 910228753Smm remove_option(&entry->options, line, len); 911228753Smm r = add_option(a, &entry->options, line, next - line); 912228753Smm if (r != ARCHIVE_OK) 913228753Smm return (r); 914228753Smm line = next; 915228753Smm } 916228753Smm} 917228753Smm 918228753Smmstatic int 919228753Smmread_mtree(struct archive_read *a, struct mtree *mtree) 920228753Smm{ 921228753Smm ssize_t len; 922228753Smm uintmax_t counter; 923228753Smm char *p; 924228753Smm struct mtree_option *global; 925228753Smm struct mtree_entry *last_entry; 926248616Smm int r, is_form_d; 927228753Smm 928228753Smm mtree->archive_format = ARCHIVE_FORMAT_MTREE; 929228753Smm mtree->archive_format_name = "mtree"; 930228753Smm 931228753Smm global = NULL; 932228753Smm last_entry = NULL; 933228753Smm 934248616Smm (void)detect_form(a, &is_form_d); 935248616Smm 936228753Smm for (counter = 1; ; ++counter) { 937232153Smm len = readline(a, mtree, &p, 65536); 938228753Smm if (len == 0) { 939228753Smm mtree->this_entry = mtree->entries; 940228753Smm free_options(global); 941228753Smm return (ARCHIVE_OK); 942228753Smm } 943228753Smm if (len < 0) { 944228753Smm free_options(global); 945248616Smm return ((int)len); 946228753Smm } 947228753Smm /* Leading whitespace is never significant, ignore it. */ 948228753Smm while (*p == ' ' || *p == '\t') { 949228753Smm ++p; 950228753Smm --len; 951228753Smm } 952228753Smm /* Skip content lines and blank lines. */ 953228753Smm if (*p == '#') 954228753Smm continue; 955228753Smm if (*p == '\r' || *p == '\n' || *p == '\0') 956228753Smm continue; 957228753Smm if (*p != '/') { 958248616Smm r = process_add_entry(a, mtree, &global, p, len, 959248616Smm &last_entry, is_form_d); 960228753Smm } else if (strncmp(p, "/set", 4) == 0) { 961228753Smm if (p[4] != ' ' && p[4] != '\t') 962228753Smm break; 963228753Smm r = process_global_set(a, &global, p); 964228753Smm } else if (strncmp(p, "/unset", 6) == 0) { 965228753Smm if (p[6] != ' ' && p[6] != '\t') 966228753Smm break; 967228753Smm r = process_global_unset(a, &global, p); 968228753Smm } else 969228753Smm break; 970228753Smm 971228753Smm if (r != ARCHIVE_OK) { 972228753Smm free_options(global); 973228753Smm return r; 974228753Smm } 975228753Smm } 976228753Smm 977228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, 978228753Smm "Can't parse line %ju", counter); 979228753Smm free_options(global); 980228753Smm return (ARCHIVE_FATAL); 981228753Smm} 982228753Smm 983228753Smm/* 984228753Smm * Read in the entire mtree file into memory on the first request. 985228753Smm * Then use the next unused file to satisfy each header request. 986228753Smm */ 987228753Smmstatic int 988228753Smmread_header(struct archive_read *a, struct archive_entry *entry) 989228753Smm{ 990228753Smm struct mtree *mtree; 991228753Smm char *p; 992228753Smm int r, use_next; 993228753Smm 994228753Smm mtree = (struct mtree *)(a->format->data); 995228753Smm 996228753Smm if (mtree->fd >= 0) { 997228753Smm close(mtree->fd); 998228753Smm mtree->fd = -1; 999228753Smm } 1000228753Smm 1001228753Smm if (mtree->entries == NULL) { 1002228753Smm mtree->resolver = archive_entry_linkresolver_new(); 1003228753Smm if (mtree->resolver == NULL) 1004228753Smm return ARCHIVE_FATAL; 1005228753Smm archive_entry_linkresolver_set_strategy(mtree->resolver, 1006228753Smm ARCHIVE_FORMAT_MTREE); 1007228753Smm r = read_mtree(a, mtree); 1008228753Smm if (r != ARCHIVE_OK) 1009228753Smm return (r); 1010228753Smm } 1011228753Smm 1012228753Smm a->archive.archive_format = mtree->archive_format; 1013228753Smm a->archive.archive_format_name = mtree->archive_format_name; 1014228753Smm 1015228753Smm for (;;) { 1016228753Smm if (mtree->this_entry == NULL) 1017228753Smm return (ARCHIVE_EOF); 1018228753Smm if (strcmp(mtree->this_entry->name, "..") == 0) { 1019228753Smm mtree->this_entry->used = 1; 1020228753Smm if (archive_strlen(&mtree->current_dir) > 0) { 1021228753Smm /* Roll back current path. */ 1022228753Smm p = mtree->current_dir.s 1023228753Smm + mtree->current_dir.length - 1; 1024228753Smm while (p >= mtree->current_dir.s && *p != '/') 1025228753Smm --p; 1026228753Smm if (p >= mtree->current_dir.s) 1027228753Smm --p; 1028228753Smm mtree->current_dir.length 1029228753Smm = p - mtree->current_dir.s + 1; 1030228753Smm } 1031228753Smm } 1032228753Smm if (!mtree->this_entry->used) { 1033228753Smm use_next = 0; 1034228753Smm r = parse_file(a, entry, mtree, mtree->this_entry, &use_next); 1035228753Smm if (use_next == 0) 1036228753Smm return (r); 1037228753Smm } 1038228753Smm mtree->this_entry = mtree->this_entry->next; 1039228753Smm } 1040228753Smm} 1041228753Smm 1042228753Smm/* 1043228753Smm * A single file can have multiple lines contribute specifications. 1044228753Smm * Parse as many lines as necessary, then pull additional information 1045228753Smm * from a backing file on disk as necessary. 1046228753Smm */ 1047228753Smmstatic int 1048228753Smmparse_file(struct archive_read *a, struct archive_entry *entry, 1049228753Smm struct mtree *mtree, struct mtree_entry *mentry, int *use_next) 1050228753Smm{ 1051228753Smm const char *path; 1052228753Smm struct stat st_storage, *st; 1053228753Smm struct mtree_entry *mp; 1054228753Smm struct archive_entry *sparse_entry; 1055232153Smm int r = ARCHIVE_OK, r1, parsed_kws; 1056228753Smm 1057228753Smm mentry->used = 1; 1058228753Smm 1059228753Smm /* Initialize reasonable defaults. */ 1060232153Smm archive_entry_set_filetype(entry, AE_IFREG); 1061228753Smm archive_entry_set_size(entry, 0); 1062228753Smm archive_string_empty(&mtree->contents_name); 1063228753Smm 1064228753Smm /* Parse options from this line. */ 1065228753Smm parsed_kws = 0; 1066228753Smm r = parse_line(a, entry, mtree, mentry, &parsed_kws); 1067228753Smm 1068228753Smm if (mentry->full) { 1069228753Smm archive_entry_copy_pathname(entry, mentry->name); 1070228753Smm /* 1071228753Smm * "Full" entries are allowed to have multiple lines 1072228753Smm * and those lines aren't required to be adjacent. We 1073228753Smm * don't support multiple lines for "relative" entries 1074228753Smm * nor do we make any attempt to merge data from 1075228753Smm * separate "relative" and "full" entries. (Merging 1076228753Smm * "relative" and "full" entries would require dealing 1077228753Smm * with pathname canonicalization, which is a very 1078228753Smm * tricky subject.) 1079228753Smm */ 1080228753Smm for (mp = mentry->next; mp != NULL; mp = mp->next) { 1081228753Smm if (mp->full && !mp->used 1082228753Smm && strcmp(mentry->name, mp->name) == 0) { 1083228753Smm /* Later lines override earlier ones. */ 1084228753Smm mp->used = 1; 1085228753Smm r1 = parse_line(a, entry, mtree, mp, 1086228753Smm &parsed_kws); 1087228753Smm if (r1 < r) 1088228753Smm r = r1; 1089228753Smm } 1090228753Smm } 1091228753Smm } else { 1092228753Smm /* 1093228753Smm * Relative entries require us to construct 1094228753Smm * the full path and possibly update the 1095228753Smm * current directory. 1096228753Smm */ 1097228753Smm size_t n = archive_strlen(&mtree->current_dir); 1098228753Smm if (n > 0) 1099228753Smm archive_strcat(&mtree->current_dir, "/"); 1100228753Smm archive_strcat(&mtree->current_dir, mentry->name); 1101228753Smm archive_entry_copy_pathname(entry, mtree->current_dir.s); 1102228753Smm if (archive_entry_filetype(entry) != AE_IFDIR) 1103228753Smm mtree->current_dir.length = n; 1104228753Smm } 1105228753Smm 1106228753Smm /* 1107228753Smm * Try to open and stat the file to get the real size 1108228753Smm * and other file info. It would be nice to avoid 1109228753Smm * this here so that getting a listing of an mtree 1110228753Smm * wouldn't require opening every referenced contents 1111228753Smm * file. But then we wouldn't know the actual 1112228753Smm * contents size, so I don't see a really viable way 1113228753Smm * around this. (Also, we may want to someday pull 1114228753Smm * other unspecified info from the contents file on 1115228753Smm * disk.) 1116228753Smm */ 1117228753Smm mtree->fd = -1; 1118228753Smm if (archive_strlen(&mtree->contents_name) > 0) 1119228753Smm path = mtree->contents_name.s; 1120228753Smm else 1121228753Smm path = archive_entry_pathname(entry); 1122228753Smm 1123228753Smm if (archive_entry_filetype(entry) == AE_IFREG || 1124228753Smm archive_entry_filetype(entry) == AE_IFDIR) { 1125248616Smm mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC); 1126248616Smm __archive_ensure_cloexec_flag(mtree->fd); 1127228753Smm if (mtree->fd == -1 && 1128228753Smm (errno != ENOENT || 1129228753Smm archive_strlen(&mtree->contents_name) > 0)) { 1130228753Smm archive_set_error(&a->archive, errno, 1131228753Smm "Can't open %s", path); 1132228753Smm r = ARCHIVE_WARN; 1133228753Smm } 1134228753Smm } 1135228753Smm 1136228753Smm st = &st_storage; 1137228753Smm if (mtree->fd >= 0) { 1138228753Smm if (fstat(mtree->fd, st) == -1) { 1139228753Smm archive_set_error(&a->archive, errno, 1140228753Smm "Could not fstat %s", path); 1141228753Smm r = ARCHIVE_WARN; 1142228753Smm /* If we can't stat it, don't keep it open. */ 1143228753Smm close(mtree->fd); 1144228753Smm mtree->fd = -1; 1145228753Smm st = NULL; 1146228753Smm } 1147228753Smm } else if (lstat(path, st) == -1) { 1148228753Smm st = NULL; 1149228753Smm } 1150228753Smm 1151228753Smm /* 1152228753Smm * Check for a mismatch between the type in the specification and 1153228753Smm * the type of the contents object on disk. 1154228753Smm */ 1155228753Smm if (st != NULL) { 1156232153Smm if ( 1157232153Smm ((st->st_mode & S_IFMT) == S_IFREG && 1158232153Smm archive_entry_filetype(entry) == AE_IFREG) 1159232153Smm#ifdef S_IFLNK 1160232153Smm || ((st->st_mode & S_IFMT) == S_IFLNK && 1161232153Smm archive_entry_filetype(entry) == AE_IFLNK) 1162232153Smm#endif 1163232153Smm#ifdef S_IFSOCK 1164232153Smm || ((st->st_mode & S_IFSOCK) == S_IFSOCK && 1165232153Smm archive_entry_filetype(entry) == AE_IFSOCK) 1166232153Smm#endif 1167232153Smm#ifdef S_IFCHR 1168232153Smm || ((st->st_mode & S_IFMT) == S_IFCHR && 1169232153Smm archive_entry_filetype(entry) == AE_IFCHR) 1170232153Smm#endif 1171232153Smm#ifdef S_IFBLK 1172232153Smm || ((st->st_mode & S_IFMT) == S_IFBLK && 1173232153Smm archive_entry_filetype(entry) == AE_IFBLK) 1174232153Smm#endif 1175232153Smm || ((st->st_mode & S_IFMT) == S_IFDIR && 1176232153Smm archive_entry_filetype(entry) == AE_IFDIR) 1177232153Smm#ifdef S_IFIFO 1178232153Smm || ((st->st_mode & S_IFMT) == S_IFIFO && 1179232153Smm archive_entry_filetype(entry) == AE_IFIFO) 1180232153Smm#endif 1181232153Smm ) { 1182232153Smm /* Types match. */ 1183232153Smm } else { 1184232153Smm /* Types don't match; bail out gracefully. */ 1185232153Smm if (mtree->fd >= 0) 1186232153Smm close(mtree->fd); 1187232153Smm mtree->fd = -1; 1188232153Smm if (parsed_kws & MTREE_HAS_OPTIONAL) { 1189232153Smm /* It's not an error for an optional entry 1190232153Smm to not match disk. */ 1191232153Smm *use_next = 1; 1192232153Smm } else if (r == ARCHIVE_OK) { 1193228753Smm archive_set_error(&a->archive, 1194228753Smm ARCHIVE_ERRNO_MISC, 1195228753Smm "mtree specification has different type for %s", 1196228753Smm archive_entry_pathname(entry)); 1197228753Smm r = ARCHIVE_WARN; 1198228753Smm } 1199228753Smm return r; 1200228753Smm } 1201228753Smm } 1202228753Smm 1203228753Smm /* 1204228753Smm * If there is a contents file on disk, pick some of the metadata 1205228753Smm * from that file. For most of these, we only set it from the contents 1206228753Smm * if it wasn't already parsed from the specification. 1207228753Smm */ 1208228753Smm if (st != NULL) { 1209248616Smm if (((parsed_kws & MTREE_HAS_DEVICE) == 0 || 1210248616Smm (parsed_kws & MTREE_HAS_NOCHANGE) != 0) && 1211228753Smm (archive_entry_filetype(entry) == AE_IFCHR || 1212228753Smm archive_entry_filetype(entry) == AE_IFBLK)) 1213228753Smm archive_entry_set_rdev(entry, st->st_rdev); 1214248616Smm if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 || 1215248616Smm (parsed_kws & MTREE_HAS_NOCHANGE) != 0) 1216228753Smm archive_entry_set_gid(entry, st->st_gid); 1217248616Smm if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 || 1218248616Smm (parsed_kws & MTREE_HAS_NOCHANGE) != 0) 1219228753Smm archive_entry_set_uid(entry, st->st_uid); 1220248616Smm if ((parsed_kws & MTREE_HAS_MTIME) == 0 || 1221248616Smm (parsed_kws & MTREE_HAS_NOCHANGE) != 0) { 1222228753Smm#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1223228753Smm archive_entry_set_mtime(entry, st->st_mtime, 1224228753Smm st->st_mtimespec.tv_nsec); 1225228753Smm#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1226228753Smm archive_entry_set_mtime(entry, st->st_mtime, 1227228753Smm st->st_mtim.tv_nsec); 1228228753Smm#elif HAVE_STRUCT_STAT_ST_MTIME_N 1229228753Smm archive_entry_set_mtime(entry, st->st_mtime, 1230228753Smm st->st_mtime_n); 1231228753Smm#elif HAVE_STRUCT_STAT_ST_UMTIME 1232228753Smm archive_entry_set_mtime(entry, st->st_mtime, 1233228753Smm st->st_umtime*1000); 1234228753Smm#elif HAVE_STRUCT_STAT_ST_MTIME_USEC 1235228753Smm archive_entry_set_mtime(entry, st->st_mtime, 1236228753Smm st->st_mtime_usec*1000); 1237228753Smm#else 1238228753Smm archive_entry_set_mtime(entry, st->st_mtime, 0); 1239228753Smm#endif 1240228753Smm } 1241248616Smm if ((parsed_kws & MTREE_HAS_NLINK) == 0 || 1242248616Smm (parsed_kws & MTREE_HAS_NOCHANGE) != 0) 1243228753Smm archive_entry_set_nlink(entry, st->st_nlink); 1244248616Smm if ((parsed_kws & MTREE_HAS_PERM) == 0 || 1245248616Smm (parsed_kws & MTREE_HAS_NOCHANGE) != 0) 1246228753Smm archive_entry_set_perm(entry, st->st_mode); 1247248616Smm if ((parsed_kws & MTREE_HAS_SIZE) == 0 || 1248248616Smm (parsed_kws & MTREE_HAS_NOCHANGE) != 0) 1249228753Smm archive_entry_set_size(entry, st->st_size); 1250228753Smm archive_entry_set_ino(entry, st->st_ino); 1251228753Smm archive_entry_set_dev(entry, st->st_dev); 1252228753Smm 1253228753Smm archive_entry_linkify(mtree->resolver, &entry, &sparse_entry); 1254228753Smm } else if (parsed_kws & MTREE_HAS_OPTIONAL) { 1255228753Smm /* 1256228753Smm * Couldn't open the entry, stat it or the on-disk type 1257228753Smm * didn't match. If this entry is optional, just ignore it 1258228753Smm * and read the next header entry. 1259228753Smm */ 1260228753Smm *use_next = 1; 1261228753Smm return ARCHIVE_OK; 1262228753Smm } 1263228753Smm 1264228753Smm mtree->cur_size = archive_entry_size(entry); 1265228753Smm mtree->offset = 0; 1266228753Smm 1267228753Smm return r; 1268228753Smm} 1269228753Smm 1270228753Smm/* 1271228753Smm * Each line contains a sequence of keywords. 1272228753Smm */ 1273228753Smmstatic int 1274228753Smmparse_line(struct archive_read *a, struct archive_entry *entry, 1275228753Smm struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws) 1276228753Smm{ 1277228753Smm struct mtree_option *iter; 1278228753Smm int r = ARCHIVE_OK, r1; 1279228753Smm 1280228753Smm for (iter = mp->options; iter != NULL; iter = iter->next) { 1281228753Smm r1 = parse_keyword(a, mtree, entry, iter, parsed_kws); 1282228753Smm if (r1 < r) 1283228753Smm r = r1; 1284228753Smm } 1285232153Smm if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) { 1286228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, 1287228753Smm "Missing type keyword in mtree specification"); 1288228753Smm return (ARCHIVE_WARN); 1289228753Smm } 1290228753Smm return (r); 1291228753Smm} 1292228753Smm 1293228753Smm/* 1294228753Smm * Device entries have one of the following forms: 1295228753Smm * raw dev_t 1296228753Smm * format,major,minor[,subdevice] 1297228753Smm * 1298228753Smm * Just use major and minor, no translation etc is done 1299228753Smm * between formats. 1300228753Smm */ 1301228753Smmstatic int 1302228753Smmparse_device(struct archive *a, struct archive_entry *entry, char *val) 1303228753Smm{ 1304228753Smm char *comma1, *comma2; 1305228753Smm 1306228753Smm comma1 = strchr(val, ','); 1307228753Smm if (comma1 == NULL) { 1308238856Smm archive_entry_set_dev(entry, (dev_t)mtree_atol10(&val)); 1309228753Smm return (ARCHIVE_OK); 1310228753Smm } 1311228753Smm ++comma1; 1312228753Smm comma2 = strchr(comma1, ','); 1313228753Smm if (comma2 == NULL) { 1314228753Smm archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, 1315228753Smm "Malformed device attribute"); 1316228753Smm return (ARCHIVE_WARN); 1317228753Smm } 1318228753Smm ++comma2; 1319238856Smm archive_entry_set_rdevmajor(entry, (dev_t)mtree_atol(&comma1)); 1320238856Smm archive_entry_set_rdevminor(entry, (dev_t)mtree_atol(&comma2)); 1321228753Smm return (ARCHIVE_OK); 1322228753Smm} 1323228753Smm 1324228753Smm/* 1325228753Smm * Parse a single keyword and its value. 1326228753Smm */ 1327228753Smmstatic int 1328228753Smmparse_keyword(struct archive_read *a, struct mtree *mtree, 1329232153Smm struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws) 1330228753Smm{ 1331228753Smm char *val, *key; 1332228753Smm 1333232153Smm key = opt->value; 1334228753Smm 1335228753Smm if (*key == '\0') 1336228753Smm return (ARCHIVE_OK); 1337228753Smm 1338248616Smm if (strcmp(key, "nochange") == 0) { 1339248616Smm *parsed_kws |= MTREE_HAS_NOCHANGE; 1340248616Smm return (ARCHIVE_OK); 1341248616Smm } 1342228753Smm if (strcmp(key, "optional") == 0) { 1343228753Smm *parsed_kws |= MTREE_HAS_OPTIONAL; 1344228753Smm return (ARCHIVE_OK); 1345228753Smm } 1346228753Smm if (strcmp(key, "ignore") == 0) { 1347228753Smm /* 1348228753Smm * The mtree processing is not recursive, so 1349228753Smm * recursion will only happen for explicitly listed 1350228753Smm * entries. 1351228753Smm */ 1352228753Smm return (ARCHIVE_OK); 1353228753Smm } 1354228753Smm 1355228753Smm val = strchr(key, '='); 1356228753Smm if (val == NULL) { 1357228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, 1358228753Smm "Malformed attribute \"%s\" (%d)", key, key[0]); 1359228753Smm return (ARCHIVE_WARN); 1360228753Smm } 1361228753Smm 1362228753Smm *val = '\0'; 1363228753Smm ++val; 1364228753Smm 1365228753Smm switch (key[0]) { 1366228753Smm case 'c': 1367228753Smm if (strcmp(key, "content") == 0 1368228753Smm || strcmp(key, "contents") == 0) { 1369228753Smm parse_escapes(val, NULL); 1370228753Smm archive_strcpy(&mtree->contents_name, val); 1371228753Smm break; 1372228753Smm } 1373228753Smm if (strcmp(key, "cksum") == 0) 1374228753Smm break; 1375228753Smm case 'd': 1376228753Smm if (strcmp(key, "device") == 0) { 1377228753Smm *parsed_kws |= MTREE_HAS_DEVICE; 1378228753Smm return parse_device(&a->archive, entry, val); 1379228753Smm } 1380228753Smm case 'f': 1381228753Smm if (strcmp(key, "flags") == 0) { 1382228753Smm *parsed_kws |= MTREE_HAS_FFLAGS; 1383228753Smm archive_entry_copy_fflags_text(entry, val); 1384228753Smm break; 1385228753Smm } 1386228753Smm case 'g': 1387228753Smm if (strcmp(key, "gid") == 0) { 1388228753Smm *parsed_kws |= MTREE_HAS_GID; 1389228753Smm archive_entry_set_gid(entry, mtree_atol10(&val)); 1390228753Smm break; 1391228753Smm } 1392228753Smm if (strcmp(key, "gname") == 0) { 1393228753Smm *parsed_kws |= MTREE_HAS_GNAME; 1394228753Smm archive_entry_copy_gname(entry, val); 1395228753Smm break; 1396228753Smm } 1397228753Smm case 'l': 1398228753Smm if (strcmp(key, "link") == 0) { 1399228753Smm archive_entry_copy_symlink(entry, val); 1400228753Smm break; 1401228753Smm } 1402228753Smm case 'm': 1403228753Smm if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) 1404228753Smm break; 1405228753Smm if (strcmp(key, "mode") == 0) { 1406228753Smm if (val[0] >= '0' && val[0] <= '9') { 1407228753Smm *parsed_kws |= MTREE_HAS_PERM; 1408228753Smm archive_entry_set_perm(entry, 1409238856Smm (mode_t)mtree_atol8(&val)); 1410228753Smm } else { 1411228753Smm archive_set_error(&a->archive, 1412228753Smm ARCHIVE_ERRNO_FILE_FORMAT, 1413228753Smm "Symbolic mode \"%s\" unsupported", val); 1414228753Smm return ARCHIVE_WARN; 1415228753Smm } 1416228753Smm break; 1417228753Smm } 1418228753Smm case 'n': 1419228753Smm if (strcmp(key, "nlink") == 0) { 1420228753Smm *parsed_kws |= MTREE_HAS_NLINK; 1421238856Smm archive_entry_set_nlink(entry, 1422238856Smm (unsigned int)mtree_atol10(&val)); 1423228753Smm break; 1424228753Smm } 1425228753Smm case 'r': 1426228753Smm if (strcmp(key, "rmd160") == 0 || 1427228753Smm strcmp(key, "rmd160digest") == 0) 1428228753Smm break; 1429228753Smm case 's': 1430228753Smm if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) 1431228753Smm break; 1432228753Smm if (strcmp(key, "sha256") == 0 || 1433228753Smm strcmp(key, "sha256digest") == 0) 1434228753Smm break; 1435228753Smm if (strcmp(key, "sha384") == 0 || 1436228753Smm strcmp(key, "sha384digest") == 0) 1437228753Smm break; 1438228753Smm if (strcmp(key, "sha512") == 0 || 1439228753Smm strcmp(key, "sha512digest") == 0) 1440228753Smm break; 1441228753Smm if (strcmp(key, "size") == 0) { 1442228753Smm archive_entry_set_size(entry, mtree_atol10(&val)); 1443228753Smm break; 1444228753Smm } 1445228753Smm case 't': 1446228753Smm if (strcmp(key, "tags") == 0) { 1447228753Smm /* 1448228753Smm * Comma delimited list of tags. 1449228753Smm * Ignore the tags for now, but the interface 1450228753Smm * should be extended to allow inclusion/exclusion. 1451228753Smm */ 1452228753Smm break; 1453228753Smm } 1454228753Smm if (strcmp(key, "time") == 0) { 1455232153Smm int64_t m; 1456232153Smm int64_t my_time_t_max = get_time_t_max(); 1457232153Smm int64_t my_time_t_min = get_time_t_min(); 1458228753Smm long ns; 1459228753Smm 1460228753Smm *parsed_kws |= MTREE_HAS_MTIME; 1461232153Smm m = mtree_atol10(&val); 1462232153Smm /* Replicate an old mtree bug: 1463232153Smm * 123456789.1 represents 123456789 1464232153Smm * seconds and 1 nanosecond. */ 1465228753Smm if (*val == '.') { 1466228753Smm ++val; 1467228753Smm ns = (long)mtree_atol10(&val); 1468228753Smm } else 1469228753Smm ns = 0; 1470232153Smm if (m > my_time_t_max) 1471232153Smm m = my_time_t_max; 1472232153Smm else if (m < my_time_t_min) 1473232153Smm m = my_time_t_min; 1474232153Smm archive_entry_set_mtime(entry, (time_t)m, ns); 1475228753Smm break; 1476228753Smm } 1477228753Smm if (strcmp(key, "type") == 0) { 1478228753Smm switch (val[0]) { 1479228753Smm case 'b': 1480228753Smm if (strcmp(val, "block") == 0) { 1481232153Smm archive_entry_set_filetype(entry, AE_IFBLK); 1482228753Smm break; 1483228753Smm } 1484228753Smm case 'c': 1485228753Smm if (strcmp(val, "char") == 0) { 1486232153Smm archive_entry_set_filetype(entry, AE_IFCHR); 1487228753Smm break; 1488228753Smm } 1489228753Smm case 'd': 1490228753Smm if (strcmp(val, "dir") == 0) { 1491232153Smm archive_entry_set_filetype(entry, AE_IFDIR); 1492228753Smm break; 1493228753Smm } 1494228753Smm case 'f': 1495228753Smm if (strcmp(val, "fifo") == 0) { 1496232153Smm archive_entry_set_filetype(entry, AE_IFIFO); 1497228753Smm break; 1498228753Smm } 1499228753Smm if (strcmp(val, "file") == 0) { 1500232153Smm archive_entry_set_filetype(entry, AE_IFREG); 1501228753Smm break; 1502228753Smm } 1503228753Smm case 'l': 1504228753Smm if (strcmp(val, "link") == 0) { 1505232153Smm archive_entry_set_filetype(entry, AE_IFLNK); 1506228753Smm break; 1507228753Smm } 1508228753Smm default: 1509228753Smm archive_set_error(&a->archive, 1510228753Smm ARCHIVE_ERRNO_FILE_FORMAT, 1511232153Smm "Unrecognized file type \"%s\"; assuming \"file\"", val); 1512232153Smm archive_entry_set_filetype(entry, AE_IFREG); 1513228753Smm return (ARCHIVE_WARN); 1514228753Smm } 1515232153Smm *parsed_kws |= MTREE_HAS_TYPE; 1516228753Smm break; 1517228753Smm } 1518228753Smm case 'u': 1519228753Smm if (strcmp(key, "uid") == 0) { 1520228753Smm *parsed_kws |= MTREE_HAS_UID; 1521228753Smm archive_entry_set_uid(entry, mtree_atol10(&val)); 1522228753Smm break; 1523228753Smm } 1524228753Smm if (strcmp(key, "uname") == 0) { 1525228753Smm *parsed_kws |= MTREE_HAS_UNAME; 1526228753Smm archive_entry_copy_uname(entry, val); 1527228753Smm break; 1528228753Smm } 1529228753Smm default: 1530228753Smm archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, 1531228753Smm "Unrecognized key %s=%s", key, val); 1532228753Smm return (ARCHIVE_WARN); 1533228753Smm } 1534228753Smm return (ARCHIVE_OK); 1535228753Smm} 1536228753Smm 1537228753Smmstatic int 1538232153Smmread_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) 1539228753Smm{ 1540228753Smm size_t bytes_to_read; 1541228753Smm ssize_t bytes_read; 1542228753Smm struct mtree *mtree; 1543228753Smm 1544228753Smm mtree = (struct mtree *)(a->format->data); 1545228753Smm if (mtree->fd < 0) { 1546228753Smm *buff = NULL; 1547228753Smm *offset = 0; 1548228753Smm *size = 0; 1549228753Smm return (ARCHIVE_EOF); 1550228753Smm } 1551228753Smm if (mtree->buff == NULL) { 1552228753Smm mtree->buffsize = 64 * 1024; 1553228753Smm mtree->buff = malloc(mtree->buffsize); 1554228753Smm if (mtree->buff == NULL) { 1555228753Smm archive_set_error(&a->archive, ENOMEM, 1556228753Smm "Can't allocate memory"); 1557228753Smm return (ARCHIVE_FATAL); 1558228753Smm } 1559228753Smm } 1560228753Smm 1561228753Smm *buff = mtree->buff; 1562228753Smm *offset = mtree->offset; 1563232153Smm if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset) 1564238856Smm bytes_to_read = (size_t)(mtree->cur_size - mtree->offset); 1565228753Smm else 1566228753Smm bytes_to_read = mtree->buffsize; 1567228753Smm bytes_read = read(mtree->fd, mtree->buff, bytes_to_read); 1568228753Smm if (bytes_read < 0) { 1569228753Smm archive_set_error(&a->archive, errno, "Can't read"); 1570228753Smm return (ARCHIVE_WARN); 1571228753Smm } 1572228753Smm if (bytes_read == 0) { 1573228753Smm *size = 0; 1574228753Smm return (ARCHIVE_EOF); 1575228753Smm } 1576228753Smm mtree->offset += bytes_read; 1577228753Smm *size = bytes_read; 1578228753Smm return (ARCHIVE_OK); 1579228753Smm} 1580228753Smm 1581228753Smm/* Skip does nothing except possibly close the contents file. */ 1582228753Smmstatic int 1583228753Smmskip(struct archive_read *a) 1584228753Smm{ 1585228753Smm struct mtree *mtree; 1586228753Smm 1587228753Smm mtree = (struct mtree *)(a->format->data); 1588228753Smm if (mtree->fd >= 0) { 1589228753Smm close(mtree->fd); 1590228753Smm mtree->fd = -1; 1591228753Smm } 1592228753Smm return (ARCHIVE_OK); 1593228753Smm} 1594228753Smm 1595228753Smm/* 1596228753Smm * Since parsing backslash sequences always makes strings shorter, 1597228753Smm * we can always do this conversion in-place. 1598228753Smm */ 1599228753Smmstatic void 1600228753Smmparse_escapes(char *src, struct mtree_entry *mentry) 1601228753Smm{ 1602228753Smm char *dest = src; 1603228753Smm char c; 1604228753Smm 1605228753Smm if (mentry != NULL && strcmp(src, ".") == 0) 1606228753Smm mentry->full = 1; 1607228753Smm 1608228753Smm while (*src != '\0') { 1609228753Smm c = *src++; 1610228753Smm if (c == '/' && mentry != NULL) 1611228753Smm mentry->full = 1; 1612228753Smm if (c == '\\') { 1613228753Smm switch (src[0]) { 1614228753Smm case '0': 1615228753Smm if (src[1] < '0' || src[1] > '7') { 1616228753Smm c = 0; 1617228753Smm ++src; 1618228753Smm break; 1619228753Smm } 1620228753Smm /* FALLTHROUGH */ 1621228753Smm case '1': 1622228753Smm case '2': 1623228753Smm case '3': 1624228753Smm if (src[1] >= '0' && src[1] <= '7' && 1625228753Smm src[2] >= '0' && src[2] <= '7') { 1626228753Smm c = (src[0] - '0') << 6; 1627228753Smm c |= (src[1] - '0') << 3; 1628228753Smm c |= (src[2] - '0'); 1629228753Smm src += 3; 1630228753Smm } 1631228753Smm break; 1632228753Smm case 'a': 1633228753Smm c = '\a'; 1634228753Smm ++src; 1635228753Smm break; 1636228753Smm case 'b': 1637228753Smm c = '\b'; 1638228753Smm ++src; 1639228753Smm break; 1640228753Smm case 'f': 1641228753Smm c = '\f'; 1642228753Smm ++src; 1643228753Smm break; 1644228753Smm case 'n': 1645228753Smm c = '\n'; 1646228753Smm ++src; 1647228753Smm break; 1648228753Smm case 'r': 1649228753Smm c = '\r'; 1650228753Smm ++src; 1651228753Smm break; 1652228753Smm case 's': 1653228753Smm c = ' '; 1654228753Smm ++src; 1655228753Smm break; 1656228753Smm case 't': 1657228753Smm c = '\t'; 1658228753Smm ++src; 1659228753Smm break; 1660228753Smm case 'v': 1661228753Smm c = '\v'; 1662228753Smm ++src; 1663228753Smm break; 1664228753Smm } 1665228753Smm } 1666228753Smm *dest++ = c; 1667228753Smm } 1668228753Smm *dest = '\0'; 1669228753Smm} 1670228753Smm 1671228753Smm/* 1672228753Smm * Note that this implementation does not (and should not!) obey 1673228753Smm * locale settings; you cannot simply substitute strtol here, since 1674228753Smm * it does obey locale. 1675228753Smm */ 1676228753Smmstatic int64_t 1677228753Smmmtree_atol8(char **p) 1678228753Smm{ 1679228753Smm int64_t l, limit, last_digit_limit; 1680228753Smm int digit, base; 1681228753Smm 1682228753Smm base = 8; 1683228753Smm limit = INT64_MAX / base; 1684228753Smm last_digit_limit = INT64_MAX % base; 1685228753Smm 1686228753Smm l = 0; 1687228753Smm digit = **p - '0'; 1688228753Smm while (digit >= 0 && digit < base) { 1689228753Smm if (l>limit || (l == limit && digit > last_digit_limit)) { 1690228753Smm l = INT64_MAX; /* Truncate on overflow. */ 1691228753Smm break; 1692228753Smm } 1693228753Smm l = (l * base) + digit; 1694228753Smm digit = *++(*p) - '0'; 1695228753Smm } 1696228753Smm return (l); 1697228753Smm} 1698228753Smm 1699228753Smm/* 1700228753Smm * Note that this implementation does not (and should not!) obey 1701228753Smm * locale settings; you cannot simply substitute strtol here, since 1702228753Smm * it does obey locale. 1703228753Smm */ 1704228753Smmstatic int64_t 1705228753Smmmtree_atol10(char **p) 1706228753Smm{ 1707228753Smm int64_t l, limit, last_digit_limit; 1708228753Smm int base, digit, sign; 1709228753Smm 1710228753Smm base = 10; 1711228753Smm 1712228753Smm if (**p == '-') { 1713228753Smm sign = -1; 1714232153Smm limit = ((uint64_t)(INT64_MAX) + 1) / base; 1715232153Smm last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base; 1716228753Smm ++(*p); 1717232153Smm } else { 1718228753Smm sign = 1; 1719232153Smm limit = INT64_MAX / base; 1720232153Smm last_digit_limit = INT64_MAX % base; 1721232153Smm } 1722228753Smm 1723228753Smm l = 0; 1724228753Smm digit = **p - '0'; 1725228753Smm while (digit >= 0 && digit < base) { 1726232153Smm if (l > limit || (l == limit && digit > last_digit_limit)) 1727232153Smm return (sign < 0) ? INT64_MIN : INT64_MAX; 1728228753Smm l = (l * base) + digit; 1729228753Smm digit = *++(*p) - '0'; 1730228753Smm } 1731228753Smm return (sign < 0) ? -l : l; 1732228753Smm} 1733228753Smm 1734232153Smm/* Parse a hex digit. */ 1735232153Smmstatic int 1736232153Smmparsehex(char c) 1737232153Smm{ 1738232153Smm if (c >= '0' && c <= '9') 1739232153Smm return c - '0'; 1740232153Smm else if (c >= 'a' && c <= 'f') 1741232153Smm return c - 'a'; 1742232153Smm else if (c >= 'A' && c <= 'F') 1743232153Smm return c - 'A'; 1744232153Smm else 1745232153Smm return -1; 1746232153Smm} 1747232153Smm 1748228753Smm/* 1749228753Smm * Note that this implementation does not (and should not!) obey 1750228753Smm * locale settings; you cannot simply substitute strtol here, since 1751228753Smm * it does obey locale. 1752228753Smm */ 1753228753Smmstatic int64_t 1754228753Smmmtree_atol16(char **p) 1755228753Smm{ 1756228753Smm int64_t l, limit, last_digit_limit; 1757228753Smm int base, digit, sign; 1758228753Smm 1759228753Smm base = 16; 1760228753Smm 1761228753Smm if (**p == '-') { 1762228753Smm sign = -1; 1763232153Smm limit = ((uint64_t)(INT64_MAX) + 1) / base; 1764232153Smm last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base; 1765228753Smm ++(*p); 1766232153Smm } else { 1767228753Smm sign = 1; 1768232153Smm limit = INT64_MAX / base; 1769232153Smm last_digit_limit = INT64_MAX % base; 1770232153Smm } 1771228753Smm 1772228753Smm l = 0; 1773232153Smm digit = parsehex(**p); 1774228753Smm while (digit >= 0 && digit < base) { 1775232153Smm if (l > limit || (l == limit && digit > last_digit_limit)) 1776232153Smm return (sign < 0) ? INT64_MIN : INT64_MAX; 1777228753Smm l = (l * base) + digit; 1778232153Smm digit = parsehex(*++(*p)); 1779228753Smm } 1780228753Smm return (sign < 0) ? -l : l; 1781228753Smm} 1782228753Smm 1783228753Smmstatic int64_t 1784228753Smmmtree_atol(char **p) 1785228753Smm{ 1786228753Smm if (**p != '0') 1787228753Smm return mtree_atol10(p); 1788228753Smm if ((*p)[1] == 'x' || (*p)[1] == 'X') { 1789228753Smm *p += 2; 1790228753Smm return mtree_atol16(p); 1791228753Smm } 1792228753Smm return mtree_atol8(p); 1793228753Smm} 1794228753Smm 1795228753Smm/* 1796228753Smm * Returns length of line (including trailing newline) 1797228753Smm * or negative on error. 'start' argument is updated to 1798228753Smm * point to first character of line. 1799228753Smm */ 1800228753Smmstatic ssize_t 1801228753Smmreadline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit) 1802228753Smm{ 1803228753Smm ssize_t bytes_read; 1804228753Smm ssize_t total_size = 0; 1805228753Smm ssize_t find_off = 0; 1806228753Smm const void *t; 1807228753Smm const char *s; 1808228753Smm void *p; 1809228753Smm char *u; 1810228753Smm 1811228753Smm /* Accumulate line in a line buffer. */ 1812228753Smm for (;;) { 1813228753Smm /* Read some more. */ 1814228753Smm t = __archive_read_ahead(a, 1, &bytes_read); 1815228753Smm if (t == NULL) 1816228753Smm return (0); 1817228753Smm if (bytes_read < 0) 1818228753Smm return (ARCHIVE_FATAL); 1819228753Smm s = t; /* Start of line? */ 1820228753Smm p = memchr(t, '\n', bytes_read); 1821228753Smm /* If we found '\n', trim the read. */ 1822228753Smm if (p != NULL) { 1823228753Smm bytes_read = 1 + ((const char *)p) - s; 1824228753Smm } 1825228753Smm if (total_size + bytes_read + 1 > limit) { 1826228753Smm archive_set_error(&a->archive, 1827228753Smm ARCHIVE_ERRNO_FILE_FORMAT, 1828228753Smm "Line too long"); 1829228753Smm return (ARCHIVE_FATAL); 1830228753Smm } 1831228753Smm if (archive_string_ensure(&mtree->line, 1832228753Smm total_size + bytes_read + 1) == NULL) { 1833228753Smm archive_set_error(&a->archive, ENOMEM, 1834228753Smm "Can't allocate working buffer"); 1835228753Smm return (ARCHIVE_FATAL); 1836228753Smm } 1837228753Smm memcpy(mtree->line.s + total_size, t, bytes_read); 1838228753Smm __archive_read_consume(a, bytes_read); 1839228753Smm total_size += bytes_read; 1840228753Smm /* Null terminate. */ 1841228753Smm mtree->line.s[total_size] = '\0'; 1842228753Smm /* If we found an unescaped '\n', clean up and return. */ 1843228753Smm for (u = mtree->line.s + find_off; *u; ++u) { 1844228753Smm if (u[0] == '\n') { 1845228753Smm *start = mtree->line.s; 1846228753Smm return total_size; 1847228753Smm } 1848228753Smm if (u[0] == '#') { 1849228753Smm if (p == NULL) 1850228753Smm break; 1851228753Smm *start = mtree->line.s; 1852228753Smm return total_size; 1853228753Smm } 1854228753Smm if (u[0] != '\\') 1855228753Smm continue; 1856228753Smm if (u[1] == '\\') { 1857228753Smm ++u; 1858228753Smm continue; 1859228753Smm } 1860228753Smm if (u[1] == '\n') { 1861228753Smm memmove(u, u + 1, 1862228753Smm total_size - (u - mtree->line.s) + 1); 1863228753Smm --total_size; 1864228753Smm ++u; 1865228753Smm break; 1866228753Smm } 1867228753Smm if (u[1] == '\0') 1868228753Smm break; 1869228753Smm } 1870228753Smm find_off = u - mtree->line.s; 1871228753Smm } 1872228753Smm} 1873