├── .builds ├── alpine.yml ├── debian.yml ├── freebsd.yml ├── netbsd.yml └── openbsd.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── arg.h └── pax.c /.builds/alpine.yml: -------------------------------------------------------------------------------- 1 | image: alpine/latest 2 | sources: 3 | - https://git.sr.ht/~mcf/pax 4 | tasks: 5 | - build: make -C pax 6 | -------------------------------------------------------------------------------- /.builds/debian.yml: -------------------------------------------------------------------------------- 1 | image: debian/stable 2 | sources: 3 | - https://git.sr.ht/~mcf/pax 4 | tasks: 5 | - build: make -C pax 6 | -------------------------------------------------------------------------------- /.builds/freebsd.yml: -------------------------------------------------------------------------------- 1 | image: freebsd/latest 2 | sources: 3 | - https://git.sr.ht/~mcf/pax 4 | tasks: 5 | - build: make -C pax CC=cc 6 | -------------------------------------------------------------------------------- /.builds/netbsd.yml: -------------------------------------------------------------------------------- 1 | image: netbsd/latest 2 | sources: 3 | - https://git.sr.ht/~mcf/pax 4 | tasks: 5 | - build: make -C pax 6 | -------------------------------------------------------------------------------- /.builds/openbsd.yml: -------------------------------------------------------------------------------- 1 | image: openbsd/latest 2 | sources: 3 | - https://git.sr.ht/~mcf/pax 4 | tasks: 5 | - build: make -C pax 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /config.mk 2 | /pax 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | CFLAGS+=-std=c99 -Wall -Wpedantic 4 | 5 | -include config.mk 6 | 7 | .PHONY: all 8 | all: pax 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -f pax 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![builds.sr.ht status](https://builds.sr.ht/~mcf/pax/commits/master.svg)](https://builds.sr.ht/~mcf/pax/commits/master) 2 | 3 | # pax 4 | 5 | This is an implementation of POSIX pax(1). 6 | 7 | ## Not yet implemented 8 | 9 | - `-a` 10 | - `-o listopt` 11 | - `-o invalid` 12 | - `-i` 13 | -------------------------------------------------------------------------------- /arg.h: -------------------------------------------------------------------------------- 1 | extern const char *argv0; 2 | 3 | #define ARGBEGIN \ 4 | for (;;) { \ 5 | if (argc > 0) \ 6 | ++argv, --argc; \ 7 | if (argc == 0 || (*argv)[0] != '-') \ 8 | break; \ 9 | if ((*argv)[1] == '-' && !(*argv)[2]) { \ 10 | ++argv, --argc; \ 11 | break; \ 12 | } \ 13 | for (char *opt_ = &(*argv)[1], done_ = 0; !done_ && *opt_; ++opt_) { \ 14 | switch (*opt_) 15 | 16 | #define ARGEND \ 17 | } \ 18 | } 19 | 20 | #define EARGF(x) \ 21 | (done_ = 1, *++opt_ ? opt_ : argv[1] ? --argc, *++argv : ((x), abort(), (char *)0)) 22 | -------------------------------------------------------------------------------- /pax.c: -------------------------------------------------------------------------------- 1 | #define _DEFAULT_SOURCE /* needed for reallocarray and major/minor */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #ifndef makedev 27 | #include 28 | #endif 29 | #include "arg.h" 30 | 31 | #ifndef O_SEARCH /* not present on some BSDs */ 32 | #define O_SEARCH 0 33 | #endif 34 | 35 | #define LEN(a) (sizeof (a) / sizeof *(a)) 36 | #define ROUNDUP(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) 37 | #define MAXTIME 077777777777 38 | #define MAXSIZE 077777777777 39 | #define MAXUGID 07777777 40 | 41 | enum mode { 42 | LIST, 43 | READ, 44 | WRITE, 45 | COPY, 46 | }; 47 | 48 | enum format { 49 | CPIO, 50 | PAX, 51 | USTAR, 52 | GNUTAR, 53 | V7, 54 | }; 55 | 56 | enum field { 57 | ATIME = 1<<0, 58 | CTIME = 1<<1, 59 | GID = 1<<2, 60 | GNAME = 1<<3, 61 | LINKPATH = 1<<4, 62 | MODE = 1<<5, 63 | MTIME = 1<<6, 64 | PATH = 1<<7, 65 | SIZE = 1<<8, 66 | UID = 1<<9, 67 | UNAME = 1<<10, 68 | }; 69 | 70 | struct keyword { 71 | const char *name; 72 | enum field field; 73 | }; 74 | 75 | struct strbuf { 76 | char *str; 77 | size_t len, cap; 78 | }; 79 | 80 | struct header { 81 | /* keywords present in this header */ 82 | enum field fields; 83 | /* keywords ignored because they were overridden by an option */ 84 | enum field delete; 85 | 86 | char type; 87 | 88 | char *path; 89 | size_t pathlen; 90 | dev_t dev; 91 | ino_t ino; 92 | mode_t mode; 93 | uid_t uid; 94 | gid_t gid; 95 | nlink_t nlink; 96 | dev_t rdev; 97 | off_t size; 98 | struct timespec atime, mtime, ctime; 99 | char *link; 100 | size_t linklen; 101 | char *uname; 102 | char *gname; 103 | 104 | struct strbuf pathbuf; 105 | struct strbuf linkbuf; 106 | struct strbuf unamebuf; 107 | struct strbuf gnamebuf; 108 | 109 | /* tar-specific, pre-calculated split point between name and prefix */ 110 | char *slash; 111 | /* read this data instead of stdin */ 112 | char *data; 113 | /* source file path and flags for hard link (-l flag) */ 114 | char *file; 115 | struct timespec fileatime; 116 | int flag; 117 | }; 118 | 119 | struct account { 120 | char *name; 121 | enum field type; 122 | uid_t uid; 123 | gid_t gid; 124 | }; 125 | 126 | struct bufio { 127 | int fd, err; 128 | off_t off; 129 | char buf[64 * 1024]; 130 | char *pos, *end; 131 | }; 132 | 133 | struct replstr { 134 | regex_t old; 135 | char *new; 136 | bool global; 137 | bool print; 138 | bool symlink; 139 | struct replstr *next; 140 | }; 141 | 142 | struct file { 143 | size_t namelen; 144 | size_t pathlen; 145 | dev_t dev; 146 | struct file *next; 147 | char name[]; 148 | }; 149 | 150 | struct filelist { 151 | FILE *input; 152 | struct file *pending; 153 | }; 154 | 155 | typedef int readfn(struct bufio *, struct header *); 156 | typedef void writefn(FILE *, struct header *); 157 | 158 | static int exitstatus; 159 | static int aflag; 160 | static int cflag; 161 | static int dflag; 162 | static int kflag; 163 | static int lflag; 164 | static int nflag; 165 | static int tflag; 166 | static int uflag; 167 | static int vflag; 168 | static int Xflag; 169 | static int follow; 170 | static int preserve = ATIME | MTIME; 171 | static const struct keyword keywords[] = { 172 | {"atime", ATIME}, 173 | {"ctime", CTIME}, 174 | {"gid", GID}, 175 | {"gname", GNAME}, 176 | {"linkpath", LINKPATH}, 177 | {"mtime", MTIME}, 178 | {"path", PATH}, 179 | {"size", SIZE}, 180 | {"uid", UID}, 181 | {"uname", UNAME}, 182 | }; 183 | static struct { 184 | enum field delete; 185 | int linkdata; 186 | const char *listopt; 187 | const char *exthdrname; 188 | const char *globexthdrname; 189 | int times; 190 | } opt; 191 | static struct header exthdr, globexthdr; 192 | static struct replstr *replstr; 193 | static time_t curtime; 194 | static char **pats; 195 | static size_t patslen; 196 | static bool *patsused; 197 | static struct filelist files; 198 | static struct bufio bioin; 199 | static char *dest = ""; 200 | static int destfd = AT_FDCWD; 201 | 202 | static void 203 | fatal(const char *fmt, ...) 204 | { 205 | va_list ap; 206 | 207 | if (fmt) { 208 | va_start(ap, fmt); 209 | vfprintf(stderr, fmt, ap); 210 | va_end(ap); 211 | if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { 212 | fputc(' ', stderr); 213 | perror(NULL); 214 | } else { 215 | fputc('\n', stderr); 216 | } 217 | } else { 218 | perror(NULL); 219 | } 220 | exit(1); 221 | } 222 | 223 | static char * 224 | sbufalloc(struct strbuf *b, size_t n, size_t a) 225 | { 226 | char *s; 227 | 228 | if (n > b->cap - b->len) { 229 | if (n > SIZE_MAX - a || n + a > SIZE_MAX - b->len) 230 | fatal("path is too long"); 231 | b->cap = ROUNDUP(n, a); 232 | s = malloc(b->cap); 233 | if (!s) 234 | fatal(NULL); 235 | if (b->len) 236 | memcpy(s, b->str, b->len); 237 | free(b->str); 238 | b->str = s; 239 | } 240 | return b->str + b->len; 241 | } 242 | 243 | static int 244 | sbuffmtv(struct strbuf *b, size_t a, const char *fmt, va_list ap) 245 | { 246 | va_list aptmp; 247 | int n; 248 | 249 | va_copy(aptmp, ap); 250 | n = vsnprintf(b->str ? b->str + b->len : NULL, b->cap - b->len, fmt, aptmp); 251 | va_end(aptmp); 252 | if (n < 0) 253 | fatal("vsnprintf:"); 254 | if (n >= b->cap - b->len) { 255 | sbufalloc(b, n + 1, a); 256 | n = vsnprintf(b->str + b->len, b->cap - b->len, fmt, ap); 257 | if (n < 0) 258 | fatal("vsnprintf:"); 259 | if (n >= b->cap - b->len) 260 | fatal("vsnprintf: formatted size changed"); 261 | } 262 | b->len += n; 263 | return n; 264 | } 265 | 266 | static int 267 | sbuffmt(struct strbuf *b, size_t a, const char *fmt, ...) 268 | { 269 | va_list ap; 270 | int n; 271 | 272 | va_start(ap, fmt); 273 | n = sbuffmtv(b, a, fmt, ap); 274 | va_end(ap); 275 | return n; 276 | } 277 | 278 | static void 279 | sbufcat(struct strbuf *b, const char *s, size_t n, size_t a) 280 | { 281 | char *d; 282 | 283 | d = sbufalloc(b, n + 1, a); 284 | memcpy(d, s, n); 285 | d[n] = 0; 286 | b->len += n; 287 | } 288 | 289 | static void 290 | bioinit(struct bufio *f, int fd) 291 | { 292 | f->fd = fd; 293 | f->pos = f->end = f->buf; 294 | f->off = 0; 295 | } 296 | 297 | static size_t 298 | bioread(struct bufio *f, void *p, size_t n) 299 | { 300 | size_t l; 301 | unsigned char *d; 302 | struct iovec iov[2]; 303 | ssize_t r; 304 | 305 | d = p; 306 | if (f->pos != f->end) { 307 | l = f->end - f->pos; 308 | if (n < l) 309 | l = n; 310 | memcpy(d, f->pos, l); 311 | f->pos += l; 312 | n -= l; 313 | d += l; 314 | } 315 | iov[1].iov_base = f->buf; 316 | iov[1].iov_len = sizeof f->buf; 317 | for (; n > 0; n -= r, d += r) { 318 | iov[0].iov_base = d; 319 | iov[0].iov_len = n; 320 | r = readv(f->fd, iov, 2); 321 | if (r < 0) 322 | f->err = errno; 323 | if (r <= 0) 324 | break; 325 | if (r >= n) { 326 | f->pos = f->buf; 327 | f->end = f->buf + (r - n); 328 | r = n; 329 | } 330 | } 331 | l = d - (unsigned char *)p; 332 | f->off += l; 333 | return l; 334 | } 335 | 336 | static int 337 | bioskip(struct bufio *f, off_t n) 338 | { 339 | static bool seekfail; 340 | size_t l; 341 | ssize_t r; 342 | 343 | if (f->pos != f->end) { 344 | l = f->end - f->pos; 345 | if (n < l) { 346 | f->pos += n; 347 | return 0; 348 | } 349 | n -= l; 350 | f->pos = f->end = f->buf; 351 | } 352 | if (!seekfail) { 353 | if (n == 0 || lseek(f->fd, n, SEEK_CUR) >= 0) 354 | return 0; 355 | seekfail = true; 356 | } 357 | for (; n > 0; n -= r) { 358 | l = sizeof f->buf; 359 | if (n < l) 360 | l = n; 361 | r = read(f->fd, f->buf, l); 362 | if (r <= 0) 363 | return -1; 364 | } 365 | return 0; 366 | } 367 | 368 | static void 369 | copyblock(char *b, struct bufio *r, size_t nr, FILE *w, size_t nw) 370 | { 371 | if (bioread(r, b, nr) != nr) { 372 | if (r->err) 373 | fatal("read: %s", strerror(r->err)); 374 | fatal("archive truncated"); 375 | } 376 | if (nw > nr) 377 | memset(b + nr, 0, nw - nr); 378 | if (nw && fwrite(b, 1, nw, w) != nw) 379 | fatal("write:"); 380 | } 381 | 382 | /* nr and nw must differ by at most 8192 */ 383 | static void 384 | copy(struct bufio *r, off_t nr, FILE *w, off_t nw) 385 | { 386 | char b[8192]; 387 | 388 | assert(nr - nw <= sizeof b || nw - nr <= sizeof b); 389 | for (; nr > sizeof b && nw > sizeof b; nr -= sizeof b, nw -= sizeof b) 390 | copyblock(b, r, sizeof b, w, sizeof b); 391 | copyblock(b, r, nr, w, nw); 392 | } 393 | 394 | static struct account * 395 | findaccount(const char *name, uid_t uid, gid_t gid) 396 | { 397 | static struct account *accts; 398 | static size_t acctslen; 399 | struct account *a; 400 | 401 | for (a = accts; a < accts + acctslen; ++a) { 402 | if ((uid == -1 || uid == a->uid) && (gid == -1 || gid == a->gid) 403 | && (!name || (a->name && strcmp(a->name, name) == 0))) 404 | return a; 405 | } 406 | if ((acctslen & (acctslen - 1)) == 0) { 407 | accts = reallocarray(accts, acctslen ? acctslen * 2 : 16, sizeof *accts); 408 | if (!accts) 409 | fatal(NULL); 410 | } 411 | a = &accts[acctslen++]; 412 | a->name = NULL; 413 | a->type = 0; 414 | a->uid = -1; 415 | a->gid = -1; 416 | if (name) { 417 | a->name = strdup(name); 418 | if (!a->name) 419 | fatal(NULL); 420 | } 421 | return a; 422 | } 423 | 424 | static uid_t 425 | unametouid(const char *uname, uid_t fallback) 426 | { 427 | struct account *a; 428 | struct passwd *pw; 429 | 430 | if (!*uname) 431 | return fallback; 432 | a = findaccount(uname, -1, -1); 433 | if (~a->type & UID) { 434 | a->type |= UID; 435 | pw = getpwnam(uname); 436 | a->uid = pw ? pw->pw_uid : -1; 437 | } 438 | return a->uid != -1 ? a->uid : fallback; 439 | } 440 | 441 | static gid_t 442 | gnametogid(const char *gname, gid_t fallback) 443 | { 444 | struct account *a; 445 | struct group *gr; 446 | 447 | if (!*gname) 448 | return fallback; 449 | a = findaccount(gname, -1, -1); 450 | if (~a->type & GID) { 451 | a->type |= GID; 452 | gr = getgrnam(gname); 453 | a->gid = gr ? gr->gr_gid : -1; 454 | } 455 | return a->gid != -1 ? a->gid : fallback; 456 | } 457 | 458 | static char * 459 | uidtouname(uid_t uid, char *fallback) 460 | { 461 | struct account *a; 462 | struct passwd *pw; 463 | 464 | a = findaccount(NULL, uid, -1); 465 | if (~a->type & UID) { 466 | a->type |= UID; 467 | a->uid = uid; 468 | assert(!a->name); 469 | pw = getpwuid(uid); 470 | if (pw) { 471 | a->name = strdup(pw->pw_name); 472 | if (!a->name) 473 | fatal(NULL); 474 | } 475 | } 476 | return a->name ? a->name : fallback; 477 | } 478 | 479 | static char * 480 | gidtogname(uid_t gid, char *fallback) 481 | { 482 | struct account *a; 483 | struct group *gr; 484 | 485 | a = findaccount(NULL, -1, gid); 486 | if (~a->type & GID) { 487 | a->type |= GID; 488 | a->gid = gid; 489 | assert(!a->name); 490 | gr = getgrgid(gid); 491 | if (gr) { 492 | a->name = strdup(gr->gr_name); 493 | if (!a->name) 494 | fatal(NULL); 495 | } 496 | } 497 | return a->name ? a->name : fallback; 498 | } 499 | 500 | static unsigned long long 501 | octnum(const char *str, size_t len) 502 | { 503 | const char *end; 504 | unsigned c; 505 | unsigned long long n; 506 | 507 | n = 0; 508 | end = str + len; 509 | /* some archives have leading spaces, so skip them */ 510 | for (; str != end && *str == ' '; ++str) 511 | ; 512 | for (; str != end; ++str) { 513 | c = *str; 514 | if (c == ' ' || c == '\0') 515 | break; 516 | c -= '0'; 517 | if (c > 7) 518 | fatal("invalid number field"); 519 | n = n * 8 + c; 520 | } 521 | return n; 522 | } 523 | 524 | static unsigned long long 525 | decnum(const char *str, size_t len, char **pos) 526 | { 527 | const char *end; 528 | unsigned c; 529 | unsigned long long n; 530 | 531 | n = 0; 532 | end = str + len; 533 | for (; str != end; ++str) { 534 | c = *str - '0'; 535 | if (c > 9) 536 | break; 537 | n = n * 10 + c; 538 | } 539 | if (pos) 540 | *pos = (char *)str; 541 | return n; 542 | } 543 | 544 | static int 545 | readustar(struct bufio *f, struct header *h) 546 | { 547 | static char buf[512]; 548 | static off_t end; 549 | size_t namelen, prefixlen, linklen; 550 | unsigned long sum; 551 | int i; 552 | enum format format; 553 | 554 | assert(bioin.off <= end); 555 | if (bioskip(f, end - bioin.off) != 0 || bioread(f, buf, sizeof buf) != sizeof buf) { 556 | if (f->err) 557 | fatal("read: %s", strerror(f->err)); 558 | fatal("archive truncated"); 559 | } 560 | sum = 0; 561 | for (i = 0; i < 512; ++i) 562 | sum += ((unsigned char *)buf)[i]; 563 | if (sum == 0) 564 | return 0; 565 | for (i = 148; i < 156; ++i) 566 | sum += ' ' - ((unsigned char *)buf)[i]; 567 | if (sum != octnum(buf + 148, 8)) 568 | fatal("invalid tar header: bad checksum"); 569 | if (memcmp(buf + 257, "ustar\0" "00", 8) == 0) 570 | format = USTAR; 571 | else if (memcmp(buf + 257, "ustar ", 8) == 0) 572 | format = GNUTAR; 573 | else 574 | format = V7; 575 | h->fields = PATH | UID | GID | SIZE; 576 | namelen = strnlen(buf, 100); 577 | prefixlen = format == USTAR ? strnlen(buf + 345, 155) : 0; 578 | if (namelen == 100 || prefixlen > 0) { 579 | h->pathbuf.len = 0; 580 | if (prefixlen > 0) { 581 | sbufcat(&h->pathbuf, buf + 345, prefixlen, 1024); 582 | sbufcat(&h->pathbuf, "/", 1, 1024); 583 | } 584 | sbufcat(&h->pathbuf, buf, namelen, 1024); 585 | h->path = h->pathbuf.str; 586 | h->pathlen = h->pathbuf.len; 587 | } else { 588 | h->path = buf; 589 | h->pathlen = namelen; 590 | } 591 | h->dev = 0; 592 | h->ino = 0; 593 | h->mode = octnum(buf + 100, 8); 594 | h->uid = octnum(buf + 108, 8); 595 | h->gid = octnum(buf + 116, 8); 596 | h->nlink = 1; 597 | h->size = octnum(buf + 124, 12); 598 | end = bioin.off + ROUNDUP(h->size, 512); 599 | h->mtime = (struct timespec){.tv_sec = octnum(buf + 136, 12)}; 600 | if (format == GNUTAR) { 601 | h->fields |= ATIME | CTIME; 602 | h->atime = (struct timespec){.tv_sec = octnum(buf + 345, 12)}; 603 | h->ctime = (struct timespec){.tv_sec = octnum(buf + 357, 12)}; 604 | } 605 | h->type = buf[156]; 606 | if (h->type == AREGTYPE) 607 | h->type = REGTYPE; 608 | 609 | linklen = strnlen(buf + 157, 100); 610 | if (linklen > 0) 611 | h->fields |= LINKPATH; 612 | if (linklen == 100) { 613 | h->linkbuf.len = 0; 614 | sbufcat(&h->linkbuf, buf + 157, 100, 1024); 615 | h->link = h->linkbuf.str; 616 | h->linklen = h->linkbuf.len; 617 | } else { 618 | h->link = buf + 157; 619 | } 620 | h->linklen = linklen; 621 | if (format == V7) { 622 | h->uname = ""; 623 | h->gname = ""; 624 | } else { 625 | h->fields |= UNAME | GNAME; 626 | h->uname = buf + 265; 627 | if (!memchr(h->uname, '\0', 32)) 628 | fatal("uname is not NUL-terminated"); 629 | h->gname = buf + 297; 630 | if (!memchr(h->gname, '\0', 32)) 631 | fatal("gname is not NUL-terminated"); 632 | if (h->type == CHRTYPE || h->type == BLKTYPE) { 633 | unsigned major, minor; 634 | 635 | major = octnum(buf + 329, 8); 636 | minor = octnum(buf + 337, 8); 637 | h->rdev = makedev(major, minor); 638 | } 639 | } 640 | return 1; 641 | } 642 | 643 | static void 644 | parsetime(struct timespec *ts, const char *field, const char *str, size_t len) 645 | { 646 | const char *end = str + len; 647 | char *pos; 648 | unsigned long long subsec; 649 | size_t sublen; 650 | 651 | ts->tv_sec = decnum(str, len, &pos); 652 | if (*pos == '.') { 653 | str = ++pos; 654 | subsec = decnum(str, end - str, &pos); 655 | for (sublen = pos - str; sublen < 9; ++sublen) 656 | subsec *= 10; 657 | ts->tv_nsec = subsec % 1000000000; 658 | } 659 | if (pos != end) 660 | fatal("invalid extended header: bad %s", field); 661 | } 662 | 663 | static void 664 | extkeyval(struct header *h, const char *key, const char *val, size_t vallen) 665 | { 666 | enum field field; 667 | char *end; 668 | const struct keyword *kw; 669 | 670 | field = 0; 671 | for (kw = keywords; kw != keywords + LEN(keywords); ++kw) { 672 | if (strcmp(key, kw->name) == 0) { 673 | field = kw->field; 674 | break; 675 | } 676 | } 677 | if (!field) { 678 | if (strcmp(key, "charset") == 0) { 679 | } else if (strcmp(key, "comment") == 0) { 680 | /* ignore */ 681 | } else if (strcmp(key, "hdrcharset") == 0) { 682 | } else if (strncmp(key, "realtime.", 9) == 0) { 683 | } else if (strncmp(key, "security.", 9) == 0) { 684 | } else { 685 | fprintf(stderr, "ignoring unknown keyword '%s'\n", key); 686 | } 687 | return; 688 | } 689 | if ((h->delete | opt.delete) & field) 690 | return; 691 | 692 | switch (field) { 693 | case ATIME: 694 | parsetime(&h->atime, "atime", val, vallen); 695 | break; 696 | case CTIME: 697 | parsetime(&h->ctime, "ctime", val, vallen); 698 | break; 699 | case GID: 700 | h->gid = decnum(val, vallen, &end); 701 | if (end != val + vallen) 702 | fatal("invalid extended header: bad gid"); 703 | break; 704 | case GNAME: 705 | h->gnamebuf.len = 0; 706 | sbufcat(&h->gnamebuf, val, vallen, 256); 707 | h->gname = h->gnamebuf.str; 708 | break; 709 | case LINKPATH: 710 | h->linkbuf.len = 0; 711 | sbufcat(&h->linkbuf, val, vallen, 1024); 712 | h->link = h->linkbuf.str; 713 | h->linklen = h->linkbuf.len; 714 | break; 715 | case MTIME: 716 | parsetime(&h->mtime, "mtime", val, vallen); 717 | break; 718 | case PATH: 719 | h->pathbuf.len = 0; 720 | sbufcat(&h->pathbuf, val, vallen, 1024); 721 | h->path = h->pathbuf.str; 722 | h->pathlen = h->pathbuf.len; 723 | break; 724 | case SIZE: 725 | h->size = decnum(val, vallen, &end); 726 | if (end != val + vallen) 727 | fatal("invalid extended header: bad size"); 728 | break; 729 | case UID: 730 | h->uid = decnum(val, vallen, &end); 731 | if (end != val + vallen) 732 | fatal("invalid extended header: bad uid"); 733 | break; 734 | case UNAME: 735 | h->unamebuf.len = 0; 736 | sbufcat(&h->unamebuf, val, vallen, 256); 737 | h->uname = h->unamebuf.str; 738 | break; 739 | default: 740 | return; 741 | } 742 | h->fields |= field; 743 | } 744 | 745 | static void 746 | readexthdr(struct bufio *f, struct header *h, off_t len) 747 | { 748 | static struct strbuf buf; 749 | size_t reclen, vallen; 750 | char *rec, *end, *key, *val; 751 | 752 | if (len > SIZE_MAX) 753 | fatal("extended header is too large"); 754 | buf.len = 0; 755 | sbufalloc(&buf, len, 8192); 756 | if (bioread(f, buf.str, len) != len) { 757 | if (f->err) 758 | fatal("read: %s", strerror(f->err)); 759 | fatal("archive truncated"); 760 | } 761 | rec = buf.str; 762 | while (len > 0) { 763 | end = memchr(rec, '\n', len); 764 | if (!end) 765 | fatal("invalid extended header: record is missing newline"); 766 | *end = '\0'; 767 | reclen = decnum(rec, end - rec, &key); 768 | if (*key != ' ' || reclen != end - rec + 1) 769 | fatal("invalid extended header: invalid record"); 770 | ++key; 771 | val = strchr(key, '='); 772 | if (!val) 773 | fatal("invalid extended header: record has no '='"); 774 | *val++ = '\0'; 775 | vallen = end - val; 776 | extkeyval(h, key, val, vallen); 777 | len -= reclen; 778 | rec += reclen; 779 | } 780 | } 781 | 782 | static void 783 | readgnuhdr(struct bufio *f, struct strbuf *b, off_t len) 784 | { 785 | if (len > SIZE_MAX - 1) 786 | fatal("GNU header is too large"); 787 | b->len = 0; 788 | sbufalloc(b, len + 1, 1024); 789 | if (bioread(f, b->str, len) != len) { 790 | if (f->err) 791 | fatal("read: %s", strerror(f->err)); 792 | fatal("archive truncated"); 793 | } 794 | b->str[len] = '\0'; 795 | b->len = len; 796 | } 797 | 798 | static int 799 | readpax(struct bufio *f, struct header *h) 800 | { 801 | exthdr.fields = exthdr.delete; 802 | while (readustar(f, h)) { 803 | switch (h->type) { 804 | case 'g': 805 | readexthdr(f, &globexthdr, h->size); 806 | break; 807 | case 'x': 808 | readexthdr(f, &exthdr, h->size); 809 | break; 810 | case 'L': 811 | if ((exthdr.delete | opt.delete) & PATH) 812 | break; 813 | readgnuhdr(f, &exthdr.pathbuf, h->size); 814 | exthdr.path = exthdr.pathbuf.str; 815 | exthdr.pathlen = exthdr.pathbuf.len; 816 | exthdr.fields |= PATH; 817 | break; 818 | case 'K': 819 | if ((exthdr.delete | opt.delete) & LINKPATH) 820 | break; 821 | readgnuhdr(f, &exthdr.linkbuf, h->size); 822 | exthdr.link = exthdr.linkbuf.str; 823 | exthdr.linklen = exthdr.linkbuf.len; 824 | exthdr.fields |= LINKPATH; 825 | break; 826 | default: 827 | return 1; 828 | } 829 | } 830 | return 0; 831 | } 832 | 833 | static int 834 | readcpio(struct bufio *f, struct header *h) 835 | { 836 | static off_t end; 837 | unsigned long type; 838 | char buf[76]; 839 | 840 | if (bioskip(f, end - bioin.off) != 0 || bioread(f, buf, sizeof buf) != sizeof buf) { 841 | if (f->err) 842 | fatal("read: %s", strerror(f->err)); 843 | fatal("archive truncated"); 844 | } 845 | if (memcmp(buf, "070707", 6) != 0) 846 | fatal("invalid cpio header: bad magic"); 847 | h->pathlen = octnum(buf + 59, 6); 848 | if (h->pathlen == 0) 849 | fatal("invalid cpio header: c_namesize is 0"); 850 | h->pathbuf.len = 0; 851 | sbufalloc(&h->pathbuf, h->pathlen, 1024); 852 | h->path = h->pathbuf.str; 853 | if (bioread(f, h->path, h->pathlen) != h->pathlen) { 854 | if (f->err) 855 | fatal("read: %s", strerror(f->err)); 856 | fatal("archive truncated"); 857 | } 858 | if (h->path[--h->pathlen] != '\0') 859 | fatal("invalid cpio header: name is not NUL-terminated"); 860 | if (strcmp(h->path, "TRAILER!!!") == 0) 861 | return 0; 862 | 863 | h->fields = PATH | MODE | UID | GID | MTIME | SIZE; 864 | h->dev = octnum(buf + 6, 6); 865 | h->ino = octnum(buf + 12, 6); 866 | type = octnum(buf + 18, 6); 867 | h->mode = type & 07777; 868 | type &= ~07777; 869 | switch (type) { 870 | case C_ISDIR: h->type = DIRTYPE; break; 871 | case C_ISFIFO: h->type = FIFOTYPE; break; 872 | case C_ISREG: h->type = REGTYPE; break; 873 | case C_ISLNK: h->type = SYMTYPE; break; 874 | case C_ISBLK: h->type = BLKTYPE; break; 875 | case C_ISCHR: h->type = CHRTYPE; break; 876 | default: fatal("invalid cpio header: invalid or unsupported file type: %#o", type); 877 | } 878 | h->uid = octnum(buf + 24, 6); 879 | h->gid = octnum(buf + 30, 6); 880 | h->nlink = octnum(buf + 36, 6); 881 | h->rdev = octnum(buf + 42, 6); 882 | h->mtime = (struct timespec){.tv_sec = octnum(buf + 48, 11)}; 883 | h->size = octnum(buf + 65, 11); 884 | h->uname = ""; 885 | h->gname = ""; 886 | if (h->type == SYMTYPE) { 887 | if (h->size > SIZE_MAX - 1) 888 | fatal("symlink target is too long"); 889 | h->linklen = h->size; 890 | h->linkbuf.len = 0; 891 | h->link = sbufalloc(&h->linkbuf, h->linklen + 1, 1024); 892 | if (bioread(f, h->link, h->linklen) != h->linklen) { 893 | if (f->err) 894 | fatal("read: %s", strerror(f->err)); 895 | fatal("archive truncated"); 896 | } 897 | h->link[h->linklen] = '\0'; 898 | h->size = 0; 899 | h->fields |= LINKPATH; 900 | } else { 901 | h->link = ""; 902 | h->linklen = 0; 903 | } 904 | end = bioin.off + h->size; 905 | return 1; 906 | } 907 | 908 | static int 909 | decompress(const char *algo, int fd, pid_t *pid) 910 | { 911 | extern char **environ; 912 | posix_spawn_file_actions_t fa; 913 | int p[2], err; 914 | char *argv[3]; 915 | 916 | if (!algo) 917 | return fd; 918 | argv[0] = (char *)algo; 919 | argv[1] = "-dc"; 920 | argv[2] = NULL; 921 | if (pipe2(p, O_CLOEXEC) != 0) 922 | fatal("pipe2:"); 923 | err = posix_spawn_file_actions_init(&fa); 924 | if (err) 925 | fatal("posix_spawn_file_actions_init: %s", strerror(errno)); 926 | err = posix_spawn_file_actions_adddup2(&fa, fd, 0); 927 | if (err) 928 | fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); 929 | err = posix_spawn_file_actions_adddup2(&fa, p[1], 1); 930 | if (err) 931 | fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); 932 | err = posix_spawnp(pid, algo, &fa, NULL, argv, environ); 933 | if (err) 934 | fatal("posix_spawnp %s: %s", algo, strerror(errno)); 935 | close(p[1]); 936 | return p[0]; 937 | } 938 | 939 | static readfn * 940 | detectformat(struct bufio *f, const char *algo, pid_t *pid) 941 | { 942 | size_t l, i; 943 | ssize_t n; 944 | unsigned char *b; 945 | 946 | again: 947 | f->fd = decompress(algo, f->fd, pid); 948 | b = (unsigned char *)f->buf; 949 | for (l = 0; l < 512; l += n) { 950 | n = read(f->fd, b + l, 512 - l); 951 | if (n < 0) 952 | fatal("read:"); 953 | if (n == 0) 954 | break; 955 | } 956 | f->pos = f->buf; 957 | f->end = f->buf + l; 958 | if (l == 512) { 959 | unsigned long sum, hdrsum; 960 | 961 | sum = 0; 962 | for (i = 0; i < 512; ++i) 963 | sum += b[i]; 964 | if (sum == 0) 965 | return readpax; 966 | hdrsum = 0; 967 | for (i = 148; i < 156; ++i) { 968 | sum += ' ' - b[i]; 969 | if (b[i] >= '0' && b[i] <= '9') 970 | hdrsum = hdrsum * 8 + (b[i] - '0'); 971 | } 972 | if (sum == hdrsum) 973 | return readpax; 974 | } 975 | if (l >= 76) { 976 | if (memcmp(b, "070707", 6) == 0) 977 | return readcpio; 978 | } 979 | if (!algo) { 980 | static const struct command { 981 | char algo[6]; 982 | unsigned char magiclen; 983 | unsigned char magic[6]; 984 | } cmds[] = { 985 | {"gzip", 2, {0x1F, 0x8B}}, 986 | {"bzip2", 2, {'B', 'Z'}}, 987 | {"xz", 6, {0xFD, '7', 'z', 'X', 'Z', 0x00}}, 988 | {"zstd", 4, {0x28, 0xB5, 0x2F, 0xFD}}, 989 | {"lzip", 4, {'L', 'Z', 'I', 'P'}}, 990 | }; 991 | const struct command *c; 992 | 993 | for (c = cmds; c < cmds + LEN(cmds); ++c) { 994 | if (l >= c->magiclen && memcmp(b, c->magic, c->magiclen) == 0) { 995 | if (lseek(f->fd, 0, SEEK_SET) != 0) 996 | fatal("compression detection requires seekable input"); 997 | algo = c->algo; 998 | goto again; 999 | } 1000 | } 1001 | } 1002 | return NULL; 1003 | } 1004 | 1005 | static FILE * 1006 | compress(const char *algo, const char *name, pid_t *pid) 1007 | { 1008 | extern char **environ; 1009 | FILE *f; 1010 | int fd, p[2], err; 1011 | posix_spawn_file_actions_t fa; 1012 | char *argv[3]; 1013 | 1014 | if (!algo) { 1015 | if (name && !freopen(name, "w", stdout)) 1016 | fatal("open %s:"); 1017 | return stdout; 1018 | } 1019 | argv[0] = (char *)algo; 1020 | argv[1] = "-c"; 1021 | argv[2] = NULL; 1022 | if (name) { 1023 | fd = open(name, O_WRONLY | O_CREAT, 0666); 1024 | if (fd < 0) 1025 | fatal("open %s:"); 1026 | } else { 1027 | fd = 1; 1028 | } 1029 | if (pipe2(p, O_CLOEXEC) != 0) 1030 | fatal("pipe2:"); 1031 | f = fdopen(p[1], "w"); 1032 | if (!f) 1033 | fatal("fdopen:"); 1034 | err = posix_spawn_file_actions_init(&fa); 1035 | if (err) 1036 | fatal("posix_spawn_file_actions_init: %s", strerror(errno)); 1037 | err = posix_spawn_file_actions_adddup2(&fa, p[0], 0); 1038 | if (err) 1039 | fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); 1040 | err = posix_spawn_file_actions_adddup2(&fa, fd, 1); 1041 | if (err) 1042 | fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); 1043 | err = posix_spawnp(pid, algo, &fa, NULL, argv, environ); 1044 | if (err) 1045 | fatal("posix_spawnp %s: %s", algo, strerror(errno)); 1046 | close(fd); 1047 | close(p[0]); 1048 | return f; 1049 | } 1050 | 1051 | static char * 1052 | splitname(char *name, size_t namelen) 1053 | { 1054 | char *slash; 1055 | 1056 | if (namelen > 256) 1057 | return NULL; 1058 | slash = memchr(name + namelen - 100, '/', 100); 1059 | if (!slash || slash - name > 155) 1060 | return NULL; 1061 | return slash; 1062 | } 1063 | 1064 | static void 1065 | openfile(struct header *h) 1066 | { 1067 | int fd; 1068 | 1069 | if (h->file) { 1070 | fd = open(h->file, O_RDONLY); 1071 | if (fd < 0) 1072 | fatal("open %s:", h->file); 1073 | bioinit(&bioin, fd); 1074 | } 1075 | } 1076 | 1077 | static void 1078 | closefile(struct header *h) 1079 | { 1080 | if (h->file) { 1081 | if (tflag) 1082 | futimens(bioin.fd, (struct timespec[2]){h->fileatime, {.tv_nsec = UTIME_OMIT}}); 1083 | close(bioin.fd); 1084 | } 1085 | } 1086 | 1087 | static void 1088 | closeustar(FILE *f) 1089 | { 1090 | char pad[512]; 1091 | 1092 | memset(pad, 0, 512); 1093 | if (fwrite(pad, 512, 1, f) != 1) 1094 | fatal("write:"); 1095 | if (fwrite(pad, 512, 1, f) != 1) 1096 | fatal("write:"); 1097 | } 1098 | 1099 | static void 1100 | writeustar(FILE *f, struct header *h) 1101 | { 1102 | char buf[512], *slash; 1103 | unsigned long sum; 1104 | int i; 1105 | 1106 | if (!h) { 1107 | closeustar(f); 1108 | return; 1109 | } 1110 | slash = h->slash; 1111 | if (!slash && h->pathlen > 100) { 1112 | slash = splitname(h->path, h->pathlen); 1113 | if (!slash) 1114 | fatal("path is too long: %s\n", h->path); 1115 | } 1116 | if (slash) { 1117 | *slash = '\0'; 1118 | strncpy(buf, slash + 1, 100); 1119 | strncpy(buf + 345, h->path, 155); 1120 | } else { 1121 | strncpy(buf, h->path, 100); 1122 | memset(buf + 345, 0, 155); 1123 | } 1124 | if (h->mode < 0 || h->mode > 07777777) 1125 | fatal("mode is too large: %ju", (uintmax_t)h->mode); 1126 | snprintf(buf + 100, 8, "%.7lo", (unsigned long)h->mode); 1127 | if (h->uid < 0 || h->uid > MAXUGID) 1128 | fatal("uid is too large: %ju", (uintmax_t)h->uid); 1129 | snprintf(buf + 108, 8, "%.7lo", (unsigned long)h->uid); 1130 | if (h->gid < 0 || h->gid > MAXUGID) 1131 | fatal("gid is too large: %ju", (uintmax_t)h->gid); 1132 | snprintf(buf + 116, 8, "%.7lo", (unsigned long)h->gid); 1133 | if (h->size < 0 || h->size > MAXSIZE) 1134 | fatal("size is too large: %ju", (uintmax_t)h->size); 1135 | snprintf(buf + 124, 12, "%.11llo", (unsigned long long)h->size); 1136 | if (h->mtime.tv_sec < 0 || h->mtime.tv_sec > MAXTIME) 1137 | fatal("mtime is too large: %ju", (uintmax_t)h->mtime.tv_sec); 1138 | snprintf(buf + 136, 12, "%.11llo", (unsigned long long)h->mtime.tv_sec); 1139 | memset(buf + 148, ' ', 8); 1140 | buf[156] = h->type; 1141 | if (h->linklen > 100) 1142 | fatal("link name is too long: %s\n", h->link); 1143 | strncpy(buf + 157, h->link, 100); 1144 | memcpy(buf + 257, "ustar", 6); 1145 | memcpy(buf + 263, "00", 2); 1146 | if (strlen(h->uname) > 31) 1147 | fatal("user name is too long: %s\n", h->uname); 1148 | strncpy(buf + 265, h->uname, 32); 1149 | if (strlen(h->gname) > 31) 1150 | fatal("group name is too long: %s\n", h->gname); 1151 | strncpy(buf + 297, h->gname, 32); 1152 | if (major(h->rdev) > 07777777) 1153 | fatal("device major is too large: %ju\n", (uintmax_t)major(h->rdev)); 1154 | snprintf(buf + 329, 8, "%.7lo", (unsigned long)major(h->rdev)); 1155 | if (minor(h->rdev) > 07777777) 1156 | fatal("device minor is too large: %ju\n", (uintmax_t)minor(h->rdev)); 1157 | snprintf(buf + 337, 8, "%.7lo", (unsigned long)minor(h->rdev)); 1158 | memset(buf + 500, 0, 12); 1159 | sum = 0; 1160 | for (i = 0; i < 512; ++i) 1161 | sum += ((unsigned char *)buf)[i]; 1162 | snprintf(buf + 148, 8, "%.7lo", sum); 1163 | if (fwrite(buf, 512, 1, f) != 1) 1164 | fatal("write:"); 1165 | if (h->data) { 1166 | size_t pad; 1167 | 1168 | if (fwrite(h->data, 1, h->size, f) != h->size) 1169 | fatal("write:"); 1170 | pad = ROUNDUP(h->size, 512) - h->size; 1171 | memset(bioin.buf, 0, pad); 1172 | if (fwrite(bioin.buf, 1, pad, f) != pad) 1173 | fatal("write:"); 1174 | } else if (h->size > 0) { 1175 | openfile(h); 1176 | copy(&bioin, h->size, f, ROUNDUP(h->size, 512)); 1177 | closefile(h); 1178 | } 1179 | } 1180 | 1181 | static void 1182 | writerec(struct strbuf *ext, const char *fmt, ...) 1183 | { 1184 | static struct strbuf buf; 1185 | va_list ap; 1186 | int d, n, m, l; 1187 | 1188 | buf.len = 0; 1189 | va_start(ap, fmt); 1190 | l = sbuffmtv(&buf, 256, fmt, ap); 1191 | va_end(ap); 1192 | 1193 | d = 0; 1194 | m = 1; 1195 | for (n = l; n > 0; n /= 10) { 1196 | m *= 10; 1197 | ++d; 1198 | } 1199 | n = d + 1 + l + 1; 1200 | if (n >= m) 1201 | ++n; 1202 | sbuffmt(ext, 256, "%d %.*s\n", n, l, buf.str); 1203 | } 1204 | 1205 | static void 1206 | writetimerec(struct strbuf *ext, char *kw, struct timespec *ts) 1207 | { 1208 | if (ts->tv_nsec != 0) 1209 | writerec(ext, "%s=%ju.%.9ld", kw, (uintmax_t)ts->tv_sec, ts->tv_nsec % 1000000000); 1210 | else 1211 | writerec(ext, "%s=%ju", kw, (uintmax_t)ts->tv_sec); 1212 | } 1213 | 1214 | static void 1215 | writeexthdr(FILE *f, int type, struct header *h) 1216 | { 1217 | static struct strbuf ext; 1218 | struct header exthdr; 1219 | 1220 | ext.len = 0; 1221 | if (h->fields & PATH) 1222 | writerec(&ext, "path=%s", h->path); 1223 | if (h->fields & UID) 1224 | writerec(&ext, "uid=%ju", (uintmax_t)h->uid); 1225 | if (h->fields & GID) 1226 | writerec(&ext, "gid=%ju", (uintmax_t)h->gid); 1227 | if (h->fields & SIZE) 1228 | writerec(&ext, "size=%ju", (uintmax_t)h->size); 1229 | if (h->fields & MTIME) 1230 | writetimerec(&ext, "mtime", &h->mtime); 1231 | if (h->fields & ATIME) 1232 | writetimerec(&ext, "atime", &h->atime); 1233 | if (h->fields & CTIME) 1234 | writetimerec(&ext, "ctime", &h->ctime); 1235 | if (h->fields & UNAME) 1236 | writerec(&ext, "uname=%s", h->uname); 1237 | if (h->fields & GNAME) 1238 | writerec(&ext, "gname=%s", h->gname); 1239 | if (ext.len > 0) { 1240 | memset(&exthdr, 0, sizeof exthdr); 1241 | exthdr.path = "pax_extended_header"; 1242 | exthdr.pathlen = 20; 1243 | exthdr.mode = 0600; 1244 | exthdr.link = ""; 1245 | exthdr.uname = ""; 1246 | exthdr.gname = ""; 1247 | exthdr.size = ext.len; 1248 | exthdr.type = type; 1249 | exthdr.data = ext.str; 1250 | writeustar(f, &exthdr); 1251 | } 1252 | } 1253 | 1254 | static void 1255 | mergehdr(struct header *dst, struct header *src, enum field fields) 1256 | { 1257 | fields &= src->fields; 1258 | if (fields & PATH) { 1259 | dst->path = src->path; 1260 | dst->pathlen = src->pathlen; 1261 | } 1262 | if (fields & UID) 1263 | dst->uid = src->uid; 1264 | if (fields & GID) 1265 | dst->gid = src->gid; 1266 | if (fields & SIZE) 1267 | dst->size = src->size; 1268 | if (fields & MTIME) 1269 | dst->mtime = src->mtime; 1270 | if (fields & ATIME) 1271 | dst->atime = src->atime; 1272 | if (fields & CTIME) 1273 | dst->ctime = src->ctime; 1274 | if (fields & UNAME) 1275 | dst->uname = src->uname; 1276 | if (fields & GNAME) 1277 | dst->gname = src->gname; 1278 | if (fields & LINKPATH) { 1279 | dst->link = src->link; 1280 | dst->linklen = src->linklen; 1281 | } 1282 | dst->fields |= fields; 1283 | } 1284 | 1285 | static void 1286 | writepax(FILE *f, struct header *h) 1287 | { 1288 | enum field fields; 1289 | 1290 | if (!h) { 1291 | closeustar(f); 1292 | return; 1293 | } 1294 | if (vflag) 1295 | fprintf(stderr, "%s\n", h->path); 1296 | fields = 0; 1297 | if (h->pathlen > 100) { 1298 | h->slash = splitname(h->path, h->pathlen); 1299 | if (!h->slash) 1300 | fields |= PATH; 1301 | } 1302 | if (h->uid > MAXUGID) 1303 | fields |= UID; 1304 | if (h->gid > MAXUGID) 1305 | fields |= GID; 1306 | if (h->size > MAXSIZE) 1307 | fields |= SIZE; 1308 | if (h->mtime.tv_sec > MAXTIME || h->mtime.tv_nsec != 0) 1309 | fields |= MTIME; 1310 | if (opt.times) 1311 | fields |= ATIME | CTIME; 1312 | if (strlen(h->uname) > 31) 1313 | fields |= UNAME; 1314 | if (strlen(h->gname) > 31) 1315 | fields |= GNAME; 1316 | if (h->linklen > 100) 1317 | fields |= LINKPATH; 1318 | fields &= ~(exthdr.fields | opt.delete); 1319 | mergehdr(&exthdr, h, fields); 1320 | writeexthdr(f, 'x', &exthdr); 1321 | 1322 | /* reset fields merged into extended header */ 1323 | if (fields & PATH) 1324 | h->path = "", h->pathlen = 0; 1325 | if (fields & UID) 1326 | h->uid = 0; 1327 | if (fields & GID) 1328 | h->gid = 0; 1329 | if (fields & SIZE) 1330 | h->size = 0; 1331 | if (fields & MTIME) { 1332 | if (h->mtime.tv_sec > MAXTIME) 1333 | h->mtime.tv_sec = MAXTIME; 1334 | h->mtime.tv_nsec = 0; 1335 | } 1336 | if (fields & ATIME) { 1337 | if (h->atime.tv_sec > MAXTIME) 1338 | h->atime.tv_sec = MAXTIME; 1339 | h->atime.tv_nsec = 0; 1340 | } 1341 | if (fields & CTIME) { 1342 | if (h->ctime.tv_sec > MAXTIME) 1343 | h->ctime.tv_sec = MAXTIME; 1344 | h->ctime.tv_nsec = 0; 1345 | } 1346 | if (fields & UNAME) 1347 | h->uname = ""; 1348 | if (fields & GNAME) 1349 | h->gname = ""; 1350 | if (fields & LINKPATH) 1351 | h->link = "", h->linklen = 0; 1352 | h->fields &= ~fields; 1353 | writeustar(f, h); 1354 | } 1355 | 1356 | static void 1357 | writecpio(FILE *f, struct header *h) 1358 | { 1359 | static unsigned long ino; 1360 | char buf[77]; 1361 | unsigned long mode; 1362 | uintmax_t size; 1363 | size_t namesize; 1364 | int len; 1365 | 1366 | if (!h) { 1367 | memcpy(buf, "070707", 6); 1368 | memset(buf + 6, '0', 70); 1369 | memcpy(buf + 59, "000013", 6); 1370 | if (fwrite(buf, 1, 76, f) != 76) 1371 | fatal("write:"); 1372 | if (fwrite("TRAILER!!!", 1, 11, f) != 11) 1373 | fatal("write:"); 1374 | return; 1375 | } 1376 | if (vflag) 1377 | fprintf(stderr, "%s\n", h->path); 1378 | mode = h->mode; 1379 | switch (h->type) { 1380 | case DIRTYPE: mode |= S_IFDIR; break; 1381 | case FIFOTYPE: mode |= S_IFIFO; break; 1382 | case REGTYPE: mode |= S_IFREG; break; 1383 | case SYMTYPE: mode |= S_IFLNK; break; 1384 | case BLKTYPE: mode |= S_IFBLK; break; 1385 | case CHRTYPE: mode |= S_IFCHR; break; 1386 | default: fatal("unknown or unsupported header type"); 1387 | } 1388 | if (h->dev > 0777777) 1389 | fatal("device is too large: %ju", (uintmax_t)h->dev); 1390 | if (++ino > 0777777) 1391 | fatal("inode is too large: %lu", ino); 1392 | if (mode > 0777777) 1393 | fatal("mode is too large: %lu", mode); 1394 | if (h->uid > MAXUGID) 1395 | fatal("uid is too large: %ju", (uintmax_t)h->uid); 1396 | if (h->gid > MAXUGID) 1397 | fatal("gid is too large: %ju", (uintmax_t)h->gid); 1398 | if (h->nlink > 0777777) 1399 | fatal("nlink is too large: %ju", (uintmax_t)h->nlink); 1400 | if (h->rdev > 0777777) 1401 | fatal("device is too large: %ju", (uintmax_t)h->rdev); 1402 | if (h->mtime.tv_sec > MAXTIME) 1403 | fatal("mtime is too large: %ju", (uintmax_t)h->mtime.tv_sec); 1404 | namesize = h->pathlen; 1405 | if (namesize > 0 && h->path[namesize - 1] == '/') 1406 | --namesize; 1407 | if (namesize > 077777777777 - 1) 1408 | fatal("path is too large: %ju", (uintmax_t)h->pathlen + 1); 1409 | size = h->type == SYMTYPE ? h->linklen : h->size; 1410 | if (size > MAXSIZE) 1411 | fatal("size is too large: %ju", h->size); 1412 | len = snprintf(buf, sizeof buf, "070707%.6lo%.6lo%.6lo%.6lo%.6lo%.6lo%.6lo%.11llo%.6lo%.11jo", 1413 | (unsigned long)h->dev, ino, mode, 1414 | (unsigned long)h->uid, (unsigned long)h->gid, 1415 | (unsigned long)h->nlink, (unsigned long)h->rdev, 1416 | (unsigned long long)h->mtime.tv_sec, (unsigned long)namesize + 1, size); 1417 | assert(len == 76); 1418 | if (fwrite(buf, 1, 76, f) != 76) 1419 | fatal("write:"); 1420 | if (fwrite(h->path, 1, namesize, f) != namesize || fputc('\0', f) == EOF) 1421 | fatal("write:"); 1422 | switch (h->type) { 1423 | case SYMTYPE: 1424 | if (fwrite(h->link, 1, h->linklen, f) != h->linklen) 1425 | fatal("write:"); 1426 | break; 1427 | case REGTYPE: 1428 | openfile(h); 1429 | copy(&bioin, h->size, f, h->size); 1430 | closefile(h); 1431 | break; 1432 | default: 1433 | break; 1434 | } 1435 | } 1436 | 1437 | static void 1438 | filepush(struct filelist *files, const char *name, size_t pathlen, dev_t dev) 1439 | { 1440 | struct file *f; 1441 | size_t namelen; 1442 | 1443 | namelen = strlen(name); 1444 | f = malloc(sizeof *f + namelen + 1); 1445 | if (!f) 1446 | fatal(NULL); 1447 | memcpy(f->name, name, namelen + 1); 1448 | f->namelen = namelen; 1449 | f->pathlen = pathlen; 1450 | f->dev = dev; 1451 | f->next = files->pending; 1452 | files->pending = f; 1453 | } 1454 | 1455 | static int 1456 | readfile(struct bufio *f, struct header *h) 1457 | { 1458 | /* use our own path buffer, since we use it for traversal */ 1459 | static struct strbuf path; 1460 | struct stat st; 1461 | int flag; 1462 | DIR *dir; 1463 | struct dirent *d; 1464 | ssize_t ret; 1465 | dev_t dev; 1466 | 1467 | next: 1468 | flag = follow == 'L' ? 0 : AT_SYMLINK_NOFOLLOW; 1469 | if (files.pending) { 1470 | struct file *f; 1471 | 1472 | f = files.pending; 1473 | files.pending = f->next; 1474 | assert(f->pathlen <= path.len); 1475 | path.len = f->pathlen; 1476 | sbufcat(&path, f->name, f->namelen, 1024); 1477 | if (follow == 'H' && f->pathlen > 0) 1478 | flag &= ~AT_SYMLINK_NOFOLLOW; 1479 | dev = f->dev; 1480 | free(f); 1481 | } else { 1482 | if (!files.input) 1483 | return 0; 1484 | ret = getline(&path.str, &path.cap, files.input); 1485 | if (ret < 0) { 1486 | if (ferror(files.input)) 1487 | fatal("getline:"); 1488 | return 0; 1489 | } 1490 | if (ret > 0 && path.str[ret - 1] == '\n') 1491 | path.str[--ret] = '\0'; 1492 | path.len = ret; 1493 | dev = 0; 1494 | } 1495 | 1496 | if (fstatat(AT_FDCWD, path.str, &st, flag) != 0) 1497 | fatal("stat %s:", path.str); 1498 | if (Xflag && dev && st.st_dev != dev) 1499 | goto next; 1500 | if (S_ISDIR(st.st_mode) && path.str[path.len - 1] != '/') 1501 | sbufcat(&path, "/", 1, 1024); 1502 | h->fields = PATH | UID | GID | ATIME | MTIME | CTIME; 1503 | h->path = path.str; 1504 | h->pathlen = path.len; 1505 | h->dev = st.st_dev; 1506 | h->ino = st.st_ino; 1507 | h->mode = st.st_mode & ~S_IFMT; 1508 | h->uid = st.st_uid; 1509 | h->gid = st.st_gid; 1510 | h->nlink = st.st_nlink; 1511 | h->rdev = 0; 1512 | h->size = 0; 1513 | h->atime = st.st_atim; 1514 | h->mtime = st.st_mtim; 1515 | h->ctime = st.st_ctim; 1516 | h->uname = uidtouname(st.st_uid, ""); 1517 | h->gname = gidtogname(st.st_gid, ""); 1518 | h->link = ""; 1519 | h->linklen = 0; 1520 | h->slash = NULL; 1521 | h->data = NULL; 1522 | h->file = h->path; 1523 | h->fileatime = st.st_atim; 1524 | h->flag = flag; 1525 | switch (st.st_mode & S_IFMT) { 1526 | case S_IFREG: 1527 | h->type = REGTYPE; 1528 | h->size = st.st_size; 1529 | break; 1530 | case S_IFLNK: 1531 | h->type = SYMTYPE; 1532 | h->linkbuf.len = 0; 1533 | sbufalloc(&h->linkbuf, 1024, 1024); 1534 | for (;;) { 1535 | ret = readlink(h->path, h->linkbuf.str, h->linkbuf.cap - 1); 1536 | if (ret < 0) 1537 | fatal("readlink %s:", h->path); 1538 | if (ret < h->linkbuf.cap) 1539 | break; 1540 | if (h->linkbuf.cap > SSIZE_MAX / 2) 1541 | fatal("symlink target is too long"); 1542 | sbufalloc(&h->linkbuf, h->linkbuf.cap * 2, 1024); 1543 | } 1544 | h->linkbuf.str[ret] = '\0'; 1545 | h->linkbuf.len = ret; 1546 | h->link = h->linkbuf.str; 1547 | h->linklen = h->linkbuf.len; 1548 | break; 1549 | case S_IFCHR: 1550 | h->type = CHRTYPE; 1551 | h->rdev = st.st_rdev; 1552 | break; 1553 | case S_IFBLK: 1554 | h->type = BLKTYPE; 1555 | h->rdev = st.st_rdev; 1556 | break; 1557 | case S_IFDIR: 1558 | h->type = DIRTYPE; 1559 | dir = opendir(h->path); 1560 | if (!dir) 1561 | fatal("opendir %s:", h->path); 1562 | for (;;) { 1563 | errno = 0; 1564 | d = readdir(dir); 1565 | if (!d) 1566 | break; 1567 | if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) 1568 | continue; 1569 | filepush(&files, d->d_name, path.len, st.st_dev); 1570 | } 1571 | if (errno != 0) 1572 | fatal("readdir %s:", h->path); 1573 | closedir(dir); 1574 | break; 1575 | case S_IFIFO: 1576 | h->type = FIFOTYPE; 1577 | break; 1578 | } 1579 | return 1; 1580 | } 1581 | 1582 | static void 1583 | usage(void) 1584 | { 1585 | fprintf(stderr, "usage: pax\n"); 1586 | exit(2); 1587 | } 1588 | 1589 | static void 1590 | parseopts(char *s) 1591 | { 1592 | char *key, *val, *end, *d; 1593 | int ext; 1594 | 1595 | for (;;) { 1596 | s += strspn(s, " \t\n\v\f\r"); 1597 | if (!*s) 1598 | break; 1599 | key = s; 1600 | while (*s && *s != ',' && *s != '=') 1601 | ++s; 1602 | val = NULL; 1603 | end = NULL, ext = 0; /* silence gcc uninitialized warning */ 1604 | if (*s == '=') { 1605 | ext = s > key && s[-1] == ':'; 1606 | s[-ext] = '\0'; 1607 | val = ++s; 1608 | for (d = s; *s && *s != ','; ++s, ++d) { 1609 | if (*s == '\\') 1610 | ++s; 1611 | if (d < s) 1612 | *d = *s; 1613 | } 1614 | end = d; 1615 | } 1616 | if (*s == ',') 1617 | *s++ = '\0'; 1618 | if (strcmp(key, "linkdata") == 0) { 1619 | if (val) 1620 | fatal("option 'linkdata' must not have a value"); 1621 | opt.linkdata = 1; 1622 | } else if (strcmp(key, "times") == 0) { 1623 | if (val) 1624 | fatal("option 'times' must not have a value"); 1625 | opt.times = 1; 1626 | } else if (!val) { 1627 | fatal("option '%s' must have a value", key); 1628 | } else if (strcmp(key, "delete") == 0) { 1629 | const struct keyword *kw; 1630 | 1631 | for (kw = keywords; kw != keywords + LEN(keywords); ++kw) { 1632 | switch (fnmatch(val, kw->name, 0)) { 1633 | case 0: opt.delete |= kw->field; break; 1634 | case FNM_NOMATCH: break; 1635 | default: fatal("fnmatch error"); 1636 | } 1637 | } 1638 | } else if (strcmp(key, "exthdr.name") == 0) { 1639 | opt.exthdrname = val; 1640 | } else if (strcmp(key, "globexthdr.name") == 0) { 1641 | opt.globexthdrname = val; 1642 | } else if (strcmp(key, "invalid") == 0) { 1643 | fatal("option 'invalid' is not implemented"); 1644 | } else if (strcmp(key, "listopt") == 0) { 1645 | opt.listopt = val; 1646 | } else if (ext) { 1647 | extkeyval(&exthdr, key, val, end - val); 1648 | } else { 1649 | extkeyval(&globexthdr, key, val, end - val); 1650 | } 1651 | } 1652 | } 1653 | 1654 | static void 1655 | listhdr(FILE *f, struct header *h) 1656 | { 1657 | char mode[11], time[13], info[23]; 1658 | char unamebuf[(sizeof(uid_t) * CHAR_BIT + 2) / 3 + 1]; 1659 | char gnamebuf[(sizeof(gid_t) * CHAR_BIT + 2) / 3 + 1]; 1660 | const char *uname, *gname, *timefmt; 1661 | struct tm *tm; 1662 | 1663 | if (!h) 1664 | return; 1665 | if (opt.listopt) 1666 | fatal("listopt is not supported"); 1667 | if (!vflag) { 1668 | printf("%s\n", h->path); 1669 | return; 1670 | } 1671 | memset(mode, '-', sizeof mode - 1); 1672 | mode[10] = '\0'; 1673 | switch (h->type) { 1674 | case SYMTYPE: mode[0] = 'l'; break; 1675 | case CHRTYPE: mode[0] = 'c'; break; 1676 | case BLKTYPE: mode[0] = 'b'; break; 1677 | case DIRTYPE: mode[0] = 'd'; break; 1678 | case FIFOTYPE: mode[0] = 'p'; break; 1679 | } 1680 | if (h->mode & S_IRUSR) mode[1] = 'r'; 1681 | if (h->mode & S_IWUSR) mode[2] = 'w'; 1682 | if (h->mode & S_IXUSR) mode[3] = 'x'; 1683 | if (h->mode & S_IRGRP) mode[4] = 'r'; 1684 | if (h->mode & S_IWGRP) mode[5] = 'w'; 1685 | if (h->mode & S_IXGRP) mode[6] = 'x'; 1686 | if (h->mode & S_IROTH) mode[7] = 'r'; 1687 | if (h->mode & S_IWOTH) mode[8] = 'w'; 1688 | if (h->mode & S_IXOTH) mode[9] = 'x'; 1689 | if (h->mode & S_ISUID) mode[3] = mode[3] == 'x' ? 's' : 'S'; 1690 | if (h->mode & S_ISGID) mode[3] = mode[6] == 'x' ? 's' : 'S'; 1691 | if (h->mode & S_ISVTX) mode[9] = mode[9] == 'x' ? 't' : 'T'; 1692 | uname = h->uname; 1693 | if (!uname[0]) { 1694 | snprintf(unamebuf, sizeof unamebuf, "%ju", (uintmax_t)h->uid); 1695 | uname = unamebuf; 1696 | } 1697 | gname = h->gname; 1698 | if (!gname[0]) { 1699 | snprintf(gnamebuf, sizeof gnamebuf, "%ju", (uintmax_t)h->gid); 1700 | gname = gnamebuf; 1701 | } 1702 | timefmt = h->mtime.tv_sec + 15780000 < curtime || h->mtime.tv_sec > curtime 1703 | ? "%b %e %Y" : "%b %e %H:%M"; 1704 | tm = localtime(&h->mtime.tv_sec); 1705 | if (!tm) 1706 | fatal("localtime:"); 1707 | strftime(time, sizeof time, timefmt, tm); 1708 | if (h->type == CHRTYPE || h->type == BLKTYPE) 1709 | snprintf(info, sizeof info, "%u, %u", major(h->rdev), minor(h->rdev)); 1710 | else 1711 | snprintf(info, sizeof info, "%ju", (uintmax_t)h->size); 1712 | printf("%s %2ju %-8s %-8s %9s %s %s", mode, (uintmax_t)h->nlink, uname, gname, info, time, h->path); 1713 | switch (h->type) { 1714 | case LNKTYPE: printf(" == %s", h->link); break; 1715 | case SYMTYPE: printf(" -> %s", h->link); break; 1716 | } 1717 | putchar('\n'); 1718 | } 1719 | 1720 | static void 1721 | mkdirp(char *name, size_t len) 1722 | { 1723 | char *p; 1724 | 1725 | if (len == 0) 1726 | return; 1727 | for (p = name + 1; p < name + len - 1; ++p) { 1728 | if (*p != '/') 1729 | continue; 1730 | *p = 0; 1731 | if (mkdir(name, 0777) != 0 && errno != EEXIST) 1732 | fatal("mkdir %s:", name); 1733 | *p = '/'; 1734 | } 1735 | } 1736 | 1737 | static void 1738 | writefile(FILE *unused, struct header *h) 1739 | { 1740 | FILE *f; 1741 | int fd, retry, flags; 1742 | struct stat st; 1743 | mode_t mode; 1744 | 1745 | if (!h) 1746 | return; 1747 | if (uflag && fstatat(destfd, h->path, &st, 0) == 0) { 1748 | if (h->mtime.tv_sec < st.st_mtime || (h->mtime.tv_sec == st.st_mtime 1749 | && h->mtime.tv_nsec < st.st_mtim.tv_nsec)) 1750 | return; 1751 | } 1752 | if (vflag) 1753 | fprintf(stderr, "%s\n", h->path); 1754 | if (lflag && h->file && h->type != DIRTYPE) { 1755 | if (linkat(AT_FDCWD, h->file, destfd, h->path, h->flag) == 0) 1756 | return; 1757 | } 1758 | retry = 1; 1759 | if (0) { 1760 | retry: 1761 | retry = 0; 1762 | mkdirp(h->path, h->pathlen); 1763 | } 1764 | mode = h->mode & ~(S_ISUID | S_ISGID); 1765 | switch (h->type) { 1766 | case REGTYPE: 1767 | flags = O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC; 1768 | if (kflag) 1769 | flags |= O_EXCL; 1770 | fd = openat(destfd, h->path, flags, mode); 1771 | if (fd < 0) { 1772 | if (retry && errno == ENOENT) 1773 | goto retry; 1774 | fatal("open %s%s:", dest, h->path); 1775 | } 1776 | f = fdopen(fd, "w"); 1777 | if (!f) 1778 | fatal("open %s:", h->path); 1779 | openfile(h); 1780 | copy(&bioin, h->size, f, h->size); 1781 | closefile(h); 1782 | fclose(f); 1783 | break; 1784 | case LNKTYPE: 1785 | if (linkat(destfd, h->link, destfd, h->path, 0) != 0) { 1786 | if (retry && errno == ENOENT) 1787 | goto retry; 1788 | fatal("link %s%s:", dest, h->path); 1789 | } 1790 | break; 1791 | case SYMTYPE: 1792 | if (symlinkat(h->link, destfd, h->path) != 0) { 1793 | if (retry && errno == ENOENT) 1794 | goto retry; 1795 | fatal("symlink %s%s:", dest, h->path); 1796 | } 1797 | break; 1798 | case CHRTYPE: 1799 | case BLKTYPE: 1800 | mode |= h->type == CHRTYPE ? S_IFCHR : S_IFBLK; 1801 | if (mknodat(destfd, h->path, mode, h->rdev) != 0) { 1802 | if (retry && errno == ENOENT) 1803 | goto retry; 1804 | fatal("mknod %s%s:", dest, h->path); 1805 | } 1806 | break; 1807 | case DIRTYPE: 1808 | if (mkdirat(destfd, h->path, mode) != 0) { 1809 | if (retry && errno == ENOENT) 1810 | goto retry; 1811 | if (errno == EEXIST) { 1812 | if (fstatat(destfd, h->path, &st, 0) == 0 && S_ISDIR(st.st_mode)) 1813 | break; 1814 | errno = EEXIST; 1815 | } 1816 | fatal("mkdir %s%s:", dest, h->path); 1817 | } 1818 | break; 1819 | case FIFOTYPE: 1820 | if (mkfifoat(destfd, h->path, mode) != 0) { 1821 | if (retry && errno == ENOENT) 1822 | goto retry; 1823 | if (errno == EEXIST) { 1824 | if (fstatat(destfd, h->path, &st, 0) == 0 && S_ISFIFO(st.st_mode)) 1825 | break; 1826 | errno = EEXIST; 1827 | } 1828 | fatal("mkfifo %s%s:", dest, h->path); 1829 | } 1830 | break; 1831 | } 1832 | if (preserve & (ATIME | MTIME)) { 1833 | struct timespec ts[2]; 1834 | 1835 | ts[0] = preserve & ATIME ? h->atime : (struct timespec){.tv_nsec = UTIME_OMIT}; 1836 | ts[1] = preserve & MTIME ? h->mtime : (struct timespec){.tv_nsec = UTIME_OMIT}; 1837 | if (utimensat(destfd, h->path, ts, AT_SYMLINK_NOFOLLOW) != 0) { 1838 | fprintf(stderr, "utimens %s%s: %s\n", dest, h->path, strerror(errno)); 1839 | exitstatus = 1; 1840 | } 1841 | } 1842 | if (preserve & (UID | GID)) { 1843 | uid_t uid; 1844 | gid_t gid; 1845 | 1846 | uid = preserve & UID ? unametouid(h->uname, h->uid) : -1; 1847 | gid = preserve & GID ? gnametogid(h->gname, h->gid) : -1; 1848 | if (fchownat(destfd, h->path, uid, gid, 0) != 0) { 1849 | fprintf(stderr, "chown %s%s: %s\n", dest, h->path, strerror(errno)); 1850 | exitstatus = 1; 1851 | } else { 1852 | /* add back setuid/setgid bits if we preserved the uid/gid */ 1853 | mode = h->mode; 1854 | } 1855 | } 1856 | if (preserve & MODE) { 1857 | if (fchmodat(destfd, h->path, mode, 0) != 0) { 1858 | fprintf(stderr, "chmod %s%s: %s\n", dest, h->path, strerror(errno)); 1859 | exitstatus = 1; 1860 | } 1861 | } 1862 | } 1863 | 1864 | static int 1865 | match(struct header *h) 1866 | { 1867 | static struct dir { 1868 | char *path; 1869 | size_t pathlen; 1870 | } *dirs; 1871 | static size_t dirslen; 1872 | size_t i; 1873 | 1874 | if (patslen == 0) 1875 | return 1; 1876 | if (!dflag) { 1877 | for (struct dir *d = dirs; d < dirs + dirslen; ++d) { 1878 | if (h->pathlen >= d->pathlen && memcmp(h->path, d->path, d->pathlen) == 0) 1879 | return !cflag; 1880 | } 1881 | } 1882 | for (i = 0; i < patslen; ++i) { 1883 | if (nflag && patsused[i]) 1884 | continue; 1885 | switch (fnmatch(pats[i], h->path, FNM_PATHNAME | FNM_PERIOD)) { 1886 | case 0: 1887 | patsused[i] = 1; 1888 | if (!dflag && h->type == DIRTYPE) { 1889 | struct dir *d; 1890 | 1891 | if ((dirslen & (dirslen - 1)) == 0) { 1892 | dirs = reallocarray(dirs, dirslen ? dirslen * 2 : 32, sizeof *dirs); 1893 | if (!dirs) 1894 | fatal(NULL); 1895 | } 1896 | d = &dirs[dirslen++]; 1897 | d->pathlen = h->pathlen; 1898 | d->path = malloc(d->pathlen + 1); 1899 | if (!d->path) 1900 | fatal(NULL); 1901 | memcpy(d->path, h->path, h->pathlen); 1902 | /* add trailing slash if not already present */ 1903 | if (d->path[d->pathlen - 1] != '/') 1904 | d->path[d->pathlen++] = '/'; 1905 | } 1906 | return !cflag; 1907 | case FNM_NOMATCH: 1908 | break; 1909 | default: 1910 | fatal("fnmatch error"); 1911 | } 1912 | } 1913 | return cflag; 1914 | } 1915 | 1916 | static void 1917 | parsereplstr(char *str) 1918 | { 1919 | static struct replstr **end = &replstr; 1920 | struct replstr *r; 1921 | char *old, *new, delim; 1922 | int err; 1923 | 1924 | delim = str[0]; 1925 | if (!delim) 1926 | usage(); 1927 | old = str + 1; 1928 | str = strchr(old, delim); 1929 | if (!str) 1930 | usage(); 1931 | *str = 0; 1932 | new = str + 1; 1933 | str = strchr(new, delim); 1934 | if (!str) 1935 | usage(); 1936 | *str = 0; 1937 | 1938 | r = malloc(sizeof *r); 1939 | if (!r) 1940 | fatal(NULL); 1941 | r->next = NULL; 1942 | r->global = false; 1943 | r->print = false; 1944 | r->symlink = false; 1945 | for (;;) { 1946 | switch (*++str) { 1947 | case 'g': r->global = true; break; 1948 | case 'p': r->print = true; break; 1949 | case 's': r->symlink = false; break; 1950 | case 'S': r->symlink = true; break; 1951 | case 0: goto done; 1952 | } 1953 | } 1954 | done: 1955 | err = regcomp(&r->old, old, REG_NEWLINE); 1956 | if (err != 0) { 1957 | char errbuf[256]; 1958 | 1959 | regerror(err, &r->old, errbuf, sizeof errbuf); 1960 | fatal("invalid regular expression: %s", errbuf); 1961 | } 1962 | r->new = new; 1963 | *end = r; 1964 | end = &r->next; 1965 | } 1966 | 1967 | static int 1968 | applyrepl(struct replstr *r, struct strbuf *b, const char *old, size_t oldlen) 1969 | { 1970 | regmatch_t match[10]; 1971 | size_t i, n, l; 1972 | const char *s, *p; 1973 | char *d; 1974 | int flags; 1975 | 1976 | flags = 0; 1977 | b->len = 0; 1978 | p = old; 1979 | while (regexec(&r->old, p, LEN(match), match, flags) == 0) { 1980 | n = match[0].rm_so; 1981 | for (s = r->new; *s; ++s) { 1982 | switch (*s) { 1983 | case '&': i = 0; break; 1984 | case '\\': i = *++s - '0'; break; 1985 | default: i = -1; break; 1986 | } 1987 | n += i <= 9 ? match[i].rm_eo - match[i].rm_so : 1; 1988 | } 1989 | d = sbufalloc(b, n + 1, 1024); 1990 | b->len += n; 1991 | memcpy(d, p, match[0].rm_so); 1992 | d += match[0].rm_so; 1993 | for (s = r->new; *s; ++s) { 1994 | switch (*s) { 1995 | case '&': i = 0; break; 1996 | case '\\': i = *++s - '0'; break; 1997 | default: i = -1; break; 1998 | } 1999 | if (i <= 9) { 2000 | l = match[i].rm_eo - match[i].rm_so; 2001 | memcpy(d, p + match[i].rm_so, l); 2002 | d += l; 2003 | } else { 2004 | *d++ = *s; 2005 | } 2006 | } 2007 | flags |= REG_NOTBOL; 2008 | p += match[0].rm_eo; 2009 | if (!r->global) 2010 | break; 2011 | } 2012 | if (flags == 0) 2013 | return 0; 2014 | sbufcat(b, p, oldlen - (p - old), 1024); 2015 | if (r->print) 2016 | fprintf(stderr, "%s >> %s\n", old, b->str); 2017 | return 1; 2018 | } 2019 | 2020 | static void 2021 | replace(struct header *h) 2022 | { 2023 | static struct strbuf path, link; 2024 | struct replstr *r; 2025 | 2026 | for (r = replstr; r; r = r->next) { 2027 | if (applyrepl(r, &path, h->path, h->pathlen)) { 2028 | h->path = path.str; 2029 | h->pathlen = path.len; 2030 | break; 2031 | } 2032 | } 2033 | if (h->type != LNKTYPE && h->type != SYMTYPE) 2034 | return; 2035 | for (r = replstr; r; r = r->next) { 2036 | if (h->type == SYMTYPE && !r->symlink) 2037 | continue; 2038 | if (applyrepl(r, &link, h->link, h->linklen)) { 2039 | h->link = link.str; 2040 | h->linklen = link.len; 2041 | break; 2042 | } 2043 | } 2044 | } 2045 | 2046 | int 2047 | main(int argc, char *argv[]) 2048 | { 2049 | const char *name = NULL, *arg, *format = "pax"; 2050 | const char *algo = NULL; 2051 | enum mode mode = LIST; 2052 | struct header hdr; 2053 | readfn *readhdr = NULL; 2054 | writefn *writehdr = listhdr; 2055 | FILE *out = NULL; 2056 | pid_t pid = -1; 2057 | int i; 2058 | size_t l; 2059 | 2060 | ARGBEGIN { 2061 | case 'a': 2062 | aflag = 1; 2063 | break; 2064 | case 'b': 2065 | EARGF(usage()); 2066 | break; 2067 | case 'c': 2068 | cflag = 1; 2069 | break; 2070 | case 'd': 2071 | dflag = 1; 2072 | break; 2073 | case 'f': 2074 | name = EARGF(usage()); 2075 | break; 2076 | case 'H': 2077 | follow = 'H'; 2078 | break; 2079 | case 'j': 2080 | algo = "bzip2"; 2081 | break; 2082 | case 'J': 2083 | algo = "xz"; 2084 | break; 2085 | case 'k': 2086 | kflag = 1; 2087 | break; 2088 | case 'l': 2089 | lflag = 1; 2090 | break; 2091 | case 'L': 2092 | follow = 'L'; 2093 | break; 2094 | case 'n': 2095 | nflag = 1; 2096 | break; 2097 | case 'o': 2098 | parseopts(EARGF(usage())); 2099 | break; 2100 | case 'p': 2101 | for (arg = EARGF(usage()); *arg; ++arg) { 2102 | switch (*arg) { 2103 | case 'a': preserve &= ~ATIME; break; 2104 | case 'e': preserve = ~0; break; 2105 | case 'm': preserve &= ~MTIME; break; 2106 | case 'o': preserve |= UID | GID; break; 2107 | case 'p': preserve |= MODE; break; 2108 | default: fatal("unknown -p option"); 2109 | } 2110 | } 2111 | break; 2112 | case 'r': 2113 | mode |= READ; 2114 | break; 2115 | case 's': 2116 | parsereplstr(EARGF(usage())); 2117 | break; 2118 | case 't': 2119 | tflag = 1; 2120 | break; 2121 | case 'u': 2122 | uflag = 1; 2123 | break; 2124 | case 'v': 2125 | vflag = 1; 2126 | break; 2127 | case 'w': 2128 | mode |= WRITE; 2129 | break; 2130 | case 'x': 2131 | format = EARGF(usage()); 2132 | break; 2133 | case 'X': 2134 | Xflag = 1; 2135 | break; 2136 | case 'z': 2137 | algo = "gzip"; 2138 | break; 2139 | default: 2140 | usage(); 2141 | } ARGEND; 2142 | 2143 | curtime = time(NULL); 2144 | if (curtime == (time_t)-1) 2145 | fatal("time:"); 2146 | exthdr.fields &= ~opt.delete; 2147 | exthdr.delete = exthdr.fields; 2148 | globexthdr.fields &= ~opt.delete; 2149 | globexthdr.delete = globexthdr.fields; 2150 | 2151 | switch (mode) { 2152 | case READ: 2153 | writehdr = writefile; 2154 | /* fallthrough */ 2155 | case LIST: 2156 | if (name && strcmp(name, "-") != 0) { 2157 | bioin.fd = open(name, O_RDONLY); 2158 | if (bioin.fd < 0) 2159 | fatal("open %s:", name); 2160 | } 2161 | readhdr = detectformat(&bioin, algo, &pid); 2162 | if (!readhdr) 2163 | fatal("could not detect archive format"); 2164 | if (argc) { 2165 | pats = argv; 2166 | patslen = argc; 2167 | patsused = calloc(1, argc); 2168 | if (!patsused) 2169 | fatal(NULL); 2170 | } 2171 | break; 2172 | case WRITE: 2173 | if (name && strcmp(name, "-") == 0) 2174 | name = NULL; 2175 | out = compress(algo, name, &pid); 2176 | if (strcmp(format, "ustar") == 0) { 2177 | writehdr = writeustar; 2178 | } else if (strcmp(format, "pax") == 0) { 2179 | writehdr = writepax; 2180 | if (globexthdr.fields) 2181 | writeexthdr(stdout, 'g', &globexthdr); 2182 | } else if (strcmp(format, "cpio") == 0) { 2183 | writehdr = writecpio; 2184 | } else { 2185 | fatal("unsupported archive format '%s'", format); 2186 | } 2187 | break; 2188 | case COPY: 2189 | if (name || argc == 0) 2190 | usage(); 2191 | l = strlen(argv[--argc]); 2192 | dest = malloc(l + 2); 2193 | if (!dest) 2194 | fatal(NULL); 2195 | memcpy(dest, argv[argc], l); 2196 | memcpy(dest + l, "/", 2); 2197 | destfd = open(dest, O_SEARCH|O_DIRECTORY); 2198 | if (destfd < 0) 2199 | fatal("open %s:", dest); 2200 | writehdr = writefile; 2201 | break; 2202 | } 2203 | if (mode & WRITE) { 2204 | readhdr = readfile; 2205 | bioin.fd = -1; 2206 | for (i = 0; i < argc; ++i) 2207 | filepush(&files, argv[i], 0, 0); 2208 | if (argc == 0) 2209 | files.input = stdin; 2210 | } 2211 | 2212 | memset(&hdr, 0, sizeof hdr); 2213 | while (readhdr(&bioin, &hdr)) { 2214 | mergehdr(&hdr, &exthdr, ~0); 2215 | mergehdr(&hdr, &globexthdr, ~exthdr.fields); 2216 | if (match(&hdr)) { 2217 | replace(&hdr); 2218 | writehdr(out, &hdr); 2219 | } 2220 | } 2221 | writehdr(out, NULL); 2222 | if (out) { 2223 | if (fflush(out) != 0) 2224 | fatal("write:"); 2225 | fclose(out); 2226 | } 2227 | for (i = 0; i < patslen; ++i) { 2228 | if (!patsused[i]) 2229 | fatal("pattern not matched: %s", pats[i]); 2230 | } 2231 | 2232 | if (pid != -1) { 2233 | int st; 2234 | 2235 | if (waitpid(pid, &st, 0) == -1) 2236 | fatal("waitpid:"); 2237 | if (WIFEXITED(st) && WEXITSTATUS(st) != 0) 2238 | fatal("child exited with status %d", WEXITSTATUS(st)); 2239 | if (WIFSIGNALED(st)) 2240 | fatal("child terminated by signal %d", WTERMSIG(st)); 2241 | } 2242 | return exitstatus; 2243 | } 2244 | --------------------------------------------------------------------------------