untar.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * This file is in the public domain. Use it as you see fit.
  3. */
  4. /*
  5. * "untar" is an extremely simple tar extractor:
  6. * * A single C source file, so it should be easy to compile
  7. * and run on any system with a C compiler.
  8. * * Extremely portable standard C. The only non-ANSI function
  9. * used is mkdir().
  10. * * Reads basic ustar tar archives.
  11. * * Does not require libarchive or any other special library.
  12. *
  13. * To compile: cc -o untar untar.c
  14. *
  15. * Usage: untar <archive>
  16. *
  17. * In particular, this program should be sufficient to extract the
  18. * distribution for libarchive, allowing people to bootstrap
  19. * libarchive on systems that do not already have a tar program.
  20. *
  21. * To unpack libarchive-x.y.z.tar.gz:
  22. * * gunzip libarchive-x.y.z.tar.gz
  23. * * untar libarchive-x.y.z.tar
  24. *
  25. * Written by Tim Kientzle, March 2009.
  26. *
  27. * Released into the public domain.
  28. */
  29. /* These are all highly standard and portable headers. */
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. /* This is for mkdir(); this may need to be changed for some platforms. */
  34. #include <sys/stat.h> /* For mkdir() */
  35. #if defined(_WIN32) && !defined(__CYGWIN__)
  36. #include <windows.h>
  37. #endif
  38. /* Parse an octal number, ignoring leading and trailing nonsense. */
  39. static int
  40. parseoct(const char *p, size_t n)
  41. {
  42. int i = 0;
  43. while ((*p < '0' || *p > '7') && n > 0) {
  44. ++p;
  45. --n;
  46. }
  47. while (*p >= '0' && *p <= '7' && n > 0) {
  48. i *= 8;
  49. i += *p - '0';
  50. ++p;
  51. --n;
  52. }
  53. return (i);
  54. }
  55. /* Returns true if this is 512 zero bytes. */
  56. static int
  57. is_end_of_archive(const char *p)
  58. {
  59. int n;
  60. for (n = 511; n >= 0; --n)
  61. if (p[n] != '\0')
  62. return (0);
  63. return (1);
  64. }
  65. /* Create a directory, including parent directories as necessary. */
  66. static void
  67. create_dir(char *pathname, int mode)
  68. {
  69. char *p;
  70. int r;
  71. /* Strip trailing '/' */
  72. if (pathname[strlen(pathname) - 1] == '/')
  73. pathname[strlen(pathname) - 1] = '\0';
  74. /* Try creating the directory. */
  75. #if defined(_WIN32) && !defined(__CYGWIN__)
  76. r = _mkdir(pathname);
  77. #else
  78. r = mkdir(pathname, mode);
  79. #endif
  80. if (r != 0) {
  81. /* On failure, try creating parent directory. */
  82. p = strrchr(pathname, '/');
  83. if (p != NULL) {
  84. *p = '\0';
  85. create_dir(pathname, 0755);
  86. *p = '/';
  87. #if defined(_WIN32) && !defined(__CYGWIN__)
  88. r = _mkdir(pathname);
  89. #else
  90. r = mkdir(pathname, mode);
  91. #endif
  92. }
  93. }
  94. if (r != 0)
  95. fprintf(stderr, "Could not create directory %s\n", pathname);
  96. }
  97. /* Create a file, including parent directory as necessary. */
  98. static FILE *
  99. create_file(char *pathname, int mode)
  100. {
  101. FILE *f;
  102. f = fopen(pathname, "wb+");
  103. if (f == NULL) {
  104. /* Try creating parent dir and then creating file. */
  105. char *p = strrchr(pathname, '/');
  106. if (p != NULL) {
  107. *p = '\0';
  108. create_dir(pathname, 0755);
  109. *p = '/';
  110. f = fopen(pathname, "wb+");
  111. }
  112. }
  113. return (f);
  114. }
  115. /* Verify the tar checksum. */
  116. static int
  117. verify_checksum(const char *p)
  118. {
  119. int n, u = 0;
  120. for (n = 0; n < 512; ++n) {
  121. if (n < 148 || n > 155)
  122. /* Standard tar checksum adds unsigned bytes. */
  123. u += ((unsigned char *)p)[n];
  124. else
  125. u += 0x20;
  126. }
  127. return (u == parseoct(p + 148, 8));
  128. }
  129. /* Extract a tar archive. */
  130. static void
  131. untar(FILE *a, const char *path)
  132. {
  133. char buff[512];
  134. FILE *f = NULL;
  135. size_t bytes_read;
  136. int filesize;
  137. printf("Extracting from %s\n", path);
  138. for (;;) {
  139. bytes_read = fread(buff, 1, 512, a);
  140. if (bytes_read < 512) {
  141. fprintf(stderr,
  142. "Short read on %s: expected 512, got %d\n",
  143. path, (int)bytes_read);
  144. return;
  145. }
  146. if (is_end_of_archive(buff)) {
  147. printf("End of %s\n", path);
  148. return;
  149. }
  150. if (!verify_checksum(buff)) {
  151. fprintf(stderr, "Checksum failure\n");
  152. return;
  153. }
  154. filesize = parseoct(buff + 124, 12);
  155. switch (buff[156]) {
  156. case '1':
  157. printf(" Ignoring hardlink %s\n", buff);
  158. break;
  159. case '2':
  160. printf(" Ignoring symlink %s\n", buff);
  161. break;
  162. case '3':
  163. printf(" Ignoring character device %s\n", buff);
  164. break;
  165. case '4':
  166. printf(" Ignoring block device %s\n", buff);
  167. break;
  168. case '5':
  169. printf(" Extracting dir %s\n", buff);
  170. create_dir(buff, parseoct(buff + 100, 8));
  171. filesize = 0;
  172. break;
  173. case '6':
  174. printf(" Ignoring FIFO %s\n", buff);
  175. break;
  176. default:
  177. printf(" Extracting file %s\n", buff);
  178. f = create_file(buff, parseoct(buff + 100, 8));
  179. break;
  180. }
  181. while (filesize > 0) {
  182. bytes_read = fread(buff, 1, 512, a);
  183. if (bytes_read < 512) {
  184. fprintf(stderr,
  185. "Short read on %s: Expected 512, got %d\n",
  186. path, (int)bytes_read);
  187. return;
  188. }
  189. if (filesize < 512)
  190. bytes_read = filesize;
  191. if (f != NULL) {
  192. if (fwrite(buff, 1, bytes_read, f)
  193. != bytes_read)
  194. {
  195. fprintf(stderr, "Failed write\n");
  196. fclose(f);
  197. f = NULL;
  198. }
  199. }
  200. filesize -= bytes_read;
  201. }
  202. if (f != NULL) {
  203. fclose(f);
  204. f = NULL;
  205. }
  206. }
  207. }
  208. int
  209. main(int argc, char **argv)
  210. {
  211. FILE *a;
  212. ++argv; /* Skip program name */
  213. for ( ;*argv != NULL; ++argv) {
  214. a = fopen(*argv, "rb");
  215. if (a == NULL)
  216. fprintf(stderr, "Unable to open %s\n", *argv);
  217. else {
  218. untar(a, *argv);
  219. fclose(a);
  220. }
  221. }
  222. return (0);
  223. }