1/*- 2 * This file is in the public domain. 3 * Do with it as you will. 4 */ 5 6/*- 7 * This is a compact "tar" program whose primary goal is small size. 8 * Statically linked, it can be very small indeed. This serves a number 9 * of goals: 10 * o a testbed for libarchive (to check for link pollution), 11 * o a useful tool for space-constrained systems (boot floppies, etc), 12 * o a place to experiment with new implementation ideas for bsdtar, 13 * o a small program to demonstrate libarchive usage. 14 * 15 * Use the following macros to suppress features: 16 * NO_BZIP2 - Implies NO_BZIP2_CREATE and NO_BZIP2_EXTRACT 17 * NO_BZIP2_CREATE - Suppress bzip2 compression support. 18 * NO_BZIP2_EXTRACT - Suppress bzip2 auto-detection and decompression. 19 * NO_COMPRESS - Implies NO_COMPRESS_CREATE and NO_COMPRESS_EXTRACT 20 * NO_COMPRESS_CREATE - Suppress compress(1) compression support 21 * NO_COMPRESS_EXTRACT - Suppress compress(1) auto-detect and decompression. 22 * NO_CREATE - Suppress all archive creation support. 23 * NO_CPIO_EXTRACT - Suppress auto-detect and dearchiving of cpio archives. 24 * NO_GZIP - Implies NO_GZIP_CREATE and NO_GZIP_EXTRACT 25 * NO_GZIP_CREATE - Suppress gzip compression support. 26 * NO_GZIP_EXTRACT - Suppress gzip auto-detection and decompression. 27 * NO_LOOKUP - Try to avoid getpw/getgr routines, which can be very large 28 * NO_TAR_EXTRACT - Suppress tar extraction 29 * 30 * With all of the above macros defined (except NO_TAR_EXTRACT), you 31 * get a very small program that can recognize and extract essentially 32 * any uncompressed tar archive. On FreeBSD 5.1, this minimal program 33 * is under 64k, statically linked, which compares rather favorably to 34 * main(){printf("hello, world");} 35 * which is over 60k statically linked on the same operating system. 36 * Without any of the above macros, you get a static executable of 37 * about 180k with a lot of very sophisticated modern features. 38 * Obviously, it's trivial to add support for ISO, Zip, mtree, 39 * lzma/xz, etc. Just fill in the appropriate setup calls. 40 */ 41 42#include <sys/types.h> 43__FBSDID("$FreeBSD$"); 44 45#include <sys/stat.h> 46 47#include <archive.h> 48#include <archive_entry.h> 49#include <fcntl.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <string.h> 53#include <unistd.h> 54 55#ifndef NO_CREATE 56#include "tree.h" 57#endif 58 59/* 60 * NO_CREATE implies NO_BZIP2_CREATE and NO_GZIP_CREATE and NO_COMPRESS_CREATE. 61 */ 62#ifdef NO_CREATE 63#undef NO_BZIP2_CREATE 64#define NO_BZIP2_CREATE 65#undef NO_COMPRESS_CREATE 66#define NO_COMPRESS_CREATE 67#undef NO_GZIP_CREATE 68#define NO_GZIP_CREATE 69#endif 70 71/* 72 * The combination of NO_BZIP2_CREATE and NO_BZIP2_EXTRACT is 73 * equivalent to NO_BZIP2. 74 */ 75#ifdef NO_BZIP2_CREATE 76#ifdef NO_BZIP2_EXTRACT 77#undef NO_BZIP2 78#define NO_BZIP2 79#endif 80#endif 81 82#ifdef NO_BZIP2 83#undef NO_BZIP2_EXTRACT 84#define NO_BZIP2_EXTRACT 85#undef NO_BZIP2_CREATE 86#define NO_BZIP2_CREATE 87#endif 88 89/* 90 * The combination of NO_COMPRESS_CREATE and NO_COMPRESS_EXTRACT is 91 * equivalent to NO_COMPRESS. 92 */ 93#ifdef NO_COMPRESS_CREATE 94#ifdef NO_COMPRESS_EXTRACT 95#undef NO_COMPRESS 96#define NO_COMPRESS 97#endif 98#endif 99 100#ifdef NO_COMPRESS 101#undef NO_COMPRESS_EXTRACT 102#define NO_COMPRESS_EXTRACT 103#undef NO_COMPRESS_CREATE 104#define NO_COMPRESS_CREATE 105#endif 106 107/* 108 * The combination of NO_GZIP_CREATE and NO_GZIP_EXTRACT is 109 * equivalent to NO_GZIP. 110 */ 111#ifdef NO_GZIP_CREATE 112#ifdef NO_GZIP_EXTRACT 113#undef NO_GZIP 114#define NO_GZIP 115#endif 116#endif 117 118#ifdef NO_GZIP 119#undef NO_GZIP_EXTRACT 120#define NO_GZIP_EXTRACT 121#undef NO_GZIP_CREATE 122#define NO_GZIP_CREATE 123#endif 124 125#ifndef NO_CREATE 126static void create(const char *filename, int compress, const char **argv); 127#endif 128static void errmsg(const char *); 129static void extract(const char *filename, int do_extract, int flags); 130static int copy_data(struct archive *, struct archive *); 131static void msg(const char *); 132static void usage(void); 133 134static int verbose = 0; 135 136int 137main(int argc, const char **argv) 138{ 139 const char *filename = NULL; 140 int compress, flags, mode, opt; 141 142 (void)argc; 143 mode = 'x'; 144 verbose = 0; 145 compress = '\0'; 146 flags = ARCHIVE_EXTRACT_TIME; 147 148 /* Among other sins, getopt(3) pulls in printf(3). */ 149 while (*++argv != NULL && **argv == '-') { 150 const char *p = *argv + 1; 151 152 while ((opt = *p++) != '\0') { 153 switch (opt) { 154#ifndef NO_CREATE 155 case 'c': 156 mode = opt; 157 break; 158#endif 159 case 'f': 160 if (*p != '\0') 161 filename = p; 162 else 163 filename = *++argv; 164 p += strlen(p); 165 break; 166#ifndef NO_BZIP2_CREATE 167 case 'j': 168 compress = opt; 169 break; 170#endif 171 case 'p': 172 flags |= ARCHIVE_EXTRACT_PERM; 173 flags |= ARCHIVE_EXTRACT_ACL; 174 flags |= ARCHIVE_EXTRACT_FFLAGS; 175 break; 176 case 't': 177 mode = opt; 178 break; 179 case 'v': 180 verbose++; 181 break; 182 case 'x': 183 mode = opt; 184 break; 185#ifndef NO_BZIP2_CREATE 186 case 'y': 187 compress = opt; 188 break; 189#endif 190#ifndef NO_COMPRESS_CREATE 191 case 'Z': 192 compress = opt; 193 break; 194#endif 195#ifndef NO_GZIP_CREATE 196 case 'z': 197 compress = opt; 198 break; 199#endif 200 default: 201 usage(); 202 } 203 } 204 } 205 206 switch (mode) { 207#ifndef NO_CREATE 208 case 'c': 209 create(filename, compress, argv); 210 break; 211#endif 212 case 't': 213 extract(filename, 0, flags); 214 break; 215 case 'x': 216 extract(filename, 1, flags); 217 break; 218 } 219 220 return (0); 221} 222 223 224#ifndef NO_CREATE 225static char buff[16384]; 226 227static void 228create(const char *filename, int compress, const char **argv) 229{ 230 struct archive *a; 231 struct archive *disk; 232 struct archive_entry *entry; 233 ssize_t len; 234 int fd; 235 236 a = archive_write_new(); 237 switch (compress) { 238#ifndef NO_BZIP2_CREATE 239 case 'j': case 'y': 240 archive_write_set_compression_bzip2(a); 241 break; 242#endif 243#ifndef NO_COMPRESS_CREATE 244 case 'Z': 245 archive_write_set_compression_compress(a); 246 break; 247#endif 248#ifndef NO_GZIP_CREATE 249 case 'z': 250 archive_write_set_compression_gzip(a); 251 break; 252#endif 253 default: 254 archive_write_set_compression_none(a); 255 break; 256 } 257 archive_write_set_format_ustar(a); 258 if (strcmp(filename, "-") == 0) 259 filename = NULL; 260 archive_write_open_file(a, filename); 261 262 disk = archive_read_disk_new(); 263#ifndef NO_LOOKUP 264 archive_read_disk_set_standard_lookup(disk); 265#endif 266 while (*argv != NULL) { 267 struct tree *t = tree_open(*argv); 268 while (tree_next(t)) { 269 entry = archive_entry_new(); 270 archive_entry_set_pathname(entry, tree_current_path(t)); 271 archive_read_disk_entry_from_file(disk, entry, -1, 272 tree_current_stat(t)); 273 if (verbose) { 274 msg("a "); 275 msg(tree_current_path(t)); 276 } 277 archive_write_header(a, entry); 278 fd = open(tree_current_access_path(t), O_RDONLY); 279 len = read(fd, buff, sizeof(buff)); 280 while (len > 0) { 281 archive_write_data(a, buff, len); 282 len = read(fd, buff, sizeof(buff)); 283 } 284 close(fd); 285 archive_entry_free(entry); 286 if (verbose) 287 msg("\n"); 288 } 289 argv++; 290 } 291 archive_write_close(a); 292 archive_write_finish(a); 293} 294#endif 295 296static void 297extract(const char *filename, int do_extract, int flags) 298{ 299 struct archive *a; 300 struct archive *ext; 301 struct archive_entry *entry; 302 int r; 303 304 a = archive_read_new(); 305 ext = archive_write_disk_new(); 306 archive_write_disk_set_options(ext, flags); 307#ifndef NO_BZIP2_EXTRACT 308 archive_read_support_compression_bzip2(a); 309#endif 310#ifndef NO_GZIP_EXTRACT 311 archive_read_support_compression_gzip(a); 312#endif 313#ifndef NO_COMPRESS_EXTRACT 314 archive_read_support_compression_compress(a); 315#endif 316#ifndef NO_TAR_EXTRACT 317 archive_read_support_format_tar(a); 318#endif 319#ifndef NO_CPIO_EXTRACT 320 archive_read_support_format_cpio(a); 321#endif 322#ifndef NO_LOOKUP 323 archive_write_disk_set_standard_lookup(ext); 324#endif 325 if (filename != NULL && strcmp(filename, "-") == 0) 326 filename = NULL; 327 if ((r = archive_read_open_file(a, filename, 10240))) { 328 errmsg(archive_error_string(a)); 329 errmsg("\n"); 330 exit(r); 331 } 332 for (;;) { 333 r = archive_read_next_header(a, &entry); 334 if (r == ARCHIVE_EOF) 335 break; 336 if (r != ARCHIVE_OK) { 337 errmsg(archive_error_string(a)); 338 errmsg("\n"); 339 exit(1); 340 } 341 if (verbose && do_extract) 342 msg("x "); 343 if (verbose || !do_extract) 344 msg(archive_entry_pathname(entry)); 345 if (do_extract) { 346 r = archive_write_header(ext, entry); 347 if (r != ARCHIVE_OK) 348 errmsg(archive_error_string(a)); 349 else 350 copy_data(a, ext); 351 } 352 if (verbose || !do_extract) 353 msg("\n"); 354 } 355 archive_read_close(a); 356 archive_read_finish(a); 357 exit(0); 358} 359 360static int 361copy_data(struct archive *ar, struct archive *aw) 362{ 363 int r; 364 const void *buff; 365 size_t size; 366 off_t offset; 367 368 for (;;) { 369 r = archive_read_data_block(ar, &buff, &size, &offset); 370 if (r == ARCHIVE_EOF) { 371 errmsg(archive_error_string(ar)); 372 return (ARCHIVE_OK); 373 } 374 if (r != ARCHIVE_OK) 375 return (r); 376 r = archive_write_data_block(aw, buff, size, offset); 377 if (r != ARCHIVE_OK) { 378 errmsg(archive_error_string(ar)); 379 return (r); 380 } 381 } 382} 383 384static void 385msg(const char *m) 386{ 387 write(1, m, strlen(m)); 388} 389 390static void 391errmsg(const char *m) 392{ 393 write(2, m, strlen(m)); 394} 395 396static void 397usage(void) 398{ 399/* Many program options depend on compile options. */ 400 const char *m = "Usage: minitar [-" 401#ifndef NO_CREATE 402 "c" 403#endif 404#ifndef NO_BZIP2 405 "j" 406#endif 407 "tvx" 408#ifndef NO_BZIP2 409 "y" 410#endif 411#ifndef NO_COMPRESS 412 "Z" 413#endif 414#ifndef NO_GZIP 415 "z" 416#endif 417 "] [-f file] [file]\n"; 418 419 errmsg(m); 420 exit(1); 421} 422