extract.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  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. # ifdef HAVE_SYS_PARAM_H
  29. # include <sys/param.h>
  30. # endif
  31. #endif
  32. #ifdef STDC_HEADERS
  33. # include <stdlib.h>
  34. # include <string.h>
  35. #endif
  36. #ifdef HAVE_UNISTD_H
  37. # include <unistd.h>
  38. #endif
  39. #ifdef HAVE_SYS_MKDEV_H
  40. # include <sys/mkdev.h>
  41. #endif
  42. struct linkname
  43. {
  44. char ln_save[TAR_MAXPATHLEN];
  45. char ln_real[TAR_MAXPATHLEN];
  46. };
  47. typedef struct linkname linkname_t;
  48. static int
  49. tar_set_file_perms(TAR *t, char *realname)
  50. {
  51. mode_t mode;
  52. uid_t uid;
  53. gid_t gid;
  54. struct utimbuf ut;
  55. char *filename;
  56. char *pathname = 0;
  57. if (realname)
  58. {
  59. filename = realname;
  60. }
  61. else
  62. {
  63. pathname = th_get_pathname(t);
  64. filename = pathname;
  65. }
  66. mode = th_get_mode(t);
  67. uid = th_get_uid(t);
  68. gid = th_get_gid(t);
  69. ut.modtime = ut.actime = th_get_mtime(t);
  70. /* change owner/group */
  71. #ifndef WIN32
  72. if (geteuid() == 0)
  73. #ifdef HAVE_LCHOWN
  74. if (lchown(filename, uid, gid) == -1)
  75. {
  76. # ifdef DEBUG
  77. fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
  78. filename, uid, gid, strerror(errno));
  79. # endif
  80. #else /* ! HAVE_LCHOWN */
  81. if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
  82. {
  83. # ifdef DEBUG
  84. fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
  85. filename, uid, gid, strerror(errno));
  86. # endif
  87. #endif /* HAVE_LCHOWN */
  88. if (pathname)
  89. {
  90. free(pathname);
  91. }
  92. return -1;
  93. }
  94. /* change access/modification time */
  95. if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
  96. {
  97. #ifdef DEBUG
  98. perror("utime()");
  99. #endif
  100. if (pathname)
  101. {
  102. free(pathname);
  103. }
  104. return -1;
  105. }
  106. /* change permissions */
  107. if (!TH_ISSYM(t) && chmod(filename, mode & 07777) == -1)
  108. {
  109. #ifdef DEBUG
  110. perror("chmod()");
  111. #endif
  112. if (pathname)
  113. {
  114. free(pathname);
  115. }
  116. return -1;
  117. }
  118. #else /* WIN32 */
  119. (void)filename;
  120. (void)gid;
  121. (void)uid;
  122. (void)mode;
  123. #endif /* WIN32 */
  124. if (pathname)
  125. {
  126. free(pathname);
  127. }
  128. return 0;
  129. }
  130. /* switchboard */
  131. int
  132. tar_extract_file(TAR *t, char *realname)
  133. {
  134. int i;
  135. linkname_t *lnp;
  136. char *pathname;
  137. if (t->options & TAR_NOOVERWRITE)
  138. {
  139. struct stat s;
  140. #ifdef WIN32
  141. if (stat(realname, &s) == 0 || errno != ENOENT)
  142. #else
  143. if (lstat(realname, &s) == 0 || errno != ENOENT)
  144. #endif
  145. {
  146. errno = EEXIST;
  147. return -1;
  148. }
  149. }
  150. if (TH_ISDIR(t))
  151. {
  152. i = tar_extract_dir(t, realname);
  153. if (i == 1)
  154. i = 0;
  155. }
  156. #ifndef _WIN32
  157. else if (TH_ISLNK(t))
  158. i = tar_extract_hardlink(t, realname);
  159. else if (TH_ISSYM(t))
  160. i = tar_extract_symlink(t, realname);
  161. else if (TH_ISCHR(t))
  162. i = tar_extract_chardev(t, realname);
  163. else if (TH_ISBLK(t))
  164. i = tar_extract_blockdev(t, realname);
  165. else if (TH_ISFIFO(t))
  166. i = tar_extract_fifo(t, realname);
  167. #endif
  168. else /* if (TH_ISREG(t)) */
  169. i = tar_extract_regfile(t, realname);
  170. if (i != 0)
  171. return i;
  172. i = tar_set_file_perms(t, realname);
  173. if (i != 0)
  174. return i;
  175. lnp = (linkname_t *)calloc(1, sizeof(linkname_t));
  176. if (lnp == NULL)
  177. return -1;
  178. pathname = th_get_pathname(t);
  179. strlcpy(lnp->ln_save, pathname, sizeof(lnp->ln_save));
  180. strlcpy(lnp->ln_real, realname, sizeof(lnp->ln_real));
  181. #ifdef DEBUG
  182. printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
  183. "value=\"%s\"\n", pathname, realname);
  184. #endif
  185. if (pathname)
  186. {
  187. free(pathname);
  188. }
  189. if (libtar_hash_add(t->h, lnp) != 0)
  190. return -1;
  191. return 0;
  192. }
  193. /* extract regular file */
  194. int
  195. tar_extract_regfile(TAR *t, char *realname)
  196. {
  197. mode_t mode;
  198. size_t size;
  199. uid_t uid;
  200. gid_t gid;
  201. int fdout;
  202. ssize_t i, k;
  203. char buf[T_BLOCKSIZE];
  204. char *filename;
  205. char *pathname = 0;
  206. #ifdef DEBUG
  207. printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
  208. realname);
  209. #endif
  210. if (!TH_ISREG(t))
  211. {
  212. errno = EINVAL;
  213. return -1;
  214. }
  215. if (realname)
  216. {
  217. filename = realname;
  218. }
  219. else
  220. {
  221. pathname = th_get_pathname(t);
  222. filename = pathname;
  223. }
  224. mode = th_get_mode(t);
  225. size = th_get_size(t);
  226. uid = th_get_uid(t);
  227. gid = th_get_gid(t);
  228. /* Make a copy of the string because dirname and mkdirhier may modify the
  229. * string */
  230. strncpy(buf, filename, sizeof(buf)-1);
  231. buf[sizeof(buf)-1] = 0;
  232. if (mkdirhier(dirname(buf)) == -1)
  233. {
  234. if (pathname)
  235. {
  236. free(pathname);
  237. }
  238. return -1;
  239. }
  240. #ifdef DEBUG
  241. printf(" ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
  242. filename, mode, uid, gid, size);
  243. #endif
  244. fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
  245. #ifdef O_BINARY
  246. | O_BINARY
  247. #endif
  248. , 0666);
  249. if (fdout == -1)
  250. {
  251. #ifdef DEBUG
  252. perror("open()");
  253. #endif
  254. if (pathname)
  255. {
  256. free(pathname);
  257. }
  258. return -1;
  259. }
  260. #if 0
  261. /* change the owner. (will only work if run as root) */
  262. if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
  263. {
  264. #ifdef DEBUG
  265. perror("fchown()");
  266. #endif
  267. if (pathname)
  268. {
  269. free(pathname);
  270. }
  271. return -1;
  272. }
  273. /* make sure the mode isn't inheritted from a file we're overwriting */
  274. if (fchmod(fdout, mode & 07777) == -1)
  275. {
  276. #ifdef DEBUG
  277. perror("fchmod()");
  278. #endif
  279. if (pathname)
  280. {
  281. free(pathname);
  282. }
  283. return -1;
  284. }
  285. #endif
  286. /* extract the file */
  287. for (i = size; i > 0; i -= T_BLOCKSIZE)
  288. {
  289. k = tar_block_read(t, buf);
  290. if (k != T_BLOCKSIZE)
  291. {
  292. if (k != -1)
  293. errno = EINVAL;
  294. if (pathname)
  295. {
  296. free(pathname);
  297. }
  298. return -1;
  299. }
  300. /* write block to output file */
  301. if (write(fdout, buf,
  302. ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : (unsigned int)i)) == -1)
  303. {
  304. if (pathname)
  305. {
  306. free(pathname);
  307. }
  308. return -1;
  309. }
  310. }
  311. /* close output file */
  312. if (close(fdout) == -1)
  313. {
  314. if (pathname)
  315. {
  316. free(pathname);
  317. }
  318. return -1;
  319. }
  320. #ifdef DEBUG
  321. printf("### done extracting %s\n", filename);
  322. #endif
  323. (void)filename;
  324. (void)gid;
  325. (void)uid;
  326. (void)mode;
  327. if (pathname)
  328. {
  329. free(pathname);
  330. }
  331. return 0;
  332. }
  333. /* skip regfile */
  334. int
  335. tar_skip_regfile(TAR *t)
  336. {
  337. ssize_t i, k;
  338. size_t size;
  339. char buf[T_BLOCKSIZE];
  340. if (!TH_ISREG(t))
  341. {
  342. errno = EINVAL;
  343. return -1;
  344. }
  345. size = th_get_size(t);
  346. for (i = size; i > 0; i -= T_BLOCKSIZE)
  347. {
  348. k = tar_block_read(t, buf);
  349. if (k != T_BLOCKSIZE)
  350. {
  351. if (k != -1)
  352. errno = EINVAL;
  353. return -1;
  354. }
  355. }
  356. return 0;
  357. }
  358. /* hardlink */
  359. int
  360. tar_extract_hardlink(TAR * t, char *realname)
  361. {
  362. char *filename;
  363. char *linktgt;
  364. linkname_t *lnp;
  365. libtar_hashptr_t hp;
  366. char buf[T_BLOCKSIZE];
  367. char *pathname = 0;
  368. if (!TH_ISLNK(t))
  369. {
  370. errno = EINVAL;
  371. return -1;
  372. }
  373. if (realname)
  374. {
  375. filename = realname;
  376. }
  377. else
  378. {
  379. pathname = th_get_pathname(t);
  380. filename = pathname;
  381. }
  382. /* Make a copy of the string because dirname and mkdirhier may modify the
  383. * string */
  384. strncpy(buf, filename, sizeof(buf)-1);
  385. buf[sizeof(buf)-1] = 0;
  386. if (mkdirhier(dirname(buf)) == -1)
  387. {
  388. if (pathname)
  389. {
  390. free(pathname);
  391. }
  392. return -1;
  393. }
  394. libtar_hashptr_reset(&hp);
  395. if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
  396. (libtar_matchfunc_t)libtar_str_match) != 0)
  397. {
  398. lnp = (linkname_t *)libtar_hashptr_data(&hp);
  399. linktgt = lnp->ln_real;
  400. }
  401. else
  402. linktgt = th_get_linkname(t);
  403. #ifdef DEBUG
  404. printf(" ==> extracting: %s (link to %s)\n", filename, linktgt);
  405. #endif
  406. #ifndef WIN32
  407. if (link(linktgt, filename) == -1)
  408. #else
  409. (void)linktgt;
  410. #endif
  411. {
  412. #ifdef DEBUG
  413. perror("link()");
  414. #endif
  415. if (pathname)
  416. {
  417. free(pathname);
  418. }
  419. return -1;
  420. }
  421. #ifndef WIN32
  422. if (pathname)
  423. {
  424. free(pathname);
  425. }
  426. return 0;
  427. #endif
  428. }
  429. /* symlink */
  430. int
  431. tar_extract_symlink(TAR *t, char *realname)
  432. {
  433. char *filename;
  434. char buf[T_BLOCKSIZE];
  435. char *pathname = 0;
  436. #ifndef _WIN32
  437. if (!TH_ISSYM(t))
  438. {
  439. errno = EINVAL;
  440. return -1;
  441. }
  442. #endif
  443. if (realname)
  444. {
  445. filename = realname;
  446. }
  447. else
  448. {
  449. pathname = th_get_pathname(t);
  450. filename = pathname;
  451. }
  452. /* Make a copy of the string because dirname and mkdirhier may modify the
  453. * string */
  454. strncpy(buf, filename, sizeof(buf)-1);
  455. buf[sizeof(buf)-1] = 0;
  456. if (mkdirhier(dirname(buf)) == -1)
  457. {
  458. if (pathname)
  459. {
  460. free(pathname);
  461. }
  462. return -1;
  463. }
  464. if (unlink(filename) == -1 && errno != ENOENT)
  465. {
  466. if (pathname)
  467. {
  468. free(pathname);
  469. }
  470. return -1;
  471. }
  472. #ifdef DEBUG
  473. printf(" ==> extracting: %s (symlink to %s)\n",
  474. filename, th_get_linkname(t));
  475. #endif
  476. #ifndef WIN32
  477. if (symlink(th_get_linkname(t), filename) == -1)
  478. #endif
  479. {
  480. #ifdef DEBUG
  481. perror("symlink()");
  482. #endif
  483. if (pathname)
  484. {
  485. free(pathname);
  486. }
  487. return -1;
  488. }
  489. #ifndef WIN32
  490. if (pathname)
  491. {
  492. free(pathname);
  493. }
  494. return 0;
  495. #endif
  496. }
  497. /* character device */
  498. int
  499. tar_extract_chardev(TAR *t, char *realname)
  500. {
  501. mode_t mode;
  502. unsigned long devmaj, devmin;
  503. char *filename;
  504. char buf[T_BLOCKSIZE];
  505. char *pathname = 0;
  506. #ifndef _WIN32
  507. if (!TH_ISCHR(t))
  508. {
  509. errno = EINVAL;
  510. return -1;
  511. }
  512. #endif
  513. if (realname)
  514. {
  515. filename = realname;
  516. }
  517. else
  518. {
  519. pathname = th_get_pathname(t);
  520. filename = pathname;
  521. }
  522. mode = th_get_mode(t);
  523. devmaj = th_get_devmajor(t);
  524. devmin = th_get_devminor(t);
  525. /* Make a copy of the string because dirname and mkdirhier may modify the
  526. * string */
  527. strncpy(buf, filename, sizeof(buf)-1);
  528. buf[sizeof(buf)-1] = 0;
  529. if (mkdirhier(dirname(buf)) == -1)
  530. {
  531. if (pathname)
  532. {
  533. free(pathname);
  534. }
  535. return -1;
  536. }
  537. #ifdef DEBUG
  538. printf(" ==> extracting: %s (character device %ld,%ld)\n",
  539. filename, devmaj, devmin);
  540. #endif
  541. #if !defined(WIN32) && !defined(__VMS)
  542. if (mknod(filename, mode | S_IFCHR,
  543. compat_makedev(devmaj, devmin)) == -1)
  544. #else
  545. (void)devmin;
  546. (void)devmaj;
  547. (void)mode;
  548. #endif
  549. {
  550. #ifdef DEBUG
  551. perror("mknod()");
  552. #endif
  553. if (pathname)
  554. {
  555. free(pathname);
  556. }
  557. return -1;
  558. }
  559. #ifndef WIN32
  560. if (pathname)
  561. {
  562. free(pathname);
  563. }
  564. return 0;
  565. #endif
  566. }
  567. /* block device */
  568. int
  569. tar_extract_blockdev(TAR *t, char *realname)
  570. {
  571. mode_t mode;
  572. unsigned long devmaj, devmin;
  573. char *filename;
  574. char buf[T_BLOCKSIZE];
  575. char *pathname = 0;
  576. if (!TH_ISBLK(t))
  577. {
  578. errno = EINVAL;
  579. return -1;
  580. }
  581. if (realname)
  582. {
  583. filename = realname;
  584. }
  585. else
  586. {
  587. pathname = th_get_pathname(t);
  588. filename = pathname;
  589. }
  590. mode = th_get_mode(t);
  591. devmaj = th_get_devmajor(t);
  592. devmin = th_get_devminor(t);
  593. /* Make a copy of the string because dirname and mkdirhier may modify the
  594. * string */
  595. strncpy(buf, filename, sizeof(buf)-1);
  596. buf[sizeof(buf)-1] = 0;
  597. if (mkdirhier(dirname(buf)) == -1)
  598. {
  599. if (pathname)
  600. {
  601. free(pathname);
  602. }
  603. return -1;
  604. }
  605. #ifdef DEBUG
  606. printf(" ==> extracting: %s (block device %ld,%ld)\n",
  607. filename, devmaj, devmin);
  608. #endif
  609. #if !defined(WIN32) && !defined(__VMS)
  610. if (mknod(filename, mode | S_IFBLK,
  611. compat_makedev(devmaj, devmin)) == -1)
  612. #else
  613. (void)devmin;
  614. (void)devmaj;
  615. (void)mode;
  616. #endif
  617. {
  618. #ifdef DEBUG
  619. perror("mknod()");
  620. #endif
  621. if (pathname)
  622. {
  623. free(pathname);
  624. }
  625. return -1;
  626. }
  627. #ifndef WIN32
  628. if (pathname)
  629. {
  630. free(pathname);
  631. }
  632. return 0;
  633. #endif
  634. }
  635. /* directory */
  636. int
  637. tar_extract_dir(TAR *t, char *realname)
  638. {
  639. mode_t mode;
  640. char *filename;
  641. char buf[T_BLOCKSIZE];
  642. char *pathname = 0;
  643. size_t len = 0;
  644. if (!TH_ISDIR(t))
  645. {
  646. errno = EINVAL;
  647. return -1;
  648. }
  649. if (realname)
  650. {
  651. filename = realname;
  652. }
  653. else
  654. {
  655. pathname = th_get_pathname(t);
  656. filename = pathname;
  657. }
  658. mode = th_get_mode(t);
  659. /* Make a copy of the string because dirname and mkdirhier may modify the
  660. * string */
  661. strncpy(buf, filename, sizeof(buf)-1);
  662. buf[sizeof(buf)-1] = 0;
  663. if (mkdirhier(dirname(buf)) == -1)
  664. {
  665. if (pathname)
  666. {
  667. free(pathname);
  668. }
  669. return -1;
  670. }
  671. /* Strip trailing '/'...it confuses some Unixes (and BeOS)... */
  672. strncpy(buf, filename, sizeof(buf)-1);
  673. buf[sizeof(buf)-1] = 0;
  674. len = strlen(buf);
  675. if ((len > 0) && (buf[len-1] == '/'))
  676. {
  677. buf[len-1] = '\0';
  678. }
  679. #ifdef DEBUG
  680. printf(" ==> extracting: %s (mode %04o, directory)\n", filename,
  681. mode);
  682. #endif
  683. #ifdef WIN32
  684. if (mkdir(buf) == -1)
  685. #else
  686. if (mkdir(buf, mode & 07777) == -1)
  687. #endif
  688. {
  689. #ifdef __BORLANDC__
  690. /* There is a bug in the Borland Run time library which makes MKDIR
  691. return EACCES when it should return EEXIST
  692. if it is some other error besides directory exists
  693. then return false */
  694. if ( errno == EACCES)
  695. {
  696. errno = EEXIST;
  697. }
  698. #endif
  699. if (errno == EEXIST)
  700. {
  701. if (chmod(filename, mode & 07777) == -1)
  702. {
  703. #ifdef DEBUG
  704. perror("chmod()");
  705. #endif
  706. if (pathname)
  707. {
  708. free(pathname);
  709. }
  710. return -1;
  711. }
  712. else
  713. {
  714. #ifdef DEBUG
  715. puts(" *** using existing directory");
  716. #endif
  717. if (pathname)
  718. {
  719. free(pathname);
  720. }
  721. return 1;
  722. }
  723. }
  724. else
  725. {
  726. #ifdef DEBUG
  727. perror("mkdir()");
  728. #endif
  729. if (pathname)
  730. {
  731. free(pathname);
  732. }
  733. return -1;
  734. }
  735. }
  736. if (pathname)
  737. {
  738. free(pathname);
  739. }
  740. return 0;
  741. }
  742. /* FIFO */
  743. int
  744. tar_extract_fifo(TAR *t, char *realname)
  745. {
  746. mode_t mode;
  747. char *filename;
  748. char buf[T_BLOCKSIZE];
  749. char *pathname = 0;
  750. if (!TH_ISFIFO(t))
  751. {
  752. errno = EINVAL;
  753. return -1;
  754. }
  755. if (realname)
  756. {
  757. filename = realname;
  758. }
  759. else
  760. {
  761. pathname = th_get_pathname(t);
  762. filename = pathname;
  763. }
  764. mode = th_get_mode(t);
  765. /* Make a copy of the string because dirname and mkdirhier may modify the
  766. * string */
  767. strncpy(buf, filename, sizeof(buf)-1);
  768. buf[sizeof(buf)-1] = 0;
  769. if (mkdirhier(dirname(buf)) == -1)
  770. {
  771. if (pathname)
  772. {
  773. free(pathname);
  774. }
  775. return -1;
  776. }
  777. #ifdef DEBUG
  778. printf(" ==> extracting: %s (fifo)\n", filename);
  779. #endif
  780. #if !defined(WIN32) && !defined(__VMS)
  781. if (mkfifo(filename, mode & 07777) == -1)
  782. #else
  783. (void)mode;
  784. #endif
  785. {
  786. #ifdef DEBUG
  787. perror("mkfifo()");
  788. #endif
  789. if (pathname)
  790. {
  791. free(pathname);
  792. }
  793. return -1;
  794. }
  795. #ifndef WIN32
  796. if (pathname)
  797. {
  798. free(pathname);
  799. }
  800. return 0;
  801. #endif
  802. }