You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.6 KiB

  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP version 4.0 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 2.02 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available at through the world-wide-web at |
  10. | http://www.php.net/license/2_02.txt. |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Author: Sander Steffann (sander@steffann.nl) |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "php.h"
  19. #if HAVE_UNISTD_H
  20. #include <unistd.h>
  21. #endif
  22. #include <sys/stat.h>
  23. #ifndef MAXSYMLINKS
  24. #define MAXSYMLINKS 32
  25. #endif
  26. #ifndef S_ISDIR
  27. #define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
  28. #endif
  29. char *php_realpath(char *path, char resolved_path[]);
  30. #ifdef PHP_WIN32
  31. #define IS_SLASH(p) ((p) == '/' || (p) == '\\')
  32. #else
  33. #define IS_SLASH(p) ((p) == '/')
  34. #endif
  35. char *php_realpath(char *path, char resolved_path []) {
  36. char path_construction[MAXPATHLEN]; /* We build the result in here */
  37. char *writepos; /* Position to write next char */
  38. char path_copy[MAXPATHLEN]; /* A work-copy of the path */
  39. char *workpos; /* working position in *path */
  40. #if !defined(PHP_WIN32)
  41. char buf[MAXPATHLEN]; /* Buffer for readlink */
  42. int linklength; /* The result from readlink */
  43. #endif
  44. int linkcount = 0; /* Count symlinks to avoid loops */
  45. struct stat filestat; /* result from stat */
  46. #ifdef PHP_WIN32
  47. char *temppos; /* position while counting '.' */
  48. int dotcount; /* number of '.' */
  49. int t; /* counter */
  50. #endif
  51. /* Set the work-position to the beginning of the given path */
  52. strcpy(path_copy, path);
  53. workpos = path_copy;
  54. #ifdef PHP_WIN32
  55. /* Find out where we start - Windows version */
  56. if (IS_SLASH(*workpos)) {
  57. /* We start at the root of the current drive */
  58. /* Get the current directory */
  59. if (V_GETCWD(path_construction, MAXPATHLEN-1) == NULL) {
  60. /* Unable to get cwd */
  61. resolved_path[0] = 0;
  62. return NULL;
  63. }
  64. /* We only need the first three chars (for example "C:\") */
  65. path_construction[3] = 0;
  66. workpos++;
  67. } else if (workpos[1] == ':') {
  68. /* A drive-letter is specified, copy it */
  69. strncpy(path_construction, path, 2);
  70. strcat(path_construction, "\\");
  71. workpos++;
  72. workpos++;
  73. } else {
  74. /* Use the current directory */
  75. if (V_GETCWD(path_construction, MAXPATHLEN-1) == NULL) {
  76. /* Unable to get cwd */
  77. resolved_path[0] = 0;
  78. return NULL;
  79. }
  80. strcat(path_construction, "\\");
  81. }
  82. #else
  83. /* Find out where we start - Unix version */
  84. if (*workpos == '/') {
  85. /* We start at the root */
  86. strcpy(path_construction, "/");
  87. workpos++;
  88. } else {
  89. /* Use the current directory */
  90. if (V_GETCWD(path_construction, MAXPATHLEN-1) == NULL) {
  91. /* Unable to get cwd */
  92. resolved_path[0] = 0;
  93. return NULL;
  94. }
  95. strcat(path_construction, "/");
  96. }
  97. #endif
  98. /* Set the next-char-position */
  99. writepos = &path_construction[strlen(path_construction)];
  100. /* Go to the end, then stop */
  101. while(*workpos != 0) {
  102. /* Strip (back)slashes */
  103. while(IS_SLASH(*workpos)) workpos++;
  104. #ifdef PHP_WIN32
  105. /* reset dotcount */
  106. dotcount = 0;
  107. /* Look for .. */
  108. if ((workpos[0] == '.') && (workpos[1] != 0)) {
  109. /* Windows accepts \...\ as \..\..\, \....\ as \..\..\..\, etc */
  110. /* At least Win98 does */
  111. temppos = workpos;
  112. while(*temppos++ == '.') {
  113. dotcount++;
  114. if (!IS_SLASH(*temppos) && (*temppos != 0) && (*temppos != '.')) {
  115. /* This is not a /../ component, but a filename that starts with '.' */
  116. dotcount = 0;
  117. }
  118. }
  119. /* Go back dotcount-1 times */
  120. for (t=0 ; t<(dotcount-1) ; t++) {
  121. workpos++; /* move to next '.' */
  122. /* Can we still go back? */
  123. if ((writepos-3) <= path_construction) return NULL;
  124. /* Go back */
  125. writepos--; /* move to '\' */
  126. writepos--;
  127. while(!IS_SLASH(*writepos)) writepos--; /* skip until previous '\\' */
  128. }
  129. workpos++;
  130. }
  131. /* No special case */
  132. if (dotcount == 0) {
  133. /* Append */
  134. while(!IS_SLASH(*workpos) && (*workpos != 0)) {
  135. *writepos++ = *workpos++;
  136. }
  137. }
  138. /* Just one '.', go to next element */
  139. if (dotcount == 1) {
  140. while(!IS_SLASH(*workpos) && (*workpos != 0)) {
  141. *workpos++;
  142. }
  143. /* Avoid double \ in the result */
  144. writepos--;
  145. }
  146. /* If it was a directory, append a slash */
  147. if (IS_SLASH(*workpos)) {
  148. *writepos++ = *workpos++;
  149. }
  150. *writepos = 0;
  151. #else /* defined(PHP_WIN32) */
  152. /* Look for .. */
  153. if ((workpos[0] == '.') && (workpos[1] != 0)) {
  154. if ((workpos[1] == '.') && ((workpos[2] == '/') || (workpos[2] == 0))) {
  155. /* One directory back */
  156. /* Set pointers to right position */
  157. workpos++; /* move to second '.' */
  158. workpos++; /* move to '/' */
  159. /* Only apply .. if not in root */
  160. if ((writepos-1) > path_construction) {
  161. writepos--; /* move to '/' */
  162. while(*--writepos != '/') ; /* skip until previous '/' */
  163. }
  164. } else {
  165. if (workpos[1] == '/') {
  166. /* Found a /./ skip it */
  167. workpos++; /* move to '/' */
  168. /* Avoid double / in the result */
  169. writepos--;
  170. } else {
  171. /* No special case, the name just started with a . */
  172. /* Append */
  173. while((*workpos != '/') && (*workpos != 0)) {
  174. *writepos++ = *workpos++;
  175. }
  176. }
  177. }
  178. } else {
  179. /* No special case */
  180. /* Append */
  181. while((*workpos != '/') && (*workpos != 0)) {
  182. *writepos++ = *workpos++;
  183. }
  184. }
  185. #if HAVE_SYMLINK
  186. /* We are going to use path_construction, so close it */
  187. *writepos = 0;
  188. /* Check the current location to see if it is a symlink */
  189. if((linklength = readlink(path_construction, buf, MAXPATHLEN)) != -1) {
  190. /* Check linkcount */
  191. if (linkcount > MAXSYMLINKS) return NULL;
  192. /* Count this symlink */
  193. linkcount++;
  194. /* Set end of buf */
  195. buf[linklength] = 0;
  196. /* Check for overflow */
  197. if ((strlen(workpos) + strlen(buf) + 1) >= MAXPATHLEN) return NULL;
  198. /* Remove the symlink-component wrom path_construction */
  199. writepos--; /* move to '/' */
  200. while(*--writepos != '/') ; /* skip until previous '/' */
  201. *++writepos = 0; /* end of string after '/' */
  202. /* If the symlink starts with a '/', empty path_construction */
  203. if (*buf == '/') {
  204. *path_construction = 0;
  205. writepos = path_construction;
  206. }
  207. /* Insert symlink into path_copy */
  208. strcat(buf, workpos);
  209. strcpy(path_copy, buf);
  210. workpos = path_copy;
  211. }
  212. #endif /* HAVE_SYMLINK */
  213. /* If it was a directory, append a slash */
  214. if (*workpos == '/') {
  215. *writepos++ = *workpos++;
  216. }
  217. *writepos = 0;
  218. #endif /* defined(PHP_WIN32) */
  219. }
  220. /* Check if the resolved path is a directory */
  221. if (V_STAT(path_construction, &filestat) != 0) {
  222. if (errno != ENOENT) return NULL;
  223. } else {
  224. if (S_ISDIR(filestat.st_mode)) {
  225. /* It's a directory, append a / if needed */
  226. if (*(writepos-1) != '/') {
  227. /* Check for overflow */
  228. if ((strlen(workpos) + 2) >= MAXPATHLEN) {
  229. return NULL;
  230. }
  231. *writepos++ = '/';
  232. *writepos = 0;
  233. }
  234. }
  235. }
  236. strcpy(resolved_path, path_construction);
  237. return resolved_path;
  238. }
  239. /*
  240. * Local variables:
  241. * tab-width: 4
  242. * c-basic-offset: 4
  243. * End:
  244. */