getcwd.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
  3. *
  4. * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
  5. */
  6. /* These functions find the absolute path to the current working directory. */
  7. #include <stdlib.h>
  8. #include <errno.h>
  9. #include <sys/stat.h>
  10. #include <dirent.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <sys/param.h>
  14. #include <sys/syscall.h>
  15. /* libc_hidden_proto(getcwd) */
  16. /* libc_hidden_proto(getpagesize) */
  17. /* Experimentally off - libc_hidden_proto(strcat) */
  18. /* Experimentally off - libc_hidden_proto(strcpy) */
  19. /* Experimentally off - libc_hidden_proto(strncpy) */
  20. /* Experimentally off - libc_hidden_proto(strlen) */
  21. /* libc_hidden_proto(opendir) */
  22. /* libc_hidden_proto(readdir) */
  23. /* libc_hidden_proto(closedir) */
  24. /* libc_hidden_proto(stat) */
  25. #ifdef __NR_getcwd
  26. # define __NR___syscall_getcwd __NR_getcwd
  27. static __always_inline
  28. _syscall2(int, __syscall_getcwd, char *, buf, unsigned long, size)
  29. #else
  30. /* If the syscall is not present, we have to walk up the
  31. * directory tree till we hit the root. Now we _could_
  32. * use /proc/self/cwd if /proc is mounted... That approach
  33. * is left an an exercise for the reader... */
  34. /* Seems a few broken filesystems (like coda) don't like this */
  35. /* #undef FAST_DIR_SEARCH_POSSIBLE on Linux */
  36. /* Routine to find the step back down */
  37. static char *search_dir(dev_t this_dev, ino_t this_ino, char *path_buf, int path_size)
  38. {
  39. DIR *dp;
  40. struct dirent *d;
  41. char *ptr;
  42. int slen;
  43. struct stat st;
  44. # ifdef FAST_DIR_SEARCH_POSSIBLE
  45. /* The test is for ELKS lib 0.0.9, this should be fixed in the real kernel */
  46. int slow_search = (sizeof(ino_t) != sizeof(d->d_ino));
  47. # endif
  48. if (stat(path_buf, &st) < 0) {
  49. goto oops;
  50. }
  51. # ifdef FAST_DIR_SEARCH_POSSIBLE
  52. if (this_dev != st.st_dev)
  53. slow_search = 1;
  54. # endif
  55. slen = strlen(path_buf);
  56. ptr = path_buf + slen - 1;
  57. if (*ptr != '/') {
  58. if (slen + 2 > path_size) {
  59. goto oops;
  60. }
  61. strcpy(++ptr, "/");
  62. slen++;
  63. }
  64. slen++;
  65. dp = opendir(path_buf);
  66. if (dp == 0) {
  67. goto oops;
  68. }
  69. while ((d = readdir(dp)) != 0) {
  70. # ifdef FAST_DIR_SEARCH_POSSIBLE
  71. if (slow_search || this_ino == d->d_ino) {
  72. # endif
  73. if (slen + strlen(d->d_name) > path_size) {
  74. goto oops;
  75. }
  76. strcpy(ptr + 1, d->d_name);
  77. if (stat(path_buf, &st) < 0)
  78. continue;
  79. if (st.st_ino == this_ino && st.st_dev == this_dev) {
  80. closedir(dp);
  81. return path_buf;
  82. }
  83. # ifdef FAST_DIR_SEARCH_POSSIBLE
  84. }
  85. # endif
  86. }
  87. closedir(dp);
  88. return 0;
  89. oops:
  90. __set_errno(ERANGE);
  91. return 0;
  92. }
  93. /* Routine to go up tree */
  94. static char *recurser(char *path_buf, int path_size, dev_t root_dev, ino_t root_ino)
  95. {
  96. struct stat st;
  97. dev_t this_dev;
  98. ino_t this_ino;
  99. if (stat(path_buf, &st) < 0) {
  100. if (errno != EFAULT)
  101. goto oops;
  102. return 0;
  103. }
  104. this_dev = st.st_dev;
  105. this_ino = st.st_ino;
  106. if (this_dev == root_dev && this_ino == root_ino) {
  107. if (path_size < 2) {
  108. goto oops;
  109. }
  110. strcpy(path_buf, "/");
  111. return path_buf;
  112. }
  113. if (strlen(path_buf) + 4 > path_size) {
  114. goto oops;
  115. }
  116. strcat(path_buf, "/..");
  117. if (recurser(path_buf, path_size, root_dev, root_ino) == 0)
  118. return 0;
  119. return search_dir(this_dev, this_ino, path_buf, path_size);
  120. oops:
  121. __set_errno(ERANGE);
  122. return 0;
  123. }
  124. static __always_inline
  125. int __syscall_getcwd(char * buf, unsigned long size)
  126. {
  127. int len;
  128. char *cwd;
  129. struct stat st;
  130. int olderrno;
  131. olderrno = errno;
  132. len = -1;
  133. /* get stat for root to have a valid parameters for the terminating condition */
  134. if (stat("/", &st) < 0) {
  135. /* root dir not found! */
  136. return -1;
  137. }
  138. /* start with actual dir */
  139. if (buf) strncpy(buf, ".", size);
  140. cwd = recurser(buf, size, st.st_dev, st.st_ino);
  141. if (cwd) {
  142. len = strlen(buf) + 1;
  143. __set_errno(olderrno);
  144. }
  145. return len;
  146. }
  147. #endif /* __NR_getcwd */
  148. char *getcwd(char *buf, size_t size)
  149. {
  150. int ret;
  151. char *path;
  152. size_t alloc_size = size;
  153. if (size == 0) {
  154. if (buf != NULL) {
  155. __set_errno(EINVAL);
  156. return NULL;
  157. }
  158. alloc_size = MAX (PATH_MAX, getpagesize ());
  159. }
  160. path=buf;
  161. if (buf == NULL) {
  162. path = malloc(alloc_size);
  163. if (path == NULL)
  164. return NULL;
  165. }
  166. ret = __syscall_getcwd(path, alloc_size);
  167. if (ret >= 0)
  168. {
  169. if (buf == NULL && size == 0)
  170. buf = realloc(path, ret);
  171. if (buf == NULL)
  172. buf = path;
  173. return buf;
  174. }
  175. if (buf == NULL)
  176. free (path);
  177. return NULL;
  178. }
  179. libc_hidden_def(getcwd)