untar.m 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. * Modified by xerub, sometime in 2017.
  24. *
  25. * Released into the public domain.
  26. */
  27. /* These are all highly standard and portable headers. */
  28. #include <errno.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <fcntl.h>
  33. #include <unistd.h>
  34. /* This is for mkdir(); this may need to be changed for some platforms. */
  35. #include <sys/stat.h> /* For mkdir() */
  36. #include <Foundation/Foundation.h>
  37. /* Parse an octal number, ignoring leading and trailing nonsense. */
  38. static int
  39. parseoct(const char *p, size_t n)
  40. {
  41. int i = 0;
  42. while (*p < '0' || *p > '7') {
  43. ++p;
  44. --n;
  45. }
  46. while (*p >= '0' && *p <= '7' && n > 0) {
  47. i *= 8;
  48. i += *p - '0';
  49. ++p;
  50. --n;
  51. }
  52. return (i);
  53. }
  54. /* Returns true if this is 512 zero bytes. */
  55. static int
  56. is_end_of_archive(const char *p)
  57. {
  58. int n;
  59. for (n = 511; n >= 0; --n)
  60. if (p[n] != '\0')
  61. return (0);
  62. return (1);
  63. }
  64. /* Create a directory, including parent directories as necessary. */
  65. static void
  66. create_dir(char *pathname, int mode, int owner, int group)
  67. {
  68. char *p;
  69. int r;
  70. struct stat st;
  71. r = stat(pathname, &st);
  72. if (r == 0) {
  73. return;
  74. }
  75. /* Strip trailing '/' */
  76. if (pathname[strlen(pathname) - 1] == '/')
  77. pathname[strlen(pathname) - 1] = '\0';
  78. /* Try creating the directory. */
  79. r = mkdir(pathname, mode);
  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, -1, -1);
  86. *p = '/';
  87. r = mkdir(pathname, mode);
  88. }
  89. }
  90. if (r != 0)
  91. NSLog(@"Could not create directory %s", pathname);
  92. else if (owner >= 0 && group >= 0)
  93. chown(pathname, owner, group);
  94. }
  95. /* Create a file, including parent directory as necessary. */
  96. static int
  97. create_file(char *pathname, int mode, int owner, int group)
  98. {
  99. int f;
  100. if (unlink(pathname) && errno != ENOENT) {
  101. return -1;
  102. }
  103. f = creat(pathname, mode);
  104. if (f < 0) {
  105. /* Try creating parent dir and then creating file. */
  106. char *p = strrchr(pathname, '/');
  107. if (p != NULL) {
  108. *p = '\0';
  109. create_dir(pathname, 0755, -1, -1);
  110. *p = '/';
  111. f = creat(pathname, mode);
  112. }
  113. }
  114. fchown(f, owner, group);
  115. return (f);
  116. }
  117. /* Verify the tar checksum. */
  118. static int
  119. verify_checksum(const char *p)
  120. {
  121. int n, u = 0;
  122. for (n = 0; n < 512; ++n) {
  123. if (n < 148 || n > 155)
  124. /* Standard tar checksum adds unsigned bytes. */
  125. u += ((unsigned char *)p)[n];
  126. else
  127. u += 0x20;
  128. }
  129. return (u == parseoct(p + 148, 8));
  130. }
  131. /* Extract a tar archive. */
  132. int
  133. untar(FILE *a, const char *path)
  134. {
  135. char buff[512];
  136. int f = -1;
  137. size_t bytes_read;
  138. int filesize;
  139. NSLog(@"Extracting from %s", path);
  140. for (;;) {
  141. bytes_read = fread(buff, 1, 512, a);
  142. if (bytes_read < 512) {
  143. NSLog(@"Short read on %s: expected 512, got %d", path, (int)bytes_read);
  144. return -1;
  145. }
  146. if (is_end_of_archive(buff)) {
  147. NSLog(@"End of %s", path);
  148. return 0;
  149. }
  150. if (!verify_checksum(buff)) {
  151. NSLog(@"Checksum failure");
  152. return -2;
  153. }
  154. filesize = parseoct(buff + 124, 12);
  155. switch (buff[156]) {
  156. case '1':
  157. NSLog(@" Ignoring hardlink %s", buff);
  158. break;
  159. case '2':
  160. NSLog(@" Extracting symlink %s -> %s", buff, buff + 157);
  161. if (unlink(buff) && errno != ENOENT) {
  162. break;
  163. }
  164. symlink(buff + 157, buff);
  165. break;
  166. case '3':
  167. NSLog(@" Ignoring character device %s", buff);
  168. break;
  169. case '4':
  170. NSLog(@" Ignoring block device %s", buff);
  171. break;
  172. case '5':
  173. NSLog(@" Extracting dir %s", buff);
  174. create_dir(buff, parseoct(buff + 100, 8), parseoct(buff + 108, 8), parseoct(buff + 116, 8));
  175. filesize = 0;
  176. break;
  177. case '6':
  178. NSLog(@" Ignoring FIFO %s", buff);
  179. break;
  180. default:
  181. NSLog(@" Extracting file %s", buff);
  182. f = create_file(buff, parseoct(buff + 100, 8), parseoct(buff + 108, 8), parseoct(buff + 116, 8));
  183. break;
  184. }
  185. while (filesize > 0) {
  186. bytes_read = fread(buff, 1, 512, a);
  187. if (bytes_read < 512) {
  188. NSLog(@"Short read on %s: Expected 512, got %zd", path, bytes_read);
  189. return -3;
  190. }
  191. if (filesize < 512)
  192. bytes_read = filesize;
  193. if (f >= 0) {
  194. if (write(f, buff, bytes_read)
  195. != bytes_read)
  196. {
  197. NSLog(@"Failed write");
  198. close(f);
  199. f = -1;
  200. }
  201. }
  202. filesize -= bytes_read;
  203. }
  204. if (f >= 0) {
  205. close(f);
  206. f = -1;
  207. }
  208. }
  209. return 0;
  210. }