PageRenderTime 62ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/TSRM/tsrm_virtual_cwd.c

http://github.com/infusion/PHP
C | 1954 lines | 1760 code | 131 blank | 63 comment | 222 complexity | b10db967aef564ee60143a35cfd593a1 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2011 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 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_01.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. | Authors: Andi Gutmans <andi@zend.com> |
  16. | Sascha Schumann <sascha@schumann.cx> |
  17. | Pierre Joye <pierre@php.net> |
  18. +----------------------------------------------------------------------+
  19. */
  20. /* $Id: tsrm_virtual_cwd.c 307316 2011-01-10 00:43:08Z pajoye $ */
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <string.h>
  24. #include <stdio.h>
  25. #include <limits.h>
  26. #include <errno.h>
  27. #include <stdlib.h>
  28. #include <fcntl.h>
  29. #include <time.h>
  30. #include "tsrm_virtual_cwd.h"
  31. #include "tsrm_strtok_r.h"
  32. #ifdef TSRM_WIN32
  33. #include <io.h>
  34. #include "tsrm_win32.h"
  35. # ifndef IO_REPARSE_TAG_SYMLINK
  36. # define IO_REPARSE_TAG_SYMLINK 0xA000000C
  37. # endif
  38. # ifndef VOLUME_NAME_NT
  39. # define VOLUME_NAME_NT 0x2
  40. # endif
  41. # ifndef VOLUME_NAME_DOS
  42. # define VOLUME_NAME_DOS 0x0
  43. # endif
  44. #endif
  45. #ifndef S_IFLNK
  46. # define S_IFLNK 0120000
  47. #endif
  48. #ifdef NETWARE
  49. #include <fsio.h>
  50. #endif
  51. #ifndef HAVE_REALPATH
  52. #define realpath(x,y) strcpy(y,x)
  53. #endif
  54. #define VIRTUAL_CWD_DEBUG 0
  55. #include "TSRM.h"
  56. /* Only need mutex for popen() in Windows and NetWare because it doesn't chdir() on UNIX */
  57. #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
  58. MUTEX_T cwd_mutex;
  59. #endif
  60. #ifdef ZTS
  61. ts_rsrc_id cwd_globals_id;
  62. #else
  63. virtual_cwd_globals cwd_globals;
  64. #endif
  65. cwd_state main_cwd_state; /* True global */
  66. #ifndef TSRM_WIN32
  67. #include <unistd.h>
  68. #else
  69. #include <direct.h>
  70. #endif
  71. #ifndef S_ISDIR
  72. #define S_ISDIR(mode) ((mode) & _S_IFDIR)
  73. #endif
  74. #ifndef S_ISREG
  75. #define S_ISREG(mode) ((mode) & _S_IFREG)
  76. #endif
  77. #ifdef TSRM_WIN32
  78. #include <tchar.h>
  79. #define tsrm_strtok_r(a,b,c) _tcstok((a),(b))
  80. #define TOKENIZER_STRING "/\\"
  81. static int php_check_dots(const char *element, int n)
  82. {
  83. while (n-- > 0) if (element[n] != '.') break;
  84. return (n != -1);
  85. }
  86. #define IS_DIRECTORY_UP(element, len) \
  87. (len >= 2 && !php_check_dots(element, len))
  88. #define IS_DIRECTORY_CURRENT(element, len) \
  89. (len == 1 && element[0] == '.')
  90. #elif defined(NETWARE)
  91. /* NetWare has strtok() (in LibC) and allows both slashes in paths, like Windows --
  92. but rest of the stuff is like Unix */
  93. /* strtok() call in LibC is abending when used in a different address space -- hence using
  94. PHP's version itself for now */
  95. /*#define tsrm_strtok_r(a,b,c) strtok((a),(b))*/
  96. #define TOKENIZER_STRING "/\\"
  97. #else
  98. #define TOKENIZER_STRING "/"
  99. #endif
  100. /* default macros */
  101. #ifndef IS_DIRECTORY_UP
  102. #define IS_DIRECTORY_UP(element, len) \
  103. (len == 2 && element[0] == '.' && element[1] == '.')
  104. #endif
  105. #ifndef IS_DIRECTORY_CURRENT
  106. #define IS_DIRECTORY_CURRENT(element, len) \
  107. (len == 1 && element[0] == '.')
  108. #endif
  109. /* define this to check semantics */
  110. #define IS_DIR_OK(s) (1)
  111. #ifndef IS_DIR_OK
  112. #define IS_DIR_OK(state) (php_is_dir_ok(state) == 0)
  113. #endif
  114. #define CWD_STATE_COPY(d, s) \
  115. (d)->cwd_length = (s)->cwd_length; \
  116. (d)->cwd = (char *) malloc((s)->cwd_length+1); \
  117. memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1);
  118. #define CWD_STATE_FREE(s) \
  119. free((s)->cwd);
  120. #ifdef TSRM_WIN32
  121. #ifdef CTL_CODE
  122. #undef CTL_CODE
  123. #endif
  124. #define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
  125. #define FILE_DEVICE_FILE_SYSTEM 0x00000009
  126. #define METHOD_BUFFERED 0
  127. #define FILE_ANY_ACCESS 0
  128. #define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
  129. #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 )
  130. typedef struct {
  131. unsigned long ReparseTag;
  132. unsigned short ReparseDataLength;
  133. unsigned short Reserved;
  134. union {
  135. struct {
  136. unsigned short SubstituteNameOffset;
  137. unsigned short SubstituteNameLength;
  138. unsigned short PrintNameOffset;
  139. unsigned short PrintNameLength;
  140. unsigned long Flags;
  141. wchar_t ReparseTarget[1];
  142. } SymbolicLinkReparseBuffer;
  143. struct {
  144. unsigned short SubstituteNameOffset;
  145. unsigned short SubstituteNameLength;
  146. unsigned short PrintNameOffset;
  147. unsigned short PrintNameLength;
  148. wchar_t ReparseTarget[1];
  149. } MountPointReparseBuffer;
  150. struct {
  151. unsigned char ReparseTarget[1];
  152. } GenericReparseBuffer;
  153. };
  154. } REPARSE_DATA_BUFFER;
  155. #define SECS_BETWEEN_EPOCHS (__int64)11644473600
  156. #define SECS_TO_100NS (__int64)10000000
  157. static inline time_t FileTimeToUnixTime(const FILETIME FileTime)
  158. {
  159. __int64 UnixTime;
  160. long *nsec = NULL;
  161. SYSTEMTIME SystemTime;
  162. FileTimeToSystemTime(&FileTime, &SystemTime);
  163. UnixTime = ((__int64)FileTime.dwHighDateTime << 32) +
  164. FileTime.dwLowDateTime;
  165. UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
  166. if (nsec) {
  167. *nsec = (UnixTime % SECS_TO_100NS) * (__int64)100;
  168. }
  169. UnixTime /= SECS_TO_100NS; /* now convert to seconds */
  170. if ((time_t)UnixTime != UnixTime) {
  171. UnixTime = 0;
  172. }
  173. return (time_t)UnixTime;
  174. }
  175. CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */
  176. HINSTANCE kernel32;
  177. HANDLE hFile;
  178. DWORD dwRet;
  179. typedef BOOL (WINAPI *gfpnh_func)(HANDLE, LPTSTR, DWORD, DWORD);
  180. gfpnh_func pGetFinalPathNameByHandle;
  181. kernel32 = LoadLibrary("kernel32.dll");
  182. if (kernel32) {
  183. pGetFinalPathNameByHandle = (gfpnh_func)GetProcAddress(kernel32, "GetFinalPathNameByHandleA");
  184. if (pGetFinalPathNameByHandle == NULL) {
  185. return -1;
  186. }
  187. } else {
  188. return -1;
  189. }
  190. hFile = CreateFile(link, // file to open
  191. GENERIC_READ, // open for reading
  192. FILE_SHARE_READ, // share for reading
  193. NULL, // default security
  194. OPEN_EXISTING, // existing file only
  195. FILE_FLAG_BACKUP_SEMANTICS, // normal file
  196. NULL); // no attr. template
  197. if( hFile == INVALID_HANDLE_VALUE) {
  198. return -1;
  199. }
  200. dwRet = pGetFinalPathNameByHandle(hFile, target, MAXPATHLEN, VOLUME_NAME_DOS);
  201. if(dwRet >= MAXPATHLEN) {
  202. return -1;
  203. }
  204. CloseHandle(hFile);
  205. if(dwRet > 4) {
  206. /* Skip first 4 characters if they are "\??\" */
  207. if(target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') {
  208. char tmp[MAXPATHLEN];
  209. unsigned int offset = 4;
  210. dwRet -= 4;
  211. /* \??\UNC\ */
  212. if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') {
  213. offset += 2;
  214. dwRet -= 2;
  215. target[offset] = '\\';
  216. }
  217. memcpy(tmp, target + offset, dwRet);
  218. memcpy(target, tmp, dwRet);
  219. }
  220. }
  221. target[dwRet] = '\0';
  222. return dwRet;
  223. }
  224. /* }}} */
  225. CWD_API int php_sys_stat(const char *path, struct stat *buf) /* {{{ */
  226. {
  227. return php_sys_stat_ex(path, buf, 0);
  228. }
  229. /* }}} */
  230. CWD_API int php_sys_lstat(const char *path, struct stat *buf) /* {{{ */
  231. {
  232. return php_sys_stat_ex(path, buf, 1);
  233. }
  234. /* }}} */
  235. CWD_API int php_sys_stat_ex(const char *path, struct stat *buf, int lstat) /* {{{ */
  236. {
  237. WIN32_FILE_ATTRIBUTE_DATA data;
  238. __int64 t;
  239. const size_t path_len = strlen(path);
  240. if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) {
  241. return stat(path, buf);
  242. }
  243. if (path_len >= 1 && path[1] == ':') {
  244. if (path[0] >= 'A' && path[0] <= 'Z') {
  245. buf->st_dev = buf->st_rdev = path[0] - 'A';
  246. } else {
  247. buf->st_dev = buf->st_rdev = path[0] - 'a';
  248. }
  249. } else if (IS_UNC_PATH(path, path_len)) {
  250. buf->st_dev = buf->st_rdev = 0;
  251. } else {
  252. char cur_path[MAXPATHLEN+1];
  253. DWORD len = sizeof(cur_path);
  254. char *tmp = cur_path;
  255. while(1) {
  256. DWORD r = GetCurrentDirectory(len, tmp);
  257. if (r < len) {
  258. if (tmp[1] == ':') {
  259. if (path[0] >= 'A' && path[0] <= 'Z') {
  260. buf->st_dev = buf->st_rdev = path[0] - 'A';
  261. } else {
  262. buf->st_dev = buf->st_rdev = path[0] - 'a';
  263. }
  264. } else {
  265. buf->st_dev = buf->st_rdev = -1;
  266. }
  267. break;
  268. } else if (!r) {
  269. buf->st_dev = buf->st_rdev = -1;
  270. break;
  271. } else {
  272. len = r+1;
  273. tmp = (char*)malloc(len);
  274. }
  275. }
  276. if (tmp != cur_path) {
  277. free(tmp);
  278. }
  279. }
  280. buf->st_uid = buf->st_gid = buf->st_ino = 0;
  281. if (lstat && data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
  282. /* File is a reparse point. Get the target */
  283. HANDLE hLink = NULL;
  284. REPARSE_DATA_BUFFER * pbuffer;
  285. unsigned int retlength = 0;
  286. TSRM_ALLOCA_FLAG(use_heap_large);
  287. hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
  288. if(hLink == INVALID_HANDLE_VALUE) {
  289. return -1;
  290. }
  291. pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
  292. if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
  293. tsrm_free_alloca(pbuffer, use_heap_large);
  294. CloseHandle(hLink);
  295. return -1;
  296. }
  297. CloseHandle(hLink);
  298. if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
  299. buf->st_mode = S_IFLNK;
  300. buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
  301. }
  302. #if 0 /* Not used yet */
  303. else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
  304. buf->st_mode |=;
  305. }
  306. #endif
  307. tsrm_free_alloca(pbuffer, use_heap_large);
  308. } else {
  309. buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG;
  310. buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
  311. }
  312. if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  313. int len = strlen(path);
  314. if (path[len-4] == '.') {
  315. if (_memicmp(path+len-3, "exe", 3) == 0 ||
  316. _memicmp(path+len-3, "com", 3) == 0 ||
  317. _memicmp(path+len-3, "bat", 3) == 0 ||
  318. _memicmp(path+len-3, "cmd", 3) == 0) {
  319. buf->st_mode |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
  320. }
  321. }
  322. }
  323. buf->st_nlink = 1;
  324. t = data.nFileSizeHigh;
  325. t = t << 32;
  326. t |= data.nFileSizeLow;
  327. buf->st_size = t;
  328. buf->st_atime = FileTimeToUnixTime(data.ftLastAccessTime);
  329. buf->st_ctime = FileTimeToUnixTime(data.ftCreationTime);
  330. buf->st_mtime = FileTimeToUnixTime(data.ftLastWriteTime);
  331. return 0;
  332. }
  333. /* }}} */
  334. #endif
  335. static int php_is_dir_ok(const cwd_state *state) /* {{{ */
  336. {
  337. struct stat buf;
  338. if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode))
  339. return (0);
  340. return (1);
  341. }
  342. /* }}} */
  343. static int php_is_file_ok(const cwd_state *state) /* {{{ */
  344. {
  345. struct stat buf;
  346. if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode))
  347. return (0);
  348. return (1);
  349. }
  350. /* }}} */
  351. static void cwd_globals_ctor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */
  352. {
  353. CWD_STATE_COPY(&cwd_g->cwd, &main_cwd_state);
  354. cwd_g->realpath_cache_size = 0;
  355. cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE;
  356. cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL;
  357. memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache));
  358. }
  359. /* }}} */
  360. static void cwd_globals_dtor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */
  361. {
  362. CWD_STATE_FREE(&cwd_g->cwd);
  363. realpath_cache_clean(TSRMLS_C);
  364. }
  365. /* }}} */
  366. CWD_API void virtual_cwd_startup(void) /* {{{ */
  367. {
  368. char cwd[MAXPATHLEN];
  369. char *result;
  370. #ifdef NETWARE
  371. result = getcwdpath(cwd, NULL, 1);
  372. if(result)
  373. {
  374. char *c=cwd;
  375. while(c = strchr(c, '\\'))
  376. {
  377. *c='/';
  378. ++c;
  379. }
  380. }
  381. #else
  382. result = getcwd(cwd, sizeof(cwd));
  383. #endif
  384. if (!result) {
  385. cwd[0] = '\0';
  386. }
  387. main_cwd_state.cwd_length = strlen(cwd);
  388. #ifdef TSRM_WIN32
  389. if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') {
  390. cwd[0] = toupper(cwd[0]);
  391. }
  392. #endif
  393. main_cwd_state.cwd = strdup(cwd);
  394. #ifdef ZTS
  395. ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor);
  396. #else
  397. cwd_globals_ctor(&cwd_globals TSRMLS_CC);
  398. #endif
  399. #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
  400. cwd_mutex = tsrm_mutex_alloc();
  401. #endif
  402. }
  403. /* }}} */
  404. CWD_API void virtual_cwd_shutdown(void) /* {{{ */
  405. {
  406. #ifndef ZTS
  407. cwd_globals_dtor(&cwd_globals TSRMLS_CC);
  408. #endif
  409. #if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
  410. tsrm_mutex_free(cwd_mutex);
  411. #endif
  412. free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */
  413. }
  414. /* }}} */
  415. CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC) /* {{{ */
  416. {
  417. cwd_state *state;
  418. state = &CWDG(cwd);
  419. if (state->cwd_length == 0) {
  420. char *retval;
  421. *length = 1;
  422. retval = (char *) malloc(2);
  423. if (retval == NULL) {
  424. return NULL;
  425. }
  426. retval[0] = DEFAULT_SLASH;
  427. retval[1] = '\0';
  428. return retval;
  429. }
  430. #ifdef TSRM_WIN32
  431. /* If we have something like C: */
  432. if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') {
  433. char *retval;
  434. *length = state->cwd_length+1;
  435. retval = (char *) malloc(*length+1);
  436. if (retval == NULL) {
  437. return NULL;
  438. }
  439. memcpy(retval, state->cwd, *length);
  440. retval[0] = toupper(retval[0]);
  441. retval[*length-1] = DEFAULT_SLASH;
  442. retval[*length] = '\0';
  443. return retval;
  444. }
  445. #endif
  446. *length = state->cwd_length;
  447. return strdup(state->cwd);
  448. }
  449. /* }}} */
  450. /* Same semantics as UNIX getcwd() */
  451. CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC) /* {{{ */
  452. {
  453. size_t length;
  454. char *cwd;
  455. cwd = virtual_getcwd_ex(&length TSRMLS_CC);
  456. if (buf == NULL) {
  457. return cwd;
  458. }
  459. if (length > size-1) {
  460. free(cwd);
  461. errno = ERANGE; /* Is this OK? */
  462. return NULL;
  463. }
  464. memcpy(buf, cwd, length+1);
  465. free(cwd);
  466. return buf;
  467. }
  468. /* }}} */
  469. #ifdef PHP_WIN32
  470. static inline unsigned long realpath_cache_key(const char *path, int path_len TSRMLS_DC) /* {{{ */
  471. {
  472. register unsigned long h;
  473. char *bucket_key_start = tsrm_win32_get_path_sid_key(path TSRMLS_CC);
  474. char *bucket_key = (char *)bucket_key_start;
  475. const char *e = bucket_key + strlen(bucket_key);
  476. if (!bucket_key) {
  477. return 0;
  478. }
  479. for (h = 2166136261U; bucket_key < e;) {
  480. h *= 16777619;
  481. h ^= *bucket_key++;
  482. }
  483. HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start);
  484. return h;
  485. }
  486. /* }}} */
  487. #else
  488. static inline unsigned long realpath_cache_key(const char *path, int path_len) /* {{{ */
  489. {
  490. register unsigned long h;
  491. const char *e = path + path_len;
  492. for (h = 2166136261U; path < e;) {
  493. h *= 16777619;
  494. h ^= *path++;
  495. }
  496. return h;
  497. }
  498. /* }}} */
  499. #endif /* defined(PHP_WIN32) */
  500. CWD_API void realpath_cache_clean(TSRMLS_D) /* {{{ */
  501. {
  502. int i;
  503. for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) {
  504. realpath_cache_bucket *p = CWDG(realpath_cache)[i];
  505. while (p != NULL) {
  506. realpath_cache_bucket *r = p;
  507. p = p->next;
  508. free(r);
  509. }
  510. CWDG(realpath_cache)[i] = NULL;
  511. }
  512. CWDG(realpath_cache_size) = 0;
  513. }
  514. /* }}} */
  515. CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC) /* {{{ */
  516. {
  517. #ifdef PHP_WIN32
  518. unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC);
  519. #else
  520. unsigned long key = realpath_cache_key(path, path_len);
  521. #endif
  522. unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
  523. realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
  524. while (*bucket != NULL) {
  525. if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
  526. memcmp(path, (*bucket)->path, path_len) == 0) {
  527. realpath_cache_bucket *r = *bucket;
  528. *bucket = (*bucket)->next;
  529. CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
  530. free(r);
  531. return;
  532. } else {
  533. bucket = &(*bucket)->next;
  534. }
  535. }
  536. }
  537. /* }}} */
  538. static inline void realpath_cache_add(const char *path, int path_len, const char *realpath, int realpath_len, int is_dir, time_t t TSRMLS_DC) /* {{{ */
  539. {
  540. long size = sizeof(realpath_cache_bucket) + path_len + 1;
  541. int same = 1;
  542. if (realpath_len != path_len ||
  543. memcmp(path, realpath, path_len) != 0) {
  544. size += realpath_len + 1;
  545. same = 0;
  546. }
  547. if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
  548. realpath_cache_bucket *bucket = malloc(size);
  549. unsigned long n;
  550. if (bucket == NULL) {
  551. return;
  552. }
  553. #ifdef PHP_WIN32
  554. bucket->key = realpath_cache_key(path, path_len TSRMLS_CC);
  555. #else
  556. bucket->key = realpath_cache_key(path, path_len);
  557. #endif
  558. bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
  559. memcpy(bucket->path, path, path_len+1);
  560. bucket->path_len = path_len;
  561. if (same) {
  562. bucket->realpath = bucket->path;
  563. } else {
  564. bucket->realpath = bucket->path + (path_len + 1);
  565. memcpy(bucket->realpath, realpath, realpath_len+1);
  566. }
  567. bucket->realpath_len = realpath_len;
  568. bucket->is_dir = is_dir;
  569. #ifdef PHP_WIN32
  570. bucket->is_rvalid = 0;
  571. bucket->is_readable = 0;
  572. bucket->is_wvalid = 0;
  573. bucket->is_writable = 0;
  574. #endif
  575. bucket->expires = t + CWDG(realpath_cache_ttl);
  576. n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
  577. bucket->next = CWDG(realpath_cache)[n];
  578. CWDG(realpath_cache)[n] = bucket;
  579. CWDG(realpath_cache_size) += size;
  580. }
  581. }
  582. /* }}} */
  583. static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
  584. {
  585. #ifdef PHP_WIN32
  586. unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC);
  587. #else
  588. unsigned long key = realpath_cache_key(path, path_len);
  589. #endif
  590. unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
  591. realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
  592. while (*bucket != NULL) {
  593. if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
  594. realpath_cache_bucket *r = *bucket;
  595. *bucket = (*bucket)->next;
  596. CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
  597. free(r);
  598. } else if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
  599. memcmp(path, (*bucket)->path, path_len) == 0) {
  600. return *bucket;
  601. } else {
  602. bucket = &(*bucket)->next;
  603. }
  604. }
  605. return NULL;
  606. }
  607. /* }}} */
  608. CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
  609. {
  610. return realpath_cache_find(path, path_len, t TSRMLS_CC);
  611. }
  612. /* }}} */
  613. CWD_API int realpath_cache_size(TSRMLS_D)
  614. {
  615. return CWDG(realpath_cache_size);
  616. }
  617. CWD_API int realpath_cache_max_buckets(TSRMLS_D)
  618. {
  619. return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
  620. }
  621. CWD_API realpath_cache_bucket** realpath_cache_get_buckets(TSRMLS_D)
  622. {
  623. return CWDG(realpath_cache);
  624. }
  625. #undef LINK_MAX
  626. #define LINK_MAX 32
  627. static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, int use_realpath, int is_dir, int *link_is_dir TSRMLS_DC) /* {{{ */
  628. {
  629. int i, j, save;
  630. int directory = 0;
  631. #ifdef TSRM_WIN32
  632. WIN32_FIND_DATA data;
  633. HANDLE hFind;
  634. TSRM_ALLOCA_FLAG(use_heap_large)
  635. #else
  636. struct stat st;
  637. #endif
  638. realpath_cache_bucket *bucket;
  639. char *tmp;
  640. TSRM_ALLOCA_FLAG(use_heap)
  641. while (1) {
  642. if (len <= start) {
  643. return start;
  644. }
  645. i = len;
  646. while (i > start && !IS_SLASH(path[i-1])) {
  647. i--;
  648. }
  649. if (i == len ||
  650. (i == len - 1 && path[i] == '.')) {
  651. /* remove double slashes and '.' */
  652. len = i - 1;
  653. is_dir = 1;
  654. continue;
  655. } else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') {
  656. /* remove '..' and previous directory */
  657. if (i - 1 <= start) {
  658. return start ? start : len;
  659. }
  660. j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC);
  661. if (j > start) {
  662. j--;
  663. while (j > start && !IS_SLASH(path[j])) {
  664. j--;
  665. }
  666. if (!start) {
  667. /* leading '..' must not be removed in case of relative path */
  668. if (j == 0 && path[0] == '.' && path[1] == '.' &&
  669. IS_SLASH(path[2])) {
  670. path[3] = '.';
  671. path[4] = '.';
  672. path[5] = DEFAULT_SLASH;
  673. j = 5;
  674. } else if (j > 0 &&
  675. path[j+1] == '.' && path[j+2] == '.' &&
  676. IS_SLASH(path[j+3])) {
  677. j += 4;
  678. path[j++] = '.';
  679. path[j++] = '.';
  680. path[j] = DEFAULT_SLASH;
  681. }
  682. }
  683. } else if (!start && !j) {
  684. /* leading '..' must not be removed in case of relative path */
  685. path[0] = '.';
  686. path[1] = '.';
  687. path[2] = DEFAULT_SLASH;
  688. j = 2;
  689. }
  690. return j;
  691. }
  692. path[len] = 0;
  693. save = (use_realpath != CWD_EXPAND);
  694. if (start && save && CWDG(realpath_cache_size_limit)) {
  695. /* cache lookup for absolute path */
  696. if (!*t) {
  697. *t = time(0);
  698. }
  699. if ((bucket = realpath_cache_find(path, len, *t TSRMLS_CC)) != NULL) {
  700. if (is_dir && !bucket->is_dir) {
  701. /* not a directory */
  702. return -1;
  703. } else {
  704. if (link_is_dir) {
  705. *link_is_dir = bucket->is_dir;
  706. }
  707. memcpy(path, bucket->realpath, bucket->realpath_len + 1);
  708. return bucket->realpath_len;
  709. }
  710. }
  711. }
  712. #ifdef TSRM_WIN32
  713. if (save && (hFind = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) {
  714. if (use_realpath == CWD_REALPATH) {
  715. /* file not found */
  716. return -1;
  717. }
  718. /* continue resolution anyway but don't save result in the cache */
  719. save = 0;
  720. }
  721. if (save) {
  722. FindClose(hFind);
  723. }
  724. tmp = tsrm_do_alloca(len+1, use_heap);
  725. memcpy(tmp, path, len+1);
  726. if(save &&
  727. !(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') &&
  728. (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
  729. /* File is a reparse point. Get the target */
  730. HANDLE hLink = NULL;
  731. REPARSE_DATA_BUFFER * pbuffer;
  732. unsigned int retlength = 0;
  733. int bufindex = 0, isabsolute = 0;
  734. wchar_t * reparsetarget;
  735. BOOL isVolume = FALSE;
  736. char printname[MAX_PATH];
  737. char substitutename[MAX_PATH];
  738. int printname_len, substitutename_len;
  739. int substitutename_off = 0;
  740. if(++(*ll) > LINK_MAX) {
  741. return -1;
  742. }
  743. hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
  744. if(hLink == INVALID_HANDLE_VALUE) {
  745. return -1;
  746. }
  747. pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
  748. if (pbuffer == NULL) {
  749. return -1;
  750. }
  751. if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
  752. tsrm_free_alloca(pbuffer, use_heap_large);
  753. CloseHandle(hLink);
  754. return -1;
  755. }
  756. CloseHandle(hLink);
  757. if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
  758. reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget;
  759. printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR);
  760. isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0;
  761. if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
  762. reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR),
  763. printname_len + 1,
  764. printname, MAX_PATH, NULL, NULL
  765. )) {
  766. tsrm_free_alloca(pbuffer, use_heap_large);
  767. return -1;
  768. };
  769. printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR);
  770. printname[printname_len] = 0;
  771. substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
  772. if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
  773. reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
  774. substitutename_len + 1,
  775. substitutename, MAX_PATH, NULL, NULL
  776. )) {
  777. tsrm_free_alloca(pbuffer, use_heap_large);
  778. return -1;
  779. };
  780. substitutename[substitutename_len] = 0;
  781. }
  782. else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
  783. isabsolute = 1;
  784. reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget;
  785. printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR);
  786. if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
  787. reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR),
  788. printname_len + 1,
  789. printname, MAX_PATH, NULL, NULL
  790. )) {
  791. tsrm_free_alloca(pbuffer, use_heap_large);
  792. return -1;
  793. };
  794. printname[pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)] = 0;
  795. substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
  796. if (!WideCharToMultiByte(CP_THREAD_ACP, 0,
  797. reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
  798. substitutename_len + 1,
  799. substitutename, MAX_PATH, NULL, NULL
  800. )) {
  801. tsrm_free_alloca(pbuffer, use_heap_large);
  802. return -1;
  803. };
  804. substitutename[substitutename_len] = 0;
  805. } else {
  806. tsrm_free_alloca(pbuffer, use_heap_large);
  807. return -1;
  808. }
  809. if(isabsolute && substitutename_len > 4) {
  810. /* Do not resolve volumes (for now). A mounted point can
  811. target a volume without a drive, it is not certain that
  812. all IO functions we use in php and its deps support
  813. path with volume GUID instead of the DOS way, like:
  814. d:\test\mnt\foo
  815. \\?\Volume{62d1c3f8-83b9-11de-b108-806e6f6e6963}\foo
  816. */
  817. if (strncmp(substitutename, "\\??\\Volume{",11) == 0
  818. || strncmp(substitutename, "\\\\?\\Volume{",11) == 0
  819. || strncmp(substitutename, "\\??\\UNC\\", 8) == 0
  820. ) {
  821. isVolume = TRUE;
  822. substitutename_off = 0;
  823. } else
  824. /* do not use the \??\ and \\?\ prefix*/
  825. if (strncmp(substitutename, "\\??\\", 4) == 0
  826. || strncmp(substitutename, "\\\\?\\", 4) == 0) {
  827. substitutename_off = 4;
  828. }
  829. }
  830. if (!isVolume) {
  831. char * tmp2 = substitutename + substitutename_off;
  832. for(bufindex = 0; bufindex < (substitutename_len - substitutename_off); bufindex++) {
  833. *(path + bufindex) = *(tmp2 + bufindex);
  834. }
  835. *(path + bufindex) = 0;
  836. j = bufindex;
  837. } else {
  838. j = len;
  839. }
  840. #if VIRTUAL_CWD_DEBUG
  841. fprintf(stderr, "reparse: print: %s ", printname);
  842. fprintf(stderr, "sub: %s ", substitutename);
  843. fprintf(stderr, "resolved: %s ", path);
  844. #endif
  845. tsrm_free_alloca(pbuffer, use_heap_large);
  846. if(isabsolute == 1) {
  847. if (!((j == 3) && (path[1] == ':') && (path[2] == '\\'))) {
  848. /* use_realpath is 0 in the call below coz path is absolute*/
  849. j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory TSRMLS_CC);
  850. if(j < 0) {
  851. tsrm_free_alloca(tmp, use_heap);
  852. return -1;
  853. }
  854. }
  855. }
  856. else {
  857. if(i + j >= MAXPATHLEN - 1) {
  858. tsrm_free_alloca(tmp, use_heap);
  859. return -1;
  860. }
  861. memmove(path+i, path, j+1);
  862. memcpy(path, tmp, i-1);
  863. path[i-1] = DEFAULT_SLASH;
  864. j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
  865. if(j < 0) {
  866. tsrm_free_alloca(tmp, use_heap);
  867. return -1;
  868. }
  869. }
  870. directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  871. if(link_is_dir) {
  872. *link_is_dir = directory;
  873. }
  874. }
  875. else {
  876. if (save) {
  877. directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  878. if (is_dir && !directory) {
  879. /* not a directory */
  880. return -1;
  881. }
  882. }
  883. #elif defined(NETWARE)
  884. save = 0;
  885. tmp = tsrm_do_alloca(len+1, use_heap);
  886. memcpy(tmp, path, len+1);
  887. #else
  888. if (save && php_sys_lstat(path, &st) < 0) {
  889. if (use_realpath == CWD_REALPATH) {
  890. /* file not found */
  891. return -1;
  892. }
  893. /* continue resolution anyway but don't save result in the cache */
  894. save = 0;
  895. }
  896. tmp = tsrm_do_alloca(len+1, use_heap);
  897. memcpy(tmp, path, len+1);
  898. if (save && S_ISLNK(st.st_mode)) {
  899. if (++(*ll) > LINK_MAX || (j = php_sys_readlink(tmp, path, MAXPATHLEN)) < 0) {
  900. /* too many links or broken symlinks */
  901. tsrm_free_alloca(tmp, use_heap);
  902. return -1;
  903. }
  904. path[j] = 0;
  905. if (IS_ABSOLUTE_PATH(path, j)) {
  906. j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
  907. if (j < 0) {
  908. tsrm_free_alloca(tmp, use_heap);
  909. return -1;
  910. }
  911. } else {
  912. if (i + j >= MAXPATHLEN-1) {
  913. tsrm_free_alloca(tmp, use_heap);
  914. return -1; /* buffer overflow */
  915. }
  916. memmove(path+i, path, j+1);
  917. memcpy(path, tmp, i-1);
  918. path[i-1] = DEFAULT_SLASH;
  919. j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
  920. if (j < 0) {
  921. tsrm_free_alloca(tmp, use_heap);
  922. return -1;
  923. }
  924. }
  925. if (link_is_dir) {
  926. *link_is_dir = directory;
  927. }
  928. } else {
  929. if (save) {
  930. directory = S_ISDIR(st.st_mode);
  931. if (link_is_dir) {
  932. *link_is_dir = directory;
  933. }
  934. if (is_dir && !directory) {
  935. /* not a directory */
  936. return -1;
  937. }
  938. }
  939. #endif
  940. if (i - 1 <= start) {
  941. j = start;
  942. } else {
  943. /* some leading directories may be unaccessable */
  944. j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL TSRMLS_CC);
  945. if (j > start) {
  946. path[j++] = DEFAULT_SLASH;
  947. }
  948. }
  949. #ifdef TSRM_WIN32
  950. if (j < 0 || j + len - i >= MAXPATHLEN-1) {
  951. tsrm_free_alloca(tmp, use_heap);
  952. return -1;
  953. }
  954. if (save) {
  955. i = strlen(data.cFileName);
  956. memcpy(path+j, data.cFileName, i+1);
  957. j += i;
  958. } else {
  959. /* use the original file or directory name as it wasn't found */
  960. memcpy(path+j, tmp+i, len-i+1);
  961. j += (len-i);
  962. }
  963. }
  964. #else
  965. if (j < 0 || j + len - i >= MAXPATHLEN-1) {
  966. tsrm_free_alloca(tmp, use_heap);
  967. return -1;
  968. }
  969. memcpy(path+j, tmp+i, len-i+1);
  970. j += (len-i);
  971. }
  972. #endif
  973. if (save && start && CWDG(realpath_cache_size_limit)) {
  974. /* save absolute path in the cache */
  975. realpath_cache_add(tmp, len, path, j, directory, *t TSRMLS_CC);
  976. }
  977. tsrm_free_alloca(tmp, use_heap);
  978. return j;
  979. }
  980. }
  981. /* }}} */
  982. /* Resolve path relatively to state and put the real path into state */
  983. /* returns 0 for ok, 1 for error */
  984. CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */
  985. {
  986. int path_length = strlen(path);
  987. char resolved_path[MAXPATHLEN];
  988. int start = 1;
  989. int ll = 0;
  990. time_t t;
  991. int ret;
  992. int add_slash;
  993. void *tmp;
  994. TSRMLS_FETCH();
  995. if (path_length == 0 || path_length >= MAXPATHLEN-1) {
  996. #ifdef TSRM_WIN32
  997. # if _MSC_VER < 1300
  998. errno = EINVAL;
  999. # else
  1000. _set_errno(EINVAL);
  1001. # endif
  1002. #else
  1003. errno = EINVAL;
  1004. #endif
  1005. return 1;
  1006. }
  1007. #if VIRTUAL_CWD_DEBUG
  1008. fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path);
  1009. #endif
  1010. /* cwd_length can be 0 when getcwd() fails.
  1011. * This can happen under solaris when a dir does not have read permissions
  1012. * but *does* have execute permissions */
  1013. if (!IS_ABSOLUTE_PATH(path, path_length)) {
  1014. if (state->cwd_length == 0) {
  1015. /* resolve relative path */
  1016. start = 0;
  1017. memcpy(resolved_path , path, path_length + 1);
  1018. } else {
  1019. int state_cwd_length = state->cwd_length;
  1020. #ifdef TSRM_WIN32
  1021. if (IS_SLASH(path[0])) {
  1022. if (state->cwd[1] == ':') {
  1023. /* Copy only the drive name */
  1024. state_cwd_length = 2;
  1025. } else if (IS_UNC_PATH(state->cwd, state->cwd_length)) {
  1026. /* Copy only the share name */
  1027. state_cwd_length = 2;
  1028. while (IS_SLASH(state->cwd[state_cwd_length])) {
  1029. state_cwd_length++;
  1030. }
  1031. while (state->cwd[state_cwd_length] &&
  1032. !IS_SLASH(state->cwd[state_cwd_length])) {
  1033. state_cwd_length++;
  1034. }
  1035. while (IS_SLASH(state->cwd[state_cwd_length])) {
  1036. state_cwd_length++;
  1037. }
  1038. while (state->cwd[state_cwd_length] &&
  1039. !IS_SLASH(state->cwd[state_cwd_length])) {
  1040. state_cwd_length++;
  1041. }
  1042. }
  1043. }
  1044. #endif
  1045. if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) {
  1046. return 1;
  1047. }
  1048. memcpy(resolved_path, state->cwd, state_cwd_length);
  1049. resolved_path[state_cwd_length] = DEFAULT_SLASH;
  1050. memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1);
  1051. path_length += state_cwd_length + 1;
  1052. }
  1053. } else {
  1054. #ifdef TSRM_WIN32
  1055. if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) {
  1056. resolved_path[0] = path[0];
  1057. resolved_path[1] = ':';
  1058. resolved_path[2] = DEFAULT_SLASH;
  1059. memcpy(resolved_path + 3, path + 2, path_length - 1);
  1060. path_length++;
  1061. } else
  1062. #endif
  1063. memcpy(resolved_path, path, path_length + 1);
  1064. }
  1065. #ifdef TSRM_WIN32
  1066. if (memchr(resolved_path, '*', path_length) ||
  1067. memchr(resolved_path, '?', path_length)) {
  1068. return 1;
  1069. }
  1070. #endif
  1071. #ifdef TSRM_WIN32
  1072. if (IS_UNC_PATH(resolved_path, path_length)) {
  1073. /* skip UNC name */
  1074. resolved_path[0] = DEFAULT_SLASH;
  1075. resolved_path[1] = DEFAULT_SLASH;
  1076. start = 2;
  1077. while (!IS_SLASH(resolved_path[start])) {
  1078. if (resolved_path[start] == 0) {
  1079. goto verify;
  1080. }
  1081. resolved_path[start] = toupper(resolved_path[start]);
  1082. start++;
  1083. }
  1084. resolved_path[start++] = DEFAULT_SLASH;
  1085. while (!IS_SLASH(resolved_path[start])) {
  1086. if (resolved_path[start] == 0) {
  1087. goto verify;
  1088. }
  1089. resolved_path[start] = toupper(resolved_path[start]);
  1090. start++;
  1091. }
  1092. resolved_path[start++] = DEFAULT_SLASH;
  1093. } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
  1094. /* skip DRIVE name */
  1095. resolved_path[0] = toupper(resolved_path[0]);
  1096. resolved_path[2] = DEFAULT_SLASH;
  1097. start = 3;
  1098. }
  1099. #elif defined(NETWARE)
  1100. if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
  1101. /* skip VOLUME name */
  1102. start = 0;
  1103. while (start != ':') {
  1104. if (resolved_path[start] == 0) return -1;
  1105. start++;
  1106. }
  1107. start++;
  1108. if (!IS_SLASH(resolved_path[start])) return -1;
  1109. resolved_path[start++] = DEFAULT_SLASH;
  1110. }
  1111. #endif
  1112. add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]);
  1113. t = CWDG(realpath_cache_ttl) ? 0 : -1;
  1114. path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL TSRMLS_CC);
  1115. if (path_length < 0) {
  1116. errno = ENOENT;
  1117. return 1;
  1118. }
  1119. if (!start && !path_length) {
  1120. resolved_path[path_length++] = '.';
  1121. }
  1122. if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) {
  1123. if (path_length >= MAXPATHLEN-1) {
  1124. return -1;
  1125. }
  1126. resolved_path[path_length++] = DEFAULT_SLASH;
  1127. }
  1128. resolved_path[path_length] = 0;
  1129. #ifdef TSRM_WIN32
  1130. verify:
  1131. #endif
  1132. if (verify_path) {
  1133. cwd_state old_state;
  1134. CWD_STATE_COPY(&old_state, state);
  1135. state->cwd_length = path_length;
  1136. tmp = realloc(state->cwd, state->cwd_length+1);
  1137. if (tmp == NULL) {
  1138. #if VIRTUAL_CWD_DEBUG
  1139. fprintf (stderr, "Out of memory\n");
  1140. #endif
  1141. return 1;
  1142. }
  1143. state->cwd = (char *) tmp;
  1144. memcpy(state->cwd, resolved_path, state->cwd_length+1);
  1145. if (verify_path(state)) {
  1146. CWD_STATE_FREE(state);
  1147. *state = old_state;
  1148. ret = 1;
  1149. } else {
  1150. CWD_STATE_FREE(&old_state);
  1151. ret = 0;
  1152. }
  1153. } else {
  1154. state->cwd_length = path_length;
  1155. tmp = realloc(state->cwd, state->cwd_length+1);
  1156. if (tmp == NULL) {
  1157. #if VIRTUAL_CWD_DEBUG
  1158. fprintf (stderr, "Out of memory\n");
  1159. #endif
  1160. return 1;
  1161. }
  1162. state->cwd = (char *) tmp;
  1163. memcpy(state->cwd, resolved_path, state->cwd_length+1);
  1164. ret = 0;
  1165. }
  1166. #if VIRTUAL_CWD_DEBUG
  1167. fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd);
  1168. #endif
  1169. return (ret);
  1170. }
  1171. /* }}} */
  1172. CWD_API int virtual_chdir(const char *path TSRMLS_DC) /* {{{ */
  1173. {
  1174. return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH)?-1:0;
  1175. }
  1176. /* }}} */
  1177. CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC) /* {{{ */
  1178. {
  1179. int length = strlen(path);
  1180. char *temp;
  1181. int retval;
  1182. TSRM_ALLOCA_FLAG(use_heap)
  1183. if (length == 0) {
  1184. return 1; /* Can't cd to empty string */
  1185. }
  1186. while(--length >= 0 && !IS_SLASH(path[length])) {
  1187. }
  1188. if (length == -1) {
  1189. /* No directory only file name */
  1190. errno = ENOENT;
  1191. return -1;
  1192. }
  1193. if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */
  1194. length++;
  1195. }
  1196. temp = (char *) tsrm_do_alloca(length+1, use_heap);
  1197. memcpy(temp, path, length);
  1198. temp[length] = 0;
  1199. #if VIRTUAL_CWD_DEBUG
  1200. fprintf (stderr, "Changing directory to %s\n", temp);
  1201. #endif
  1202. retval = p_chdir(temp TSRMLS_CC);
  1203. tsrm_free_alloca(temp, use_heap);
  1204. return retval;
  1205. }
  1206. /* }}} */
  1207. CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */
  1208. {
  1209. cwd_state new_state;
  1210. char *retval;
  1211. char cwd[MAXPATHLEN];
  1212. /* realpath("") returns CWD */
  1213. if (!*path) {
  1214. new_state.cwd = (char*)malloc(1);
  1215. if (new_state.cwd == NULL) {
  1216. retval = NULL;
  1217. goto end;
  1218. }
  1219. new_state.cwd[0] = '\0';
  1220. new_state.cwd_length = 0;
  1221. if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
  1222. path = cwd;
  1223. }
  1224. } else if (!IS_ABSOLUTE_PATH(path, strlen(path))) {
  1225. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1226. } else {
  1227. new_state.cwd = (char*)malloc(1);
  1228. if (new_state.cwd == NULL) {
  1229. retval = NULL;
  1230. goto end;
  1231. }
  1232. new_state.cwd[0] = '\0';
  1233. new_state.cwd_length = 0;
  1234. }
  1235. if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)==0) {
  1236. int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length;
  1237. memcpy(real_path, new_state.cwd, len);
  1238. real_path[len] = '\0';
  1239. retval = real_path;
  1240. } else {
  1241. retval = NULL;
  1242. }
  1243. CWD_STATE_FREE(&new_state);
  1244. end:
  1245. return retval;
  1246. }
  1247. /* }}} */
  1248. CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC) /* {{{ */
  1249. {
  1250. cwd_state new_state;
  1251. int retval;
  1252. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1253. retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH);
  1254. *filepath = new_state.cwd;
  1255. return retval;
  1256. }
  1257. /* }}} */
  1258. CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC) /* {{{ */
  1259. {
  1260. return virtual_filepath_ex(path, filepath, php_is_file_ok TSRMLS_CC);
  1261. }
  1262. /* }}} */
  1263. CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC) /* {{{ */
  1264. {
  1265. cwd_state new_state;
  1266. FILE *f;
  1267. if (path[0] == '\0') { /* Fail to open empty path */
  1268. return NULL;
  1269. }
  1270. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1271. if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
  1272. CWD_STATE_FREE(&new_state);
  1273. return NULL;
  1274. }
  1275. f = fopen(new_state.cwd, mode);
  1276. CWD_STATE_FREE(&new_state);
  1277. return f;
  1278. }
  1279. /* }}} */
  1280. CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC) /* {{{ */
  1281. {
  1282. cwd_state new_state;
  1283. int ret;
  1284. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1285. if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
  1286. CWD_STATE_FREE(&new_state);
  1287. return -1;
  1288. }
  1289. #if defined(TSRM_WIN32)
  1290. ret = tsrm_win32_access(new_state.cwd, mode);
  1291. #else
  1292. ret = access(new_state.cwd, mode);
  1293. #endif
  1294. CWD_STATE_FREE(&new_state);
  1295. return ret;
  1296. }
  1297. /* }}} */
  1298. #if HAVE_UTIME
  1299. #ifdef TSRM_WIN32
  1300. static void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
  1301. {
  1302. // Note that LONGLONG is a 64-bit value
  1303. LONGLONG ll;
  1304. ll = Int32x32To64(t, 10000000) + 116444736000000000;
  1305. pft->dwLowDateTime = (DWORD)ll;
  1306. pft->dwHighDateTime = ll >> 32;
  1307. }
  1308. /* }}} */
  1309. TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
  1310. {
  1311. FILETIME mtime, atime;
  1312. HANDLE hFile;
  1313. hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
  1314. OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
  1315. /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
  1316. the CreateFile operation succeeds */
  1317. if (GetLastError() == ERROR_ALREADY_EXISTS) {
  1318. SetLastError(0);
  1319. }
  1320. if ( hFile == INVALID_HANDLE_VALUE ) {
  1321. return -1;
  1322. }
  1323. if (!buf) {
  1324. SYSTEMTIME st;
  1325. GetSystemTime(&st);
  1326. SystemTimeToFileTime(&st, &mtime);
  1327. atime = mtime;
  1328. } else {
  1329. UnixTimeToFileTime(buf->modtime, &mtime);
  1330. UnixTimeToFileTime(buf->actime, &atime);
  1331. }
  1332. if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
  1333. CloseHandle(hFile);
  1334. return -1;
  1335. }
  1336. CloseHandle(hFile);
  1337. return 1;
  1338. }
  1339. /* }}} */
  1340. #endif
  1341. CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC) /* {{{ */
  1342. {
  1343. cwd_state new_state;
  1344. int ret;
  1345. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1346. if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
  1347. CWD_STATE_FREE(&new_state);
  1348. return -1;
  1349. }
  1350. #ifdef TSRM_WIN32
  1351. ret = win32_utime(new_state.cwd, buf);
  1352. #else
  1353. ret = utime(new_state.cwd, buf);
  1354. #endif
  1355. CWD_STATE_FREE(&new_state);
  1356. return ret;
  1357. }
  1358. /* }}} */
  1359. #endif
  1360. CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC) /* {{{ */
  1361. {
  1362. cwd_state new_state;
  1363. int ret;
  1364. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1365. if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
  1366. CWD_STATE_FREE(&new_state);
  1367. return -1;
  1368. }
  1369. ret = chmod(new_state.cwd, mode);
  1370. CWD_STATE_FREE(&new_state);
  1371. return ret;
  1372. }
  1373. /* }}} */
  1374. #if !defined(TSRM_WIN32) && !defined(NETWARE)
  1375. CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC) /* {{{ */
  1376. {
  1377. cwd_state new_state;
  1378. int ret;
  1379. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1380. if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
  1381. CWD_STATE_FREE(&new_state);
  1382. return -1;
  1383. }
  1384. if (link) {
  1385. #if HAVE_LCHOWN
  1386. ret = lchown(new_state.cwd, owner, group);
  1387. #else
  1388. ret = -1;
  1389. #endif
  1390. } else {
  1391. ret = chown(new_state.cwd, owner, group);
  1392. }
  1393. CWD_STATE_FREE(&new_state);
  1394. return ret;
  1395. }
  1396. /* }}} */
  1397. #endif
  1398. CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...) /* {{{ */
  1399. {
  1400. cwd_state new_state;
  1401. int f;
  1402. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1403. if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
  1404. CWD_STATE_FREE(&new_state);
  1405. return -1;
  1406. }
  1407. if (flags & O_CREAT) {
  1408. mode_t mode;
  1409. va_list arg;
  1410. va_start(arg, flags);
  1411. mode = (mode_t) va_arg(arg, int);
  1412. va_end(arg);
  1413. f = open(new_state.cwd, flags, mode);
  1414. } else {
  1415. f = open(new_state.cwd, flags);
  1416. }
  1417. CWD_STATE_FREE(&new_state);
  1418. return f;
  1419. }
  1420. /* }}} */
  1421. CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC) /* {{{ */
  1422. {
  1423. cwd_state new_state;
  1424. int f;
  1425. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1426. if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
  1427. CWD_STATE_FREE(&new_state);
  1428. return -1;
  1429. }
  1430. f = creat(new_state.cwd, mode);
  1431. CWD_STATE_FREE(&new_state);
  1432. return f;
  1433. }
  1434. /* }}} */
  1435. CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC) /* {{{ */
  1436. {
  1437. cwd_state old_state;
  1438. cwd_state new_state;
  1439. int retval;
  1440. CWD_STATE_COPY(&old_state, &CWDG(cwd));
  1441. if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND)) {
  1442. CWD_STATE_FREE(&old_state);
  1443. return -1;
  1444. }
  1445. oldname = old_state.cwd;
  1446. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1447. if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND)) {
  1448. CWD_STATE_FREE(&old_state);
  1449. CWD_STATE_FREE(&new_state);
  1450. return -1;
  1451. }
  1452. newname = new_state.cwd;
  1453. /* rename on windows will fail if newname already exists.
  1454. MoveFileEx has to be used */
  1455. #ifdef TSRM_WIN32
  1456. /* MoveFileEx returns 0 on failure, other way 'round for this function */
  1457. retval = (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) == 0) ? -1 : 0;
  1458. #else
  1459. retval = rename(oldname, newname);
  1460. #endif
  1461. CWD_STATE_FREE(&old_state);
  1462. CWD_STATE_FREE(&new_state);
  1463. return retval;
  1464. }
  1465. /* }}} */
  1466. CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */
  1467. {
  1468. cwd_state new_state;
  1469. int retval;
  1470. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1471. if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
  1472. CWD_STATE_FREE(&new_state);
  1473. return -1;
  1474. }
  1475. retval = php_sys_stat(new_state.cwd, buf);
  1476. CWD_STATE_FREE(&new_state);
  1477. return retval;
  1478. }
  1479. /* }}} */
  1480. CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */
  1481. {
  1482. cwd_state new_state;
  1483. int retval;
  1484. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1485. if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
  1486. CWD_STATE_FREE(&new_state);
  1487. return -1;
  1488. }
  1489. retval = php_sys_lstat(new_state.cwd, buf);
  1490. CWD_STATE_FREE(&new_state);
  1491. return retval;
  1492. }
  1493. /* }}} */
  1494. CWD_API int virtual_unlink(const char *path TSRMLS_DC) /* {{{ */
  1495. {
  1496. cwd_state new_state;
  1497. int retval;
  1498. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1499. if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
  1500. CWD_STATE_FREE(&new_state);
  1501. return -1;
  1502. }
  1503. retval = unlink(new_state.cwd);
  1504. CWD_STATE_FREE(&new_state);
  1505. return retval;
  1506. }
  1507. /* }}} */
  1508. CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC) /* {{{ */
  1509. {
  1510. cwd_state new_state;
  1511. int retval;
  1512. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1513. if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH)) {
  1514. CWD_STATE_FREE(&new_state);
  1515. return -1;
  1516. }
  1517. #ifdef TSRM_WIN32
  1518. retval = mkdir(new_state.cwd);
  1519. #else
  1520. retval = mkdir(new_state.cwd, mode);
  1521. #endif
  1522. CWD_STATE_FREE(&new_state);
  1523. return retval;
  1524. }
  1525. /* }}} */
  1526. CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC) /* {{{ */
  1527. {
  1528. cwd_state new_state;
  1529. int retval;
  1530. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1531. if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND)) {
  1532. CWD_STATE_FREE(&new_state);
  1533. return -1;
  1534. }
  1535. retval = rmdir(new_state.cwd);
  1536. CWD_STATE_FREE(&new_state);
  1537. return retval;
  1538. }
  1539. /* }}} */
  1540. #ifdef TSRM_WIN32
  1541. DIR *opendir(const char *name);
  1542. #endif
  1543. CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC) /* {{{ */
  1544. {
  1545. cwd_state new_state;
  1546. DIR *retval;
  1547. CWD_STATE_COPY(&new_state, &CWDG(cwd));
  1548. if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
  1549. CWD_STATE_FREE(&new_state);
  1550. return NULL;
  1551. }
  1552. retval = opendir(new_state.cwd);
  1553. CWD_STATE_FREE(&new_state);
  1554. return retval;
  1555. }
  1556. /* }}} */
  1557. #ifdef TSRM_WIN32
  1558. CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
  1559. {
  1560. return popen_ex(command, type, CWDG(cwd).cwd, NULL);
  1561. }
  1562. /* }}} */
  1563. #elif defined(NETWARE)
  1564. /* On NetWare, the trick of prepending "cd cwd; " doesn't work so we need to perform
  1565. a VCWD_CHDIR() and mutex it
  1566. */
  1567. CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
  1568. {
  1569. char prev_cwd[MAXPATHLEN];
  1570. char *getcwd_result;
  1571. FILE *retval;
  1572. getcwd_result = VCWD_GETCWD(prev_cwd, MAXPATHLEN);
  1573. if (!getcwd_result) {
  1574. return NULL;
  1575. }
  1576. #ifdef ZTS
  1577. tsrm_mutex_lock(cwd_mutex);
  1578. #endif
  1579. VCWD_CHDIR(CWDG(cwd).cwd);
  1580. retval = popen(command, type);
  1581. VCWD_CHDIR(prev_cwd);
  1582. #ifdef ZTS
  1583. tsrm_mutex_unlock(cwd_mutex);
  1584. #endif
  1585. return retval;
  1586. }
  1587. /* }}} */
  1588. #else /* Unix */
  1589. CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
  1590. {
  1591. int command_length;
  1592. int dir_length, extra = 0;
  1593. char *command_line;
  1594. char *ptr, *dir;
  1595. FILE *retval;
  1596. command_length = strlen(command);
  1597. dir_length = CWDG(cwd).cwd_length;
  1598. dir = CWDG(cwd).cwd;
  1599. while (dir_length > 0) {
  1600. if (*dir == '\'') extra+=3;
  1601. dir++;
  1602. dir_length--;
  1603. }
  1604. dir_length = CWDG(cwd).cwd_length;
  1605. dir = CWDG(cwd).cwd;
  1606. ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1);
  1607. if (!command_line) {
  1608. return NULL;
  1609. }
  1610. memcpy(ptr, "cd ", sizeof("cd ")-1);
  1611. ptr += sizeof("cd ")-1;
  1612. if (CWDG(cwd).cwd_length == 0) {
  1613. *ptr++ = DEFAULT_SLASH;
  1614. } else {
  1615. *ptr++ = '\'';
  1616. while (dir_length > 0) {
  1617. switch (*dir) {
  1618. case '\'':
  1619. *ptr++ = '\'';
  1620. *ptr++ = '\\';
  1621. *ptr++ = '\'';
  1622. /* fall-through */
  1623. default:
  1624. *ptr++ = *dir;
  1625. }
  1626. dir++;
  1627. dir_length--;
  1628. }
  1629. *ptr++ = '\'';
  1630. }
  1631. *ptr++ = ' ';
  1632. *ptr++ = ';';
  1633. *ptr++ = ' ';
  1634. memcpy(ptr, command, command_length+1);
  1635. retval = popen(command_line, type);
  1636. free(command_line);
  1637. return retval;
  1638. }
  1639. /* }}} */
  1640. #endif
  1641. CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */
  1642. {
  1643. cwd_state new_state;
  1644. char cwd[MAXPATHLEN];
  1645. /* realpath("") returns CWD */
  1646. if (!*path) {
  1647. new_state.cwd = (char*)malloc(1);
  1648. if (new_state.cwd == NULL) {
  1649. return NULL;
  1650. }
  1651. new_state.cwd[0] = '\0';
  1652. new_state.cwd_length = 0;
  1653. if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
  1654. path = cwd;
  1655. }
  1656. } else if (!IS_ABSOLUTE_PATH(path, strlen(path)) &&
  1657. VCWD_GETCWD(cwd, MAXPATHLEN)) {
  1658. new_state.cwd = strdup(cwd);
  1659. new_state.cwd_length = strlen(cwd);
  1660. } else {
  1661. new_state.cwd = (char*)malloc(1);
  1662. if (new_state.cwd == NULL) {
  1663. return NULL;
  1664. }
  1665. new_state.cwd[0] = '\0';
  1666. new_state.cwd_length = 0;
  1667. }
  1668. if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
  1669. free(new_state.cwd);
  1670. return NULL;
  1671. }
  1672. if (real_path) {
  1673. int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length;
  1674. memcpy(real_path, new_state.cwd, copy_len);
  1675. real_path[copy_len] = '\0';
  1676. free(new_state.cwd);
  1677. return real_path;
  1678. } else {
  1679. return new_state.cwd;
  1680. }
  1681. }
  1682. /* }}} */
  1683. /*
  1684. * Local variables:
  1685. * tab-width: 4
  1686. * c-basic-offset: 4
  1687. * End:
  1688. */