1
0

extract.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. /*
  2. ** Copyright 1998-2003 University of Illinois Board of Trustees
  3. ** Copyright 1998-2003 Mark D. Roth
  4. ** All rights reserved.
  5. **
  6. ** extract.c - libtar code to extract a file from a tar archive
  7. **
  8. ** Mark D. Roth <[email protected]>
  9. ** Campus Information Technologies and Educational Services
  10. ** University of Illinois at Urbana-Champaign
  11. */
  12. #include <libtarint/internal.h>
  13. #include <stdio.h>
  14. #include <libtar/compat.h>
  15. #include <sys/types.h>
  16. #include <fcntl.h>
  17. #include <errno.h>
  18. #if defined(_WIN32) && !defined(__CYGWIN__)
  19. # ifdef _MSC_VER
  20. # include <sys/utime.h>
  21. # else
  22. # include <utime.h>
  23. # endif
  24. # include <io.h>
  25. # include <direct.h>
  26. #else
  27. # include <utime.h>
  28. # include <sys/param.h>
  29. #endif
  30. #ifdef STDC_HEADERS
  31. # include <stdlib.h>
  32. # include <string.h>
  33. #endif
  34. #ifdef HAVE_UNISTD_H
  35. # include <unistd.h>
  36. #endif
  37. #ifdef HAVE_SYS_MKDEV_H
  38. # include <sys/mkdev.h>
  39. #endif
  40. struct linkname
  41. {
  42. char ln_save[TAR_MAXPATHLEN];
  43. char ln_real[TAR_MAXPATHLEN];
  44. };
  45. typedef struct linkname linkname_t;
  46. static int
  47. tar_set_file_perms(TAR *t, char *realname)
  48. {
  49. mode_t mode;
  50. uid_t uid;
  51. gid_t gid;
  52. struct utimbuf ut;
  53. char *filename;
  54. filename = (realname ? realname : th_get_pathname(t));
  55. mode = th_get_mode(t);
  56. uid = th_get_uid(t);
  57. gid = th_get_gid(t);
  58. ut.modtime = ut.actime = th_get_mtime(t);
  59. /* change owner/group */
  60. #ifndef WIN32
  61. if (geteuid() == 0)
  62. #ifdef HAVE_LCHOWN
  63. if (lchown(filename, uid, gid) == -1)
  64. {
  65. # ifdef DEBUG
  66. fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
  67. filename, uid, gid, strerror(errno));
  68. # endif
  69. #else /* ! HAVE_LCHOWN */
  70. if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
  71. {
  72. # ifdef DEBUG
  73. fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
  74. filename, uid, gid, strerror(errno));
  75. # endif
  76. #endif /* HAVE_LCHOWN */
  77. return -1;
  78. }
  79. /* change access/modification time */
  80. if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
  81. {
  82. #ifdef DEBUG
  83. perror("utime()");
  84. #endif
  85. return -1;
  86. }
  87. /* change permissions */
  88. if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
  89. {
  90. #ifdef DEBUG
  91. perror("chmod()");
  92. #endif
  93. return -1;
  94. }
  95. #else /* WIN32 */
  96. (void)filename;
  97. (void)gid;
  98. (void)uid;
  99. (void)mode;
  100. #endif /* WIN32 */
  101. return 0;
  102. }
  103. /* switchboard */
  104. int
  105. tar_extract_file(TAR *t, char *realname)
  106. {
  107. int i;
  108. linkname_t *lnp;
  109. if (t->options & TAR_NOOVERWRITE)
  110. {
  111. struct stat s;
  112. #ifdef WIN32
  113. if (stat(realname, &s) == 0 || errno != ENOENT)
  114. #else
  115. if (lstat(realname, &s) == 0 || errno != ENOENT)
  116. #endif
  117. {
  118. errno = EEXIST;
  119. return -1;
  120. }
  121. }
  122. if (TH_ISDIR(t))
  123. {
  124. i = tar_extract_dir(t, realname);
  125. if (i == 1)
  126. i = 0;
  127. }
  128. #ifndef _WIN32
  129. else if (TH_ISLNK(t))
  130. i = tar_extract_hardlink(t, realname);
  131. else if (TH_ISSYM(t))
  132. i = tar_extract_symlink(t, realname);
  133. else if (TH_ISCHR(t))
  134. i = tar_extract_chardev(t, realname);
  135. else if (TH_ISBLK(t))
  136. i = tar_extract_blockdev(t, realname);
  137. else if (TH_ISFIFO(t))
  138. i = tar_extract_fifo(t, realname);
  139. #endif
  140. else /* if (TH_ISREG(t)) */
  141. i = tar_extract_regfile(t, realname);
  142. if (i != 0)
  143. return i;
  144. i = tar_set_file_perms(t, realname);
  145. if (i != 0)
  146. return i;
  147. lnp = (linkname_t *)calloc(1, sizeof(linkname_t));
  148. if (lnp == NULL)
  149. return -1;
  150. strlcpy(lnp->ln_save, th_get_pathname(t), sizeof(lnp->ln_save));
  151. strlcpy(lnp->ln_real, realname, sizeof(lnp->ln_real));
  152. #ifdef DEBUG
  153. printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
  154. "value=\"%s\"\n", th_get_pathname(t), realname);
  155. #endif
  156. if (libtar_hash_add(t->h, lnp) != 0)
  157. return -1;
  158. return 0;
  159. }
  160. /* extract regular file */
  161. int
  162. tar_extract_regfile(TAR *t, char *realname)
  163. {
  164. mode_t mode;
  165. size_t size;
  166. uid_t uid;
  167. gid_t gid;
  168. int fdout;
  169. int i, k;
  170. char buf[T_BLOCKSIZE];
  171. char *filename;
  172. #ifdef DEBUG
  173. printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
  174. realname);
  175. #endif
  176. if (!TH_ISREG(t))
  177. {
  178. errno = EINVAL;
  179. return -1;
  180. }
  181. filename = (realname ? realname : th_get_pathname(t));
  182. mode = th_get_mode(t);
  183. size = th_get_size(t);
  184. uid = th_get_uid(t);
  185. gid = th_get_gid(t);
  186. /* Make a copy of the string because dirname and mkdirhier may modify the
  187. * string */
  188. strncpy(buf, filename, sizeof(buf)-1);
  189. buf[sizeof(buf)-1] = 0;
  190. if (mkdirhier(dirname(buf)) == -1)
  191. {
  192. return -1;
  193. }
  194. #ifdef DEBUG
  195. printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
  196. filename, mode, uid, gid, size);
  197. #endif
  198. fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
  199. #ifdef O_BINARY
  200. | O_BINARY
  201. #endif
  202. , 0666);
  203. if (fdout == -1)
  204. {
  205. #ifdef DEBUG
  206. perror("open()");
  207. #endif
  208. return -1;
  209. }
  210. #if 0
  211. /* change the owner. (will only work if run as root) */
  212. if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
  213. {
  214. #ifdef DEBUG
  215. perror("fchown()");
  216. #endif
  217. return -1;
  218. }
  219. /* make sure the mode isn't inheritted from a file we're overwriting */
  220. if (fchmod(fdout, mode & 07777) == -1)
  221. {
  222. #ifdef DEBUG
  223. perror("fchmod()");
  224. #endif
  225. return -1;
  226. }
  227. #endif
  228. /* extract the file */
  229. for (i = size; i > 0; i -= T_BLOCKSIZE)
  230. {
  231. k = tar_block_read(t, buf);
  232. if (k != T_BLOCKSIZE)
  233. {
  234. if (k != -1)
  235. errno = EINVAL;
  236. return -1;
  237. }
  238. /* write block to output file */
  239. if (write(fdout, buf,
  240. ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
  241. return -1;
  242. }
  243. /* close output file */
  244. if (close(fdout) == -1)
  245. return -1;
  246. #ifdef DEBUG
  247. printf("### done extracting %s\n", filename);
  248. #endif
  249. (void)filename;
  250. (void)gid;
  251. (void)uid;
  252. (void)mode;
  253. return 0;
  254. }
  255. /* skip regfile */
  256. int
  257. tar_skip_regfile(TAR *t)
  258. {
  259. int i, k;
  260. size_t size;
  261. char buf[T_BLOCKSIZE];
  262. if (!TH_ISREG(t))
  263. {
  264. errno = EINVAL;
  265. return -1;
  266. }
  267. size = th_get_size(t);
  268. for (i = size; i > 0; i -= T_BLOCKSIZE)
  269. {
  270. k = tar_block_read(t, buf);
  271. if (k != T_BLOCKSIZE)
  272. {
  273. if (k != -1)
  274. errno = EINVAL;
  275. return -1;
  276. }
  277. }
  278. return 0;
  279. }
  280. /* hardlink */
  281. int
  282. tar_extract_hardlink(TAR * t, char *realname)
  283. {
  284. char *filename;
  285. char *linktgt;
  286. linkname_t *lnp;
  287. libtar_hashptr_t hp;
  288. char buf[T_BLOCKSIZE];
  289. if (!TH_ISLNK(t))
  290. {
  291. errno = EINVAL;
  292. return -1;
  293. }
  294. filename = (realname ? realname : th_get_pathname(t));
  295. /* Make a copy of the string because dirname and mkdirhier may modify the
  296. * string */
  297. strncpy(buf, filename, sizeof(buf)-1);
  298. buf[sizeof(buf)-1] = 0;
  299. if (mkdirhier(dirname(buf)) == -1)
  300. return -1;
  301. libtar_hashptr_reset(&hp);
  302. if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
  303. (libtar_matchfunc_t)libtar_str_match) != 0)
  304. {
  305. lnp = (linkname_t *)libtar_hashptr_data(&hp);
  306. linktgt = lnp->ln_real;
  307. }
  308. else
  309. linktgt = th_get_linkname(t);
  310. #ifdef DEBUG
  311. printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
  312. #endif
  313. #ifndef WIN32
  314. if (link(linktgt, filename) == -1)
  315. #else
  316. (void)linktgt;
  317. #endif
  318. {
  319. #ifdef DEBUG
  320. perror("link()");
  321. #endif
  322. return -1;
  323. }
  324. return 0;
  325. }
  326. /* symlink */
  327. int
  328. tar_extract_symlink(TAR *t, char *realname)
  329. {
  330. char *filename;
  331. char buf[T_BLOCKSIZE];
  332. #ifndef _WIN32
  333. if (!TH_ISSYM(t))
  334. {
  335. errno = EINVAL;
  336. return -1;
  337. }
  338. #endif
  339. filename = (realname ? realname : th_get_pathname(t));
  340. /* Make a copy of the string because dirname and mkdirhier may modify the
  341. * string */
  342. strncpy(buf, filename, sizeof(buf)-1);
  343. buf[sizeof(buf)-1] = 0;
  344. if (mkdirhier(dirname(buf)) == -1)
  345. return -1;
  346. if (unlink(filename) == -1 && errno != ENOENT)
  347. {
  348. return -1;
  349. }
  350. #ifdef DEBUG
  351. printf(" ==> extracting: %s (symlink to %s)\n",
  352. filename, th_get_linkname(t));
  353. #endif
  354. #ifndef WIN32
  355. if (symlink(th_get_linkname(t), filename) == -1)
  356. #endif
  357. {
  358. #ifdef DEBUG
  359. perror("symlink()");
  360. #endif
  361. return -1;
  362. }
  363. return 0;
  364. }
  365. /* character device */
  366. int
  367. tar_extract_chardev(TAR *t, char *realname)
  368. {
  369. mode_t mode;
  370. unsigned long devmaj, devmin;
  371. char *filename;
  372. char buf[T_BLOCKSIZE];
  373. #ifndef _WIN32
  374. if (!TH_ISCHR(t))
  375. {
  376. errno = EINVAL;
  377. return -1;
  378. }
  379. #endif
  380. filename = (realname ? realname : th_get_pathname(t));
  381. mode = th_get_mode(t);
  382. devmaj = th_get_devmajor(t);
  383. devmin = th_get_devminor(t);
  384. /* Make a copy of the string because dirname and mkdirhier may modify the
  385. * string */
  386. strncpy(buf, filename, sizeof(buf)-1);
  387. buf[sizeof(buf)-1] = 0;
  388. if (mkdirhier(dirname(buf)) == -1)
  389. return -1;
  390. #ifdef DEBUG
  391. printf(" ==> extracting: %s (character device %ld,%ld)\n",
  392. filename, devmaj, devmin);
  393. #endif
  394. #ifndef WIN32
  395. if (mknod(filename, mode | S_IFCHR,
  396. compat_makedev(devmaj, devmin)) == -1)
  397. #else
  398. (void)devmin;
  399. (void)devmaj;
  400. (void)mode;
  401. #endif
  402. {
  403. #ifdef DEBUG
  404. perror("mknod()");
  405. #endif
  406. return -1;
  407. }
  408. return 0;
  409. }
  410. /* block device */
  411. int
  412. tar_extract_blockdev(TAR *t, char *realname)
  413. {
  414. mode_t mode;
  415. unsigned long devmaj, devmin;
  416. char *filename;
  417. char buf[T_BLOCKSIZE];
  418. if (!TH_ISBLK(t))
  419. {
  420. errno = EINVAL;
  421. return -1;
  422. }
  423. filename = (realname ? realname : th_get_pathname(t));
  424. mode = th_get_mode(t);
  425. devmaj = th_get_devmajor(t);
  426. devmin = th_get_devminor(t);
  427. /* Make a copy of the string because dirname and mkdirhier may modify the
  428. * string */
  429. strncpy(buf, filename, sizeof(buf)-1);
  430. buf[sizeof(buf)-1] = 0;
  431. if (mkdirhier(dirname(buf)) == -1)
  432. return -1;
  433. #ifdef DEBUG
  434. printf(" ==> extracting: %s (block device %ld,%ld)\n",
  435. filename, devmaj, devmin);
  436. #endif
  437. #ifndef WIN32
  438. if (mknod(filename, mode | S_IFBLK,
  439. compat_makedev(devmaj, devmin)) == -1)
  440. #else
  441. (void)devmin;
  442. (void)devmaj;
  443. (void)mode;
  444. #endif
  445. {
  446. #ifdef DEBUG
  447. perror("mknod()");
  448. #endif
  449. return -1;
  450. }
  451. return 0;
  452. }
  453. /* directory */
  454. int
  455. tar_extract_dir(TAR *t, char *realname)
  456. {
  457. mode_t mode;
  458. char *filename;
  459. char buf[T_BLOCKSIZE];
  460. if (!TH_ISDIR(t))
  461. {
  462. errno = EINVAL;
  463. return -1;
  464. }
  465. filename = (realname ? realname : th_get_pathname(t));
  466. mode = th_get_mode(t);
  467. /* Make a copy of the string because dirname and mkdirhier may modify the
  468. * string */
  469. strncpy(buf, filename, sizeof(buf)-1);
  470. buf[sizeof(buf)-1] = 0;
  471. if (mkdirhier(dirname(buf)) == -1)
  472. return -1;
  473. #ifdef DEBUG
  474. printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
  475. mode);
  476. #endif
  477. #ifdef WIN32
  478. if (mkdir(filename) == -1)
  479. #else
  480. if (mkdir(filename, mode) == -1)
  481. #endif
  482. {
  483. #ifdef __BORLANDC__
  484. /* There is a bug in the Borland Run time library which makes MKDIR
  485. return EACCES when it should return EEXIST
  486. if it is some other error besides directory exists
  487. then return false */
  488. if ( errno == EACCES)
  489. {
  490. errno = EEXIST;
  491. }
  492. #endif
  493. if (errno == EEXIST)
  494. {
  495. if (chmod(filename, mode) == -1)
  496. {
  497. #ifdef DEBUG
  498. perror("chmod()");
  499. #endif
  500. return -1;
  501. }
  502. else
  503. {
  504. #ifdef DEBUG
  505. puts(" *** using existing directory");
  506. #endif
  507. return 1;
  508. }
  509. }
  510. else
  511. {
  512. #ifdef DEBUG
  513. perror("mkdir()");
  514. #endif
  515. return -1;
  516. }
  517. }
  518. return 0;
  519. }
  520. /* FIFO */
  521. int
  522. tar_extract_fifo(TAR *t, char *realname)
  523. {
  524. mode_t mode;
  525. char *filename;
  526. char buf[T_BLOCKSIZE];
  527. if (!TH_ISFIFO(t))
  528. {
  529. errno = EINVAL;
  530. return -1;
  531. }
  532. filename = (realname ? realname : th_get_pathname(t));
  533. mode = th_get_mode(t);
  534. /* Make a copy of the string because dirname and mkdirhier may modify the
  535. * string */
  536. strncpy(buf, filename, sizeof(buf)-1);
  537. buf[sizeof(buf)-1] = 0;
  538. if (mkdirhier(dirname(buf)) == -1)
  539. return -1;
  540. #ifdef DEBUG
  541. printf(" ==> extracting: %s (fifo)\n", filename);
  542. #endif
  543. #ifndef WIN32
  544. if (mkfifo(filename, mode) == -1)
  545. #else
  546. (void)mode;
  547. #endif
  548. {
  549. #ifdef DEBUG
  550. perror("mkfifo()");
  551. #endif
  552. return -1;
  553. }
  554. return 0;
  555. }