tarfn.c 7.1 KB

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