minitar.c 10.0 KB

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