1/* 2 * Copyright (c) 2005 Rob Braun 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Rob Braun nor the names of his contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29/* 30 * 03-Apr-2005 31 * DRI: Rob Braun <bbraun@synack.net> 32 */ 33 34#include <stdlib.h> 35#include <stdio.h> 36#include <string.h> 37#include <sys/types.h> 38#include <sys/stat.h> 39#include <fts.h> 40#include <unistd.h> 41#include <fcntl.h> 42#include <inttypes.h> 43#include <netinet/in.h> 44#include <libxml/xmlreader.h> 45#include <libxml/xmlwriter.h> 46#include <libxml/xmlstring.h> 47#include <limits.h> 48#include <getopt.h> 49#include <regex.h> 50#include <errno.h> 51#include <time.h> 52#include "xar.h" 53#include "config.h" 54 55#define SYMBOLIC 1 56#define NUMERIC 2 57static int Perms = 0; 58static int Local = 0; 59static char *Subdoc = NULL; 60static char *SubdocName = NULL; 61static char *Toccksum = NULL; 62static char *Filecksum = NULL; 63static char *Compression = NULL; 64static char *Rsize = NULL; 65static char *CompressionArg = NULL; 66static char *Chdir = NULL; 67 68static int Err = 0; 69static int List = 0; 70static int Verbose = 0; 71static int Coalesce = 0; 72static int LinkSame = 0; 73static int NoOverwrite = 0; 74static int SaveSuid = 0; 75 76struct lnode { 77 char *str; 78 regex_t reg; 79 struct lnode *next; 80}; 81 82struct lnode *Exclude = NULL; 83struct lnode *Exclude_Tail = NULL; 84struct lnode *NoCompress = NULL; 85struct lnode *NoCompress_Tail = NULL; 86struct lnode *PropInclude = NULL; 87struct lnode *PropInclude_Tail = NULL; 88struct lnode *PropExclude = NULL; 89struct lnode *PropExclude_Tail = NULL; 90 91static int32_t err_callback(int32_t sev, int32_t err, xar_errctx_t ctx, void *usrctx); 92 93static void print_file(xar_t x, xar_file_t f) { 94 if( List && Verbose ) { 95 char *size = xar_get_size(x, f); 96 char *path = xar_get_path(f); 97 char *type = xar_get_type(x, f); 98 char *mode = xar_get_mode(x, f); 99 char *user = xar_get_owner(x, f); 100 char *group = xar_get_group(x, f); 101 char *mtime = xar_get_mtime(x, f); 102 printf("%s %8s/%-8s %10s %s %s\n", mode, user, group, size, mtime, path); 103 free(size); 104 free(type); 105 free(path); 106 free(mode); 107 free(user); 108 free(group); 109 free(mtime); 110 } else if( List || Verbose ) { 111 char *path = xar_get_path(f); 112 113 if (xar_path_issane(path) == 0) 114 printf("Warning, archive contains invalid path: %s\n", path); 115 else 116 printf("%s\n", path); 117 118 free(path); 119 } 120} 121 122static void add_subdoc(xar_t x) { 123 xar_subdoc_t s; 124 int fd; 125 unsigned char *buf; 126 unsigned int len; 127 struct stat sb; 128 129 if( SubdocName == NULL ) SubdocName = "subdoc"; 130 131 fd = open(Subdoc, O_RDONLY); 132 if( fd < 0 ) { 133 fprintf(stderr, "ERROR: subdoc file %s doesn't exist. Ignoring.\n", Subdoc); 134 return; 135 } 136 s = xar_subdoc_new(x, (const char *)SubdocName); 137 fstat(fd, &sb); 138 len = sb.st_size; 139 buf = malloc(len+1); 140 if( buf == NULL ) { 141 close(fd); 142 return; 143 } 144 memset(buf, 0, len+1); 145 read(fd, buf, len); 146 close(fd); 147 148 xar_subdoc_copyin(s, buf, len); 149 150 151 return; 152} 153 154static void extract_subdoc(xar_t x, const char *name) { 155 xar_subdoc_t i; 156 157 for( i = xar_subdoc_first(x); i; i = xar_subdoc_next(i) ) { 158 const char *sname = xar_subdoc_name(i); 159 unsigned char *sdoc; 160 int fd, size; 161 if( name && strcmp(name, sname) != 0 ) 162 continue; 163 xar_subdoc_copyout(i, &sdoc, (unsigned int *)&size); 164 fd = open(Subdoc, O_WRONLY|O_CREAT|O_TRUNC, 0644); 165 if( fd < 0 ) return; 166 write(fd, sdoc, size); 167 close(fd); 168 free(sdoc); 169 } 170 171 return; 172} 173 174static int archive(const char *filename, int arglen, char *args[]) { 175 xar_t x; 176 FTS *fts; 177 FTSENT *ent; 178 int flags; 179 struct lnode *i; 180 const char *default_compression; 181 182 x = xar_open(filename, WRITE); 183 if( !x ) { 184 fprintf(stderr, "Error creating archive %s\n", filename); 185 exit(1); 186 } 187 188 if( Toccksum ) 189 xar_opt_set(x, XAR_OPT_TOCCKSUM, Toccksum); 190 191 if( Filecksum ) 192 xar_opt_set(x, XAR_OPT_FILECKSUM, Filecksum); 193 194 if( CompressionArg ) 195 xar_opt_set(x, XAR_OPT_COMPRESSIONARG, CompressionArg); 196 197 if( Compression ) 198 xar_opt_set(x, XAR_OPT_COMPRESSION, Compression); 199 200 if( Coalesce ) 201 xar_opt_set(x, XAR_OPT_COALESCE, "true"); 202 203 if( LinkSame ) 204 xar_opt_set(x, XAR_OPT_LINKSAME, "true"); 205 206 if ( Rsize != NULL ) 207 xar_opt_set(x, XAR_OPT_RSIZE, Rsize); 208 209 xar_register_errhandler(x, err_callback, NULL); 210 211 for( i = PropInclude; i; i=i->next ) { 212 xar_opt_set(x, XAR_OPT_PROPINCLUDE, i->str); 213 } 214 for( i = PropExclude; i; i=i->next ) { 215 xar_opt_set(x, XAR_OPT_PROPEXCLUDE, i->str); 216 } 217 218 if( Subdoc ) 219 add_subdoc(x); 220 221 if( Perms == SYMBOLIC ) { 222 xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_SYMBOLIC); 223 } 224 if( Perms == NUMERIC ) { 225 xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_NUMERIC); 226 } 227 228 default_compression = strdup(xar_opt_get(x, XAR_OPT_COMPRESSION)); 229 if( !default_compression ) 230 default_compression = strdup(XAR_OPT_VAL_GZIP); 231 232 flags = FTS_PHYSICAL|FTS_NOSTAT|FTS_NOCHDIR; 233 if( Local ) 234 flags |= FTS_XDEV; 235 fts = fts_open(args, flags, NULL); 236 if( !fts ) { 237 fprintf(stderr, "Error traversing file tree\n"); 238 exit(1); 239 } 240 241 while( (ent = fts_read(fts)) ) { 242 xar_file_t f; 243 int exclude_match = 1; 244 int nocompress_match = 1; 245 if( ent->fts_info == FTS_DP ) 246 continue; 247 248 if( strcmp(ent->fts_path, "/") == 0 ) 249 continue; 250 if( strcmp(ent->fts_path, ".") == 0 ) 251 continue; 252 253 for( i = Exclude; i; i=i->next ) { 254 exclude_match = regexec(&i->reg, ent->fts_path, 0, NULL, 0); 255 if( !exclude_match ) 256 break; 257 } 258 if( !exclude_match ) { 259 if( Verbose ) 260 printf("Excluding %s\n", ent->fts_path); 261 continue; 262 } 263 264 for( i = NoCompress; i; i=i->next ) { 265 nocompress_match = regexec(&i->reg, ent->fts_path, 0, NULL, 0); 266 if( !nocompress_match ) { 267 xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE); 268 break; 269 } 270 } 271 f = xar_add(x, ent->fts_path); 272 if( !f ) { 273 fprintf(stderr, "Error adding file %s\n", ent->fts_path); 274 } else { 275 print_file(x, f); 276 } 277 if( !nocompress_match ) 278 xar_opt_set(x, XAR_OPT_COMPRESSION, default_compression); 279 } 280 fts_close(fts); 281 if( xar_close(x) != 0 ) { 282 fprintf(stderr, "Error creating the archive\n"); 283 if( !Err ) 284 Err = 42; 285 } 286 287 free((char *)default_compression); 288 for( i = Exclude; i; ) { 289 struct lnode *tmp; 290 regfree(&i->reg); 291 tmp = i; 292 i = i->next; 293 free(tmp); 294 } 295 for( i = NoCompress; i; ) { 296 struct lnode *tmp; 297 regfree(&i->reg); 298 tmp = i; 299 i = i->next; 300 free(tmp); 301 } 302 303 return Err; 304} 305 306static int extract(const char *filename, int arglen, char *args[]) { 307 xar_t x; 308 xar_iter_t i; 309 xar_file_t f; 310 int files_extracted = 0; 311 int argi; 312 struct lnode *extract_files = NULL; 313 struct lnode *extract_tail = NULL; 314 struct lnode *lnodei = NULL; 315 struct lnode *dirs = NULL; 316 317 for(argi = 0; args[argi]; argi++) { 318 struct lnode *tmp; 319 int err; 320 tmp = malloc(sizeof(struct lnode)); 321 tmp->str = strdup(args[argi]); 322 tmp->next = NULL; 323 err = regcomp(&tmp->reg, tmp->str, REG_NOSUB); 324 if( err ) { 325 char errstr[1024]; 326 regerror(err, &tmp->reg, errstr, sizeof(errstr)); 327 printf("Error with regular expression %s: %s\n", tmp->str, errstr); 328 exit(1); 329 } 330 if( extract_files == NULL ) { 331 extract_files = tmp; 332 extract_tail = tmp; 333 } else { 334 extract_tail->next = tmp; 335 extract_tail = tmp; 336 } 337 338 /* Add a clause for recursive extraction */ 339 tmp = malloc(sizeof(struct lnode)); 340 asprintf(&tmp->str, "%s/.*", args[argi]); 341 tmp->next = NULL; 342 err = regcomp(&tmp->reg, tmp->str, REG_NOSUB); 343 if( err ) { 344 char errstr[1024]; 345 regerror(err, &tmp->reg, errstr, sizeof(errstr)); 346 printf("Error with regular expression %s: %s\n", tmp->str, errstr); 347 exit(1); 348 } 349 if( extract_files == NULL ) { 350 extract_files = tmp; 351 extract_tail = tmp; 352 } else { 353 extract_tail->next = tmp; 354 extract_tail = tmp; 355 } 356 } 357 358 x = xar_open(filename, READ); 359 if( !x ) { 360 fprintf(stderr, "Error opening xar archive: %s\n", filename); 361 exit(1); 362 } 363 364 if(Chdir) { 365 if( chdir(Chdir) != 0 ) { 366 fprintf(stderr, "Unable to chdir to %s\n", Chdir); 367 exit(1); 368 } 369 } 370 371 xar_register_errhandler(x, err_callback, NULL); 372 373 if( Perms == SYMBOLIC ) { 374 xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_SYMBOLIC); 375 } 376 if( Perms == NUMERIC ) { 377 xar_opt_set(x, XAR_OPT_OWNERSHIP, XAR_OPT_VAL_NUMERIC); 378 } 379 if ( Rsize != NULL ) { 380 xar_opt_set(x, XAR_OPT_RSIZE, Rsize); 381 } 382 if( SaveSuid ) { 383 xar_opt_set(x, XAR_OPT_SAVESUID, XAR_OPT_VAL_TRUE); 384 } 385 386 i = xar_iter_new(); 387 if( !i ) { 388 fprintf(stderr, "Error creating xar iterator\n"); 389 exit(1); 390 } 391 392 for(f = xar_file_first(x, i); f; f = xar_file_next(i)) { 393 int matched = 0; 394 int exclude_match = 1; 395 struct lnode *i; 396 397 char *path = xar_get_path(f); 398 399 if( args[0] ) { 400 for(i = extract_files; i != NULL; i = i->next) { 401 int extract_match = 1; 402 403 extract_match = regexec(&i->reg, path, 0, NULL, 0); 404 if( !extract_match ) { 405 matched = 1; 406 break; 407 } 408 } 409 } else { 410 matched = 1; 411 } 412 413 for( i = Exclude; i; i=i->next ) { 414 exclude_match = regexec(&i->reg, path, 0, NULL, 0); 415 if( !exclude_match ) 416 break; 417 } 418 if( !exclude_match ) { 419 if( Verbose ) 420 printf("Excluding %s\n", path); 421 free(path); 422 continue; 423 } 424 425 if (!xar_path_issane(path)) { 426 if (Verbose) 427 printf("Warning, not extracting file \"%s\" because it's path is invalid.\n", path); 428 free(path); 429 continue; 430 } 431 432 if( matched ) { 433 struct stat sb; 434 if( NoOverwrite && (lstat(path, &sb) == 0) ) { 435 printf("%s already exists, not overwriting\n", path); 436 } else { 437 const char *prop = NULL; 438 int deferred = 0; 439 if( xar_prop_get(f, "type", &prop) == 0 ) { 440 if( strcmp(prop, "directory") == 0 ) { 441 struct lnode *tmpl = calloc(sizeof(struct lnode),1); 442 tmpl->str = (char *)f; 443 tmpl->next = dirs; 444 dirs = tmpl; 445 deferred = 1; 446 } 447 } 448 if( ! deferred ) { 449 files_extracted++; 450 print_file(x, f); 451 xar_extract(x, f); 452 } 453 } 454 } 455 free(path); 456 } 457 for(lnodei = dirs; lnodei; lnodei = lnodei->next) { 458 files_extracted++; 459 print_file(x,(xar_file_t)lnodei->str); 460 xar_extract(x, (xar_file_t)lnodei->str); 461 } 462 if( args[0] && (files_extracted == 0) ) { 463 fprintf(stderr, "No files matched extraction criteria.\n"); 464 Err = 3; 465 } 466 467 if( Subdoc ) 468 extract_subdoc(x, NULL); 469 470 xar_iter_free(i); 471 if( xar_close(x) != 0 ) { 472 fprintf(stderr, "Error extracting the archive\n"); 473 if( !Err ) 474 Err = 42; 475 } 476 477 for(lnodei = extract_files; lnodei != NULL; ) { 478 struct lnode *tmp; 479 free(lnodei->str); 480 regfree(&lnodei->reg); 481 tmp = lnodei; 482 lnodei = lnodei->next; 483 free(tmp); 484 } 485 return Err; 486} 487 488static int list_subdocs(const char *filename) { 489 xar_t x; 490 xar_subdoc_t s; 491 492 x = xar_open(filename, READ); 493 if( !x ) { 494 fprintf(stderr, "Error opening xar archive: %s\n", filename); 495 exit(1); 496 } 497 498 for(s = xar_subdoc_first(x); s; s = xar_subdoc_next(s)) { 499 printf("%s\n", xar_subdoc_name(s)); 500 } 501 xar_close(x); 502 503 return Err; 504} 505 506static int list(const char *filename, int arglen, char *args[]) { 507 xar_t x; 508 xar_iter_t i; 509 xar_file_t f; 510 int argi = 0; 511 struct lnode *list_files = NULL; 512 struct lnode *list_tail = NULL; 513 struct lnode *lnodei = NULL; 514 515 for(argi = 0; args[argi]; argi++) { 516 struct lnode *tmp; 517 int err; 518 tmp = malloc(sizeof(struct lnode)); 519 tmp->str = strdup(args[argi]); 520 tmp->next = NULL; 521 err = regcomp(&tmp->reg, tmp->str, REG_NOSUB); 522 if( err ) { 523 char errstr[1024]; 524 regerror(err, &tmp->reg, errstr, sizeof(errstr)); 525 printf("Error with regular expression %s: %s\n", tmp->str, errstr); 526 exit(1); 527 } 528 if( list_files == NULL ) { 529 list_files = tmp; 530 list_tail = tmp; 531 } else { 532 list_tail->next = tmp; 533 list_tail = tmp; 534 } 535 } 536 537 x = xar_open(filename, READ); 538 if( !x ) { 539 fprintf(stderr, "Error opening xar archive: %s\n", filename); 540 exit(1); 541 } 542 543 i = xar_iter_new(); 544 if( !i ) { 545 fprintf(stderr, "Error creating xar iterator\n"); 546 exit(1); 547 } 548 549 for(f = xar_file_first(x, i); f; f = xar_file_next(i)) { 550 int matched = 0; 551 552 if( args[0] ) { 553 char *path = xar_get_path(f); 554 555 if (xar_path_issane(path) == 0) { 556 fprintf(stderr, "Warning, archive contains invalid path: %s\n", path); 557 free(path); 558 continue; 559 } 560 561 for(lnodei = list_files; lnodei != NULL; lnodei = lnodei->next) { 562 int list_match = 1; 563 564 list_match = regexec(&lnodei->reg, path, 0, NULL, 0); 565 if( !list_match ) { 566 matched = 1; 567 break; 568 } 569 } 570 free(path); 571 } else { 572 matched = 1; 573 } 574 575 if( matched ) 576 print_file(x, f); 577 } 578 579 xar_iter_free(i); 580 xar_close(x); 581 582 for(lnodei = list_files; lnodei != NULL; ) { 583 struct lnode *tmp; 584 free(lnodei->str); 585 regfree(&lnodei->reg); 586 tmp = lnodei; 587 lnodei = lnodei->next; 588 free(tmp); 589 } 590 591 return Err; 592} 593 594static int dumptoc(const char *filename, const char* tocfile) { 595 xar_t x; 596 x = xar_open(filename, READ); 597 if( !x ) { 598 fprintf(stderr, "Error opening xar archive: %s\n", filename); 599 exit(1); 600 } 601 602 xar_serialize(x, tocfile); 603 xar_close(x); 604 return Err; 605} 606 607static int dump_header(const char *filename) { 608 int fd; 609 xar_header_t xh; 610 611 if(filename == NULL) 612 fd = 0; 613 else { 614 fd = open(filename, O_RDONLY); 615 if( fd < 0 ) { 616 perror("open"); 617 exit(1); 618 } 619 } 620 621 if( read(fd, &xh, sizeof(xh)) < sizeof(xh) ) { 622 fprintf(stderr, "error reading header\n"); 623 exit(1); 624 } 625 626 printf("magic: 0x%x ", ntohl(xh.magic)); 627 if( ntohl(xh.magic) != XAR_HEADER_MAGIC ) 628 printf("(BAD)\n"); 629 else 630 printf("(OK)\n"); 631 printf("size: %d\n", ntohs(xh.size)); 632 printf("version: %d\n", ntohs(xh.version)); 633 printf("Compressed TOC length: %" PRId64 "\n", xar_ntoh64(xh.toc_length_compressed)); 634 printf("Uncompressed TOC length: %" PRId64 "\n", xar_ntoh64(xh.toc_length_uncompressed)); 635 printf("Checksum algorithm: %d ", ntohl(xh.cksum_alg)); 636 switch( ntohl(xh.cksum_alg) ) { 637 case XAR_CKSUM_NONE: printf("(none)\n"); 638 break; 639 case XAR_CKSUM_SHA1: printf("(SHA1)\n"); 640 break; 641 case XAR_CKSUM_SHA256: printf("(SHA256)\n"); 642 break; 643 case XAR_CKSUM_SHA512: printf("(SHA512)\n"); 644 break; 645 case XAR_CKSUM_MD5: printf("(MD5)\n"); 646 break; 647 default: printf("(unknown)\n"); 648 break; 649 }; 650 651 return 0; 652} 653 654static int32_t err_callback(int32_t sev, int32_t err, xar_errctx_t ctx, void *usrctx) { 655 xar_file_t f; 656 xar_t x; 657 const char *str; 658 int e; 659 660 x = xar_err_get_archive(ctx); 661 f = xar_err_get_file(ctx); 662 str = xar_err_get_string(ctx); 663 e = xar_err_get_errno(ctx); 664 665 switch(sev) { 666 case XAR_SEVERITY_DEBUG: 667 case XAR_SEVERITY_INFO: 668 break; 669 case XAR_SEVERITY_WARNING: 670 printf("%s\n", str); 671 break; 672 case XAR_SEVERITY_NORMAL: 673 if( (err = XAR_ERR_ARCHIVE_CREATION) && f ) 674 print_file(x, f); 675 break; 676 case XAR_SEVERITY_NONFATAL: 677 case XAR_SEVERITY_FATAL: 678 Err = 2; 679 printf("Error while "); 680 if( err == XAR_ERR_ARCHIVE_CREATION ) printf("creating"); 681 if( err == XAR_ERR_ARCHIVE_EXTRACTION ) printf("extracting"); 682 printf(" archive"); 683 if( f ) { 684 const char *file = xar_get_path(f); 685 if( file ) printf(":(%s)", file); 686 free((char *)file); 687 } 688 if( str ) printf(": %s", str); 689 if( err && e ) printf(" (%s)", strerror(e)); 690 if( sev == XAR_SEVERITY_NONFATAL ) { 691 printf(" - ignored"); 692 printf("\n"); 693 } else { 694 printf("\n"); 695 exit(1); 696 } 697 break; 698 } 699 return 0; 700} 701 702static void usage(const char *prog) { 703 fprintf(stderr, "Usage: %s -[ctx][v] -f <archive> ...\n", prog); 704 fprintf(stderr, "\t-c Creates an archive\n"); 705 fprintf(stderr, "\t-x Extracts an archive\n"); 706 fprintf(stderr, "\t-t Lists an archive\n"); 707 fprintf(stderr, "\t-f <filename> Specifies an archive to operate on [REQUIRED!]\n"); 708 fprintf(stderr, "\t-v Print filenames as they are archived\n"); 709 fprintf(stderr, "\t-C <path> On extract, chdir to this location\n"); 710 fprintf(stderr, "\t-n name Provides a name for a subdocument\n"); 711 fprintf(stderr, "\t-s <filename> On extract, specifies the file to extract\n"); 712 fprintf(stderr, "\t subdocuments to.\n"); 713 fprintf(stderr, "\t On archival, specifies an xml file to add\n"); 714 fprintf(stderr, "\t as a subdocument.\n"); 715 fprintf(stderr, "\t-l On archival, stay on the local device.\n"); 716 fprintf(stderr, "\t-p On extract, set ownership based on symbolic\n"); 717 fprintf(stderr, "\t names, if possible.\n"); 718 fprintf(stderr, "\t-P On extract, set ownership based on uid/gid.\n"); 719 fprintf(stderr, "\t--toc-cksum Specifies the hashing algorithm to use for\n"); 720 fprintf(stderr, "\t xml header verification.\n"); 721 fprintf(stderr, "\t Valid values: none, md5, sha1, sha256, and sha512\n"); 722 fprintf(stderr, "\t Default: sha1\n"); 723 fprintf(stderr, "\t--file-cksum Specifies the hashing algorithm to use for\n"); 724 fprintf(stderr, "\t file content verification.\n"); 725 fprintf(stderr, "\t Valid values: none, md5, sha1, sha256, and sha512\n"); 726 fprintf(stderr, "\t Default: sha1\n"); 727 fprintf(stderr, "\t--dump-toc=<filename> Has xar dump the xml header into the\n"); 728 fprintf(stderr, "\t specified file.\n"); 729 fprintf(stderr, "\t--dump-header Prints out the xar binary header information\n"); 730 fprintf(stderr, "\t--compression Specifies the compression type to use.\n"); 731 fprintf(stderr, "\t Valid values: none, gzip, bzip2, lzma\n"); 732 fprintf(stderr, "\t Default: gzip\n"); 733 fprintf(stderr, "\t-a Synonym for \"--compression=lzma\"\n"); 734 fprintf(stderr, "\t-j Synonym for \"--compression=bzip2\"\n"); 735 fprintf(stderr, "\t-z Synonym for \"--compression=gzip\"\n"); 736 fprintf(stderr, "\t--compression-args=arg Specifies arguments to be passed\n"); 737 fprintf(stderr, "\t to the compression engine.\n"); 738 fprintf(stderr, "\t--compress-heap Compress entire heap instead of individual files.\n"); 739 fprintf(stderr, "\t Currently limited to gzip compression.\n"); 740 fprintf(stderr, "\t--list-subdocs List the subdocuments in the xml header\n"); 741 fprintf(stderr, "\t--extract-subdoc=name Extracts the specified subdocument\n"); 742 fprintf(stderr, "\t to a document in cwd named <name>.xml\n"); 743 fprintf(stderr, "\t--exclude POSIX regular expression of files to \n"); 744 fprintf(stderr, "\t ignore while archiving.\n"); 745 fprintf(stderr, "\t--rsize Specifies the size of the buffer used\n"); 746 fprintf(stderr, "\t for read IO operations in bytes.\n"); 747 fprintf(stderr, "\t--coalesce-heap When archived files are identical, only store one copy\n"); 748 fprintf(stderr, "\t This option creates an archive which\n"); 749 fprintf(stderr, "\t is not streamable\n"); 750 fprintf(stderr, "\t--link-same Hardlink identical files\n"); 751 fprintf(stderr, "\t--no-compress POSIX regular expression of files\n"); 752 fprintf(stderr, "\t to archive, but not compress.\n"); 753 fprintf(stderr, "\t--prop-include File properties to include in archive\n"); 754 fprintf(stderr, "\t--prop-exclude File properties to exclude in archive\n"); 755 fprintf(stderr, "\t--distribution Only includes a subset of file properties\n"); 756 fprintf(stderr, "\t appropriate for archive distribution\n"); 757 fprintf(stderr, "\t--keep-existing Do not overwrite existing files while extracting\n"); 758 fprintf(stderr, "\t-k Synonym for --keep-existing\n"); 759 fprintf(stderr, "\t--keep-setuid Preserve the suid/sgid bits when extracting\n"); 760 fprintf(stderr, "\t--version Print xar's version number\n"); 761 762 return; 763} 764 765static void print_version() { 766 printf("xar %s\n", XAR_VERSION); 767} 768 769int main(int argc, char *argv[]) { 770 int ret; 771 char *filename = NULL; 772 char command = 0, c; 773 char **args; 774 const char *tocfile = NULL; 775 int arglen, i, err; 776 xar_t x; 777 int loptind = 0; 778 int required_dash_f = 0; /* This release requires us to use -f */ 779 struct lnode *tmp; 780 long int longtmp; 781 struct option o[] = { 782 {"toc-cksum", required_argument, 0, 1}, 783 {"file-cksum", required_argument, 0, 19}, // out of order to avoid regressions 784 {"dump-toc", required_argument, 0, 'd'}, 785 {"compression", required_argument, 0, 2}, 786 {"list-subdocs", no_argument, 0, 3}, 787 {"help", no_argument, 0, 'h'}, 788 {"version", no_argument, 0, 4}, 789 {"dump-header", no_argument, 0, 5}, 790 {"extract-subdoc", required_argument, 0, 6}, 791 {"exclude", required_argument, 0, 7}, 792 {"rsize", required_argument, 0, 8}, 793 {"coalesce-heap", no_argument, 0, 9}, 794 {"link-same", no_argument, 0, 10}, 795 {"no-compress", required_argument, 0, 11}, 796 {"prop-include", required_argument, 0, 12}, 797 {"prop-exclude", required_argument, 0, 13}, 798 {"distribution", no_argument, 0, 14}, 799 {"keep-existing", no_argument, 0, 15}, 800 {"keep-setuid", no_argument, 0, 16}, 801 {"compression-args", required_argument, 0, 17}, 802 { 0, 0, 0, 0} 803 }; 804 805 if( argc < 2 ) { 806 usage(argv[0]); 807 exit(1); 808 } 809 810 while( (c = getopt_long(argc, argv, "axcC:vtjzf:hpPln:s:d:vk", o, &loptind)) != -1 ) { 811 switch(c) { 812 case 1 : // toc-cksum 813 if( !optarg ) { 814 usage(argv[0]); 815 exit(1); 816 } 817 if( (strcmp(optarg, XAR_OPT_VAL_NONE) != 0) && 818 (strcmp(optarg, XAR_OPT_VAL_SHA1) != 0) && 819 (strcmp(optarg, XAR_OPT_VAL_SHA256) != 0) && 820 (strcmp(optarg, XAR_OPT_VAL_SHA512) != 0) && 821 (strcmp(optarg, XAR_OPT_VAL_MD5) != 0) ) { 822 usage(argv[0]); 823 exit(1); 824 } 825 Toccksum = optarg; 826 break; 827 case 19 : // file-cksum 828 if( !optarg ) { 829 usage(argv[0]); 830 exit(1); 831 } 832 if( (strcmp(optarg, XAR_OPT_VAL_NONE) != 0) && 833 (strcmp(optarg, XAR_OPT_VAL_SHA1) != 0) && 834 (strcmp(optarg, XAR_OPT_VAL_SHA256) != 0) && 835 (strcmp(optarg, XAR_OPT_VAL_SHA512) != 0) && 836 (strcmp(optarg, XAR_OPT_VAL_MD5) != 0) ) { 837 usage(argv[0]); 838 exit(1); 839 } 840 Filecksum = optarg; 841 break; 842 case 2 : // compression 843 if( !optarg ) { 844 usage(argv[0]); 845 exit(1); 846 } 847 if( (strcmp(optarg, XAR_OPT_VAL_NONE) != 0) && 848 (strcmp(optarg, XAR_OPT_VAL_GZIP) != 0) && 849 (strcmp(optarg, XAR_OPT_VAL_BZIP) != 0) && 850 (strcmp(optarg, XAR_OPT_VAL_LZMA) != 0) ) { 851 usage(argv[0]); 852 exit(1); 853 } 854 Compression = optarg; 855 break; 856 case 3 : // list-subdocs 857 if( command && (command != 3) ) { 858 fprintf(stderr, "Conflicting commands specified\n"); 859 exit(1); 860 } 861 command = 3; 862 break; 863 case 4 : // version 864 print_version(); 865 exit(0); 866 case 'd': // dump-toc 867 if( !optarg ) { 868 usage(argv[0]); 869 exit(1); 870 } 871 tocfile = optarg; 872 command = 'd'; 873 break; 874 case 5 : // dump-header 875 command = 5; 876 break; 877 case 6 : // extract-subdoc 878 SubdocName = optarg; 879 asprintf(&Subdoc, "%s.xml", SubdocName); 880 if( !command ) 881 command = 6; 882 break; 883 case 7 : // exclude 884 tmp = malloc(sizeof(struct lnode)); 885 tmp->str = optarg; 886 tmp->next = NULL; 887 err = regcomp(&tmp->reg, tmp->str, REG_NOSUB); 888 if( err ) { 889 char errstr[1024]; 890 regerror(err, &tmp->reg, errstr, sizeof(errstr)); 891 printf("Error with regular expression %s: %s\n", tmp->str, errstr); 892 exit(1); 893 } 894 if( Exclude == NULL ) { 895 Exclude = tmp; 896 Exclude_Tail = tmp; 897 } else { 898 Exclude_Tail->next = tmp; 899 Exclude_Tail = tmp; 900 } 901 break; 902 case 8 : // rsize 903 if ( !optarg ) { 904 usage(argv[0]); 905 exit(1); 906 } 907 longtmp = strtol(optarg, NULL, 10); 908 if( (((longtmp == LONG_MIN) || (longtmp == LONG_MAX)) && (errno == ERANGE)) || (longtmp < 1) ) { 909 fprintf(stderr, "Invalid rsize value: %s\n", optarg); 910 exit(5); 911 } 912 Rsize = optarg; 913 break; 914 case 9 : Coalesce = 1; break; // coalesce-heap 915 case 10 : LinkSame = 1; break; // link-same 916 case 11 : // no-compress 917 tmp = malloc(sizeof(struct lnode)); 918 tmp->str = optarg; 919 tmp->next = NULL; 920 err = regcomp(&tmp->reg, tmp->str, REG_NOSUB); 921 if( err ) { 922 char errstr[1024]; 923 regerror(err, &tmp->reg, errstr, sizeof(errstr)); 924 printf("Error with regular expression %s: %s\n", tmp->str, errstr); 925 exit(1); 926 } 927 if( NoCompress == NULL ) { 928 NoCompress = tmp; 929 NoCompress_Tail = tmp; 930 } else { 931 NoCompress_Tail->next = tmp; 932 NoCompress_Tail = tmp; 933 } 934 break; 935 case 12 : // prop-include 936 tmp = malloc(sizeof(struct lnode)); 937 tmp->str = optarg; 938 tmp->next = NULL; 939 if( PropInclude == NULL ) { 940 PropInclude = tmp; 941 PropInclude_Tail = tmp; 942 } else { 943 PropInclude_Tail->next = tmp; 944 PropInclude_Tail = tmp; 945 } 946 break; 947 case 13 : // prop-exclude 948 tmp = malloc(sizeof(struct lnode)); 949 tmp->str = optarg; 950 tmp->next = NULL; 951 if( PropExclude == NULL ) { 952 PropExclude = tmp; 953 PropExclude_Tail = tmp; 954 } else { 955 PropExclude_Tail->next = tmp; 956 PropExclude_Tail = tmp; 957 } 958 break; 959 case 14 : // distribution 960 { 961 char *props[] = { "type", "data", "mode", "name" }; 962 int i; 963 for( i = 0; i < 4; i++ ) { 964 tmp = malloc(sizeof(struct lnode)); 965 tmp->str = strdup(props[i]); 966 tmp->next = NULL; 967 if( PropInclude == NULL ) { 968 PropInclude = tmp; 969 PropInclude_Tail = tmp; 970 } else { 971 PropInclude_Tail->next = tmp; 972 PropInclude_Tail = tmp; 973 } 974 } 975 } 976 break; 977 case 'k': // keep-existing 978 case 15 : 979 NoOverwrite++; 980 break; 981 case 16 : // keep-setuid 982 SaveSuid++; 983 break; 984 case 17 : // compression-args 985 CompressionArg = optarg; 986 break; 987 case 'C': // chdir to 988 if( !optarg ) { 989 usage(argv[0]); 990 exit(1); 991 } 992 Chdir = optarg; 993 break; 994 case 'c': // create 995 case 'x': // extract 996 case 't': // list 997 if( command && (command != 's') ) { 998 usage(argv[0]); 999 fprintf(stderr, "Conflicting command flags: %c and %c specified\n", c, command); 1000 exit(1); 1001 } 1002 if( c == 't' ) 1003 List = 1; 1004 command = c; 1005 break; 1006 case 'a': 1007 Compression = "lzma"; 1008 break; 1009 case 'j': 1010 Compression = "bzip2"; 1011 break; 1012 case 'z': 1013 Compression = "gzip"; 1014 break; 1015 case 'f': // filename 1016 required_dash_f = 1; 1017 filename = optarg; 1018 break; 1019 case 'p': // set ownership based on symbolic names (if possible) 1020 Perms = SYMBOLIC; 1021 break; 1022 case 'P': // set ownership based on uid/gid 1023 Perms = NUMERIC; 1024 break; 1025 case 'l': // stay on local device 1026 Local = 1; 1027 break; 1028 case 'n': // provide subdocument name 1029 SubdocName = optarg; 1030 break; 1031 case 's': // extract subdocuments to/add subdocuments from 1032 Subdoc = optarg; 1033 if( !command ) 1034 command = 's'; 1035 break; 1036 case 'v': // print filenames 1037 Verbose++; 1038 break; 1039 case 'h': // help 1040 default: 1041 usage(argv[0]); 1042 exit(1); 1043 } 1044 } 1045 1046 if (! required_dash_f) { 1047 usage(argv[0]); 1048 fprintf(stderr, "\n -f option is REQUIRED\n"); 1049 exit(1); 1050 } 1051 1052 switch(command) { 1053 case 5 : 1054 return dump_header(filename); 1055 case 3 : 1056 return list_subdocs(filename); 1057 case 'c': 1058 if( optind == argc ) { 1059 usage(argv[0]); 1060 fprintf(stderr, "No files to operate on.\n"); 1061 exit(1); 1062 } 1063 arglen = argc - optind; 1064 args = malloc(sizeof(char*) * (arglen+1)); 1065 memset(args, 0, sizeof(char*) * (arglen+1)); 1066 for( i = 0; i < arglen; i++ ) 1067 args[i] = strdup(argv[optind + i]); 1068 1069 return archive(filename, arglen, args); 1070 case 'd': 1071 if( !tocfile ) { 1072 usage(argv[0]); 1073 exit(1); 1074 } 1075 return dumptoc(filename, tocfile); 1076 case 'x': 1077 arglen = argc - optind; 1078 args = malloc(sizeof(char*) * (arglen+1)); 1079 for( i = 0; i < arglen; i++ ) 1080 args[i] = strdup(argv[optind + i]); 1081 args[i] = NULL; 1082 return extract(filename, arglen, args); 1083 case 't': 1084 arglen = argc - optind; 1085 args = calloc(sizeof(char*) * (arglen+1),1); 1086 for( i = 0; i < arglen; i++ ) 1087 args[i] = strdup(argv[optind + i]); 1088 ret = list(filename, arglen, args); 1089 for( i = 0; i < arglen; i++ ) 1090 free(args[i]); 1091 case 6 : 1092 case 's': 1093 x = xar_open(filename, READ); 1094 if( !x ) { 1095 fprintf(stderr, "Error opening xar archive: %s\n", filename); 1096 exit(1); 1097 } 1098 xar_register_errhandler(x, err_callback, NULL); 1099 extract_subdoc(x, SubdocName); 1100 xar_close(x); 1101 exit(Err); 1102 break; 1103 default: 1104 usage(argv[0]); 1105 fprintf(stderr, "Unrecognized command.\n"); 1106 exit(1); 1107 } 1108 1109 /* unreached */ 1110 exit(0); 1111} 1112