tarfn.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * Functions for extracting tar archives.
  3. * Bruce Perens, April-May 1995
  4. * Copyright (C) 1995 Bruce Perens
  5. * This is free software under the GNU General Public License.
  6. */
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <stdio.h>
  10. #include <unistd.h>
  11. #include <pwd.h>
  12. #include <grp.h>
  13. #include <errno.h>
  14. #include <tarfn.h>
  15. struct TarHeader {
  16. char Name[100];
  17. char Mode[8];
  18. char UserID[8];
  19. char GroupID[8];
  20. char Size[12];
  21. char ModificationTime[12];
  22. char Checksum[8];
  23. char LinkFlag;
  24. char LinkName[100];
  25. char MagicNumber[8];
  26. char UserName[32];
  27. char GroupName[32];
  28. char MajorDevice[8];
  29. char MinorDevice[8];
  30. };
  31. typedef struct TarHeader TarHeader;
  32. static const unsigned int TarChecksumOffset
  33. = (unsigned int)&(((TarHeader *)NULL)->Checksum);
  34. /* Octal-ASCII-to-long */
  35. static long
  36. OtoL(const char * s, int size)
  37. {
  38. int n = 0;
  39. while ( *s == ' ' ) {
  40. s++;
  41. size--;
  42. }
  43. while ( --size >= 0 && *s >= '0' && *s <= '7' )
  44. n = (n * 010) + (*s++ - '0');
  45. return n;
  46. }
  47. /* String block to C null-terminated string */
  48. char *
  49. StoC(const char *s, int size)
  50. {
  51. int len;
  52. char * str;
  53. len = strnlen(s, size);
  54. str = malloc(len + 1);
  55. memcpy(str, s, len);
  56. str[len] = 0;
  57. return str;
  58. }
  59. static int
  60. DecodeTarHeader(char * block, TarInfo * d)
  61. {
  62. TarHeader * h = (TarHeader *)block;
  63. unsigned char * s = (unsigned char *)block;
  64. struct passwd * passwd = NULL;
  65. struct group * group = NULL;
  66. unsigned int i;
  67. long sum;
  68. long checksum;
  69. if ( *h->UserName )
  70. passwd = getpwnam(h->UserName);
  71. if ( *h->GroupName )
  72. group = getgrnam(h->GroupName);
  73. d->Name = StoC(h->Name, sizeof(h->Name));
  74. d->LinkName = StoC(h->LinkName, sizeof(h->LinkName));
  75. d->Mode = (mode_t)OtoL(h->Mode, sizeof(h->Mode));
  76. d->Size = (size_t)OtoL(h->Size, sizeof(h->Size));
  77. d->ModTime = (time_t)OtoL(h->ModificationTime
  78. ,sizeof(h->ModificationTime));
  79. d->Device = ((OtoL(h->MajorDevice, sizeof(h->MajorDevice)) & 0xff) << 8)
  80. | (OtoL(h->MinorDevice, sizeof(h->MinorDevice)) & 0xff);
  81. checksum = OtoL(h->Checksum, sizeof(h->Checksum));
  82. d->UserID = (uid_t)OtoL(h->UserID, sizeof(h->UserID));
  83. d->GroupID = (gid_t)OtoL(h->GroupID, sizeof(h->GroupID));
  84. d->Type = (TarFileType)h->LinkFlag;
  85. if ( passwd )
  86. d->UserID = passwd->pw_uid;
  87. if ( group )
  88. d->GroupID = group->gr_gid;
  89. sum = ' ' * sizeof(h->Checksum);/* Treat checksum field as all blank */
  90. for ( i = TarChecksumOffset; i > 0; i-- )
  91. sum += *s++;
  92. s += sizeof(h->Checksum); /* Skip the real checksum field */
  93. for ( i = (512 - TarChecksumOffset - sizeof(h->Checksum)); i > 0; i-- )
  94. sum += *s++;
  95. return ( sum == checksum );
  96. }
  97. typedef struct symlinkList {
  98. TarInfo h;
  99. struct symlinkList *next;
  100. } symlinkList;
  101. extern int
  102. TarExtractor(
  103. void * userData
  104. ,const TarFunctions * functions)
  105. {
  106. int status;
  107. char buffer[512];
  108. TarInfo h;
  109. char *next_long_name, *next_long_link;
  110. char *bp;
  111. char **longp;
  112. int long_read;
  113. symlinkList *symListTop, *symListBottom, *symListPointer;
  114. next_long_name = NULL;
  115. next_long_link = NULL;
  116. long_read = 0;
  117. symListBottom = symListPointer = symListTop = malloc(sizeof(symlinkList));
  118. symListTop->next = NULL;
  119. h.UserData = userData;
  120. while ( (status = functions->Read(userData, buffer, 512)) == 512 ) {
  121. int nameLength;
  122. if ( !DecodeTarHeader(buffer, &h) ) {
  123. if ( h.Name[0] == '\0' ) {
  124. status = 0; /* End of tape */
  125. } else {
  126. errno = 0; /* Indicates broken tarfile */
  127. status = -1; /* Header checksum error */
  128. }
  129. break;
  130. }
  131. if (next_long_name) {
  132. h.Name = next_long_name;
  133. }
  134. if (next_long_link) {
  135. h.LinkName = next_long_link;
  136. }
  137. next_long_name = NULL;
  138. next_long_link = NULL;
  139. if ( h.Name[0] == '\0' ) {
  140. errno = 0; /* Indicates broken tarfile */
  141. status = -1; /* Bad header data */
  142. break;
  143. }
  144. nameLength = strlen(h.Name);
  145. switch ( h.Type ) {
  146. case NormalFile0:
  147. case NormalFile1:
  148. /* Compatibility with pre-ANSI ustar */
  149. if ( h.Name[nameLength - 1] != '/' ) {
  150. status = (*functions->ExtractFile)(&h);
  151. break;
  152. }
  153. /* Else, Fall Through */
  154. case Directory:
  155. h.Name[nameLength - 1] = '\0';
  156. status = (*functions->MakeDirectory)(&h);
  157. break;
  158. case HardLink:
  159. status = (*functions->MakeHardLink)(&h);
  160. break;
  161. case SymbolicLink:
  162. memcpy(&symListBottom->h, &h, sizeof(TarInfo));
  163. if ((symListBottom->h.Name = strdup(h.Name)) == NULL) {
  164. status = -1;
  165. errno = 0;
  166. break;
  167. }
  168. if ((symListBottom->h.LinkName = strdup(h.LinkName)) == NULL) {
  169. free(symListBottom->h.Name);
  170. status = -1;
  171. errno = 0;
  172. break;
  173. }
  174. if ((symListBottom->next = malloc(sizeof(symlinkList))) == NULL) {
  175. free(symListBottom->h.LinkName);
  176. free(symListBottom->h.Name);
  177. status = -1;
  178. errno = 0;
  179. break;
  180. }
  181. symListBottom = symListBottom->next;
  182. symListBottom->next = NULL;
  183. status = 0;
  184. break;
  185. case CharacterDevice:
  186. case BlockDevice:
  187. case FIFO:
  188. status = (*functions->MakeSpecialFile)(&h);
  189. break;
  190. case GNU_LONGLINK:
  191. case GNU_LONGNAME:
  192. // set longp to the location of the long filename or link
  193. // we're trying to deal with
  194. longp = ((h.Type == GNU_LONGNAME)
  195. ? &next_long_name
  196. : &next_long_link);
  197. if (*longp)
  198. free(*longp);
  199. if (NULL == (*longp = (char *)malloc(h.Size))) {
  200. /* malloc failed, so bail */
  201. errno = 0;
  202. status = -1;
  203. break;
  204. }
  205. bp = *longp;
  206. // the way the GNU long{link,name} stuff works is like this:
  207. // The first header is a "dummy" header that contains the size
  208. // of the filename. The next N headers contain the filename.
  209. // After the headers with the filename comes the "real" header
  210. // with a bogus name or link.
  211. for (long_read = h.Size; long_read > 0;
  212. long_read -= 512) {
  213. int copysize;
  214. status = functions->Read(userData, buffer, 512);
  215. // if we didn't get 512 bytes read, punt
  216. if (512 != status) {
  217. if ( status > 0 ) { /* Read partial header record */
  218. errno = 0;
  219. status = -1;
  220. }
  221. break;
  222. }
  223. copysize = long_read > 512 ? 512 : long_read;
  224. memcpy (bp, buffer, copysize);
  225. bp += copysize;
  226. };
  227. // This decode function expects status to be 0 after
  228. // the case statement if we successfully decoded. I
  229. // guess what we just did was successful.
  230. status = 0;
  231. break;
  232. default:
  233. errno = 0; /* Indicates broken tarfile */
  234. status = -1; /* Bad header field */
  235. }
  236. if ( status != 0 )
  237. break; /* Pass on status from coroutine */
  238. }
  239. while(symListPointer->next) {
  240. if ( status == 0 )
  241. status = (*functions->MakeSymbolicLink)(&symListPointer->h);
  242. symListBottom = symListPointer->next;
  243. free(symListPointer->h.Name);
  244. free(symListPointer->h.LinkName);
  245. free(symListPointer);
  246. symListPointer = symListBottom;
  247. }
  248. free(symListPointer);
  249. free(h.Name);
  250. free(h.LinkName);
  251. if ( status > 0 ) { /* Read partial header record */
  252. errno = 0; /* Indicates broken tarfile */
  253. return -1;
  254. } else {
  255. return status; /* Whatever I/O function returned */
  256. }
  257. }