Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

457 lines
9.3 KiB

  1. //
  2. // Stuff to deal with tar-format files
  3. //
  4. #include <sys/stat.h>
  5. #include <stdlib.h>
  6. #include <tar.h>
  7. #include <stdio.h>
  8. #include <fcntl.h>
  9. #include <dirent.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. #include "buf.h"
  13. #include "psxarc.h"
  14. #include "tarhead.h"
  15. #include "links.h"
  16. static unsigned long round_up(int, int);
  17. static int tarAtoi(char *);
  18. static void tarItoa(long, char *, size_t);
  19. static void tar_dodir(PBUF pb, char *pchfile, struct stat *psb);
  20. static char *modestring(PTAR_HEAD);
  21. extern int fVerbose;
  22. void
  23. TarRead(PBUF pb)
  24. {
  25. char name[155 + 1 + 100 + 1]; // (prefix '/' name '\0')
  26. char linkname[TNAMSIZ + 1];
  27. unsigned long size, i;
  28. int fdout;
  29. PTAR_HEAD buf = (PTAR_HEAD)pb->data;
  30. if (0 != strcmp(buf->s.magic, TMAGIC)) {
  31. fprintf(stderr, "%s: bad magic number\n", progname);
  32. exit(1);
  33. }
  34. name[sizeof(name) - 1] = '\0';
  35. do {
  36. (void)strncpy(name, buf->s.prefix, 155);
  37. // make sure 'name' is null-terminated.
  38. name[155] = '\0';
  39. if ('\0' != name[0]) {
  40. (void)strcat(name, "/");
  41. }
  42. (void)strncat(name, buf->s.name, 100);
  43. if (fVerbose) {
  44. printf("%s\n", name);
  45. }
  46. if (DIRTYPE == buf->s.typeflag) {
  47. if (-1 == mkdir(name, 0777)) {
  48. fprintf(stderr, "%s: mkdir: ", progname);
  49. perror(name);
  50. }
  51. bfill(pb);
  52. continue;
  53. }
  54. if (FIFOTYPE == buf->s.typeflag) {
  55. if (-1 == mkfifo(name, 0666)) {
  56. fprintf(stderr, "%s: mkfifo: ", progname);
  57. perror(name);
  58. }
  59. bfill(pb);
  60. continue;
  61. }
  62. if (LNKTYPE == buf->s.typeflag) {
  63. strncpy(linkname, buf->s.linkname, sizeof(linkname));
  64. if (-1 == link(linkname, name)) {
  65. fprintf(stderr, "%s: link %s, %s: ",
  66. progname, linkname, name);
  67. perror("");
  68. continue;
  69. }
  70. }
  71. // regular file
  72. if (-1 == (fdout = open(name, O_WRONLY | O_CREAT, 0666))) {
  73. fprintf(stderr, "%s: open: ", progname);
  74. perror(name);
  75. continue;
  76. }
  77. size = tarAtoi(buf->s.size);
  78. if (size > 0) {
  79. bfill(pb);
  80. for (i = 0; i < size; ++i) {
  81. int c;
  82. c = bgetc(pb);
  83. write(fdout, &c, 1);
  84. }
  85. }
  86. (void)close(fdout);
  87. bfill(pb);
  88. } while (0 != buf->s.magic[0]);
  89. }
  90. //
  91. // TarWrite -- calls tar_dodir for directories, which calls back here.
  92. //
  93. void
  94. TarWrite(PBUF pb, char **ppchFiles, int count)
  95. {
  96. PTAR_HEAD pt;
  97. auto struct stat statbuf;
  98. int fdin;
  99. int i;
  100. unsigned chksum;
  101. // We reach into the buffer routines until it's time to write.
  102. // Brutal but effective.
  103. pt = (PTAR_HEAD)pb->data;
  104. while (count > 0) {
  105. int len;
  106. memset(pt, 0, sizeof(*pt));
  107. strcpy(pt->s.magic, TMAGIC);
  108. strncpy(pt->s.version, TVERSION, TVERSLEN);
  109. if (fVerbose) {
  110. fprintf(stderr, "%s\n", *ppchFiles);
  111. }
  112. if (-1 == stat(*ppchFiles, &statbuf)) {
  113. fprintf(stderr, "%s: stat: ");
  114. perror(*ppchFiles);
  115. continue;
  116. }
  117. len = strlen(*ppchFiles);
  118. if (len > TNAMSIZ + 155 + 1) {
  119. // the filename just won't fit. Do something
  120. // reasonable.
  121. } else if (len <= TNAMSIZ) {
  122. strncpy(pt->s.name, *ppchFiles, TNAMSIZ);
  123. } else {
  124. char *pch;
  125. // We try to put as much of the filename as will fit
  126. // into the 'name' portion, and the rest goes in
  127. // the prefix. To do this, we start 101 characters
  128. // from the end; if that character is a slash, we
  129. // split the string there. If it's not, we split the
  130. // string at the next slash to the right.
  131. pch = *ppchFiles + (len - TNAMSIZ - 1);
  132. if ('/' != *pch) {
  133. pch = strchr(pch, '/');
  134. if (NULL == pch) {
  135. // XXX.mjb: This filename has a trailing
  136. // component more than 100 chars
  137. // long. Do something reasonable.
  138. --count;
  139. ++ppchFiles;
  140. continue;
  141. }
  142. }
  143. *pch = '\0';
  144. strncpy(pt->s.name, pch + 1, TNAMSIZ);
  145. strncpy(pt->s.prefix, *ppchFiles, 155);
  146. }
  147. //
  148. // XXX.mjb: this assumes tar mode bits are the same as
  149. // the POSIX implementation's mode bits. Should really
  150. // call a function to convert between.
  151. //
  152. tarItoa(statbuf.st_mode & 0777, pt->s.mode,
  153. sizeof(pt->s.mode));
  154. #if 0
  155. tarItoa(statbuf.st_uid, pt->s.uid, sizeof(pt->s.uid));
  156. tarItoa(statbuf.st_gid, pt->s.gid, sizeof(pt->s.gid));
  157. tarItoa(statbuf.st_mtime, pt->s.mtime, sizeof(pt->s.mtime));
  158. #endif
  159. if (S_ISDIR(statbuf.st_mode)) {
  160. pt->s.typeflag = DIRTYPE;
  161. memset(pt->s.size, '0', sizeof(pt->s.size));
  162. // put the directory entry on the tape
  163. bflush(pb);
  164. // put the directory contents on tape
  165. tar_dodir(pb, *ppchFiles, &statbuf);
  166. ++ppchFiles;
  167. --count;
  168. continue;
  169. }
  170. if (S_ISFIFO(statbuf.st_mode)) {
  171. pt->s.typeflag = FIFOTYPE;
  172. memset(pt->s.size, '0', sizeof(pt->s.size));
  173. bflush(pb);
  174. ++ppchFiles;
  175. --count;
  176. continue;
  177. }
  178. if (statbuf.st_nlink > 1) {
  179. PLINKFILE p;
  180. if (NULL != (p = GetLinkByIno(statbuf.st_ino))) {
  181. pt->s.typeflag = LNKTYPE;
  182. memset(pt->s.size, '0', sizeof(pt->s.size));
  183. strncpy(pt->s.linkname, p->name, TNAMSIZ);
  184. bflush(pb);
  185. ++ppchFiles;
  186. --count;
  187. continue;
  188. }
  189. AddLinkList(&statbuf, *ppchFiles);
  190. }
  191. pt->s.typeflag = REGTYPE;
  192. tarItoa((int)statbuf.st_size, pt->s.size, sizeof(pt->s.size));
  193. //
  194. // compute the checksum for the header
  195. //
  196. memset(pt->s.chksum, ' ', sizeof(pt->s.chksum));
  197. for (i = 0, chksum = 0; i < sizeof(pt->buf); ++i) {
  198. chksum += pt->buf[i];
  199. }
  200. tarItoa(chksum, pt->s.chksum, sizeof(pt->s.chksum));
  201. fdin = open(*ppchFiles, O_RDONLY, 0);
  202. if (-1 == fdin) {
  203. fprintf(stderr, "%s: open: ");
  204. perror(*ppchFiles);
  205. continue;
  206. }
  207. // write the header
  208. bflush(pb);
  209. if (0 == statbuf.st_size) {
  210. //
  211. // special case: don't write any data blocks.
  212. //
  213. close(fdin);
  214. ++ppchFiles;
  215. --count;
  216. continue;
  217. }
  218. // copy the file data
  219. // XXX.mjb: what should happen here if we find that we can't
  220. // read the number of bytes we thought we'd be able to
  221. // read? (The file could change size, or some kind
  222. // of error could occur.) We can't leave the data too
  223. // small, or we'll hose the rest of the tar file. So
  224. // we write extra blocks of zeroes. What if the file
  225. // turns out to be longer than expected? Print a
  226. // warning and continue?
  227. memset(pb->data, 0, sizeof(pb->data));
  228. while (statbuf.st_size > 0) {
  229. int nbytes;
  230. char c;
  231. nbytes = read(fdin, &c, 1);
  232. if (-1 == nbytes) {
  233. // error occurs before we have all the data.
  234. }
  235. bputc(pb, c);
  236. --statbuf.st_size;
  237. }
  238. bflush(pb);
  239. close(fdin);
  240. ++ppchFiles;
  241. --count;
  242. }
  243. }
  244. void
  245. TarList(PBUF pb)
  246. {
  247. PTAR_HEAD pt;
  248. char name[155 + 1 + 100 + 1]; // "prefix / name \0"
  249. unsigned long size, i;
  250. char *pch;
  251. pt = (PTAR_HEAD)pb->data;
  252. do {
  253. (void)strncpy(name, pt->s.prefix, 155);
  254. name[155] = '\0';
  255. if ('\0' != name[0]) {
  256. (void)strcat(name, "/");
  257. }
  258. (void)strncat(name, pt->s.name, 100);
  259. size = tarAtoi(pt->s.size);
  260. if (!fVerbose) {
  261. printf("%s\n", name);
  262. } else {
  263. pch = modestring(pt);
  264. printf("%s %6ld %s\n", pch, size, name);
  265. }
  266. size = round_up(size, 512);
  267. for (i = 0; i < size; ++i) {
  268. bfill(pb);
  269. }
  270. bfill(pb);
  271. } while (0 != pt->s.magic[0]);
  272. }
  273. //
  274. // tarAtof -- translate tar-style octal strings to decimal
  275. //
  276. static int
  277. tarAtoi(char *pch)
  278. {
  279. int num = 0;
  280. while ('\0' != *pch && ' ' != *pch) {
  281. num = num * 8 + (*pch - '0');
  282. ++pch;
  283. }
  284. return num;
  285. }
  286. //
  287. // tarItoa -- for writing numeric fields in tar headers.
  288. void
  289. tarItoa(long i, char *pch, size_t len)
  290. {
  291. // XXX.mjb: should check width < len
  292. sprintf(pch, "%-o", i);
  293. }
  294. //
  295. // round_up -- round num up to the nearest multiple of m.
  296. //
  297. unsigned long
  298. round_up(int num, int m)
  299. {
  300. return (num + (m - 1))/m;
  301. }
  302. static void
  303. tar_dodir(
  304. PBUF pb, // the tar image file, being written
  305. char *pchfile, // the directory file to be added to the archv
  306. struct stat *psb // result of stat on the directory file.
  307. )
  308. {
  309. DIR *dp;
  310. struct dirent *dirent;
  311. char *pch;
  312. dp = opendir(pchfile);
  313. if (NULL == dp) {
  314. fprintf(stderr, "%s: opendir: ", progname);
  315. perror(pchfile);
  316. return;
  317. }
  318. while (NULL != (dirent = readdir(dp))) {
  319. if ('.' == dirent->d_name[0] &&
  320. ('\0' == dirent->d_name[1] ||
  321. 0 == strcmp(dirent->d_name, ".."))) {
  322. continue;
  323. }
  324. //
  325. // Recurse. We append the file name read from the directory
  326. // to the directory name we were given and call TarWrite to
  327. // put it on the tape. It could be a directory, so we could
  328. // end up back here. This means that we must allocate the
  329. // space for the pathname dynamically. This seems like it
  330. // will be a big time-waster.
  331. //
  332. // strlen + 2: one extra for '/', one extra for null.
  333. pch = malloc(strlen(pchfile) + strlen(dirent->d_name) + 2);
  334. if (NULL == pch) {
  335. fprintf(stderr, "%s: virtual memory exhausted\n",
  336. progname);
  337. exit(4);
  338. }
  339. strcpy(pch, pchfile);
  340. strcat(pch, "/");
  341. strcat(pch, dirent->d_name);
  342. TarWrite(pb, &pch, 1);
  343. free(pch);
  344. }
  345. (void)closedir(dp);
  346. }
  347. static char *
  348. modestring(PTAR_HEAD pt)
  349. {
  350. static char sb[11];
  351. unsigned long mask, mode;
  352. char *rwx = "rwxrwxrwx";
  353. char *string;
  354. sb[11] = '\0';
  355. string = sb;
  356. switch (pt->s.typeflag) {
  357. case LNKTYPE:
  358. case REGTYPE:
  359. string[0] = '-';
  360. break;
  361. case DIRTYPE:
  362. string[0] = 'd';
  363. break;
  364. case FIFOTYPE:
  365. string[0] = 'f';
  366. break;
  367. case SYMTYPE:
  368. string[0] = 'l';
  369. break;
  370. case BLKTYPE:
  371. string[0] = 'b';
  372. break;
  373. case CHRTYPE:
  374. string[0] = 'c';
  375. break;
  376. case CONTTYPE:
  377. string[0] = '=';
  378. break;
  379. default:
  380. fprintf(stderr, "modestring shouldn't get here\n");
  381. }
  382. string++;
  383. mode = tarAtoi(pt->s.mode);
  384. for (mask = 0400; mask != 0; mask >>= 1) {
  385. if (mode & mask) {
  386. *string++ = *rwx++;
  387. } else {
  388. *string++ = '-';
  389. rwx++;
  390. }
  391. }
  392. return sb;
  393. }