#ifdef TSRM_WIN32
#include <io.h>
#include "tsrm_win32.h"
+# ifndef IO_REPARSE_TAG_SYMLINK
+# define IO_REPARSE_TAG_SYMLINK 0xA000000C
+# endif
#endif
#ifdef NETWARE
#ifdef TSRM_WIN32
+#ifdef CTL_CODE
+#undef CTL_CODE
+#endif
+#define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+#define FILE_DEVICE_FILE_SYSTEM 0x00000009
+#define METHOD_BUFFERED 0
+#define FILE_ANY_ACCESS 0
+#define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 )
+
+typedef struct {
+ unsigned long ReparseTag;
+ unsigned short ReparseDataLength;
+ unsigned short Reserved;
+ union {
+ struct {
+ unsigned short SubstituteNameOffset;
+ unsigned short SubstituteNameLength;
+ unsigned short PrintNameOffset;
+ unsigned short PrintNameLength;
+ unsigned long Flags;
+ wchar_t ReparseTarget[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ unsigned short SubstituteNameOffset;
+ unsigned short SubstituteNameLength;
+ unsigned short PrintNameOffset;
+ unsigned short PrintNameLength;
+ wchar_t ReparseTarget[1];
+ } MountPointReparseBuffer;
+ struct {
+ unsigned char ReparseTarget[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER;
+
#define SECS_BETWEEN_EPOCHS (__int64)11644473600
#define SECS_TO_100NS (__int64)10000000
static inline time_t FileTimeToUnixTime(const FILETIME FileTime)
}
bucket->realpath_len = realpath_len;
bucket->is_dir = is_dir;
+#ifdef PHP_WIN32
+ bucket->is_rvalid = 0;
+ bucket->is_readable = 0;
+ bucket->is_wvalid = 0;
+ bucket->is_writable = 0;
+#endif
bucket->expires = t + CWDG(realpath_cache_ttl);
n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
bucket->next = CWDG(realpath_cache)[n];
}
/* }}} */
+CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
+{
+ return realpath_cache_find(path, path_len, t TSRMLS_CC);
+}
+/* }}} */
+
#undef LINK_MAX
#define LINK_MAX 32
#endif
realpath_cache_bucket *bucket;
char *tmp;
- TSRM_ALLOCA_FLAG(use_heap);
+ TSRM_ALLOCA_FLAG(use_heap)
+ TSRM_ALLOCA_FLAG(use_heap_large)
while (1) {
if (len <= start) {
/* continue resolution anyway but don't save result in the cache */
save = 0;
}
+
if (save) {
- directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- if (is_dir && !directory) {
- /* not a directory */
- FindClose(hFind);
- return -1;
- }
+ FindClose(hFind);
}
+
tmp = tsrm_do_alloca(len+1, use_heap);
memcpy(tmp, path, len+1);
+
+ if(save && (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+ /* File is a reparse point. Get the target */
+ HANDLE hLink = NULL;
+ REPARSE_DATA_BUFFER * pbuffer;
+ unsigned int retlength = 0, rname_off = 0;
+ int bufindex = 0, rname_len = 0, isabsolute = 0;
+ wchar_t * reparsetarget;
+
+ if(++(*ll) > LINK_MAX) {
+ return -1;
+ }
+
+ hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if(hLink == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+
+ pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
+ if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
+ tsrm_free_alloca(pbuffer, use_heap_large);
+ CloseHandle(hLink);
+ return -1;
+ }
+
+ CloseHandle(hLink);
+
+ if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+ rname_len = pbuffer->SymbolicLinkReparseBuffer.PrintNameLength/2;
+ rname_off = pbuffer->SymbolicLinkReparseBuffer.PrintNameOffset/2;
+ reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget;
+ isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0;
+ }
+ else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+ rname_len = pbuffer->MountPointReparseBuffer.PrintNameLength/2;
+ rname_off = pbuffer->MountPointReparseBuffer.PrintNameOffset/2;
+ reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget;
+ isabsolute = 1;
+ }
+ else {
+ tsrm_free_alloca(pbuffer, use_heap_large);
+ return -1;
+ }
+
+ /* Convert wide string to narrow string */
+ for(bufindex = 0; bufindex < rname_len; bufindex++) {
+ *(path + bufindex) = (char)(reparsetarget[rname_off + bufindex]);
+ }
+
+ *(path + bufindex) = 0;
+ tsrm_free_alloca(pbuffer, use_heap_large);
+ j = bufindex;
+
+ if(isabsolute == 1) {
+ /* use_realpath is 0 in the call below coz path is absolute*/
+ j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory TSRMLS_CC);
+ if(j < 0) {
+ tsrm_free_alloca(tmp, use_heap);
+ return -1;
+ }
+ }
+ else {
+ if(i + j >= MAXPATHLEN - 1) {
+ tsrm_free_alloca(tmp, use_heap);
+ return -1;
+ }
+
+ memmove(path+i, path, j+1);
+ memcpy(path, tmp, i-1);
+ path[i-1] = DEFAULT_SLASH;
+ j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
+ if(j < 0) {
+ tsrm_free_alloca(tmp, use_heap);
+ return -1;
+ }
+ }
+
+ if(link_is_dir) {
+ *link_is_dir = directory;
+ }
+ }
+ else {
+ if (save) {
+ directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ if (is_dir && !directory) {
+ /* not a directory */
+ return -1;
+ }
+ }
+
#elif defined(NETWARE)
save = 0;
tmp = tsrm_do_alloca(len+1, use_heap);
#ifdef TSRM_WIN32
if (j < 0 || j + len - i >= MAXPATHLEN-1) {
tsrm_free_alloca(tmp, use_heap);
- if (save) FindClose(hFind);
return -1;
}
if (save) {
i = strlen(data.cFileName);
memcpy(path+j, data.cFileName, i+1);
j += i;
- FindClose(hFind);
} else {
/* use the original file or directory name as it wasn't found */
memcpy(path+j, tmp+i, len-i+1);
j += (len-i);
}
+ }
#else
if (j < 0 || j + len - i >= MAXPATHLEN-1) {
tsrm_free_alloca(tmp, use_heap);
#ifdef TSRM_WIN32
#include "tsrm_win32.h"
+#include "tsrm_virtual_cwd.h"
#ifdef ZTS
static ts_rsrc_id win32_globals_id;
static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC)
{
+ HANDLE process_token = NULL;
+
globals->process = NULL;
globals->shm = NULL;
globals->process_size = 0;
globals->shm_size = 0;
globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com");
globals->impersonation_token = NULL;
+
+ /* Access check requires impersonation token. Create a duplicate token. */
+ if(OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &process_token)) {
+ DuplicateToken(process_token, SecurityImpersonation, &globals->impersonation_token);
+ }
+
+ /* impersonation_token will be closed when the process dies */
+ if(process_token != NULL) {
+ CloseHandle(process_token);
+ process_token = NULL;
+ }
}
static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC)
BYTE * psec_desc = NULL;
BOOL fAccess = FALSE;
HANDLE process_token = NULL;
+
+ realpath_cache_bucket * bucket = NULL;
+ char * real_path = NULL;
+ time_t t;
+
TSRMLS_FETCH();
if (mode == 1 /*X_OK*/) {
return GetBinaryType(pathname, &type) ? 0 : -1;
} else {
if(access(pathname, mode)) {
- return errno;
+ return errno;
}
- /* Do a full access check because access() will only check read-only attribute */
- if(mode == 0 || mode > 6) {
+ if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
+ real_path = (char *)malloc(MAX_PATH);
+ if(tsrm_realpath(pathname, real_path TSRMLS_CC) == NULL) {
+ goto Finished;
+ }
+ pathname = real_path;
+ }
+
+ if (CWDG(realpath_cache_size_limit)) {
+ t = time(0);
+ bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
+ if(bucket == NULL && real_path == NULL) {
+ /* We used the pathname directly. Call tsrm_realpath */
+ /* so that entry is created in realpath cache */
+ real_path = (char *)malloc(MAX_PATH);
+ if(tsrm_realpath(pathname, real_path TSRMLS_CC) != NULL) {
+ pathname = real_path;
+ bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
+ }
+ }
+ }
+
+ /* Do a full access check because access() will only check read-only attribute */
+ if(mode == 0 || mode > 6) {
+ if(bucket != NULL && bucket->is_rvalid) {
+ fAccess = bucket->is_readable;
+ goto Finished;
+ }
+ desired_access = FILE_GENERIC_READ;
+ } else if(mode <= 2) {
+ if(bucket != NULL && bucket->is_wvalid) {
+ fAccess = bucket->is_writable;
+ goto Finished;
+ }
+ desired_access = FILE_GENERIC_WRITE;
+ } else if(mode <= 4) {
+ if(bucket != NULL && bucket->is_rvalid) {
+ fAccess = bucket->is_readable;
+ goto Finished;
+ }
desired_access = FILE_GENERIC_READ;
- } else if(mode <= 2) {
- desired_access = FILE_GENERIC_WRITE;
- } else if(mode <= 4) {
- desired_access = FILE_GENERIC_READ;
- } else { // if(mode <= 6)
- desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+ } else { // if(mode <= 6)
+ if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
+ fAccess = bucket->is_readable & bucket->is_writable;
+ goto Finished;
+ }
+ desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+ }
+
+ if(TWG(impersonation_token) == NULL) {
+ goto Finished;
}
/* Get size of security buffer. Call is expected to fail */
goto Finished;
}
- if(TWG(impersonation_token) == NULL) {
-
- if(!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY, &process_token)) {
- goto Finished;
- }
-
- /* Access check requires impersonation token. Create a duplicate token. */
- if(!DuplicateToken(process_token, SecurityImpersonation, &TWG(impersonation_token))) {
- goto Finished;
- }
- }
-
if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
goto Finished;
}
-Finished:
-
- /* impersonation_token will be closed when the process dies */
- if(process_token != NULL) {
- CloseHandle(process_token);
- process_token = NULL;
+ /* Keep the result in realpath_cache */
+ if(bucket != NULL) {
+ if(desired_access == FILE_GENERIC_READ) {
+ bucket->is_rvalid = 1;
+ bucket->is_readable = fAccess;
+ }
+ else if(desired_access == FILE_GENERIC_WRITE) {
+ bucket->is_wvalid = 1;
+ bucket->is_writable = fAccess;
+ }
}
+Finished:
if(psec_desc != NULL) {
free(psec_desc);
psec_desc = NULL;
}
+ if(real_path != NULL) {
+ free(real_path);
+ real_path = NULL;
+ }
+
if(fAccess == FALSE) {
errno = EACCES;
return errno;
}
fflush(process->stream);
- fclose(process->stream);
+ fclose(process->stream);
WaitForSingleObject(process->prochnd, INFINITE);
GetExitCodeProcess(process->prochnd, &termstat);