zend_virtual_cwd.c 49 KB

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