getcwd.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /* $OpenBSD: getcwd.c,v 1.14 2005/08/08 08:05:34 espie Exp */
  2. /*
  3. * Copyright (c) 1989, 1991, 1993
  4. * The Regents of the University of California. All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. Neither the name of the University nor the names of its contributors
  15. * may be used to endorse or promote products derived from this software
  16. * without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  21. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  22. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  24. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  25. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  27. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  28. * SUCH DAMAGE.
  29. */
  30. /* OPENBSD ORIGINAL: lib/libc/gen/getcwd.c */
  31. #include "includes.h"
  32. #if !defined(HAVE_GETCWD)
  33. #include <sys/param.h>
  34. #include <sys/stat.h>
  35. #include <errno.h>
  36. #include <dirent.h>
  37. #include <sys/dir.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include "includes.h"
  42. #define ISDOT(dp) \
  43. (dp->d_name[0] == '.' && (dp->d_name[1] == '\0' || \
  44. (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
  45. char *
  46. getcwd(char *pt, size_t size)
  47. {
  48. struct dirent *dp;
  49. DIR *dir = NULL;
  50. dev_t dev;
  51. ino_t ino;
  52. int first;
  53. char *bpt, *bup;
  54. struct stat s;
  55. dev_t root_dev;
  56. ino_t root_ino;
  57. size_t ptsize, upsize;
  58. int save_errno;
  59. char *ept, *eup, *up;
  60. /*
  61. * If no buffer specified by the user, allocate one as necessary.
  62. * If a buffer is specified, the size has to be non-zero. The path
  63. * is built from the end of the buffer backwards.
  64. */
  65. if (pt) {
  66. ptsize = 0;
  67. if (!size) {
  68. errno = EINVAL;
  69. return (NULL);
  70. }
  71. ept = pt + size;
  72. } else {
  73. if ((pt = malloc(ptsize = MAXPATHLEN)) == NULL)
  74. return (NULL);
  75. ept = pt + ptsize;
  76. }
  77. bpt = ept - 1;
  78. *bpt = '\0';
  79. /*
  80. * Allocate bytes for the string of "../"'s.
  81. * Should always be enough (it's 340 levels). If it's not, allocate
  82. * as necessary. Special * case the first stat, it's ".", not "..".
  83. */
  84. if ((up = malloc(upsize = MAXPATHLEN)) == NULL)
  85. goto err;
  86. eup = up + upsize;
  87. bup = up;
  88. up[0] = '.';
  89. up[1] = '\0';
  90. /* Save root values, so know when to stop. */
  91. if (stat("/", &s))
  92. goto err;
  93. root_dev = s.st_dev;
  94. root_ino = s.st_ino;
  95. errno = 0; /* XXX readdir has no error return. */
  96. for (first = 1;; first = 0) {
  97. /* Stat the current level. */
  98. if (lstat(up, &s))
  99. goto err;
  100. /* Save current node values. */
  101. ino = s.st_ino;
  102. dev = s.st_dev;
  103. /* Check for reaching root. */
  104. if (root_dev == dev && root_ino == ino) {
  105. *--bpt = '/';
  106. /*
  107. * It's unclear that it's a requirement to copy the
  108. * path to the beginning of the buffer, but it's always
  109. * been that way and stuff would probably break.
  110. */
  111. memmove(pt, bpt, ept - bpt);
  112. free(up);
  113. return (pt);
  114. }
  115. /*
  116. * Build pointer to the parent directory, allocating memory
  117. * as necessary. Max length is 3 for "../", the largest
  118. * possible component name, plus a trailing NUL.
  119. */
  120. if (bup + 3 + MAXNAMLEN + 1 >= eup) {
  121. char *nup;
  122. if ((nup = realloc(up, upsize *= 2)) == NULL)
  123. goto err;
  124. bup = nup + (bup - up);
  125. up = nup;
  126. eup = up + upsize;
  127. }
  128. *bup++ = '.';
  129. *bup++ = '.';
  130. *bup = '\0';
  131. /* Open and stat parent directory. */
  132. if (!(dir = opendir(up)) || fstat(dirfd(dir), &s))
  133. goto err;
  134. /* Add trailing slash for next directory. */
  135. *bup++ = '/';
  136. /*
  137. * If it's a mount point, have to stat each element because
  138. * the inode number in the directory is for the entry in the
  139. * parent directory, not the inode number of the mounted file.
  140. */
  141. save_errno = 0;
  142. if (s.st_dev == dev) {
  143. for (;;) {
  144. if (!(dp = readdir(dir)))
  145. goto notfound;
  146. if (dp->d_fileno == ino)
  147. break;
  148. }
  149. } else
  150. for (;;) {
  151. if (!(dp = readdir(dir)))
  152. goto notfound;
  153. if (ISDOT(dp))
  154. continue;
  155. memcpy(bup, dp->d_name, dp->d_namlen + 1);
  156. /* Save the first error for later. */
  157. if (lstat(up, &s)) {
  158. if (!save_errno)
  159. save_errno = errno;
  160. errno = 0;
  161. continue;
  162. }
  163. if (s.st_dev == dev && s.st_ino == ino)
  164. break;
  165. }
  166. /*
  167. * Check for length of the current name, preceding slash,
  168. * leading slash.
  169. */
  170. if (bpt - pt < dp->d_namlen + (first ? 1 : 2)) {
  171. size_t len;
  172. char *npt;
  173. if (!ptsize) {
  174. errno = ERANGE;
  175. goto err;
  176. }
  177. len = ept - bpt;
  178. if ((npt = realloc(pt, ptsize *= 2)) == NULL)
  179. goto err;
  180. bpt = npt + (bpt - pt);
  181. pt = npt;
  182. ept = pt + ptsize;
  183. memmove(ept - len, bpt, len);
  184. bpt = ept - len;
  185. }
  186. if (!first)
  187. *--bpt = '/';
  188. bpt -= dp->d_namlen;
  189. memcpy(bpt, dp->d_name, dp->d_namlen);
  190. (void)closedir(dir);
  191. /* Truncate any file name. */
  192. *bup = '\0';
  193. }
  194. notfound:
  195. /*
  196. * If readdir set errno, use it, not any saved error; otherwise,
  197. * didn't find the current directory in its parent directory, set
  198. * errno to ENOENT.
  199. */
  200. if (!errno)
  201. errno = save_errno ? save_errno : ENOENT;
  202. /* FALLTHROUGH */
  203. err:
  204. save_errno = errno;
  205. if (ptsize)
  206. free(pt);
  207. free(up);
  208. if (dir)
  209. (void)closedir(dir);
  210. errno = save_errno;
  211. return (NULL);
  212. }
  213. #endif /* !defined(HAVE_GETCWD) */