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.

285 lines
7.6 KiB

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