untar.c 5.6 KB

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