tree.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. /*-
  2. * Copyright 2006-2025 Tarsnap Backup Inc.
  3. * All rights reserved.
  4. *
  5. * Portions of the file below are covered by the following license:
  6. *
  7. * Copyright (c) 2003-2007 Tim Kientzle
  8. * All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
  20. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  21. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  22. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
  23. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  24. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  28. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. /*-
  31. * This is a new directory-walking system that addresses a number
  32. * of problems I've had with fts(3). In particular, it has no
  33. * pathname-length limits (other than the size of 'int'), handles
  34. * deep logical traversals, uses considerably less memory, and has
  35. * an opaque interface (easier to modify in the future).
  36. *
  37. * Internally, it keeps a single list of "tree_entry" items that
  38. * represent filesystem objects that require further attention.
  39. * Non-directories are not kept in memory: they are pulled from
  40. * readdir(), returned to the client, then freed as soon as possible.
  41. * Any directory entry to be traversed gets pushed onto the stack.
  42. *
  43. * There is surprisingly little information that needs to be kept for
  44. * each item on the stack. Just the name, depth (represented here as the
  45. * string length of the parent directory's pathname), and some markers
  46. * indicating how to get back to the parent (via chdir("..") for a
  47. * regular dir or via fchdir(2) for a symlink).
  48. */
  49. #include "bsdtar_platform.h"
  50. __FBSDID("$FreeBSD: src/usr.bin/tar/tree.c,v 1.9 2008/11/27 05:49:52 kientzle Exp $");
  51. #ifdef HAVE_SYS_STAT_H
  52. #include <sys/stat.h>
  53. #endif
  54. #ifdef HAVE_DIRENT_H
  55. #include <dirent.h>
  56. #endif
  57. #ifdef HAVE_ERRNO_H
  58. #include <errno.h>
  59. #endif
  60. #ifdef HAVE_FCNTL_H
  61. #include <fcntl.h>
  62. #endif
  63. #ifdef HAVE_LIMITS_H
  64. #include <limits.h>
  65. #endif
  66. #ifdef HAVE_STDLIB_H
  67. #include <stdlib.h>
  68. #endif
  69. #ifdef HAVE_STRING_H
  70. #include <string.h>
  71. #endif
  72. #ifdef HAVE_UNISTD_H
  73. #include <unistd.h>
  74. #endif
  75. #include "fileutil.h"
  76. #include "tree.h"
  77. /*
  78. * TODO:
  79. * 1) Loop checking.
  80. * 3) Arbitrary logical traversals by closing/reopening intermediate fds.
  81. */
  82. struct tree_entry {
  83. struct tree_entry *next;
  84. struct tree_entry *parent;
  85. char *name;
  86. size_t dirname_length;
  87. dev_t dev;
  88. ino_t ino;
  89. #ifdef HAVE_FCHDIR
  90. int fd;
  91. #elif defined(_WIN32) && !defined(__CYGWIN__)
  92. char *fullpath;
  93. #else
  94. #error fchdir function required.
  95. #endif
  96. int flags;
  97. };
  98. /* Definitions for tree_entry.flags bitmap. */
  99. #define isDir 1 /* This entry is a regular directory. */
  100. #define isDirLink 2 /* This entry is a symbolic link to a directory. */
  101. #define needsPreVisit 4 /* This entry needs to be previsited. */
  102. #define needsPostVisit 8 /* This entry needs to be postvisited. */
  103. /*
  104. * Local data for this package.
  105. */
  106. struct tree {
  107. struct tree_entry *stack;
  108. struct tree_entry *current;
  109. DIR *d;
  110. #ifdef HAVE_FCHDIR
  111. int initialDirFd;
  112. #elif defined(_WIN32) && !defined(__CYGWIN__)
  113. char *initialDir;
  114. #endif
  115. int flags;
  116. int visit_type;
  117. int tree_errno; /* Error code from last failed operation. */
  118. char *buff;
  119. const char *basename;
  120. size_t buff_length;
  121. size_t path_length;
  122. size_t dirname_length;
  123. char realpath[PATH_MAX + 1];
  124. size_t realpath_dirname_length;
  125. int realpath_valid;
  126. char realpath_symlink[PATH_MAX + 1];
  127. int depth;
  128. int openCount;
  129. int maxOpenCount;
  130. int noatime;
  131. struct stat lst;
  132. struct stat st;
  133. };
  134. /* Definitions for tree.flags bitmap. */
  135. #define needsReturn 8 /* Marks first entry as not having been returned yet. */
  136. #define hasStat 16 /* The st entry is set. */
  137. #define hasLstat 32 /* The lst entry is set. */
  138. #ifdef HAVE_DIRENT_D_NAMLEN
  139. /* BSD extension; avoids need for a strlen() call. */
  140. #define D_NAMELEN(dp) (dp)->d_namlen
  141. #else
  142. #define D_NAMELEN(dp) (strlen((dp)->d_name))
  143. #endif
  144. static void
  145. errmsg(const char *m)
  146. {
  147. size_t s = strlen(m);
  148. ssize_t written;
  149. while (s > 0) {
  150. written = write(2, m, strlen(m));
  151. if (written <= 0)
  152. return;
  153. m += written;
  154. s -= written;
  155. }
  156. }
  157. #if 0
  158. #include <stdio.h>
  159. void
  160. tree_dump(struct tree *t, FILE *out)
  161. {
  162. struct tree_entry *te;
  163. fprintf(out, "\tdepth: %d\n", t->depth);
  164. fprintf(out, "\tbuff: %s\n", t->buff);
  165. fprintf(out, "\tpwd: "); fflush(stdout); system("pwd");
  166. fprintf(out, "\taccess: %s\n", t->basename);
  167. fprintf(out, "\tstack:\n");
  168. for (te = t->stack; te != NULL; te = te->next) {
  169. fprintf(out, "\t\tte->name: %s%s%s\n", te->name,
  170. te->flags & needsPreVisit ? "" : " *",
  171. t->current == te ? " (current)" : "");
  172. }
  173. }
  174. #endif
  175. /*
  176. * Attempt to opendir() with O_NOATIME if requested. This is not supported by
  177. * all operating systems or filesystems. If any error occurs, do not print any
  178. * message, and opendir() without O_NOATIME.
  179. */
  180. static DIR*
  181. tree_opendir(const char *path, int noatime)
  182. {
  183. #ifndef HAVE_FDOPENDIR
  184. (void)noatime; /* UNUSED */
  185. return (opendir(path));
  186. #else
  187. const int flags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
  188. DIR *dir;
  189. int fd;
  190. int saved_errno;
  191. /* Open a fd with noatime (if applicable). */
  192. if ((fd = fileutil_open_noatime(path, flags, noatime)) < 0)
  193. goto err0;
  194. /* Re-open as a DIR*. */
  195. if ((dir = fdopendir(fd)) == NULL)
  196. goto err1;
  197. /* Success! */
  198. return (dir);
  199. err1:
  200. saved_errno = errno;
  201. close(fd);
  202. errno = saved_errno;
  203. err0:
  204. /* Failure! */
  205. return (NULL);
  206. #endif
  207. }
  208. /*
  209. * Add a directory path to the current stack.
  210. */
  211. static void
  212. tree_push(struct tree *t, const char *path)
  213. {
  214. struct tree_entry *te;
  215. te = malloc(sizeof(*te));
  216. if (te == NULL)
  217. abort();
  218. memset(te, 0, sizeof(*te));
  219. te->next = t->stack;
  220. t->stack = te;
  221. #ifdef HAVE_FCHDIR
  222. te->fd = -1;
  223. #elif defined(_WIN32) && !defined(__CYGWIN__)
  224. te->fullpath = NULL;
  225. #endif
  226. te->name = strdup(path);
  227. te->flags = needsPreVisit | needsPostVisit;
  228. te->dirname_length = t->dirname_length;
  229. }
  230. /*
  231. * Append a name to the current path.
  232. */
  233. static void
  234. tree_append(struct tree *t, const char *name, size_t name_length)
  235. {
  236. char *p;
  237. size_t size_needed;
  238. if (t->buff != NULL)
  239. t->buff[t->dirname_length] = '\0';
  240. /* Strip trailing '/' from name, unless entire name is "/". */
  241. while (name_length > 1 && name[name_length - 1] == '/')
  242. name_length--;
  243. /* Resize pathname buffer as needed. */
  244. size_needed = name_length + 1 + t->dirname_length + 1;
  245. if (t->buff_length < size_needed) {
  246. if (t->buff_length < 1024)
  247. t->buff_length = 1024;
  248. while (t->buff_length < size_needed)
  249. t->buff_length *= 2;
  250. t->buff = realloc(t->buff, t->buff_length);
  251. }
  252. if (t->buff == NULL)
  253. abort();
  254. p = t->buff + t->dirname_length;
  255. t->path_length = t->dirname_length + name_length;
  256. /* Add a separating '/' if it's needed. */
  257. if (t->dirname_length > 0 && p[-1] != '/') {
  258. *p++ = '/';
  259. t->path_length ++;
  260. }
  261. strncpy(p, name, name_length);
  262. p[name_length] = '\0';
  263. t->basename = p;
  264. /* Adjust canonical name. */
  265. if ((t->realpath_valid) &&
  266. (t->realpath_dirname_length + name_length + 1 <= PATH_MAX)) {
  267. t->realpath[t->realpath_dirname_length] = '/';
  268. memcpy(t->realpath + t->realpath_dirname_length + 1,
  269. name, name_length);
  270. t->realpath[t->realpath_dirname_length + name_length + 1] = 0;
  271. } else {
  272. t->realpath_valid = 0;
  273. }
  274. }
  275. /*
  276. * Open a directory tree for traversal.
  277. */
  278. struct tree *
  279. tree_open(const char *path, int noatime)
  280. {
  281. struct tree *t;
  282. t = malloc(sizeof(*t));
  283. if (t == NULL)
  284. abort();
  285. memset(t, 0, sizeof(*t));
  286. t->noatime = noatime;
  287. tree_append(t, path, strlen(path));
  288. #ifdef HAVE_FCHDIR
  289. t->initialDirFd = open(".", O_RDONLY);
  290. #elif defined(_WIN32) && !defined(__CYGWIN__)
  291. t->initialDir = getcwd(NULL, 0);
  292. #endif
  293. /*
  294. * During most of the traversal, items are set up and then
  295. * returned immediately from tree_next(). That doesn't work
  296. * for the very first entry, so we set a flag for this special
  297. * case.
  298. */
  299. t->flags = needsReturn;
  300. return (t);
  301. }
  302. /*
  303. * We've finished a directory; ascend back to the parent.
  304. */
  305. static int
  306. tree_ascend(struct tree *t)
  307. {
  308. struct tree_entry *te;
  309. int r = 0;
  310. te = t->stack;
  311. t->depth--;
  312. if (te->flags & isDirLink) {
  313. #ifdef HAVE_FCHDIR
  314. if (fchdir(te->fd) != 0) {
  315. t->tree_errno = errno;
  316. r = TREE_ERROR_FATAL;
  317. }
  318. close(te->fd);
  319. #elif defined(_WIN32) && !defined(__CYGWIN__)
  320. if (chdir(te->fullpath) != 0) {
  321. t->tree_errno = errno;
  322. r = TREE_ERROR_FATAL;
  323. }
  324. free(te->fullpath);
  325. te->fullpath = NULL;
  326. #endif
  327. t->openCount--;
  328. } else {
  329. if (chdir("..") != 0) {
  330. t->tree_errno = errno;
  331. r = TREE_ERROR_FATAL;
  332. }
  333. }
  334. /* Figure out where we are. */
  335. if (getcwd(t->realpath, PATH_MAX) != NULL) {
  336. t->realpath_dirname_length = strlen(t->realpath);
  337. if (t->realpath[0] == '/' && t->realpath[1] == '\0')
  338. t->realpath_dirname_length = 0;
  339. t->realpath_valid = 1;
  340. } else {
  341. t->realpath_valid = 0;
  342. }
  343. return (r);
  344. }
  345. /*
  346. * Pop the working stack.
  347. */
  348. static void
  349. tree_pop(struct tree *t)
  350. {
  351. struct tree_entry *te;
  352. t->buff[t->dirname_length] = '\0';
  353. if (t->stack == t->current && t->current != NULL)
  354. t->current = t->current->parent;
  355. te = t->stack;
  356. t->stack = te->next;
  357. t->dirname_length = te->dirname_length;
  358. t->basename = t->buff + t->dirname_length;
  359. /* Special case: starting dir doesn't skip leading '/'. */
  360. if (t->dirname_length > 0)
  361. t->basename++;
  362. free(te->name);
  363. free(te);
  364. }
  365. /*
  366. * Get the next item in the tree traversal.
  367. */
  368. int
  369. tree_next(struct tree *t)
  370. {
  371. struct dirent *de = NULL;
  372. int r;
  373. /* If we're called again after a fatal error, that's an API
  374. * violation. Just crash now. */
  375. if (t->visit_type == TREE_ERROR_FATAL) {
  376. const char *msg = "Unable to continue traversing"
  377. " directory hierarchy after a fatal error.\n";
  378. errmsg(msg);
  379. *(volatile int *)0 = 1; /* Deliberate SEGV; NULL pointer dereference. */
  380. exit(1); /* In case the SEGV didn't work. */
  381. }
  382. /* Handle the startup case by returning the initial entry. */
  383. if (t->flags & needsReturn) {
  384. t->flags &= ~needsReturn;
  385. return (t->visit_type = TREE_REGULAR);
  386. }
  387. while (t->stack != NULL) {
  388. /* If there's an open dir, get the next entry from there. */
  389. while (t->d != NULL) {
  390. errno = 0;
  391. de = readdir(t->d);
  392. if (de == NULL) {
  393. if (errno) {
  394. /* If readdir fails, we're screwed. */
  395. t->tree_errno = errno;
  396. closedir(t->d);
  397. t->d = NULL;
  398. t->visit_type = TREE_ERROR_FATAL;
  399. return (t->visit_type);
  400. }
  401. /* Reached end of directory. */
  402. closedir(t->d);
  403. t->d = NULL;
  404. } else if (de->d_name[0] == '.'
  405. && de->d_name[1] == '\0') {
  406. /* Skip '.' */
  407. } else if (de->d_name[0] == '.'
  408. && de->d_name[1] == '.'
  409. && de->d_name[2] == '\0') {
  410. /* Skip '..' */
  411. } else {
  412. /*
  413. * Append the path to the current path
  414. * and return it.
  415. */
  416. tree_append(t, de->d_name, D_NAMELEN(de));
  417. t->flags &= ~hasLstat;
  418. t->flags &= ~hasStat;
  419. return (t->visit_type = TREE_REGULAR);
  420. }
  421. }
  422. /* If the current dir needs to be visited, set it up. */
  423. if (t->stack->flags & needsPreVisit) {
  424. t->current = t->stack;
  425. tree_append(t, t->stack->name, strlen(t->stack->name));
  426. t->stack->flags &= ~needsPreVisit;
  427. /* If it is a link, set up fd for the ascent. */
  428. if (t->stack->flags & isDirLink) {
  429. #ifdef HAVE_FCHDIR
  430. t->stack->fd = open(".", O_RDONLY);
  431. #elif defined(_WIN32) && !defined(__CYGWIN__)
  432. t->stack->fullpath = getcwd(NULL, 0);
  433. #endif
  434. t->openCount++;
  435. if (t->openCount > t->maxOpenCount)
  436. t->maxOpenCount = t->openCount;
  437. }
  438. t->dirname_length = t->path_length;
  439. if (chdir(t->stack->name) != 0) {
  440. /* chdir() failed; return error */
  441. t->tree_errno = errno;
  442. tree_pop(t);
  443. return (t->visit_type = TREE_ERROR_DIR);
  444. }
  445. t->depth++;
  446. t->d = tree_opendir(".", t->noatime);
  447. if (t->d == NULL) {
  448. t->tree_errno = errno;
  449. r = tree_ascend(t); /* Undo "chdir" */
  450. tree_pop(t);
  451. t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
  452. return (t->visit_type);
  453. }
  454. t->flags &= ~hasLstat;
  455. t->flags &= ~hasStat;
  456. t->basename = ".";
  457. /* Figure out where we are. */
  458. if (getcwd(t->realpath, PATH_MAX) != NULL) {
  459. t->realpath_dirname_length =
  460. strlen(t->realpath);
  461. if (t->realpath[0] == '/' &&
  462. t->realpath[1] == '\0')
  463. t->realpath_dirname_length = 0;
  464. t->realpath_valid = 1;
  465. } else {
  466. t->realpath_valid = 0;
  467. }
  468. return (t->visit_type = TREE_POSTDESCENT);
  469. }
  470. /* We've done everything necessary for the top stack entry. */
  471. if (t->stack->flags & needsPostVisit) {
  472. r = tree_ascend(t);
  473. tree_pop(t);
  474. t->flags &= ~hasLstat;
  475. t->flags &= ~hasStat;
  476. t->visit_type = r != 0 ? r : TREE_POSTASCENT;
  477. return (t->visit_type);
  478. }
  479. }
  480. return (t->visit_type = 0);
  481. }
  482. /*
  483. * Return error code.
  484. */
  485. int
  486. tree_errno(struct tree *t)
  487. {
  488. return (t->tree_errno);
  489. }
  490. /*
  491. * Called by the client to mark the directory just returned from
  492. * tree_next() as needing to be visited.
  493. */
  494. void
  495. tree_descend(struct tree *t)
  496. {
  497. if (t->visit_type != TREE_REGULAR)
  498. return;
  499. if (tree_current_is_physical_dir(t)) {
  500. tree_push(t, t->basename);
  501. t->stack->flags |= isDir;
  502. } else if (tree_current_is_dir(t)) {
  503. tree_push(t, t->basename);
  504. t->stack->flags |= isDirLink;
  505. }
  506. }
  507. /*
  508. * Get the stat() data for the entry just returned from tree_next().
  509. */
  510. const struct stat *
  511. tree_current_stat(struct tree *t)
  512. {
  513. if (!(t->flags & hasStat)) {
  514. if (stat(t->basename, &t->st) != 0)
  515. return NULL;
  516. t->flags |= hasStat;
  517. }
  518. return (&t->st);
  519. }
  520. /*
  521. * Get the lstat() data for the entry just returned from tree_next().
  522. */
  523. const struct stat *
  524. tree_current_lstat(struct tree *t)
  525. {
  526. if (!(t->flags & hasLstat)) {
  527. if (lstat(t->basename, &t->lst) != 0)
  528. return NULL;
  529. t->flags |= hasLstat;
  530. }
  531. return (&t->lst);
  532. }
  533. /*
  534. * Test whether current entry is a dir or link to a dir.
  535. */
  536. int
  537. tree_current_is_dir(struct tree *t)
  538. {
  539. const struct stat *st;
  540. /*
  541. * If we already have lstat() info, then try some
  542. * cheap tests to determine if this is a dir.
  543. */
  544. if (t->flags & hasLstat) {
  545. /* If lstat() says it's a dir, it must be a dir. */
  546. if (S_ISDIR(tree_current_lstat(t)->st_mode))
  547. return 1;
  548. /* Not a dir; might be a link to a dir. */
  549. /* If it's not a link, then it's not a link to a dir. */
  550. if (!S_ISLNK(tree_current_lstat(t)->st_mode))
  551. return 0;
  552. /*
  553. * It's a link, but we don't know what it's a link to,
  554. * so we'll have to use stat().
  555. */
  556. }
  557. st = tree_current_stat(t);
  558. /* If we can't stat it, it's not a dir. */
  559. if (st == NULL)
  560. return 0;
  561. /* Use the definitive test. Hopefully this is cached. */
  562. return (S_ISDIR(st->st_mode));
  563. }
  564. /*
  565. * Test whether current entry is a physical directory. Usually, we
  566. * already have at least one of stat() or lstat() in memory, so we
  567. * use tricks to try to avoid an extra trip to the disk.
  568. */
  569. int
  570. tree_current_is_physical_dir(struct tree *t)
  571. {
  572. const struct stat *st;
  573. /*
  574. * If stat() says it isn't a dir, then it's not a dir.
  575. * If stat() data is cached, this check is free, so do it first.
  576. */
  577. if ((t->flags & hasStat)
  578. && (!S_ISDIR(tree_current_stat(t)->st_mode)))
  579. return 0;
  580. /*
  581. * Either stat() said it was a dir (in which case, we have
  582. * to determine whether it's really a link to a dir) or
  583. * stat() info wasn't available. So we use lstat(), which
  584. * hopefully is already cached.
  585. */
  586. st = tree_current_lstat(t);
  587. /* If we can't stat it, it's not a dir. */
  588. if (st == NULL)
  589. return 0;
  590. /* Use the definitive test. Hopefully this is cached. */
  591. return (S_ISDIR(st->st_mode));
  592. }
  593. /*
  594. * Test whether current entry is a symbolic link.
  595. */
  596. int
  597. tree_current_is_physical_link(struct tree *t)
  598. {
  599. const struct stat *st = tree_current_lstat(t);
  600. if (st == NULL)
  601. return 0;
  602. return (S_ISLNK(st->st_mode));
  603. }
  604. /*
  605. * Return the access path for the entry just returned from tree_next().
  606. */
  607. const char *
  608. tree_current_access_path(struct tree *t)
  609. {
  610. return (t->basename);
  611. }
  612. /*
  613. * Return the full path for the entry just returned from tree_next().
  614. */
  615. const char *
  616. tree_current_path(struct tree *t)
  617. {
  618. return (t->buff);
  619. }
  620. /*
  621. * Return what you would get by calling realpath(3) on the path returned
  622. * by tree_current_access_path(t). In most cases this avoids needing to
  623. * call realpath(3).
  624. */
  625. const char *
  626. tree_current_realpath(struct tree *t)
  627. {
  628. if (tree_current_is_physical_link(t))
  629. return (realpath(t->basename, t->realpath_symlink));
  630. else if (t->realpath_valid)
  631. return (t->realpath);
  632. else
  633. return (realpath(t->basename, t->realpath));
  634. }
  635. /*
  636. * Return the length of the path for the entry just returned from tree_next().
  637. */
  638. size_t
  639. tree_current_pathlen(struct tree *t)
  640. {
  641. return (t->path_length);
  642. }
  643. /*
  644. * Return the nesting depth of the entry just returned from tree_next().
  645. */
  646. int
  647. tree_current_depth(struct tree *t)
  648. {
  649. return (t->depth);
  650. }
  651. /*
  652. * Terminate the traversal and release any resources.
  653. */
  654. int
  655. tree_close(struct tree *t)
  656. {
  657. int rc = 0;
  658. /* Release anything remaining in the stack. */
  659. while (t->stack != NULL)
  660. tree_pop(t);
  661. if (t->buff)
  662. free(t->buff);
  663. /* chdir() back to where we started. */
  664. #ifdef HAVE_FCHDIR
  665. if (t->initialDirFd >= 0) {
  666. rc = fchdir(t->initialDirFd);
  667. close(t->initialDirFd);
  668. t->initialDirFd = -1;
  669. }
  670. #elif defined(_WIN32) && !defined(__CYGWIN__)
  671. if (t->initialDir != NULL) {
  672. rc = chdir(t->initialDir);
  673. free(t->initialDir);
  674. t->initialDir = NULL;
  675. }
  676. #endif
  677. free(t);
  678. return (rc);
  679. }