From c4dd7a1a684490673e25aaf4fabec5df138854c4 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Thu, 14 Mar 2013 05:42:27 +0000 Subject: Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2. --- TSRM/LICENSE | 26 + TSRM/Makefile.am | 6 + TSRM/TODO | 2 + TSRM/TSRM.c | 794 ++++++++++++++++++ TSRM/TSRM.dsp | 186 +++++ TSRM/TSRM.h | 186 +++++ TSRM/acinclude.m4 | 5 + TSRM/build.mk | 43 + TSRM/buildconf | 33 + TSRM/config.w32 | 5 + TSRM/configure.in | 35 + TSRM/readdir.h | 45 ++ TSRM/threads.m4 | 173 ++++ TSRM/tsrm.m4 | 130 +++ TSRM/tsrm_config.w32.h | 22 + TSRM/tsrm_config_common.h | 70 ++ TSRM/tsrm_nw.c | 240 ++++++ TSRM/tsrm_nw.h | 29 + TSRM/tsrm_strtok_r.c | 63 ++ TSRM/tsrm_strtok_r.h | 6 + TSRM/tsrm_virtual_cwd.c | 1976 +++++++++++++++++++++++++++++++++++++++++++++ TSRM/tsrm_virtual_cwd.h | 338 ++++++++ TSRM/tsrm_win32.c | 723 +++++++++++++++++ TSRM/tsrm_win32.h | 110 +++ 24 files changed, 5246 insertions(+) create mode 100644 TSRM/LICENSE create mode 100644 TSRM/Makefile.am create mode 100644 TSRM/TODO create mode 100644 TSRM/TSRM.c create mode 100644 TSRM/TSRM.dsp create mode 100644 TSRM/TSRM.h create mode 100644 TSRM/acinclude.m4 create mode 100644 TSRM/build.mk create mode 100755 TSRM/buildconf create mode 100644 TSRM/config.w32 create mode 100644 TSRM/configure.in create mode 100644 TSRM/readdir.h create mode 100644 TSRM/threads.m4 create mode 100644 TSRM/tsrm.m4 create mode 100644 TSRM/tsrm_config.w32.h create mode 100644 TSRM/tsrm_config_common.h create mode 100644 TSRM/tsrm_nw.c create mode 100644 TSRM/tsrm_nw.h create mode 100644 TSRM/tsrm_strtok_r.c create mode 100644 TSRM/tsrm_strtok_r.h create mode 100644 TSRM/tsrm_virtual_cwd.c create mode 100644 TSRM/tsrm_virtual_cwd.h create mode 100644 TSRM/tsrm_win32.c create mode 100644 TSRM/tsrm_win32.h (limited to 'TSRM') diff --git a/TSRM/LICENSE b/TSRM/LICENSE new file mode 100644 index 0000000..84b8348 --- /dev/null +++ b/TSRM/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 1999-2006, Andi Gutmans, Sascha Schumann, Zeev Suraski. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Neither name of the copyright holders nor the names of their contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/TSRM/Makefile.am b/TSRM/Makefile.am new file mode 100644 index 0000000..91e585b --- /dev/null +++ b/TSRM/Makefile.am @@ -0,0 +1,6 @@ +## process this file with automake to produce Makefile.am +AUTOMAKE_OPTIONS=foreign +noinst_LTLIBRARIES=libtsrm.la +libtsrm_la_SOURCES = TSRM.c tsrm_strtok_r.c tsrm_virtual_cwd.c + +depend: diff --git a/TSRM/TODO b/TSRM/TODO new file mode 100644 index 0000000..82b4fed --- /dev/null +++ b/TSRM/TODO @@ -0,0 +1,2 @@ +- Improve the lock in ts_resource_ex() in order to cover less code. + This can probably be done by more careful hash table access diff --git a/TSRM/TSRM.c b/TSRM/TSRM.c new file mode 100644 index 0000000..efdea5c --- /dev/null +++ b/TSRM/TSRM.c @@ -0,0 +1,794 @@ +/* + +----------------------------------------------------------------------+ + | Thread Safe Resource Manager | + +----------------------------------------------------------------------+ + | Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski | + | This source file is subject to the TSRM license, that is bundled | + | with this package in the file LICENSE | + +----------------------------------------------------------------------+ + | Authors: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +#include "TSRM.h" + +#ifdef ZTS + +#include + +#if HAVE_STDARG_H +#include +#endif + +typedef struct _tsrm_tls_entry tsrm_tls_entry; + +struct _tsrm_tls_entry { + void **storage; + int count; + THREAD_T thread_id; + tsrm_tls_entry *next; +}; + + +typedef struct { + size_t size; + ts_allocate_ctor ctor; + ts_allocate_dtor dtor; + int done; +} tsrm_resource_type; + + +/* The memory manager table */ +static tsrm_tls_entry **tsrm_tls_table=NULL; +static int tsrm_tls_table_size; +static ts_rsrc_id id_count; + +/* The resource sizes table */ +static tsrm_resource_type *resource_types_table=NULL; +static int resource_types_table_size; + + +static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */ + +/* New thread handlers */ +static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler; +static tsrm_thread_end_func_t tsrm_new_thread_end_handler; + +/* Debug support */ +int tsrm_error(int level, const char *format, ...); + +/* Read a resource from a thread's resource storage */ +static int tsrm_error_level; +static FILE *tsrm_error_file; + +#if TSRM_DEBUG +#define TSRM_ERROR(args) tsrm_error args +#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ + { \ + int unshuffled_offset = TSRM_UNSHUFFLE_RSRC_ID(offset); \ + \ + if (offset==0) { \ + return &array; \ + } else if ((unshuffled_offset)>=0 && (unshuffled_offset)<(range)) { \ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Successfully fetched resource id %d for thread id %ld - 0x%0.8X", \ + unshuffled_offset, (long) thread_resources->thread_id, array[unshuffled_offset])); \ + return array[unshuffled_offset]; \ + } else { \ + TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Resource id %d is out of range (%d..%d)", \ + unshuffled_offset, TSRM_SHUFFLE_RSRC_ID(0), TSRM_SHUFFLE_RSRC_ID(thread_resources->count-1))); \ + return NULL; \ + } \ + } +#else +#define TSRM_ERROR(args) +#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \ + if (offset==0) { \ + return &array; \ + } else { \ + return array[TSRM_UNSHUFFLE_RSRC_ID(offset)]; \ + } +#endif + +#if defined(PTHREADS) +/* Thread local storage */ +static pthread_key_t tls_key; +# define tsrm_tls_set(what) pthread_setspecific(tls_key, (void*)(what)) +# define tsrm_tls_get() pthread_getspecific(tls_key) + +#elif defined(TSRM_ST) +static int tls_key; +# define tsrm_tls_set(what) st_thread_setspecific(tls_key, (void*)(what)) +# define tsrm_tls_get() st_thread_getspecific(tls_key) + +#elif defined(TSRM_WIN32) +static DWORD tls_key; +# define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what)) +# define tsrm_tls_get() TlsGetValue(tls_key) + +#elif defined(BETHREADS) +static int32 tls_key; +# define tsrm_tls_set(what) tls_set(tls_key, (void*)(what)) +# define tsrm_tls_get() (tsrm_tls_entry*)tls_get(tls_key) + +#else +# define tsrm_tls_set(what) +# define tsrm_tls_get() NULL +# warning tsrm_set_interpreter_context is probably broken on this platform +#endif + +/* Startup TSRM (call once for the entire process) */ +TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename) +{ +#if defined(GNUPTH) + pth_init(); +#elif defined(PTHREADS) + pthread_key_create( &tls_key, 0 ); +#elif defined(TSRM_ST) + st_init(); + st_key_create(&tls_key, 0); +#elif defined(TSRM_WIN32) + tls_key = TlsAlloc(); +#elif defined(BETHREADS) + tls_key = tls_allocate(); +#endif + + tsrm_error_file = stderr; + tsrm_error_set(debug_level, debug_filename); + tsrm_tls_table_size = expected_threads; + + tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *)); + if (!tsrm_tls_table) { + TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table")); + return 0; + } + id_count=0; + + resource_types_table_size = expected_resources; + resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type)); + if (!resource_types_table) { + TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table")); + free(tsrm_tls_table); + tsrm_tls_table = NULL; + return 0; + } + + tsmm_mutex = tsrm_mutex_alloc(); + + tsrm_new_thread_begin_handler = tsrm_new_thread_end_handler = NULL; + + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources", expected_threads, expected_resources)); + return 1; +} + + +/* Shutdown TSRM (call once for the entire process) */ +TSRM_API void tsrm_shutdown(void) +{ + int i; + + if (tsrm_tls_table) { + for (i=0; inext; + for (j=0; jcount; j++) { + if (p->storage[j]) { + if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) { + resource_types_table[j].dtor(p->storage[j], &p->storage); + } + free(p->storage[j]); + } + } + free(p->storage); + free(p); + p = next_p; + } + } + free(tsrm_tls_table); + tsrm_tls_table = NULL; + } + if (resource_types_table) { + free(resource_types_table); + resource_types_table=NULL; + } + tsrm_mutex_free(tsmm_mutex); + tsmm_mutex = NULL; + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM")); + if (tsrm_error_file!=stderr) { + fclose(tsrm_error_file); + } +#if defined(GNUPTH) + pth_kill(); +#elif defined(PTHREADS) + pthread_setspecific(tls_key, 0); + pthread_key_delete(tls_key); +#elif defined(TSRM_WIN32) + TlsFree(tls_key); +#endif +} + + +/* allocates a new thread-safe-resource id */ +TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) +{ + int i; + + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size)); + + tsrm_mutex_lock(tsmm_mutex); + + /* obtain a resource id */ + *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id)); + + /* store the new resource type in the resource sizes table */ + if (resource_types_table_size < id_count) { + resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count); + if (!resource_types_table) { + tsrm_mutex_unlock(tsmm_mutex); + TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource")); + *rsrc_id = 0; + return 0; + } + resource_types_table_size = id_count; + } + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; + resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; + + /* enlarge the arrays for the already active threads */ + for (i=0; icount < id_count) { + int j; + + p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count); + for (j=p->count; jstorage[j] = (void *) malloc(resource_types_table[j].size); + if (resource_types_table[j].ctor) { + resource_types_table[j].ctor(p->storage[j], &p->storage); + } + } + p->count = id_count; + } + p = p->next; + } + } + tsrm_mutex_unlock(tsmm_mutex); + + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id)); + return *rsrc_id; +} + + +static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id) +{ + int i; + + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id)); + (*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry)); + (*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count); + (*thread_resources_ptr)->count = id_count; + (*thread_resources_ptr)->thread_id = thread_id; + (*thread_resources_ptr)->next = NULL; + + /* Set thread local storage to this new thread resources structure */ + tsrm_tls_set(*thread_resources_ptr); + + if (tsrm_new_thread_begin_handler) { + tsrm_new_thread_begin_handler(thread_id, &((*thread_resources_ptr)->storage)); + } + for (i=0; istorage[i] = NULL; + } else + { + (*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size); + if (resource_types_table[i].ctor) { + resource_types_table[i].ctor((*thread_resources_ptr)->storage[i], &(*thread_resources_ptr)->storage); + } + } + } + + if (tsrm_new_thread_end_handler) { + tsrm_new_thread_end_handler(thread_id, &((*thread_resources_ptr)->storage)); + } + + tsrm_mutex_unlock(tsmm_mutex); +} + + +/* fetches the requested resource for the current thread */ +TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id) +{ + THREAD_T thread_id; + int hash_value; + tsrm_tls_entry *thread_resources; + +#ifdef NETWARE + /* The below if loop is added for NetWare to fix an abend while unloading PHP + * when an Apache unload command is issued on the system console. + * While exiting from PHP, at the end for some reason, this function is called + * with tsrm_tls_table = NULL. When this happened, the server abends when + * tsrm_tls_table is accessed since it is NULL. + */ + if(tsrm_tls_table) { +#endif + if (!th_id) { + /* Fast path for looking up the resources for the current + * thread. Its used by just about every call to + * ts_resource_ex(). This avoids the need for a mutex lock + * and our hashtable lookup. + */ + thread_resources = tsrm_tls_get(); + + if (thread_resources) { + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for current thread %d", id, (long) thread_resources->thread_id)); + /* Read a specific resource from the thread's resources. + * This is called outside of a mutex, so have to be aware about external + * changes to the structure as we read it. + */ + TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); + } + thread_id = tsrm_thread_id(); + } else { + thread_id = *th_id; + } + + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Fetching resource id %d for thread %ld", id, (long) thread_id)); + tsrm_mutex_lock(tsmm_mutex); + + hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size); + thread_resources = tsrm_tls_table[hash_value]; + + if (!thread_resources) { + allocate_new_resource(&tsrm_tls_table[hash_value], thread_id); + return ts_resource_ex(id, &thread_id); + } else { + do { + if (thread_resources->thread_id == thread_id) { + break; + } + if (thread_resources->next) { + thread_resources = thread_resources->next; + } else { + allocate_new_resource(&thread_resources->next, thread_id); + return ts_resource_ex(id, &thread_id); + /* + * thread_resources = thread_resources->next; + * break; + */ + } + } while (thread_resources); + } + tsrm_mutex_unlock(tsmm_mutex); + /* Read a specific resource from the thread's resources. + * This is called outside of a mutex, so have to be aware about external + * changes to the structure as we read it. + */ + TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count); +#ifdef NETWARE + } /* if(tsrm_tls_table) */ +#endif +} + +/* frees an interpreter context. You are responsible for making sure that + * it is not linked into the TSRM hash, and not marked as the current interpreter */ +void tsrm_free_interpreter_context(void *context) +{ + tsrm_tls_entry *next, *thread_resources = (tsrm_tls_entry*)context; + int i; + + while (thread_resources) { + next = thread_resources->next; + + for (i=0; icount; i++) { + if (resource_types_table[i].dtor) { + resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage); + } + } + for (i=0; icount; i++) { + free(thread_resources->storage[i]); + } + free(thread_resources->storage); + free(thread_resources); + thread_resources = next; + } +} + +void *tsrm_set_interpreter_context(void *new_ctx) +{ + tsrm_tls_entry *current; + + current = tsrm_tls_get(); + + /* TODO: unlink current from the global linked list, and replace it + * it with the new context, protected by mutex where/if appropriate */ + + /* Set thread local storage to this new thread resources structure */ + tsrm_tls_set(new_ctx); + + /* return old context, so caller can restore it when they're done */ + return current; +} + + +/* allocates a new interpreter context */ +void *tsrm_new_interpreter_context(void) +{ + tsrm_tls_entry *new_ctx, *current; + THREAD_T thread_id; + + thread_id = tsrm_thread_id(); + tsrm_mutex_lock(tsmm_mutex); + + current = tsrm_tls_get(); + + allocate_new_resource(&new_ctx, thread_id); + + /* switch back to the context that was in use prior to our creation + * of the new one */ + return tsrm_set_interpreter_context(current); +} + + +/* frees all resources allocated for the current thread */ +void ts_free_thread(void) +{ + tsrm_tls_entry *thread_resources; + int i; + THREAD_T thread_id = tsrm_thread_id(); + int hash_value; + tsrm_tls_entry *last=NULL; + + tsrm_mutex_lock(tsmm_mutex); + hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size); + thread_resources = tsrm_tls_table[hash_value]; + + while (thread_resources) { + if (thread_resources->thread_id == thread_id) { + for (i=0; icount; i++) { + if (resource_types_table[i].dtor) { + resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage); + } + } + for (i=0; icount; i++) { + free(thread_resources->storage[i]); + } + free(thread_resources->storage); + if (last) { + last->next = thread_resources->next; + } else { + tsrm_tls_table[hash_value] = thread_resources->next; + } + tsrm_tls_set(0); + free(thread_resources); + break; + } + if (thread_resources->next) { + last = thread_resources; + } + thread_resources = thread_resources->next; + } + tsrm_mutex_unlock(tsmm_mutex); +} + + +/* frees all resources allocated for all threads except current */ +void ts_free_worker_threads(void) +{ + tsrm_tls_entry *thread_resources; + int i; + THREAD_T thread_id = tsrm_thread_id(); + int hash_value; + tsrm_tls_entry *last=NULL; + + tsrm_mutex_lock(tsmm_mutex); + hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size); + thread_resources = tsrm_tls_table[hash_value]; + + while (thread_resources) { + if (thread_resources->thread_id != thread_id) { + for (i=0; icount; i++) { + if (resource_types_table[i].dtor) { + resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage); + } + } + for (i=0; icount; i++) { + free(thread_resources->storage[i]); + } + free(thread_resources->storage); + if (last) { + last->next = thread_resources->next; + } else { + tsrm_tls_table[hash_value] = thread_resources->next; + } + free(thread_resources); + if (last) { + thread_resources = last->next; + } else { + thread_resources = tsrm_tls_table[hash_value]; + } + } else { + if (thread_resources->next) { + last = thread_resources; + } + thread_resources = thread_resources->next; + } + } + tsrm_mutex_unlock(tsmm_mutex); +} + + +/* deallocates all occurrences of a given id */ +void ts_free_id(ts_rsrc_id id) +{ + int i; + int j = TSRM_UNSHUFFLE_RSRC_ID(id); + + tsrm_mutex_lock(tsmm_mutex); + + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Freeing resource id %d", id)); + + if (tsrm_tls_table) { + for (i=0; icount > j && p->storage[j]) { + if (resource_types_table && resource_types_table[j].dtor) { + resource_types_table[j].dtor(p->storage[j], &p->storage); + } + free(p->storage[j]); + p->storage[j] = NULL; + } + p = p->next; + } + } + } + resource_types_table[j].done = 1; + + tsrm_mutex_unlock(tsmm_mutex); + + TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d", id)); +} + + + + +/* + * Utility Functions + */ + +/* Obtain the current thread id */ +TSRM_API THREAD_T tsrm_thread_id(void) +{ +#ifdef TSRM_WIN32 + return GetCurrentThreadId(); +#elif defined(GNUPTH) + return pth_self(); +#elif defined(PTHREADS) + return pthread_self(); +#elif defined(NSAPI) + return systhread_current(); +#elif defined(PI3WEB) + return PIThread_getCurrent(); +#elif defined(TSRM_ST) + return st_thread_self(); +#elif defined(BETHREADS) + return find_thread(NULL); +#endif +} + + +/* Allocate a mutex */ +TSRM_API MUTEX_T tsrm_mutex_alloc(void) +{ + MUTEX_T mutexp; +#ifdef TSRM_WIN32 + mutexp = malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection(mutexp); +#elif defined(GNUPTH) + mutexp = (MUTEX_T) malloc(sizeof(*mutexp)); + pth_mutex_init(mutexp); +#elif defined(PTHREADS) + mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init(mutexp,NULL); +#elif defined(NSAPI) + mutexp = crit_init(); +#elif defined(PI3WEB) + mutexp = PIPlatform_allocLocalMutex(); +#elif defined(TSRM_ST) + mutexp = st_mutex_new(); +#elif defined(BETHREADS) + mutexp = (beos_ben*)malloc(sizeof(beos_ben)); + mutexp->ben = 0; + mutexp->sem = create_sem(1, "PHP sempahore"); +#endif +#ifdef THR_DEBUG + printf("Mutex created thread: %d\n",mythreadid()); +#endif + return( mutexp ); +} + + +/* Free a mutex */ +TSRM_API void tsrm_mutex_free(MUTEX_T mutexp) +{ + if (mutexp) { +#ifdef TSRM_WIN32 + DeleteCriticalSection(mutexp); + free(mutexp); +#elif defined(GNUPTH) + free(mutexp); +#elif defined(PTHREADS) + pthread_mutex_destroy(mutexp); + free(mutexp); +#elif defined(NSAPI) + crit_terminate(mutexp); +#elif defined(PI3WEB) + PISync_delete(mutexp); +#elif defined(TSRM_ST) + st_mutex_destroy(mutexp); +#elif defined(BETHREADS) + delete_sem(mutexp->sem); + free(mutexp); +#endif + } +#ifdef THR_DEBUG + printf("Mutex freed thread: %d\n",mythreadid()); +#endif +} + + +/* + Lock a mutex. + A return value of 0 indicates success +*/ +TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp) +{ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id())); +#ifdef TSRM_WIN32 + EnterCriticalSection(mutexp); + return 0; +#elif defined(GNUPTH) + if (pth_mutex_acquire(mutexp, 0, NULL)) { + return 0; + } + return -1; +#elif defined(PTHREADS) + return pthread_mutex_lock(mutexp); +#elif defined(NSAPI) + crit_enter(mutexp); + return 0; +#elif defined(PI3WEB) + return PISync_lock(mutexp); +#elif defined(TSRM_ST) + return st_mutex_lock(mutexp); +#elif defined(BETHREADS) + if (atomic_add(&mutexp->ben, 1) != 0) + return acquire_sem(mutexp->sem); + return 0; +#endif +} + + +/* + Unlock a mutex. + A return value of 0 indicates success +*/ +TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp) +{ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id())); +#ifdef TSRM_WIN32 + LeaveCriticalSection(mutexp); + return 0; +#elif defined(GNUPTH) + if (pth_mutex_release(mutexp)) { + return 0; + } + return -1; +#elif defined(PTHREADS) + return pthread_mutex_unlock(mutexp); +#elif defined(NSAPI) + crit_exit(mutexp); + return 0; +#elif defined(PI3WEB) + return PISync_unlock(mutexp); +#elif defined(TSRM_ST) + return st_mutex_unlock(mutexp); +#elif defined(BETHREADS) + if (atomic_add(&mutexp->ben, -1) != 1) + return release_sem(mutexp->sem); + return 0; +#endif +} + +/* + Changes the signal mask of the calling thread +*/ +#ifdef HAVE_SIGPROCMASK +TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset) +{ + TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id())); + /* TODO: add support for other APIs */ +#ifdef PTHREADS + return pthread_sigmask(how, set, oldset); +#else + return sigprocmask(how, set, oldset); +#endif +} +#endif + + +TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler) +{ + void *retval = (void *) tsrm_new_thread_begin_handler; + + tsrm_new_thread_begin_handler = new_thread_begin_handler; + return retval; +} + + +TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler) +{ + void *retval = (void *) tsrm_new_thread_end_handler; + + tsrm_new_thread_end_handler = new_thread_end_handler; + return retval; +} + + + +/* + * Debug support + */ + +#if TSRM_DEBUG +int tsrm_error(int level, const char *format, ...) +{ + if (level<=tsrm_error_level) { + va_list args; + int size; + + fprintf(tsrm_error_file, "TSRM: "); + va_start(args, format); + size = vfprintf(tsrm_error_file, format, args); + va_end(args); + fprintf(tsrm_error_file, "\n"); + fflush(tsrm_error_file); + return size; + } else { + return 0; + } +} +#endif + + +void tsrm_error_set(int level, char *debug_filename) +{ + tsrm_error_level = level; + +#if TSRM_DEBUG + if (tsrm_error_file!=stderr) { /* close files opened earlier */ + fclose(tsrm_error_file); + } + + if (debug_filename) { + tsrm_error_file = fopen(debug_filename, "w"); + if (!tsrm_error_file) { + tsrm_error_file = stderr; + } + } else { + tsrm_error_file = stderr; + } +#endif +} + +#endif /* ZTS */ diff --git a/TSRM/TSRM.dsp b/TSRM/TSRM.dsp new file mode 100644 index 0000000..1a5693f --- /dev/null +++ b/TSRM/TSRM.dsp @@ -0,0 +1,186 @@ +# Microsoft Developer Studio Project File - Name="TSRM" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=TSRM - Win32 Debug_TS +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "TSRM.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "TSRM.mak" CFG="TSRM - Win32 Debug_TS" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "TSRM - Win32 Debug_TS" (based on "Win32 (x86) Static Library") +!MESSAGE "TSRM - Win32 Release_TS" (based on "Win32 (x86) Static Library") +!MESSAGE "TSRM - Win32 Release_TS_inline" (based on "Win32 (x86) Static Library") +!MESSAGE "TSRM - Win32 Release_TSDbg" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "TSRM - Win32 Debug_TS" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "TSRM___Win32_Debug_TS" +# PROP BASE Intermediate_Dir "TSRM___Win32_Debug_TS" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug_TS" +# PROP Intermediate_Dir "Debug_TS" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "C:\Projects\TSRM" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /D "_DEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=1 /YX /FD /GZ /c +# ADD BASE RSC /l 0x40d /d "_DEBUG" +# ADD RSC /l 0x40d /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "TSRM - Win32 Release_TS" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "TSRM___Win32_Release_TS" +# PROP BASE Intermediate_Dir "TSRM___Win32_Release_TS" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release_TS" +# PROP Intermediate_Dir "Release_TS" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c +# ADD BASE RSC /l 0x40d /d "NDEBUG" +# ADD RSC /l 0x40d /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "TSRM - Win32 Release_TS_inline" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "TSRM___Win32_Release_TS_inline" +# PROP BASE Intermediate_Dir "TSRM___Win32_Release_TS_inline" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release_TS_inline" +# PROP Intermediate_Dir "Release_TS_inline" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "TSRM_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c +# ADD BASE RSC /l 0x40d /d "NDEBUG" +# ADD RSC /l 0x40d /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "TSRM - Win32 Release_TSDbg" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "TSRM___Win32_Release_TSDbg" +# PROP BASE Intermediate_Dir "TSRM___Win32_Release_TSDbg" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release_TSDbg" +# PROP Intermediate_Dir "Release_TSDbg" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c +# ADD BASE RSC /l 0x40d /d "NDEBUG" +# ADD RSC /l 0x40d /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "TSRM - Win32 Debug_TS" +# Name "TSRM - Win32 Release_TS" +# Name "TSRM - Win32 Release_TS_inline" +# Name "TSRM - Win32 Release_TSDbg" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\TSRM.c +# End Source File +# Begin Source File + +SOURCE=.\tsrm_strtok_r.c +# End Source File +# Begin Source File + +SOURCE=.\tsrm_virtual_cwd.c +# End Source File +# Begin Source File + +SOURCE=.\tsrm_win32.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\readdir.h +# End Source File +# Begin Source File + +SOURCE=.\TSRM.h +# End Source File +# Begin Source File + +SOURCE=.\tsrm_config.w32.h +# End Source File +# Begin Source File + +SOURCE=.\tsrm_config_common.h +# End Source File +# Begin Source File + +SOURCE=.\tsrm_strtok_r.h +# End Source File +# Begin Source File + +SOURCE=.\tsrm_virtual_cwd.h +# End Source File +# Begin Source File + +SOURCE=.\tsrm_win32.h +# End Source File +# End Group +# End Target +# End Project diff --git a/TSRM/TSRM.h b/TSRM/TSRM.h new file mode 100644 index 0000000..b232429 --- /dev/null +++ b/TSRM/TSRM.h @@ -0,0 +1,186 @@ +/* + +----------------------------------------------------------------------+ + | Thread Safe Resource Manager | + +----------------------------------------------------------------------+ + | Copyright (c) 1999-2011, Andi Gutmans, Sascha Schumann, Zeev Suraski | + | This source file is subject to the TSRM license, that is bundled | + | with this package in the file LICENSE | + +----------------------------------------------------------------------+ + | Authors: Zeev Suraski | + +----------------------------------------------------------------------+ +*/ + +#ifndef TSRM_H +#define TSRM_H + +#if !defined(__CYGWIN__) && defined(WIN32) +# define TSRM_WIN32 +# include "tsrm_config.w32.h" +#else +# include +#endif + +#ifdef TSRM_WIN32 +# ifdef TSRM_EXPORTS +# define TSRM_API __declspec(dllexport) +# else +# define TSRM_API __declspec(dllimport) +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define TSRM_API __attribute__ ((visibility("default"))) +#else +# define TSRM_API +#endif + +#ifdef _WIN64 +typedef __int64 tsrm_intptr_t; +typedef unsigned __int64 tsrm_uintptr_t; +#else +typedef long tsrm_intptr_t; +typedef unsigned long tsrm_uintptr_t; +#endif + +/* Only compile multi-threading functions if we're in ZTS mode */ +#ifdef ZTS + +#ifdef TSRM_WIN32 +# ifndef TSRM_INCLUDE_FULL_WINDOWS_HEADERS +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +#elif defined(GNUPTH) +# include +#elif defined(PTHREADS) +# include +#elif defined(TSRM_ST) +# include +#elif defined(BETHREADS) +#include +#include +#endif + +typedef int ts_rsrc_id; + +/* Define THREAD_T and MUTEX_T */ +#ifdef TSRM_WIN32 +# define THREAD_T DWORD +# define MUTEX_T CRITICAL_SECTION * +#elif defined(GNUPTH) +# define THREAD_T pth_t +# define MUTEX_T pth_mutex_t * +#elif defined(PTHREADS) +# define THREAD_T pthread_t +# define MUTEX_T pthread_mutex_t * +#elif defined(NSAPI) +# define THREAD_T SYS_THREAD +# define MUTEX_T CRITICAL +#elif defined(PI3WEB) +# define THREAD_T PIThread * +# define MUTEX_T PISync * +#elif defined(TSRM_ST) +# define THREAD_T st_thread_t +# define MUTEX_T st_mutex_t +#elif defined(BETHREADS) +# define THREAD_T thread_id +typedef struct { + sem_id sem; + int32 ben; +} beos_ben; +# define MUTEX_T beos_ben * +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +typedef void (*ts_allocate_ctor)(void *, void ***); +typedef void (*ts_allocate_dtor)(void *, void ***); + +#define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts + +#ifdef __cplusplus +extern "C" { +#endif + +/* startup/shutdown */ +TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename); +TSRM_API void tsrm_shutdown(void); + +/* allocates a new thread-safe-resource id */ +TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor); + +/* fetches the requested resource for the current thread */ +TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id); +#define ts_resource(id) ts_resource_ex(id, NULL) + +/* frees all resources allocated for the current thread */ +TSRM_API void ts_free_thread(void); + +/* frees all resources allocated for all threads except current */ +void ts_free_worker_threads(void); + +/* deallocates all occurrences of a given id */ +TSRM_API void ts_free_id(ts_rsrc_id id); + + +/* Debug support */ +#define TSRM_ERROR_LEVEL_ERROR 1 +#define TSRM_ERROR_LEVEL_CORE 2 +#define TSRM_ERROR_LEVEL_INFO 3 + +typedef void (*tsrm_thread_begin_func_t)(THREAD_T thread_id, void ***tsrm_ls); +typedef void (*tsrm_thread_end_func_t)(THREAD_T thread_id, void ***tsrm_ls); + + +TSRM_API int tsrm_error(int level, const char *format, ...); +TSRM_API void tsrm_error_set(int level, char *debug_filename); + +/* utility functions */ +TSRM_API THREAD_T tsrm_thread_id(void); +TSRM_API MUTEX_T tsrm_mutex_alloc(void); +TSRM_API void tsrm_mutex_free(MUTEX_T mutexp); +TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp); +TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp); +#ifdef HAVE_SIGPROCMASK +TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset); +#endif + +TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler); +TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler); + +/* these 3 APIs should only be used by people that fully understand the threading model + * used by PHP/Zend and the selected SAPI. */ +TSRM_API void *tsrm_new_interpreter_context(void); +TSRM_API void *tsrm_set_interpreter_context(void *new_ctx); +TSRM_API void tsrm_free_interpreter_context(void *context); + +#define TSRM_SHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)+1) +#define TSRM_UNSHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)-1) + +#define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL) +#define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx +#define TSRMLS_SET_CTX(ctx) ctx = (void ***) tsrm_ls +#define TSRMG(id, type, element) (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element) +#define TSRMLS_D void ***tsrm_ls +#define TSRMLS_DC , TSRMLS_D +#define TSRMLS_C tsrm_ls +#define TSRMLS_CC , TSRMLS_C + +#ifdef __cplusplus +} +#endif + +#else /* non ZTS */ + +#define TSRMLS_FETCH() +#define TSRMLS_FETCH_FROM_CTX(ctx) +#define TSRMLS_SET_CTX(ctx) +#define TSRMLS_D void +#define TSRMLS_DC +#define TSRMLS_C +#define TSRMLS_CC + +#endif /* ZTS */ + +#endif /* TSRM_H */ diff --git a/TSRM/acinclude.m4 b/TSRM/acinclude.m4 new file mode 100644 index 0000000..fcf97fd --- /dev/null +++ b/TSRM/acinclude.m4 @@ -0,0 +1,5 @@ + +AC_DEFUN([AM_SET_LIBTOOL_VARIABLE],[ + LIBTOOL='$(SHELL) $(top_builddir)/libtool $1' +]) + diff --git a/TSRM/build.mk b/TSRM/build.mk new file mode 100644 index 0000000..f5756af --- /dev/null +++ b/TSRM/build.mk @@ -0,0 +1,43 @@ +# Makefile to generate build tools +# +# Standard usage: +# make -f build.mk +# +# Written by Sascha Schumann +# +# $Id$ + + +LT_TARGETS = ltmain.sh ltconfig + +config_h_in = tsrm_config.h.in + +makefile_am_files = Makefile.am +makefile_in_files = $(makefile_am_files:.am=.in) +makefile_files = $(makefile_am_files:e.am=e) + +targets = $(makefile_in_files) $(LT_TARGETS) configure $(config_h_in) + +all: $(targets) + +clean: + rm -f $(targets) + +$(LT_TARGETS): + rm -f $(LT_TARGETS) + libtoolize --automake $(AMFLAGS) -f + +$(makefile_in_files): $(makefile_am_files) + automake -a -i $(AMFLAGS) $(makefile_files) + +aclocal.m4: configure.in acinclude.m4 + aclocal + +$(config_h_in): configure.in +# explicitly remove target since autoheader does not seem to work +# correctly otherwise (timestamps are not updated) + @rm -f $@ + autoheader + +configure: aclocal.m4 configure.in + autoconf diff --git a/TSRM/buildconf b/TSRM/buildconf new file mode 100755 index 0000000..fe8dee6 --- /dev/null +++ b/TSRM/buildconf @@ -0,0 +1,33 @@ +#!/bin/sh + +case "$1" in +--copy) + automake_flags=--copy + shift +;; +esac + +libtoolize --force --automake $automake_flags + +mv aclocal.m4 aclocal.m4.old 2>/dev/null +aclocal +if cmp aclocal.m4.old aclocal.m4 > /dev/null 2>&1; then + echo "buildconf: keeping ${1}aclocal.m4" + mv aclocal.m4.old aclocal.m4 +else + echo "buildconf: created or modified ${1}aclocal.m4" +fi + +autoheader + +automake --add-missing --include-deps $automake_flags + +mv configure configure.old 2>/dev/null +autoconf +if cmp configure.old configure > /dev/null 2>&1; then + echo "buildconf: keeping ${1}configure" + mv configure.old configure +else + echo "buildconf: created or modified ${1}configure" +fi + diff --git a/TSRM/config.w32 b/TSRM/config.w32 new file mode 100644 index 0000000..5498f8e --- /dev/null +++ b/TSRM/config.w32 @@ -0,0 +1,5 @@ +// vim:ft=javascript +// $Id$ + +ADD_SOURCES("TSRM", "TSRM.c tsrm_strtok_r.c tsrm_virtual_cwd.c tsrm_win32.c"); + diff --git a/TSRM/configure.in b/TSRM/configure.in new file mode 100644 index 0000000..6f10f5a --- /dev/null +++ b/TSRM/configure.in @@ -0,0 +1,35 @@ +dnl $Id$ +dnl +dnl Minimalistic configure.in for TSRM. +dnl + +AC_INIT(TSRM.c) +AM_INIT_AUTOMAKE(TSRM, 1.0, nodefine) +AM_CONFIG_HEADER(tsrm_config.h) + +AH_TOP([ +#undef PTHREADS +]) + +sinclude(tsrm.m4) + +TSRM_BASIC_CHECKS +TSRM_THREADS_CHECKS + +AM_PROG_LIBTOOL +if test "$enable_debug" != "yes"; then + AM_SET_LIBTOOL_VARIABLE([--silent]) +fi + +dnl TSRM_PTHREAD + +AC_CHECK_HEADERS( +utime.h \ +dirent.h \ +stdarg.h \ +alloca.h \ +unistd.h \ +limits.h +) + +AC_OUTPUT(Makefile) diff --git a/TSRM/readdir.h b/TSRM/readdir.h new file mode 100644 index 0000000..0665810 --- /dev/null +++ b/TSRM/readdir.h @@ -0,0 +1,45 @@ +#ifndef READDIR_H +#define READDIR_H + + +/* + * Structures and types used to implement opendir/readdir/closedir + * on Windows 95/NT. + */ + +#include + +#include +#include +#include +#include +#include + +/* struct dirent - same as Unix */ + +struct dirent { + long d_ino; /* inode (always 1 in WIN32) */ + off_t d_off; /* offset to this dirent */ + unsigned short d_reclen; /* length of d_name */ + char d_name[_MAX_FNAME + 1]; /* filename (null terminated) */ +}; + + +/* typedef DIR - not the same as Unix */ +typedef struct { + HANDLE handle; /* _findfirst/_findnext handle */ + short offset; /* offset into directory */ + short finished; /* 1 if there are not more files */ + WIN32_FIND_DATA fileinfo; /* from _findfirst/_findnext */ + char *dir; /* the dir we are reading */ + struct dirent dent; /* the dirent to return */ +} DIR; + +/* Function prototypes */ +DIR *opendir(const char *); +struct dirent *readdir(DIR *); +int readdir_r(DIR *, struct dirent *, struct dirent **); +int closedir(DIR *); +int rewinddir(DIR *); + +#endif /* READDIR_H */ diff --git a/TSRM/threads.m4 b/TSRM/threads.m4 new file mode 100644 index 0000000..38494ce --- /dev/null +++ b/TSRM/threads.m4 @@ -0,0 +1,173 @@ +dnl Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions +dnl are met: +dnl +dnl 1. Redistributions of source code must retain the above copyright +dnl notice, this list of conditions and the following disclaimer. +dnl +dnl 2. Redistributions in binary form must reproduce the above copyright +dnl notice, this list of conditions and the following disclaimer in +dnl the documentation and/or other materials provided with the +dnl distribution. +dnl +dnl THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY +dnl EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +dnl IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +dnl PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SASCHA SCHUMANN OR +dnl HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +dnl NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +dnl LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +dnl HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +dnl STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +dnl ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +dnl OF THE POSSIBILITY OF SUCH DAMAGE. + +dnl +dnl PTHREADS_FLAGS +dnl +dnl Set some magic defines to achieve POSIX threads conformance +dnl +AC_DEFUN([PTHREADS_FLAGS],[ + if test -z "$host_alias" && test -n "$host"; then + host_alias=$host + fi + if test -z "$host_alias"; then + AC_MSG_ERROR(host_alias is not set. Make sure to run config.guess) + fi + case $host_alias in + *solaris*) + PTHREAD_FLAGS="-D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT";; + *freebsd*) + PTHREAD_FLAGS="-D_REENTRANT -D_THREAD_SAFE";; + *linux*) + PTHREAD_FLAGS=-D_REENTRANT;; + *aix*) + PTHREAD_FLAGS=-D_THREAD_SAFE;; + *irix*) + PTHREAD_FLAGS=-D_POSIX_THREAD_SAFE_FUNCTIONS;; + *hpux*) + PTHREAD_FLAGS=-D_REENTRANT;; + *sco*) + PTHREAD_FLAGS=-D_REENTRANT;; +dnl Solves sigwait() problem, creates problems with u_long etc. +dnl PTHREAD_FLAGS="-D_REENTRANT -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=199506 -D_XOPEN_SOURCE_EXTENDED=1";; + esac + + if test -n "$PTHREAD_FLAGS"; then + CPPFLAGS="$CPPFLAGS $PTHREAD_FLAGS" + fi +])dnl +dnl +dnl PTHREADS_CHECK_COMPILE +dnl +dnl Check whether the current setup can use POSIX threads calls +dnl +AC_DEFUN([PTHREADS_CHECK_COMPILE], [ +AC_TRY_RUN( [ +#include +#include + +void *thread_routine(void *data) { + return data; +} + +int main() { + pthread_t thd; + pthread_mutexattr_t mattr; + int data = 1; + pthread_mutexattr_init(&mattr); + return pthread_create(&thd, NULL, thread_routine, &data); +} ], [ + pthreads_working=yes + ], [ + pthreads_working=no + ], [ + dnl For cross compiling running this test is of no use. NetWare supports pthreads + pthreads_working=no + case $host_alias in + *netware*) + pthreads_working=yes + esac +] +) ] )dnl +dnl +dnl PTHREADS_CHECK() +dnl +dnl Try to find a way to enable POSIX threads +dnl +dnl Magic flags +dnl -kthread gcc (FreeBSD) +dnl -Kthread UDK cc (UnixWare) +dnl -mt WorkShop cc (Solaris) +dnl -mthreads gcc (AIX) +dnl -pthread gcc (Linux, FreeBSD, NetBSD, OpenBSD) +dnl -pthreads gcc (Solaris) +dnl -qthreaded AIX cc V5 +dnl -threads gcc (HP-UX) +dnl +AC_DEFUN([PTHREADS_CHECK],[ + +if test "$beos_threads" = "1"; then + pthreads_working="yes" + ac_cv_pthreads_cflags="" +else + save_CFLAGS=$CFLAGS + save_LIBS=$LIBS + PTHREADS_ASSIGN_VARS + PTHREADS_CHECK_COMPILE + LIBS=$save_LIBS + CFLAGS=$save_CFLAGS + + AC_CACHE_CHECK(for pthreads_cflags,ac_cv_pthreads_cflags,[ + ac_cv_pthreads_cflags= + if test "$pthreads_working" != "yes"; then + for flag in -kthread -pthread -pthreads -mthreads -Kthread -threads -mt -qthreaded; do + ac_save=$CFLAGS + CFLAGS="$CFLAGS $flag" + PTHREADS_CHECK_COMPILE + CFLAGS=$ac_save + if test "$pthreads_working" = "yes"; then + ac_cv_pthreads_cflags=$flag + break + fi + done + fi +fi +]) + +AC_CACHE_CHECK(for pthreads_lib, ac_cv_pthreads_lib,[ +ac_cv_pthreads_lib= +if test "$pthreads_working" != "yes"; then + for lib in pthread pthreads c_r; do + ac_save=$LIBS + LIBS="$LIBS -l$lib" + PTHREADS_CHECK_COMPILE + LIBS=$ac_save + if test "$pthreads_working" = "yes"; then + ac_cv_pthreads_lib=$lib + break + fi + done +fi +]) + +if test "$pthreads_working" = "yes"; then + threads_result="POSIX-Threads found" +else + threads_result="POSIX-Threads not found" +fi +])dnl +dnl +dnl +AC_DEFUN([PTHREADS_ASSIGN_VARS],[ +if test -n "$ac_cv_pthreads_lib"; then + LIBS="$LIBS -l$ac_cv_pthreads_lib" +fi + +if test -n "$ac_cv_pthreads_cflags"; then + CFLAGS="$CFLAGS $ac_cv_pthreads_cflags" +fi +])dnl diff --git a/TSRM/tsrm.m4 b/TSRM/tsrm.m4 new file mode 100644 index 0000000..b53a4bb --- /dev/null +++ b/TSRM/tsrm.m4 @@ -0,0 +1,130 @@ + +dnl TSRM_CHECK_GCC_ARG(ARG, ACTION-IF-FOUND, ACTION-IF-NOT_FOUND) +AC_DEFUN([TSRM_CHECK_GCC_ARG],[ + gcc_arg_name=[ac_cv_gcc_arg]translit($1,A-Z-,a-z_) + AC_CACHE_CHECK([whether $CC supports $1], [ac_cv_gcc_arg]translit($1,A-Z-,a-z_), [ + echo 'void somefunc() { };' > conftest.c + cmd='$CC $1 -c conftest.c' + if eval $cmd 2>&1 | egrep -e $1 >/dev/null ; then + ac_result=no + else + ac_result=yes + fi + eval $gcc_arg_name=$ac_result + rm -f conftest.* + ]) + if eval test "\$$gcc_arg_name" = "yes"; then + $2 + else + : + $3 + fi +]) + +AC_DEFUN([TSRM_BASIC_CHECKS],[ + +AC_REQUIRE([AC_PROG_CC])dnl +dnl AC_REQUIRE([AM_PROG_CC_STDC])dnl +AC_REQUIRE([AC_PROG_CC_C_O])dnl +AC_REQUIRE([AC_PROG_RANLIB])dnl + +AC_CHECK_HEADERS(stdarg.h) + +AC_CHECK_FUNCS(sigprocmask) + +]) + + +AC_DEFUN([TSRM_CHECK_PTH],[ + +AC_MSG_CHECKING(for GNU Pth) +PTH_PREFIX="`$1 --prefix`" +if test -z "$PTH_PREFIX"; then + AC_MSG_RESULT(Please check your Pth installation) +fi + +CPPFLAGS="$CPPFLAGS `$1 --cflags`" +LDFLAGS="$LDFLAGS `$1 --ldflags`" +LIBS="$LIBS `$1 --libs`" + +AC_DEFINE(GNUPTH, 1, [Whether you use GNU Pth]) +AC_MSG_RESULT(yes - installed in $PTH_PREFIX) + +]) + +AC_DEFUN([TSRM_CHECK_ST],[ + if test -r "$1/include/st.h"; then + CPPFLAGS="$CPPFLAGS -I$1/include" + LDFLAGS="$LDFLAGS -L$1/lib" + elif test -r "$1/st.h"; then + CPPFLAGS="$CPPFLAGS -I$1" + LDFLAGS="$LDFLAGS -L$1" + fi + AC_CHECK_HEADERS(st.h,[],[ + AC_MSG_ERROR([Sorry[,] I was unable to locate the State Threads header file. Please specify the prefix using --with-tsrm-st=/prefix]) + ]) + LIBS="$LIBS -lst" + AC_MSG_CHECKING(for SGI's State Threads) + AC_MSG_RESULT(yes) + AC_DEFINE(TSRM_ST, 1, [ ]) +]) + +sinclude(threads.m4) + +AC_DEFUN([TSRM_CHECK_PTHREADS],[ + +PTHREADS_CHECK + +if test "$beos_threads" = "1"; then + AC_DEFINE(BETHREADS, 1, Whether to use native BeOS threads) +else + if test "$pthreads_working" != "yes"; then + AC_MSG_ERROR(Your system seems to lack POSIX threads.) + fi + + AC_DEFINE(PTHREADS, 1, Whether to use Pthreads) + + AC_MSG_CHECKING(for POSIX threads) + AC_MSG_RESULT(yes) +fi +]) + + +AC_DEFUN([TSRM_THREADS_CHECKS],[ + +dnl For the thread implementations, we always use --with-* +dnl to maintain consistency + +AC_ARG_WITH(tsrm-pth, +[ --with-tsrm-pth[=pth-config] + Use GNU Pth],[ + TSRM_PTH=$withval +],[ + TSRM_PTH=no +]) + +AC_ARG_WITH(tsrm-st, +[ --with-tsrm-st Use SGI's State Threads],[ + TSRM_ST=$withval +],[ + TSRM_ST=no +]) + +AC_ARG_WITH(tsrm-pthreads, +[ --with-tsrm-pthreads Use POSIX threads (default)],[ + TSRM_PTHREADS=$withval +],[ + TSRM_PTHREADS=yes +]) + +test "$TSRM_PTH" = "yes" && TSRM_PTH=pth-config + +if test "$TSRM_PTH" != "no"; then + TSRM_CHECK_PTH($TSRM_PTH) +elif test "$TSRM_ST" != "no"; then + TSRM_CHECK_ST($TSRM_ST) +elif test "$TSRM_PTHREADS" != "no"; then + TSRM_CHECK_PTHREADS +fi + +]) diff --git a/TSRM/tsrm_config.w32.h b/TSRM/tsrm_config.w32.h new file mode 100644 index 0000000..14c6443 --- /dev/null +++ b/TSRM/tsrm_config.w32.h @@ -0,0 +1,22 @@ +#ifndef TSRM_CONFIG_W32_H +#define TSRM_CONFIG_W32_H + +#include <../main/config.w32.h> + +#define HAVE_UTIME 1 +#define HAVE_ALLOCA 1 +#define HAVE_REALPATH 1 + +#include +#include +#include + +#undef inline +#ifdef ZEND_WIN32_FORCE_INLINE +# define inline __forceinline +#else +# define inline +#endif + + +#endif diff --git a/TSRM/tsrm_config_common.h b/TSRM/tsrm_config_common.h new file mode 100644 index 0000000..83b6b9b --- /dev/null +++ b/TSRM/tsrm_config_common.h @@ -0,0 +1,70 @@ +#ifndef TSRM_CONFIG_COMMON_H +#define TSRM_CONFIG_COMMON_H + +#ifndef __CYGWIN__ +# if WINNT|WIN32 +# define TSRM_WIN32 +# endif +#endif + +#ifdef TSRM_WIN32 +# include "tsrm_config.w32.h" +#else +# include +# include +#endif + +#if HAVE_ALLOCA_H && !defined(_ALLOCA_H) +# include +#endif + +/* AIX requires this to be the first thing in the file. */ +#ifndef __GNUC__ +# ifndef HAVE_ALLOCA_H +# ifdef _AIX +#pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +# ifndef NETWARE +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_LIMITS_H +#include +#endif + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# elif defined(MAX_PATH) +# define MAXPATHLEN MAX_PATH +# else +# define MAXPATHLEN 256 +# endif +#endif + +#if (HAVE_ALLOCA || (defined (__GNUC__) && __GNUC__ >= 2)) +# define TSRM_ALLOCA_MAX_SIZE 4096 +# define TSRM_ALLOCA_FLAG(name) \ + int name; +# define tsrm_do_alloca_ex(size, limit, use_heap) \ + ((use_heap = ((size) > (limit))) ? malloc(size) : alloca(size)) +# define tsrm_do_alloca(size, use_heap) \ + tsrm_do_alloca_ex(size, TSRM_ALLOCA_MAX_SIZE, use_heap) +# define tsrm_free_alloca(p, use_heap) \ + do { if (use_heap) free(p); } while (0) +#else +# define TSRM_ALLOCA_FLAG(name) +# define tsrm_do_alloca(p, use_heap) malloc(p) +# define tsrm_free_alloca(p, use_heap) free(p) +#endif + +#endif /* TSRM_CONFIG_COMMON_H */ diff --git a/TSRM/tsrm_nw.c b/TSRM/tsrm_nw.c new file mode 100644 index 0000000..d9963fa --- /dev/null +++ b/TSRM/tsrm_nw.c @@ -0,0 +1,240 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Venkat Raghavan S | + | Anantha Kesari H Y | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include +#include + +#include "TSRM.h" + +#ifdef NETWARE + +#ifdef USE_MKFIFO +#include +#elif !defined(USE_PIPE_OPEN) /* NXFifoOpen */ +#include +#endif + +#include +#include + +#include + +#include "mktemp.h" + +/* strtok() call in LibC is abending when used in a different address space + * -- hence using PHP's version itself for now + */ +#include "tsrm_strtok_r.h" +#define tsrm_strtok_r(a,b,c) strtok((a),(b)) + +#define WHITESPACE " \t" +#define MAX_ARGS 10 + + +TSRM_API FILE* popen(const char *commandline, const char *type) +{ + char *command = NULL, *argv[MAX_ARGS] = {'\0'}, **env = NULL; + char *tempName = "sys:/php/temp/phpXXXXXX.tmp"; + char *filePath = NULL; + char *ptr = NULL; + int ptrLen = 0, argc = 0, i = 0, envCount = 0, err = 0; + FILE *stream = NULL; +#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO) + int pipe_handle; + int mode = O_RDONLY; +#else + NXHandle_t pipe_handle; + NXMode_t mode = NX_O_RDONLY; +#endif + NXExecEnvSpec_t envSpec; + NXNameSpec_t nameSpec; + NXVmId_t newVM = 0; + + /* Check for validity of input parameters */ + if (!commandline || !type) + return NULL; + + /* Get temporary file name */ + filePath = mktemp(tempName); + if (!filePath) + return NULL; + + /* Set pipe mode according to type -- for now allow only "r" or "w" */ + if (strcmp(type, "r") == 0) +#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO) + mode = O_RDONLY; +#else + mode = NX_O_RDONLY; +#endif + else if (strcmp(type, "w") == 0) +#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO) + mode = O_WRONLY; +#else + mode = NX_O_WRONLY; +#endif + else + return NULL; + +#ifdef USE_PIPE_OPEN + pipe_handle = pipe_open(filePath, mode); + if (pipe_handle == -1) + return NULL; +#elif defined(USE_MKFIFO) + pipe_handle = mkfifo(filePath, mode); + if (pipe_handle == -1) + return NULL; +#else + /* - NetWare doesn't require first parameter + * - Allowing LibC to choose the buffer size for now + */ + err = NXFifoOpen(0, filePath, mode, 0, &pipe_handle); + if (err) + return NULL; +#endif + + /* Copy the environment variables in preparation for the spawn call */ + envCount = NXGetEnvCount() + 1; /* add one for NULL */ + env = (char **) NXMemAlloc(sizeof(char *) * envCount, 0); + if (!env) + return NULL; + + err = NXCopyEnv(env, envCount); + if (err) { + NXMemFree (env); + return NULL; + } + + /* Separate commandline string into words */ + ptr = tsrm_strtok_r((char*)commandline, WHITESPACE, NULL); + ptrLen = strlen(ptr); + + command = (char*)malloc(ptrLen + 1); + if (!command) { + NXMemFree (env); + return NULL; + } + + strcpy (command, ptr); + + ptr = tsrm_strtok_r(NULL, WHITESPACE, NULL); + while (ptr && (argc < MAX_ARGS)) { + ptrLen = strlen(ptr); + + argv[argc] = (char*)malloc(ptrLen + 1); + if (!argv[argc]) { + NXMemFree (env); + if (command) + free (command); + + for (i = 0; i < argc; i++) { + if (argv[i]) + free (argv[i]); + } + + return NULL; + } + + strcpy (argv[argc], ptr); + argc++; + ptr = tsrm_strtok_r(NULL, WHITESPACE, NULL); + } + + /* Setup the execution environment and spawn new process */ + envSpec.esFlags = 0; /* Not used */ + envSpec.esArgc = argc; + envSpec.esArgv = (void **) argv; + envSpec.esEnv = (void **) env; + +/* envSpec.esStdin.ssType = */ + envSpec.esStdout.ssType = NX_OBJ_FIFO; + envSpec.esStderr.ssType = NX_OBJ_FILE; + + /* 'ssHandle' is not a struct/union/class member */ +/* + envSpec.esStdin.ssHandle = + envSpec.esStdout.ssHandle = + envSpec.esStderr.ssHandle = -1; +*/ + envSpec.esStdin.ssPathCtx = NULL; + envSpec.esStdout.ssPathCtx = NULL; + envSpec.esStderr.ssPathCtx = NULL; + +#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO) + if (mode == O_RDONLY) { +#else + if (mode == NX_O_RDONLY) { +#endif + envSpec.esStdin.ssPath = filePath; + envSpec.esStdout.ssPath = stdout; + } else { /* Write Only */ + envSpec.esStdin.ssPath = stdin; + envSpec.esStdout.ssPath = filePath; + } + + envSpec.esStderr.ssPath = stdout; + + nameSpec.ssType = NX_OBJ_FIFO; +/* nameSpec.ssHandle = 0; */ /* 'ssHandle' is not a struct/union/class member */ + nameSpec.ssPathCtx = NULL; /* Not used */ + nameSpec.ssPath = argv[0]; + err = NXVmSpawn(&nameSpec, &envSpec, 0, &newVM); + if (!err) + /* Get file pointer corresponding to the pipe (file) opened */ + stream = fdopen(pipe_handle, type); + + /* Clean-up */ + if (env) + NXMemFree (env); + + if (pipe_handle) +#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO) + close(pipe_handle); +#else + NXClose(pipe_handle); +#endif + + if (command) + free (command); + + for (i = 0; i < argc; i++) { + if (argv[i]) + free (argv[i]); + } + + return stream; +} + +TSRM_API int pclose(FILE* stream) +{ + int err = 0; + NXHandle_t fd = 0; + + /* Get the process associated with this pipe (file) handle and terminate it */ + fd = fileno(stream); + NXClose (fd); + + err = fclose(stream); + + return err; +} + +#endif /* NETWARE */ diff --git a/TSRM/tsrm_nw.h b/TSRM/tsrm_nw.h new file mode 100644 index 0000000..1f93bd5 --- /dev/null +++ b/TSRM/tsrm_nw.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Venkat Raghavan S | + | Anantha Kesari H Y | + +----------------------------------------------------------------------+ +*/ + + +#ifndef TSRM_NW_H +#define TSRM_NW_H + +#include "TSRM.h" + +TSRM_API FILE* popen(const char *command, const char *type); +TSRM_API int pclose(FILE* stream); + +#endif diff --git a/TSRM/tsrm_strtok_r.c b/TSRM/tsrm_strtok_r.c new file mode 100644 index 0000000..e9ad26a --- /dev/null +++ b/TSRM/tsrm_strtok_r.c @@ -0,0 +1,63 @@ +#include + +#include "tsrm_config_common.h" +#include "tsrm_strtok_r.h" + +static inline int in_character_class(char ch, const char *delim) +{ + while (*delim) { + if (*delim == ch) { + return 1; + } + delim++; + } + return 0; +} + +char *tsrm_strtok_r(char *s, const char *delim, char **last) +{ + char *token; + + if (s == NULL) { + s = *last; + } + + while (*s && in_character_class(*s, delim)) { + s++; + } + if (!*s) { + return NULL; + } + + token = s; + + while (*s && !in_character_class(*s, delim)) { + s++; + } + if (!*s) { + *last = s; + } else { + *s = '\0'; + *last = s + 1; + } + return token; +} + +#if 0 + +main() +{ + char foo[] = "/foo/bar//\\barbara"; + char *last; + char *token; + + token = tsrm_strtok_r(foo, "/\\", &last); + while (token) { + printf ("Token = '%s'\n", token); + token = tsrm_strtok_r(NULL, "/\\", &last); + } + + return 0; +} + +#endif diff --git a/TSRM/tsrm_strtok_r.h b/TSRM/tsrm_strtok_r.h new file mode 100644 index 0000000..8c9e819 --- /dev/null +++ b/TSRM/tsrm_strtok_r.h @@ -0,0 +1,6 @@ +#ifndef TSRM_STRTOK_R +#define TSRM_STRTOK_R + +char *tsrm_strtok_r(char *s, const char *delim, char **last); + +#endif diff --git a/TSRM/tsrm_virtual_cwd.c b/TSRM/tsrm_virtual_cwd.c new file mode 100644 index 0000000..3e211fa --- /dev/null +++ b/TSRM/tsrm_virtual_cwd.c @@ -0,0 +1,1976 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Sascha Schumann | + | Pierre Joye | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tsrm_virtual_cwd.h" +#include "tsrm_strtok_r.h" + +#ifdef TSRM_WIN32 +#include +#include "tsrm_win32.h" +# ifndef IO_REPARSE_TAG_SYMLINK +# define IO_REPARSE_TAG_SYMLINK 0xA000000C +# endif + +# ifndef IO_REPARSE_TAG_DEDUP +# define IO_REPARSE_TAG_DEDUP 0x80000013 +# endif + +# ifndef VOLUME_NAME_NT +# define VOLUME_NAME_NT 0x2 +# endif + +# ifndef VOLUME_NAME_DOS +# define VOLUME_NAME_DOS 0x0 +# endif +#endif + +#ifndef S_IFLNK +# define S_IFLNK 0120000 +#endif + +#ifdef NETWARE +#include +#endif + +#ifndef HAVE_REALPATH +#define realpath(x,y) strcpy(y,x) +#endif + +#define VIRTUAL_CWD_DEBUG 0 + +#include "TSRM.h" + +/* Only need mutex for popen() in Windows and NetWare because it doesn't chdir() on UNIX */ +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) +MUTEX_T cwd_mutex; +#endif + +#ifdef ZTS +ts_rsrc_id cwd_globals_id; +#else +virtual_cwd_globals cwd_globals; +#endif + +cwd_state main_cwd_state; /* True global */ + +#ifndef TSRM_WIN32 +#include +#else +#include +#endif + +#ifndef S_ISDIR +#define S_ISDIR(mode) ((mode) & _S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) ((mode) & _S_IFREG) +#endif + +#ifdef TSRM_WIN32 +#include +#define tsrm_strtok_r(a,b,c) _tcstok((a),(b)) +#define TOKENIZER_STRING "/\\" + +static int php_check_dots(const char *element, int n) +{ + while (n-- > 0) if (element[n] != '.') break; + + return (n != -1); +} + +#define IS_DIRECTORY_UP(element, len) \ + (len >= 2 && !php_check_dots(element, len)) + +#define IS_DIRECTORY_CURRENT(element, len) \ + (len == 1 && element[0] == '.') + +#elif defined(NETWARE) +/* NetWare has strtok() (in LibC) and allows both slashes in paths, like Windows -- + but rest of the stuff is like Unix */ +/* strtok() call in LibC is abending when used in a different address space -- hence using + PHP's version itself for now */ +/*#define tsrm_strtok_r(a,b,c) strtok((a),(b))*/ +#define TOKENIZER_STRING "/\\" + +#else +#define TOKENIZER_STRING "/" +#endif + + +/* default macros */ + +#ifndef IS_DIRECTORY_UP +#define IS_DIRECTORY_UP(element, len) \ + (len == 2 && element[0] == '.' && element[1] == '.') +#endif + +#ifndef IS_DIRECTORY_CURRENT +#define IS_DIRECTORY_CURRENT(element, len) \ + (len == 1 && element[0] == '.') +#endif + +/* define this to check semantics */ +#define IS_DIR_OK(s) (1) + +#ifndef IS_DIR_OK +#define IS_DIR_OK(state) (php_is_dir_ok(state) == 0) +#endif + + +#define CWD_STATE_COPY(d, s) \ + (d)->cwd_length = (s)->cwd_length; \ + (d)->cwd = (char *) malloc((s)->cwd_length+1); \ + memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1); + +#define CWD_STATE_FREE(s) \ + free((s)->cwd); + +#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) +{ + __int64 UnixTime; + long *nsec = NULL; + SYSTEMTIME SystemTime; + FileTimeToSystemTime(&FileTime, &SystemTime); + + UnixTime = ((__int64)FileTime.dwHighDateTime << 32) + + FileTime.dwLowDateTime; + + UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS); + + if (nsec) { + *nsec = (UnixTime % SECS_TO_100NS) * (__int64)100; + } + + UnixTime /= SECS_TO_100NS; /* now convert to seconds */ + + if ((time_t)UnixTime != UnixTime) { + UnixTime = 0; + } + return (time_t)UnixTime; +} + +CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */ + HINSTANCE kernel32; + HANDLE hFile; + DWORD dwRet; + + typedef BOOL (WINAPI *gfpnh_func)(HANDLE, LPTSTR, DWORD, DWORD); + gfpnh_func pGetFinalPathNameByHandle; + + kernel32 = LoadLibrary("kernel32.dll"); + + if (kernel32) { + pGetFinalPathNameByHandle = (gfpnh_func)GetProcAddress(kernel32, "GetFinalPathNameByHandleA"); + if (pGetFinalPathNameByHandle == NULL) { + return -1; + } + } else { + return -1; + } + + hFile = CreateFile(link, // file to open + GENERIC_READ, // open for reading + FILE_SHARE_READ, // share for reading + NULL, // default security + OPEN_EXISTING, // existing file only + FILE_FLAG_BACKUP_SEMANTICS, // normal file + NULL); // no attr. template + + if( hFile == INVALID_HANDLE_VALUE) { + return -1; + } + + dwRet = pGetFinalPathNameByHandle(hFile, target, MAXPATHLEN, VOLUME_NAME_DOS); + if(dwRet >= MAXPATHLEN) { + return -1; + } + + CloseHandle(hFile); + + if(dwRet > 4) { + /* Skip first 4 characters if they are "\??\" */ + if(target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') { + char tmp[MAXPATHLEN]; + unsigned int offset = 4; + dwRet -= 4; + + /* \??\UNC\ */ + if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') { + offset += 2; + dwRet -= 2; + target[offset] = '\\'; + } + + memcpy(tmp, target + offset, dwRet); + memcpy(target, tmp, dwRet); + } + } + + target[dwRet] = '\0'; + return dwRet; +} +/* }}} */ + +CWD_API int php_sys_stat_ex(const char *path, struct stat *buf, int lstat) /* {{{ */ +{ + WIN32_FILE_ATTRIBUTE_DATA data; + __int64 t; + const size_t path_len = strlen(path); + + if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) { + return stat(path, buf); + } + + if (path_len >= 1 && path[1] == ':') { + if (path[0] >= 'A' && path[0] <= 'Z') { + buf->st_dev = buf->st_rdev = path[0] - 'A'; + } else { + buf->st_dev = buf->st_rdev = path[0] - 'a'; + } + } else if (IS_UNC_PATH(path, path_len)) { + buf->st_dev = buf->st_rdev = 0; + } else { + char cur_path[MAXPATHLEN+1]; + DWORD len = sizeof(cur_path); + char *tmp = cur_path; + + while(1) { + DWORD r = GetCurrentDirectory(len, tmp); + if (r < len) { + if (tmp[1] == ':') { + if (path[0] >= 'A' && path[0] <= 'Z') { + buf->st_dev = buf->st_rdev = path[0] - 'A'; + } else { + buf->st_dev = buf->st_rdev = path[0] - 'a'; + } + } else { + buf->st_dev = buf->st_rdev = -1; + } + break; + } else if (!r) { + buf->st_dev = buf->st_rdev = -1; + break; + } else { + len = r+1; + tmp = (char*)malloc(len); + } + } + if (tmp != cur_path) { + free(tmp); + } + } + + buf->st_uid = buf->st_gid = buf->st_ino = 0; + + if (lstat && 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; + TSRM_ALLOCA_FLAG(use_heap_large); + + 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) { + buf->st_mode = S_IFLNK; + 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)); + } + +#if 0 /* Not used yet */ + else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + buf->st_mode |=; + } +#endif + tsrm_free_alloca(pbuffer, use_heap_large); + } else { + buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG; + 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)); + } + + if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + int len = strlen(path); + + if (path[len-4] == '.') { + if (_memicmp(path+len-3, "exe", 3) == 0 || + _memicmp(path+len-3, "com", 3) == 0 || + _memicmp(path+len-3, "bat", 3) == 0 || + _memicmp(path+len-3, "cmd", 3) == 0) { + buf->st_mode |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)); + } + } + } + + buf->st_nlink = 1; + t = data.nFileSizeHigh; + t = t << 32; + t |= data.nFileSizeLow; + buf->st_size = t; + buf->st_atime = FileTimeToUnixTime(data.ftLastAccessTime); + buf->st_ctime = FileTimeToUnixTime(data.ftCreationTime); + buf->st_mtime = FileTimeToUnixTime(data.ftLastWriteTime); + return 0; +} +/* }}} */ +#endif + +static int php_is_dir_ok(const cwd_state *state) /* {{{ */ +{ + struct stat buf; + + if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode)) + return (0); + + return (1); +} +/* }}} */ + +static int php_is_file_ok(const cwd_state *state) /* {{{ */ +{ + struct stat buf; + + if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode)) + return (0); + + return (1); +} +/* }}} */ + +static void cwd_globals_ctor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */ +{ + CWD_STATE_COPY(&cwd_g->cwd, &main_cwd_state); + cwd_g->realpath_cache_size = 0; + cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE; + cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL; + memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache)); +} +/* }}} */ + +static void cwd_globals_dtor(virtual_cwd_globals *cwd_g TSRMLS_DC) /* {{{ */ +{ + CWD_STATE_FREE(&cwd_g->cwd); + realpath_cache_clean(TSRMLS_C); +} +/* }}} */ + +CWD_API void virtual_cwd_startup(void) /* {{{ */ +{ + char cwd[MAXPATHLEN]; + char *result; + +#ifdef NETWARE + result = getcwdpath(cwd, NULL, 1); + if(result) + { + char *c=cwd; + while(c = strchr(c, '\\')) + { + *c='/'; + ++c; + } + } +#else + result = getcwd(cwd, sizeof(cwd)); +#endif + if (!result) { + cwd[0] = '\0'; + } + + main_cwd_state.cwd_length = strlen(cwd); +#ifdef TSRM_WIN32 + if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') { + cwd[0] = toupper(cwd[0]); + } +#endif + main_cwd_state.cwd = strdup(cwd); + +#ifdef ZTS + ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor); +#else + cwd_globals_ctor(&cwd_globals TSRMLS_CC); +#endif + +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) + cwd_mutex = tsrm_mutex_alloc(); +#endif +} +/* }}} */ + +CWD_API void virtual_cwd_shutdown(void) /* {{{ */ +{ +#ifndef ZTS + cwd_globals_dtor(&cwd_globals TSRMLS_CC); +#endif +#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS) + tsrm_mutex_free(cwd_mutex); +#endif + + free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */ +} +/* }}} */ + +CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC) /* {{{ */ +{ + cwd_state *state; + + state = &CWDG(cwd); + + if (state->cwd_length == 0) { + char *retval; + + *length = 1; + retval = (char *) malloc(2); + if (retval == NULL) { + return NULL; + } + retval[0] = DEFAULT_SLASH; + retval[1] = '\0'; + return retval; + } + +#ifdef TSRM_WIN32 + /* If we have something like C: */ + if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') { + char *retval; + + *length = state->cwd_length+1; + retval = (char *) malloc(*length+1); + if (retval == NULL) { + return NULL; + } + memcpy(retval, state->cwd, *length); + retval[0] = toupper(retval[0]); + retval[*length-1] = DEFAULT_SLASH; + retval[*length] = '\0'; + return retval; + } +#endif + *length = state->cwd_length; + return strdup(state->cwd); +} +/* }}} */ + +/* Same semantics as UNIX getcwd() */ +CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC) /* {{{ */ +{ + size_t length; + char *cwd; + + cwd = virtual_getcwd_ex(&length TSRMLS_CC); + + if (buf == NULL) { + return cwd; + } + if (length > size-1) { + free(cwd); + errno = ERANGE; /* Is this OK? */ + return NULL; + } + memcpy(buf, cwd, length+1); + free(cwd); + return buf; +} +/* }}} */ + +#ifdef PHP_WIN32 +static inline unsigned long realpath_cache_key(const char *path, int path_len TSRMLS_DC) /* {{{ */ +{ + register unsigned long h; + char *bucket_key_start = tsrm_win32_get_path_sid_key(path TSRMLS_CC); + char *bucket_key = (char *)bucket_key_start; + const char *e = bucket_key + strlen(bucket_key); + + if (!bucket_key) { + return 0; + } + + for (h = 2166136261U; bucket_key < e;) { + h *= 16777619; + h ^= *bucket_key++; + } + HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start); + return h; +} +/* }}} */ +#else +static inline unsigned long realpath_cache_key(const char *path, int path_len) /* {{{ */ +{ + register unsigned long h; + const char *e = path + path_len; + + for (h = 2166136261U; path < e;) { + h *= 16777619; + h ^= *path++; + } + + return h; +} +/* }}} */ +#endif /* defined(PHP_WIN32) */ + +CWD_API void realpath_cache_clean(TSRMLS_D) /* {{{ */ +{ + int i; + + for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) { + realpath_cache_bucket *p = CWDG(realpath_cache)[i]; + while (p != NULL) { + realpath_cache_bucket *r = p; + p = p->next; + free(r); + } + CWDG(realpath_cache)[i] = NULL; + } + CWDG(realpath_cache_size) = 0; +} +/* }}} */ + +CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC) /* {{{ */ +{ +#ifdef PHP_WIN32 + unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + unsigned long key = realpath_cache_key(path, path_len); +#endif + unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n]; + + while (*bucket != NULL) { + if (key == (*bucket)->key && path_len == (*bucket)->path_len && + memcmp(path, (*bucket)->path, path_len) == 0) { + realpath_cache_bucket *r = *bucket; + *bucket = (*bucket)->next; + + /* if the pointers match then only subtract the length of the path */ + if(r->path == r->realpath) { + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1; + } else { + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1; + } + + free(r); + return; + } else { + bucket = &(*bucket)->next; + } + } +} +/* }}} */ + +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) /* {{{ */ +{ + long size = sizeof(realpath_cache_bucket) + path_len + 1; + int same = 1; + + if (realpath_len != path_len || + memcmp(path, realpath, path_len) != 0) { + size += realpath_len + 1; + same = 0; + } + + if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) { + realpath_cache_bucket *bucket = malloc(size); + unsigned long n; + + if (bucket == NULL) { + return; + } + +#ifdef PHP_WIN32 + bucket->key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + bucket->key = realpath_cache_key(path, path_len); +#endif + bucket->path = (char*)bucket + sizeof(realpath_cache_bucket); + memcpy(bucket->path, path, path_len+1); + bucket->path_len = path_len; + if (same) { + bucket->realpath = bucket->path; + } else { + bucket->realpath = bucket->path + (path_len + 1); + memcpy(bucket->realpath, realpath, realpath_len+1); + } + 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]; + CWDG(realpath_cache)[n] = bucket; + CWDG(realpath_cache_size) += size; + } +} +/* }}} */ + +static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */ +{ +#ifdef PHP_WIN32 + unsigned long key = realpath_cache_key(path, path_len TSRMLS_CC); +#else + unsigned long key = realpath_cache_key(path, path_len); +#endif + + unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); + realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n]; + + while (*bucket != NULL) { + if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) { + realpath_cache_bucket *r = *bucket; + *bucket = (*bucket)->next; + + /* if the pointers match then only subtract the length of the path */ + if(r->path == r->realpath) { + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1; + } else { + CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1; + } + free(r); + } else if (key == (*bucket)->key && path_len == (*bucket)->path_len && + memcmp(path, (*bucket)->path, path_len) == 0) { + return *bucket; + } else { + bucket = &(*bucket)->next; + } + } + return NULL; +} +/* }}} */ + +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); +} +/* }}} */ + +CWD_API int realpath_cache_size(TSRMLS_D) +{ + return CWDG(realpath_cache_size); +} + +CWD_API int realpath_cache_max_buckets(TSRMLS_D) +{ + return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0])); +} + +CWD_API realpath_cache_bucket** realpath_cache_get_buckets(TSRMLS_D) +{ + return CWDG(realpath_cache); +} + + +#undef LINK_MAX +#define LINK_MAX 32 + +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) /* {{{ */ +{ + int i, j, save; + int directory = 0; +#ifdef TSRM_WIN32 + WIN32_FIND_DATA data; + HANDLE hFind; + TSRM_ALLOCA_FLAG(use_heap_large) +#else + struct stat st; +#endif + realpath_cache_bucket *bucket; + char *tmp; + TSRM_ALLOCA_FLAG(use_heap) + + while (1) { + if (len <= start) { + if (link_is_dir) { + *link_is_dir = 1; + } + return start; + } + + i = len; + while (i > start && !IS_SLASH(path[i-1])) { + i--; + } + + if (i == len || + (i == len - 1 && path[i] == '.')) { + /* remove double slashes and '.' */ + len = i - 1; + is_dir = 1; + continue; + } else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') { + /* remove '..' and previous directory */ + is_dir = 1; + if (link_is_dir) { + *link_is_dir = 1; + } + if (i - 1 <= start) { + return start ? start : len; + } + j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + j--; + while (j > start && !IS_SLASH(path[j])) { + j--; + } + if (!start) { + /* leading '..' must not be removed in case of relative path */ + if (j == 0 && path[0] == '.' && path[1] == '.' && + IS_SLASH(path[2])) { + path[3] = '.'; + path[4] = '.'; + path[5] = DEFAULT_SLASH; + j = 5; + } else if (j > 0 && + path[j+1] == '.' && path[j+2] == '.' && + IS_SLASH(path[j+3])) { + j += 4; + path[j++] = '.'; + path[j++] = '.'; + path[j] = DEFAULT_SLASH; + } + } + } else if (!start && !j) { + /* leading '..' must not be removed in case of relative path */ + path[0] = '.'; + path[1] = '.'; + path[2] = DEFAULT_SLASH; + j = 2; + } + return j; + } + + path[len] = 0; + + save = (use_realpath != CWD_EXPAND); + + if (start && save && CWDG(realpath_cache_size_limit)) { + /* cache lookup for absolute path */ + if (!*t) { + *t = time(0); + } + if ((bucket = realpath_cache_find(path, len, *t TSRMLS_CC)) != NULL) { + if (is_dir && !bucket->is_dir) { + /* not a directory */ + return -1; + } else { + if (link_is_dir) { + *link_is_dir = bucket->is_dir; + } + memcpy(path, bucket->realpath, bucket->realpath_len + 1); + return bucket->realpath_len; + } + } + } + +#ifdef TSRM_WIN32 + if (save && (hFind = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) { + if (use_realpath == CWD_REALPATH) { + /* file not found */ + return -1; + } + /* continue resolution anyway but don't save result in the cache */ + save = 0; + } + + if (save) { + FindClose(hFind); + } + + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); + + if(save && + !(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') && + (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; + int bufindex = 0, isabsolute = 0; + wchar_t * reparsetarget; + BOOL isVolume = FALSE; + char printname[MAX_PATH]; + char substitutename[MAX_PATH]; + int printname_len, substitutename_len; + int substitutename_off = 0; + + 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 (pbuffer == NULL) { + return -1; + } + 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) { + reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0; + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR), + printname_len + 1, + printname, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + printname[printname_len] = 0; + + substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), + substitutename_len + 1, + substitutename, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + substitutename[substitutename_len] = 0; + } + else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + isabsolute = 1; + reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget; + printname_len = pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR), + printname_len + 1, + printname, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + printname[pbuffer->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)] = 0; + + substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + if (!WideCharToMultiByte(CP_THREAD_ACP, 0, + reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), + substitutename_len + 1, + substitutename, MAX_PATH, NULL, NULL + )) { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + }; + substitutename[substitutename_len] = 0; + } + else if (pbuffer->ReparseTag == IO_REPARSE_TAG_DEDUP) { + isabsolute = 1; + memcpy(substitutename, path, len + 1); + substitutename_len = len; + } else { + tsrm_free_alloca(pbuffer, use_heap_large); + return -1; + } + + if(isabsolute && substitutename_len > 4) { + /* Do not resolve volumes (for now). A mounted point can + target a volume without a drive, it is not certain that + all IO functions we use in php and its deps support + path with volume GUID instead of the DOS way, like: + d:\test\mnt\foo + \\?\Volume{62d1c3f8-83b9-11de-b108-806e6f6e6963}\foo + */ + if (strncmp(substitutename, "\\??\\Volume{",11) == 0 + || strncmp(substitutename, "\\\\?\\Volume{",11) == 0 + || strncmp(substitutename, "\\??\\UNC\\", 8) == 0 + ) { + isVolume = TRUE; + substitutename_off = 0; + } else + /* do not use the \??\ and \\?\ prefix*/ + if (strncmp(substitutename, "\\??\\", 4) == 0 + || strncmp(substitutename, "\\\\?\\", 4) == 0) { + substitutename_off = 4; + } + } + + if (!isVolume) { + char * tmp2 = substitutename + substitutename_off; + for(bufindex = 0; bufindex < (substitutename_len - substitutename_off); bufindex++) { + *(path + bufindex) = *(tmp2 + bufindex); + } + + *(path + bufindex) = 0; + j = bufindex; + } else { + j = len; + } + + +#if VIRTUAL_CWD_DEBUG + fprintf(stderr, "reparse: print: %s ", printname); + fprintf(stderr, "sub: %s ", substitutename); + fprintf(stderr, "resolved: %s ", path); +#endif + tsrm_free_alloca(pbuffer, use_heap_large); + + if(isabsolute == 1) { + if (!((j == 3) && (path[1] == ':') && (path[2] == '\\'))) { + /* 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; + } + } + directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + + 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); + memcpy(tmp, path, len+1); +#else + if (save && php_sys_lstat(path, &st) < 0) { + if (use_realpath == CWD_REALPATH) { + /* file not found */ + return -1; + } + /* continue resolution anyway but don't save result in the cache */ + save = 0; + } + + tmp = tsrm_do_alloca(len+1, use_heap); + memcpy(tmp, path, len+1); + + if (save && S_ISLNK(st.st_mode)) { + if (++(*ll) > LINK_MAX || (j = php_sys_readlink(tmp, path, MAXPATHLEN)) < 0) { + /* too many links or broken symlinks */ + tsrm_free_alloca(tmp, use_heap); + return -1; + } + path[j] = 0; + if (IS_ABSOLUTE_PATH(path, j)) { + j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, 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; /* buffer overflow */ + } + 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 = S_ISDIR(st.st_mode); + if (link_is_dir) { + *link_is_dir = directory; + } + if (is_dir && !directory) { + /* not a directory */ + tsrm_free_alloca(tmp, use_heap); + return -1; + } + } +#endif + if (i - 1 <= start) { + j = start; + } else { + /* some leading directories may be unaccessable */ + j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL TSRMLS_CC); + if (j > start) { + path[j++] = DEFAULT_SLASH; + } + } +#ifdef TSRM_WIN32 + if (j < 0 || j + len - i >= MAXPATHLEN-1) { + tsrm_free_alloca(tmp, use_heap); + return -1; + } + if (save) { + i = strlen(data.cFileName); + memcpy(path+j, data.cFileName, i+1); + j += i; + } 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); + return -1; + } + memcpy(path+j, tmp+i, len-i+1); + j += (len-i); + } +#endif + + if (save && start && CWDG(realpath_cache_size_limit)) { + /* save absolute path in the cache */ + realpath_cache_add(tmp, len, path, j, directory, *t TSRMLS_CC); + } + + tsrm_free_alloca(tmp, use_heap); + return j; + } +} +/* }}} */ + +/* Resolve path relatively to state and put the real path into state */ +/* returns 0 for ok, 1 for error */ +CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath TSRMLS_DC) /* {{{ */ +{ + int path_length = strlen(path); + char resolved_path[MAXPATHLEN]; + int start = 1; + int ll = 0; + time_t t; + int ret; + int add_slash; + void *tmp; + + if (path_length == 0 || path_length >= MAXPATHLEN-1) { +#ifdef TSRM_WIN32 +# if _MSC_VER < 1300 + errno = EINVAL; +# else + _set_errno(EINVAL); +# endif +#else + errno = EINVAL; +#endif + return 1; + } + +#if VIRTUAL_CWD_DEBUG + fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path); +#endif + + /* cwd_length can be 0 when getcwd() fails. + * This can happen under solaris when a dir does not have read permissions + * but *does* have execute permissions */ + if (!IS_ABSOLUTE_PATH(path, path_length)) { + if (state->cwd_length == 0) { + /* resolve relative path */ + start = 0; + memcpy(resolved_path , path, path_length + 1); + } else { + int state_cwd_length = state->cwd_length; + +#ifdef TSRM_WIN32 + if (IS_SLASH(path[0])) { + if (state->cwd[1] == ':') { + /* Copy only the drive name */ + state_cwd_length = 2; + } else if (IS_UNC_PATH(state->cwd, state->cwd_length)) { + /* Copy only the share name */ + state_cwd_length = 2; + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + while (state->cwd[state_cwd_length] && + !IS_SLASH(state->cwd[state_cwd_length])) { + state_cwd_length++; + } + } + } +#endif + if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) { + return 1; + } + memcpy(resolved_path, state->cwd, state_cwd_length); + if (resolved_path[state_cwd_length-1] == DEFAULT_SLASH) { + memcpy(resolved_path + state_cwd_length, path, path_length + 1); + path_length += state_cwd_length; + } else { + resolved_path[state_cwd_length] = DEFAULT_SLASH; + memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1); + path_length += state_cwd_length + 1; + } + } + } else { +#ifdef TSRM_WIN32 + if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) { + resolved_path[0] = path[0]; + resolved_path[1] = ':'; + resolved_path[2] = DEFAULT_SLASH; + memcpy(resolved_path + 3, path + 2, path_length - 1); + path_length++; + } else +#endif + memcpy(resolved_path, path, path_length + 1); + } + +#ifdef TSRM_WIN32 + if (memchr(resolved_path, '*', path_length) || + memchr(resolved_path, '?', path_length)) { + return 1; + } +#endif + +#ifdef TSRM_WIN32 + if (IS_UNC_PATH(resolved_path, path_length)) { + /* skip UNC name */ + resolved_path[0] = DEFAULT_SLASH; + resolved_path[1] = DEFAULT_SLASH; + start = 2; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + while (!IS_SLASH(resolved_path[start])) { + if (resolved_path[start] == 0) { + goto verify; + } + resolved_path[start] = toupper(resolved_path[start]); + start++; + } + resolved_path[start++] = DEFAULT_SLASH; + } else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip DRIVE name */ + resolved_path[0] = toupper(resolved_path[0]); + resolved_path[2] = DEFAULT_SLASH; + start = 3; + } +#elif defined(NETWARE) + if (IS_ABSOLUTE_PATH(resolved_path, path_length)) { + /* skip VOLUME name */ + start = 0; + while (start != ':') { + if (resolved_path[start] == 0) return -1; + start++; + } + start++; + if (!IS_SLASH(resolved_path[start])) return -1; + resolved_path[start++] = DEFAULT_SLASH; + } +#endif + + add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]); + t = CWDG(realpath_cache_ttl) ? 0 : -1; + path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL TSRMLS_CC); + + if (path_length < 0) { + errno = ENOENT; + return 1; + } + + if (!start && !path_length) { + resolved_path[path_length++] = '.'; + } + if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) { + if (path_length >= MAXPATHLEN-1) { + return -1; + } + resolved_path[path_length++] = DEFAULT_SLASH; + } + resolved_path[path_length] = 0; + +#ifdef TSRM_WIN32 +verify: +#endif + if (verify_path) { + cwd_state old_state; + + CWD_STATE_COPY(&old_state, state); + state->cwd_length = path_length; + + tmp = realloc(state->cwd, state->cwd_length+1); + if (tmp == NULL) { +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Out of memory\n"); +#endif + return 1; + } + state->cwd = (char *) tmp; + + memcpy(state->cwd, resolved_path, state->cwd_length+1); + if (verify_path(state)) { + CWD_STATE_FREE(state); + *state = old_state; + ret = 1; + } else { + CWD_STATE_FREE(&old_state); + ret = 0; + } + } else { + state->cwd_length = path_length; + tmp = realloc(state->cwd, state->cwd_length+1); + if (tmp == NULL) { +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Out of memory\n"); +#endif + return 1; + } + state->cwd = (char *) tmp; + + memcpy(state->cwd, resolved_path, state->cwd_length+1); + ret = 0; + } + +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd); +#endif + return (ret); +} +/* }}} */ + +CWD_API int virtual_chdir(const char *path TSRMLS_DC) /* {{{ */ +{ + return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH TSRMLS_CC)?-1:0; +} +/* }}} */ + +CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC) /* {{{ */ +{ + int length = strlen(path); + char *temp; + int retval; + TSRM_ALLOCA_FLAG(use_heap) + + if (length == 0) { + return 1; /* Can't cd to empty string */ + } + while(--length >= 0 && !IS_SLASH(path[length])) { + } + + if (length == -1) { + /* No directory only file name */ + errno = ENOENT; + return -1; + } + + if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */ + length++; + } + temp = (char *) tsrm_do_alloca(length+1, use_heap); + memcpy(temp, path, length); + temp[length] = 0; +#if VIRTUAL_CWD_DEBUG + fprintf (stderr, "Changing directory to %s\n", temp); +#endif + retval = p_chdir(temp TSRMLS_CC); + tsrm_free_alloca(temp, use_heap); + return retval; +} +/* }}} */ + +CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + char *retval; + char cwd[MAXPATHLEN]; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + retval = NULL; + goto end; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, strlen(path))) { + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + } else { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + retval = NULL; + goto end; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)==0) { + int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length; + + memcpy(real_path, new_state.cwd, len); + real_path[len] = '\0'; + retval = real_path; + } else { + retval = NULL; + } + + CWD_STATE_FREE(&new_state); +end: + return retval; +} +/* }}} */ + +CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH TSRMLS_CC); + + *filepath = new_state.cwd; + + return retval; + +} +/* }}} */ + +CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC) /* {{{ */ +{ + return virtual_filepath_ex(path, filepath, php_is_file_ok TSRMLS_CC); +} +/* }}} */ + +CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + FILE *f; + + if (path[0] == '\0') { /* Fail to open empty path */ + return NULL; + } + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return NULL; + } + + f = fopen(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#if defined(TSRM_WIN32) + ret = tsrm_win32_access(new_state.cwd, mode TSRMLS_CC); +#else + ret = access(new_state.cwd, mode); +#endif + + CWD_STATE_FREE(&new_state); + + return ret; +} +/* }}} */ + +#if HAVE_UTIME +#ifdef TSRM_WIN32 +static void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */ +{ + // Note that LONGLONG is a 64-bit value + LONGLONG ll; + + ll = Int32x32To64(t, 10000000) + 116444736000000000; + pft->dwLowDateTime = (DWORD)ll; + pft->dwHighDateTime = ll >> 32; +} +/* }}} */ + +TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */ +{ + FILETIME mtime, atime; + HANDLE hFile; + + hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + /* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but + the CreateFile operation succeeds */ + if (GetLastError() == ERROR_ALREADY_EXISTS) { + SetLastError(0); + } + + if ( hFile == INVALID_HANDLE_VALUE ) { + return -1; + } + + if (!buf) { + SYSTEMTIME st; + GetSystemTime(&st); + SystemTimeToFileTime(&st, &mtime); + atime = mtime; + } else { + UnixTimeToFileTime(buf->modtime, &mtime); + UnixTimeToFileTime(buf->actime, &atime); + } + if (!SetFileTime(hFile, NULL, &atime, &mtime)) { + CloseHandle(hFile); + return -1; + } + CloseHandle(hFile); + return 1; +} +/* }}} */ +#endif + +CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#ifdef TSRM_WIN32 + ret = win32_utime(new_state.cwd, buf); +#else + ret = utime(new_state.cwd, buf); +#endif + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ +#endif + +CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + ret = chmod(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ + +#if !defined(TSRM_WIN32) && !defined(NETWARE) +CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int ret; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + if (link) { +#if HAVE_LCHOWN + ret = lchown(new_state.cwd, owner, group); +#else + ret = -1; +#endif + } else { + ret = chown(new_state.cwd, owner, group); + } + + CWD_STATE_FREE(&new_state); + return ret; +} +/* }}} */ +#endif + +CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...) /* {{{ */ +{ + cwd_state new_state; + int f; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + if (flags & O_CREAT) { + mode_t mode; + va_list arg; + + va_start(arg, flags); + mode = (mode_t) va_arg(arg, int); + va_end(arg); + + f = open(new_state.cwd, flags, mode); + } else { + f = open(new_state.cwd, flags); + } + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int f; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + f = creat(new_state.cwd, mode); + + CWD_STATE_FREE(&new_state); + return f; +} +/* }}} */ + +CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC) /* {{{ */ +{ + cwd_state old_state; + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&old_state, &CWDG(cwd)); + if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&old_state); + return -1; + } + oldname = old_state.cwd; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&old_state); + CWD_STATE_FREE(&new_state); + return -1; + } + newname = new_state.cwd; + + /* rename on windows will fail if newname already exists. + MoveFileEx has to be used */ +#ifdef TSRM_WIN32 + /* MoveFileEx returns 0 on failure, other way 'round for this function */ + retval = (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) == 0) ? -1 : 0; +#else + retval = rename(oldname, newname); +#endif + + CWD_STATE_FREE(&old_state); + CWD_STATE_FREE(&new_state); + + return retval; +} +/* }}} */ + +CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = php_sys_stat(new_state.cwd, buf); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = php_sys_lstat(new_state.cwd, buf); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_unlink(const char *path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = unlink(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + +#ifdef TSRM_WIN32 + retval = mkdir(new_state.cwd); +#else + retval = mkdir(new_state.cwd, mode); +#endif + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + int retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return -1; + } + + retval = rmdir(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +#ifdef TSRM_WIN32 +DIR *opendir(const char *name); +#endif + +CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + DIR *retval; + + CWD_STATE_COPY(&new_state, &CWDG(cwd)); + if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH TSRMLS_CC)) { + CWD_STATE_FREE(&new_state); + return NULL; + } + + retval = opendir(new_state.cwd); + + CWD_STATE_FREE(&new_state); + return retval; +} +/* }}} */ + +#ifdef TSRM_WIN32 +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + return popen_ex(command, type, CWDG(cwd).cwd, NULL TSRMLS_CC); +} +/* }}} */ +#elif defined(NETWARE) +/* On NetWare, the trick of prepending "cd cwd; " doesn't work so we need to perform + a VCWD_CHDIR() and mutex it + */ +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + char prev_cwd[MAXPATHLEN]; + char *getcwd_result; + FILE *retval; + + getcwd_result = VCWD_GETCWD(prev_cwd, MAXPATHLEN); + if (!getcwd_result) { + return NULL; + } + +#ifdef ZTS + tsrm_mutex_lock(cwd_mutex); +#endif + + VCWD_CHDIR(CWDG(cwd).cwd); + retval = popen(command, type); + VCWD_CHDIR(prev_cwd); + +#ifdef ZTS + tsrm_mutex_unlock(cwd_mutex); +#endif + + return retval; +} +/* }}} */ +#else /* Unix */ +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */ +{ + int command_length; + int dir_length, extra = 0; + char *command_line; + char *ptr, *dir; + FILE *retval; + + command_length = strlen(command); + + dir_length = CWDG(cwd).cwd_length; + dir = CWDG(cwd).cwd; + while (dir_length > 0) { + if (*dir == '\'') extra+=3; + dir++; + dir_length--; + } + dir_length = CWDG(cwd).cwd_length; + dir = CWDG(cwd).cwd; + + ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1); + if (!command_line) { + return NULL; + } + memcpy(ptr, "cd ", sizeof("cd ")-1); + ptr += sizeof("cd ")-1; + + if (CWDG(cwd).cwd_length == 0) { + *ptr++ = DEFAULT_SLASH; + } else { + *ptr++ = '\''; + while (dir_length > 0) { + switch (*dir) { + case '\'': + *ptr++ = '\''; + *ptr++ = '\\'; + *ptr++ = '\''; + /* fall-through */ + default: + *ptr++ = *dir; + } + dir++; + dir_length--; + } + *ptr++ = '\''; + } + + *ptr++ = ' '; + *ptr++ = ';'; + *ptr++ = ' '; + + memcpy(ptr, command, command_length+1); + retval = popen(command_line, type); + + free(command_line); + return retval; +} +/* }}} */ +#endif + +CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */ +{ + cwd_state new_state; + char cwd[MAXPATHLEN]; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, strlen(path)) && + VCWD_GETCWD(cwd, MAXPATHLEN)) { + new_state.cwd = strdup(cwd); + new_state.cwd_length = strlen(cwd); + } else { + new_state.cwd = (char*)malloc(1); + if (new_state.cwd == NULL) { + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH TSRMLS_CC)) { + free(new_state.cwd); + return NULL; + } + + if (real_path) { + int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length; + memcpy(real_path, new_state.cwd, copy_len); + real_path[copy_len] = '\0'; + free(new_state.cwd); + return real_path; + } else { + return new_state.cwd; + } +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/TSRM/tsrm_virtual_cwd.h b/TSRM/tsrm_virtual_cwd.h new file mode 100644 index 0000000..8aac4aa --- /dev/null +++ b/TSRM/tsrm_virtual_cwd.h @@ -0,0 +1,338 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans | + | Sascha Schumann | + | Pierre Joye | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef VIRTUAL_CWD_H +#define VIRTUAL_CWD_H + +#include "TSRM.h" +#include "tsrm_config_common.h" + +#include +#include +#include + +#ifdef HAVE_UTIME_H +#include +#endif + +#ifdef HAVE_STDARG_H +#include +#endif + +#ifdef ZTS +#define VIRTUAL_DIR +#endif + +#ifndef TSRM_WIN32 +#include +#else +#include +#endif + +#if defined(__osf__) || defined(_AIX) +#include +#endif + +#ifdef TSRM_WIN32 +#include "readdir.h" +#include +/* mode_t isn't defined on Windows */ +typedef unsigned short mode_t; + +#define DEFAULT_SLASH '\\' +#define DEFAULT_DIR_SEPARATOR ';' +#define IS_SLASH(c) ((c) == '/' || (c) == '\\') +#define IS_SLASH_P(c) (*(c) == '/' || \ + (*(c) == '\\' && !IsDBCSLeadByte(*(c-1)))) + +/* COPY_WHEN_ABSOLUTE is 2 under Win32 because by chance both regular absolute paths + in the file system and UNC paths need copying of two characters */ +#define COPY_WHEN_ABSOLUTE(path) 2 +#define IS_UNC_PATH(path, len) \ + (len >= 2 && IS_SLASH(path[0]) && IS_SLASH(path[1])) +#define IS_ABSOLUTE_PATH(path, len) \ + (len >= 2 && ((isalpha(path[0]) && path[1] == ':') || IS_UNC_PATH(path, len))) + +#elif defined(NETWARE) +#ifdef HAVE_DIRENT_H +#include +#endif + +#define DEFAULT_SLASH '/' +#define DEFAULT_DIR_SEPARATOR ';' +#define IS_SLASH(c) ((c) == '/' || (c) == '\\') +#define IS_SLASH_P(c) IS_SLASH(*(c)) +/* Colon indicates volume name, either first character should be forward slash or backward slash */ +#define IS_ABSOLUTE_PATH(path, len) \ + ((strchr(path, ':') != NULL) || ((len >= 1) && ((path[0] == '/') || (path[0] == '\\')))) + +#else +#ifdef HAVE_DIRENT_H +#include +#endif + +#define DEFAULT_SLASH '/' + +#ifdef __riscos__ +#define DEFAULT_DIR_SEPARATOR ';' +#else +#define DEFAULT_DIR_SEPARATOR ':' +#endif + +#define IS_SLASH(c) ((c) == '/') +#define IS_SLASH_P(c) (*(c) == '/') + +#endif + + +#ifndef COPY_WHEN_ABSOLUTE +#define COPY_WHEN_ABSOLUTE(path) 0 +#endif + +#ifndef IS_ABSOLUTE_PATH +#define IS_ABSOLUTE_PATH(path, len) \ + (IS_SLASH(path[0])) +#endif + +#ifdef TSRM_EXPORTS +#define CWD_EXPORTS +#endif + +#ifdef TSRM_WIN32 +# ifdef CWD_EXPORTS +# define CWD_API __declspec(dllexport) +# else +# define CWD_API __declspec(dllimport) +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define CWD_API __attribute__ ((visibility("default"))) +#else +# define CWD_API +#endif + +#ifdef TSRM_WIN32 +CWD_API int php_sys_stat_ex(const char *path, struct stat *buf, int lstat); +# define php_sys_stat(path, buf) php_sys_stat_ex(path, buf, 0) +# define php_sys_lstat(path, buf) php_sys_stat_ex(path, buf, 1) +CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len); +#else +# define php_sys_stat stat +# define php_sys_lstat lstat +# ifdef HAVE_SYMLINK +# define php_sys_readlink(link, target, target_len) readlink(link, target, target_len) +# endif +#endif + +typedef struct _cwd_state { + char *cwd; + int cwd_length; +} cwd_state; + +typedef int (*verify_path_func)(const cwd_state *); + +CWD_API void virtual_cwd_startup(void); +CWD_API void virtual_cwd_shutdown(void); +CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC); +CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC); +CWD_API int virtual_chdir(const char *path TSRMLS_DC); +CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC); +CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC); +CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC); +CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC); +CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC); +CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...); +CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC); +CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC); +CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC); +CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC); +CWD_API int virtual_unlink(const char *path TSRMLS_DC); +CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC); +CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC); +CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC); +CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC); +CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC); +#if defined(TSRM_WIN32) +/* these are not defined in win32 headers */ +#ifndef W_OK +#define W_OK 0x02 +#endif +#ifndef R_OK +#define R_OK 0x04 +#endif +#ifndef X_OK +#define X_OK 0x01 +#endif +#ifndef F_OK +#define F_OK 0x00 +#endif +#endif + +#if HAVE_UTIME +CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC); +#endif +CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC); +#if !defined(TSRM_WIN32) && !defined(NETWARE) +CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC); +#endif + +/* One of the following constants must be used as the last argument + in virtual_file_ex() call. */ + +#define CWD_EXPAND 0 /* expand "." and ".." but dont resolve symlinks */ +#define CWD_FILEPATH 1 /* resolve symlinks if file is exist otherwise expand */ +#define CWD_REALPATH 2 /* call realpath(), resolve symlinks. File must exist */ + +CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath TSRMLS_DC); + +CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC); + +#define REALPATH_CACHE_TTL (2*60) /* 2 minutes */ +#define REALPATH_CACHE_SIZE 0 /* disabled while php.ini isn't loaded */ + +typedef struct _realpath_cache_bucket { + unsigned long key; + char *path; + int path_len; + char *realpath; + int realpath_len; + int is_dir; + time_t expires; +#ifdef PHP_WIN32 + unsigned char is_rvalid; + unsigned char is_readable; + unsigned char is_wvalid; + unsigned char is_writable; +#endif + struct _realpath_cache_bucket *next; +} realpath_cache_bucket; + +typedef struct _virtual_cwd_globals { + cwd_state cwd; + long realpath_cache_size; + long realpath_cache_size_limit; + long realpath_cache_ttl; + realpath_cache_bucket *realpath_cache[1024]; +} virtual_cwd_globals; + +#ifdef ZTS +extern ts_rsrc_id cwd_globals_id; +# define CWDG(v) TSRMG(cwd_globals_id, virtual_cwd_globals *, v) +#else +extern virtual_cwd_globals cwd_globals; +# define CWDG(v) (cwd_globals.v) +#endif + +CWD_API void realpath_cache_clean(TSRMLS_D); +CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC); +CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC); +CWD_API int realpath_cache_size(TSRMLS_D); +CWD_API int realpath_cache_max_buckets(TSRMLS_D); +CWD_API realpath_cache_bucket** realpath_cache_get_buckets(TSRMLS_D); + +/* The actual macros to be used in programs using TSRM + * If the program defines VIRTUAL_DIR it will use the + * virtual_* functions + */ + +#ifdef VIRTUAL_DIR + +#define VCWD_GETCWD(buff, size) virtual_getcwd(buff, size TSRMLS_CC) +#define VCWD_FOPEN(path, mode) virtual_fopen(path, mode TSRMLS_CC) +/* Because open() has two modes, we have to macros to replace it */ +#define VCWD_OPEN(path, flags) virtual_open(path TSRMLS_CC, flags) +#define VCWD_OPEN_MODE(path, flags, mode) virtual_open(path TSRMLS_CC, flags, mode) +#define VCWD_CREAT(path, mode) virtual_creat(path, mode TSRMLS_CC) +#define VCWD_CHDIR(path) virtual_chdir(path TSRMLS_CC) +#define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, virtual_chdir TSRMLS_CC) +#define VCWD_GETWD(buf) +#define VCWD_REALPATH(path, real_path) virtual_realpath(path, real_path TSRMLS_CC) +#define VCWD_RENAME(oldname, newname) virtual_rename(oldname, newname TSRMLS_CC) +#define VCWD_STAT(path, buff) virtual_stat(path, buff TSRMLS_CC) +# define VCWD_LSTAT(path, buff) virtual_lstat(path, buff TSRMLS_CC) +#define VCWD_UNLINK(path) virtual_unlink(path TSRMLS_CC) +#define VCWD_MKDIR(pathname, mode) virtual_mkdir(pathname, mode TSRMLS_CC) +#define VCWD_RMDIR(pathname) virtual_rmdir(pathname TSRMLS_CC) +#define VCWD_OPENDIR(pathname) virtual_opendir(pathname TSRMLS_CC) +#define VCWD_POPEN(command, type) virtual_popen(command, type TSRMLS_CC) +#define VCWD_ACCESS(pathname, mode) virtual_access(pathname, mode TSRMLS_CC) +#if HAVE_UTIME +#define VCWD_UTIME(path, time) virtual_utime(path, time TSRMLS_CC) +#endif +#define VCWD_CHMOD(path, mode) virtual_chmod(path, mode TSRMLS_CC) +#if !defined(TSRM_WIN32) && !defined(NETWARE) +#define VCWD_CHOWN(path, owner, group) virtual_chown(path, owner, group, 0 TSRMLS_CC) +#if HAVE_LCHOWN +#define VCWD_LCHOWN(path, owner, group) virtual_chown(path, owner, group, 1 TSRMLS_CC) +#endif +#endif + +#else + +#define VCWD_GETCWD(buff, size) getcwd(buff, size) +#define VCWD_FOPEN(path, mode) fopen(path, mode) +#define VCWD_OPEN(path, flags) open(path, flags) +#define VCWD_OPEN_MODE(path, flags, mode) open(path, flags, mode) +#define VCWD_CREAT(path, mode) creat(path, mode) +/* rename on windows will fail if newname already exists. + MoveFileEx has to be used */ +#if defined(TSRM_WIN32) +# define VCWD_RENAME(oldname, newname) (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) == 0 ? -1 : 0) +#else +# define VCWD_RENAME(oldname, newname) rename(oldname, newname) +#endif +#define VCWD_CHDIR(path) chdir(path) +#define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, chdir) +#define VCWD_GETWD(buf) getwd(buf) +#define VCWD_STAT(path, buff) php_sys_stat(path, buff) +#define VCWD_LSTAT(path, buff) lstat(path, buff) +#define VCWD_UNLINK(path) unlink(path) +#define VCWD_MKDIR(pathname, mode) mkdir(pathname, mode) +#define VCWD_RMDIR(pathname) rmdir(pathname) +#define VCWD_OPENDIR(pathname) opendir(pathname) +#define VCWD_POPEN(command, type) popen(command, type) +#if defined(TSRM_WIN32) +#define VCWD_ACCESS(pathname, mode) tsrm_win32_access(pathname, mode TSRMLS_CC) +#else +#define VCWD_ACCESS(pathname, mode) access(pathname, mode) +#endif + +#define VCWD_REALPATH(path, real_path) tsrm_realpath(path, real_path TSRMLS_CC) + +#if HAVE_UTIME +# ifdef TSRM_WIN32 +# define VCWD_UTIME(path, time) win32_utime(path, time) +# else +# define VCWD_UTIME(path, time) utime(path, time) +# endif +#endif + +#define VCWD_CHMOD(path, mode) chmod(path, mode) +#if !defined(TSRM_WIN32) && !defined(NETWARE) +#define VCWD_CHOWN(path, owner, group) chown(path, owner, group) +#if HAVE_LCHOWN +#define VCWD_LCHOWN(path, owner, group) lchown(path, owner, group) +#endif +#endif + +#endif + +#endif /* VIRTUAL_CWD_H */ diff --git a/TSRM/tsrm_win32.c b/TSRM/tsrm_win32.c new file mode 100644 index 0000000..0ced6db --- /dev/null +++ b/TSRM/tsrm_win32.c @@ -0,0 +1,723 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Beulshausen | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include +#include +#include +#include +#include +#include + +#define TSRM_INCLUDE_FULL_WINDOWS_HEADERS +#include "SAPI.h" +#include "TSRM.h" + +#ifdef TSRM_WIN32 +#include +#include "tsrm_win32.h" +#include "tsrm_virtual_cwd.h" + +#ifdef ZTS +static ts_rsrc_id win32_globals_id; +#else +static tsrm_win32_globals win32_globals; +#endif + +static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC) +{ + globals->process = NULL; + globals->shm = NULL; + globals->process_size = 0; + globals->shm_size = 0; + globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com"); + + /* Set it to INVALID_HANDLE_VALUE + * It will be initialized correctly in tsrm_win32_access or set to + * NULL if no impersonation has been done. + * the impersonated token can't be set here as the impersonation + * will happen later, in fcgi_accept_request (or whatever is the + * SAPI being used). + */ + globals->impersonation_token = INVALID_HANDLE_VALUE; + globals->impersonation_token_sid = NULL; +} + +static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC) +{ + shm_pair *ptr; + + if (globals->process) { + free(globals->process); + } + + if (globals->shm) { + for (ptr = globals->shm; ptr < (globals->shm + globals->shm_size); ptr++) { + UnmapViewOfFile(ptr->addr); + CloseHandle(ptr->segment); + UnmapViewOfFile(ptr->descriptor); + CloseHandle(ptr->info); + } + free(globals->shm); + } + + free(globals->comspec); + + if (globals->impersonation_token && globals->impersonation_token != INVALID_HANDLE_VALUE ) { + CloseHandle(globals->impersonation_token); + } + if (globals->impersonation_token_sid) { + free(globals->impersonation_token_sid); + } +} + +TSRM_API void tsrm_win32_startup(void) +{ +#ifdef ZTS + ts_allocate_id(&win32_globals_id, sizeof(tsrm_win32_globals), (ts_allocate_ctor)tsrm_win32_ctor, (ts_allocate_ctor)tsrm_win32_dtor); +#else + tsrm_win32_ctor(&win32_globals TSRMLS_CC); +#endif +} + +TSRM_API void tsrm_win32_shutdown(void) +{ +#ifndef ZTS + tsrm_win32_dtor(&win32_globals TSRMLS_CC); +#endif +} + +char * tsrm_win32_get_path_sid_key(const char *pathname TSRMLS_DC) +{ + PSID pSid = TWG(impersonation_token_sid); + DWORD sid_len = pSid ? GetLengthSid(pSid) : 0; + TCHAR *ptcSid = NULL; + char *bucket_key = NULL; + + if (!pSid) { + bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + 1); + if (!bucket_key) { + return NULL; + } + memcpy(bucket_key, pathname, strlen(pathname)); + return bucket_key; + } + + if (!ConvertSidToStringSid(pSid, &ptcSid)) { + return NULL; + } + + bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + strlen(ptcSid) + 1); + if (!bucket_key) { + LocalFree(ptcSid); + return NULL; + } + + memcpy(bucket_key, ptcSid, strlen(ptcSid)); + memcpy(bucket_key + strlen(ptcSid), pathname, strlen(pathname) + 1); + + LocalFree(ptcSid); + return bucket_key; +} + + +PSID tsrm_win32_get_token_sid(HANDLE hToken) +{ + BOOL bSuccess = FALSE; + DWORD dwLength = 0; + PTOKEN_USER pTokenUser = NULL; + PSID sid; + PSID *ppsid = &sid; + DWORD sid_len; + PSID pResultSid = NULL; + + /* Get the actual size of the TokenUser structure */ + if (!GetTokenInformation( + hToken, TokenUser, (LPVOID) pTokenUser, 0, &dwLength)) { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + goto Finished; + } + + pTokenUser = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); + if (pTokenUser == NULL) { + goto Finished; + } + } + + /* and fetch it now */ + if (!GetTokenInformation( + hToken, TokenUser, (LPVOID) pTokenUser, dwLength, &dwLength)) { + goto Finished; + } + + sid_len = GetLengthSid(pTokenUser->User.Sid); + + /* ConvertSidToStringSid(pTokenUser->User.Sid, &ptcSidOwner); */ + pResultSid = malloc(sid_len); + if (!pResultSid) { + goto Finished; + } + if (!CopySid(sid_len, pResultSid, pTokenUser->User.Sid)) { + goto Finished; + } + HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser); + return pResultSid; + +Finished: + if (pResultSid) { + free(pResultSid); + } + /* Free the buffer for the token groups. */ + if (pTokenUser != NULL) { + HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser); + } + return NULL; +} + +TSRM_API int tsrm_win32_access(const char *pathname, int mode TSRMLS_DC) +{ + time_t t; + HANDLE thread_token = NULL; + PSID token_sid; + SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; + GENERIC_MAPPING gen_map = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; + DWORD priv_set_length = sizeof(PRIVILEGE_SET); + + PRIVILEGE_SET privilege_set = {0}; + DWORD sec_desc_length = 0, desired_access = 0, granted_access = 0; + BYTE * psec_desc = NULL; + BOOL fAccess = FALSE; + + BOOL bucket_key_alloc = FALSE; + realpath_cache_bucket * bucket = NULL; + char * real_path = NULL; + + if (mode == 1 /*X_OK*/) { + DWORD type; + return GetBinaryType(pathname, &type) ? 0 : -1; + } else { + 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(access(pathname, mode)) { + free(real_path); + return errno; + } + + /* If only existence check is made, return now */ + if (mode == 0) { + free(real_path); + return 0; + } + +/* Only in NTS when impersonate==1 (aka FastCGI) */ + + /* + AccessCheck() requires an impersonation token. We first get a primary + token and then create a duplicate impersonation token. The + impersonation token is not actually assigned to the thread, but is + used in the call to AccessCheck. Thus, this function itself never + impersonates, but does use the identity of the thread. If the thread + was impersonating already, this function uses that impersonation context. + */ + if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) { + DWORD err = GetLastError(); + if (GetLastError() == ERROR_NO_TOKEN) { + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) { + TWG(impersonation_token) = NULL; + goto Finished; + } + } + } + + /* token_sid will be freed in tsrmwin32_dtor */ + token_sid = tsrm_win32_get_token_sid(thread_token); + if (!token_sid) { + if (TWG(impersonation_token_sid)) { + free(TWG(impersonation_token_sid)); + } + TWG(impersonation_token_sid) = NULL; + goto Finished; + } + + /* Different identity, we need a new impersontated token as well */ + if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) { + if (TWG(impersonation_token_sid)) { + free(TWG(impersonation_token_sid)); + } + TWG(impersonation_token_sid) = token_sid; + + /* Duplicate the token as impersonated token */ + if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) { + goto Finished; + } + } else { + /* we already have it, free it then */ + free(token_sid); + } + + 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|FILE_FLAG_BACKUP_SEMANTICS; + } 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 */ + if(GetFileSecurity(pathname, sec_info, NULL, 0, &sec_desc_length)) { + goto Finished; + } + + psec_desc = (BYTE *)malloc(sec_desc_length); + if(psec_desc == NULL || + !GetFileSecurity(pathname, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) { + goto Finished; + } + + MapGenericMask(&desired_access, &gen_map); + + if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) { + goto Finished_Impersonate; + } + + /* Keep the result in realpath_cache */ + if(bucket != NULL) { + if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) { + bucket->is_rvalid = 1; + bucket->is_readable = fAccess; + } + else if(desired_access == FILE_GENERIC_WRITE) { + bucket->is_wvalid = 1; + bucket->is_writable = fAccess; + } else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) { + bucket->is_rvalid = 1; + bucket->is_readable = fAccess; + bucket->is_wvalid = 1; + bucket->is_writable = fAccess; + } + } + +Finished_Impersonate: + if(psec_desc != NULL) { + free(psec_desc); + psec_desc = NULL; + } + +Finished: + if(thread_token != NULL) { + CloseHandle(thread_token); + } + if(real_path != NULL) { + free(real_path); + real_path = NULL; + } + + if(fAccess == FALSE) { + errno = EACCES; + return errno; + } else { + return 0; + } + } +} + + +static process_pair *process_get(FILE *stream TSRMLS_DC) +{ + process_pair *ptr; + process_pair *newptr; + + for (ptr = TWG(process); ptr < (TWG(process) + TWG(process_size)); ptr++) { + if (ptr->stream == stream) { + break; + } + } + + if (ptr < (TWG(process) + TWG(process_size))) { + return ptr; + } + + newptr = (process_pair*)realloc((void*)TWG(process), (TWG(process_size)+1)*sizeof(process_pair)); + if (newptr == NULL) { + return NULL; + } + + TWG(process) = newptr; + ptr = newptr + TWG(process_size); + TWG(process_size)++; + return ptr; +} + +static shm_pair *shm_get(int key, void *addr) +{ + shm_pair *ptr; + shm_pair *newptr; + TSRMLS_FETCH(); + + for (ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) { + if (!ptr->descriptor) { + continue; + } + if (!addr && ptr->descriptor->shm_perm.key == key) { + break; + } else if (ptr->addr == addr) { + break; + } + } + + if (ptr < (TWG(shm) + TWG(shm_size))) { + return ptr; + } + + newptr = (shm_pair*)realloc((void*)TWG(shm), (TWG(shm_size)+1)*sizeof(shm_pair)); + if (newptr == NULL) { + return NULL; + } + + TWG(shm) = newptr; + ptr = newptr + TWG(shm_size); + TWG(shm_size)++; + return ptr; +} + +static HANDLE dupHandle(HANDLE fh, BOOL inherit) { + HANDLE copy, self = GetCurrentProcess(); + if (!DuplicateHandle(self, fh, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) { + return NULL; + } + return copy; +} + +TSRM_API FILE *popen(const char *command, const char *type) +{ + TSRMLS_FETCH(); + + return popen_ex(command, type, NULL, NULL TSRMLS_CC); +} + +TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env TSRMLS_DC) +{ + FILE *stream = NULL; + int fno, type_len = strlen(type), read, mode; + STARTUPINFO startup; + PROCESS_INFORMATION process; + SECURITY_ATTRIBUTES security; + HANDLE in, out; + DWORD dwCreateFlags = 0; + BOOL res; + process_pair *proc; + char *cmd; + int i; + char *ptype = (char *)type; + HANDLE thread_token = NULL; + HANDLE token_user = NULL; + BOOL asuser = TRUE; + + if (!type) { + return NULL; + } + + /*The following two checks can be removed once we drop XP support */ + type_len = strlen(type); + if (type_len <1 || type_len > 2) { + return NULL; + } + + for (i=0; i < type_len; i++) { + if (!(*ptype == 'r' || *ptype == 'w' || *ptype == 'b' || *ptype == 't')) { + return NULL; + } + ptype++; + } + + security.nLength = sizeof(SECURITY_ATTRIBUTES); + security.bInheritHandle = TRUE; + security.lpSecurityDescriptor = NULL; + + if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) { + return NULL; + } + + memset(&startup, 0, sizeof(STARTUPINFO)); + memset(&process, 0, sizeof(PROCESS_INFORMATION)); + + startup.cb = sizeof(STARTUPINFO); + startup.dwFlags = STARTF_USESTDHANDLES; + startup.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + read = (type[0] == 'r') ? TRUE : FALSE; + mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT; + + if (read) { + in = dupHandle(in, FALSE); + startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup.hStdOutput = out; + } else { + out = dupHandle(out, FALSE); + startup.hStdInput = in; + startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + } + + dwCreateFlags = NORMAL_PRIORITY_CLASS; + if (strcmp(sapi_module.name, "cli") != 0) { + dwCreateFlags |= CREATE_NO_WINDOW; + } + + /* Get a token with the impersonated user. */ + if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) { + DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user); + } else { + DWORD err = GetLastError(); + if (err == ERROR_NO_TOKEN) { + asuser = FALSE; + } + } + + cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")+2); + if (!cmd) { + return NULL; + } + + sprintf(cmd, "%s /c \"%s\"", TWG(comspec), command); + if (asuser) { + res = CreateProcessAsUser(token_user, NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process); + CloseHandle(token_user); + } else { + res = CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process); + } + free(cmd); + + if (!res) { + return NULL; + } + + CloseHandle(process.hThread); + proc = process_get(NULL TSRMLS_CC); + + if (read) { + fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode); + CloseHandle(out); + } else { + fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode); + CloseHandle(in); + } + + stream = _fdopen(fno, type); + proc->prochnd = process.hProcess; + proc->stream = stream; + return stream; +} + +TSRM_API int pclose(FILE *stream) +{ + DWORD termstat = 0; + process_pair *process; + TSRMLS_FETCH(); + + if ((process = process_get(stream TSRMLS_CC)) == NULL) { + return 0; + } + + fflush(process->stream); + fclose(process->stream); + + WaitForSingleObject(process->prochnd, INFINITE); + GetExitCodeProcess(process->prochnd, &termstat); + process->stream = NULL; + CloseHandle(process->prochnd); + + return termstat; +} + +TSRM_API int shmget(int key, int size, int flags) +{ + shm_pair *shm; + char shm_segment[26], shm_info[29]; + HANDLE shm_handle, info_handle; + BOOL created = FALSE; + + if (size < 0) { + return -1; + } + + sprintf(shm_segment, "TSRM_SHM_SEGMENT:%d", key); + sprintf(shm_info, "TSRM_SHM_DESCRIPTOR:%d", key); + + shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment); + info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info); + + if ((!shm_handle && !info_handle)) { + if (flags & IPC_CREAT) { + shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, shm_segment); + info_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), shm_info); + created = TRUE; + } + if ((!shm_handle || !info_handle)) { + return -1; + } + } else { + if (flags & IPC_EXCL) { + return -1; + } + } + + shm = shm_get(key, NULL); + shm->segment = shm_handle; + shm->info = info_handle; + shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL); + + if (created) { + shm->descriptor->shm_perm.key = key; + shm->descriptor->shm_segsz = size; + shm->descriptor->shm_ctime = time(NULL); + shm->descriptor->shm_cpid = getpid(); + shm->descriptor->shm_perm.mode = flags; + + shm->descriptor->shm_perm.cuid = shm->descriptor->shm_perm.cgid= 0; + shm->descriptor->shm_perm.gid = shm->descriptor->shm_perm.uid = 0; + shm->descriptor->shm_atime = shm->descriptor->shm_dtime = 0; + shm->descriptor->shm_lpid = shm->descriptor->shm_nattch = 0; + shm->descriptor->shm_perm.mode = shm->descriptor->shm_perm.seq = 0; + } + + if (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz ) { + CloseHandle(shm->segment); + UnmapViewOfFile(shm->descriptor); + CloseHandle(shm->info); + return -1; + } + + return key; +} + +TSRM_API void *shmat(int key, const void *shmaddr, int flags) +{ + shm_pair *shm = shm_get(key, NULL); + + if (!shm->segment) { + return (void*)-1; + } + + shm->descriptor->shm_atime = time(NULL); + shm->descriptor->shm_lpid = getpid(); + shm->descriptor->shm_nattch++; + + shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL); + + return shm->addr; +} + +TSRM_API int shmdt(const void *shmaddr) +{ + shm_pair *shm = shm_get(0, (void*)shmaddr); + + if (!shm->segment) { + return -1; + } + + shm->descriptor->shm_dtime = time(NULL); + shm->descriptor->shm_lpid = getpid(); + shm->descriptor->shm_nattch--; + + return UnmapViewOfFile(shm->addr) ? 0 : -1; +} + +TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf) { + shm_pair *shm = shm_get(key, NULL); + + if (!shm->segment) { + return -1; + } + + switch (cmd) { + case IPC_STAT: + memcpy(buf, shm->descriptor, sizeof(struct shmid_ds)); + return 0; + + case IPC_SET: + shm->descriptor->shm_ctime = time(NULL); + shm->descriptor->shm_perm.uid = buf->shm_perm.uid; + shm->descriptor->shm_perm.gid = buf->shm_perm.gid; + shm->descriptor->shm_perm.mode = buf->shm_perm.mode; + return 0; + + case IPC_RMID: + if (shm->descriptor->shm_nattch < 1) { + shm->descriptor->shm_perm.key = -1; + } + return 0; + + default: + return -1; + } +} + +TSRM_API char *realpath(char *orig_path, char *buffer) +{ + int ret = GetFullPathName(orig_path, _MAX_PATH, buffer, NULL); + if(!ret || ret > _MAX_PATH) { + return NULL; + } + return buffer; +} + +#endif diff --git a/TSRM/tsrm_win32.h b/TSRM/tsrm_win32.h new file mode 100644 index 0000000..c77f69c --- /dev/null +++ b/TSRM/tsrm_win32.h @@ -0,0 +1,110 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Beulshausen | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef TSRM_WIN32_H +#define TSRM_WIN32_H + +#include "TSRM.h" +#include + +struct ipc_perm { + int key; + unsigned short uid; + unsigned short gid; + unsigned short cuid; + unsigned short cgid; + unsigned short mode; + unsigned short seq; +}; + +struct shmid_ds { + struct ipc_perm shm_perm; + int shm_segsz; + time_t shm_atime; + time_t shm_dtime; + time_t shm_ctime; + unsigned short shm_cpid; + unsigned short shm_lpid; + short shm_nattch; +}; + +typedef struct { + FILE *stream; + HANDLE prochnd; +} process_pair; + +typedef struct { + void *addr; + HANDLE info; + HANDLE segment; + struct shmid_ds *descriptor; +} shm_pair; + +typedef struct { + process_pair *process; + shm_pair *shm; + int process_size; + int shm_size; + char *comspec; + HANDLE impersonation_token; + PSID impersonation_token_sid; +} tsrm_win32_globals; + +#ifdef ZTS +# define TWG(v) TSRMG(win32_globals_id, tsrm_win32_globals *, v) +#else +# define TWG(v) (win32_globals.v) +#endif + +#define IPC_PRIVATE 0 +#define IPC_CREAT 00001000 +#define IPC_EXCL 00002000 +#define IPC_NOWAIT 00004000 + +#define IPC_RMID 0 +#define IPC_SET 1 +#define IPC_STAT 2 +#define IPC_INFO 3 + +#define SHM_R PAGE_READONLY +#define SHM_W PAGE_READWRITE + +#define SHM_RDONLY FILE_MAP_READ +#define SHM_RND FILE_MAP_WRITE +#define SHM_REMAP FILE_MAP_COPY + +char * tsrm_win32_get_path_sid_key(const char *pathname TSRMLS_DC); + +TSRM_API void tsrm_win32_startup(void); +TSRM_API void tsrm_win32_shutdown(void); + +TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env TSRMLS_DC); +TSRM_API FILE *popen(const char *command, const char *type); +TSRM_API int pclose(FILE *stream); +TSRM_API int tsrm_win32_access(const char *pathname, int mode TSRMLS_DC); +TSRM_API int win32_utime(const char *filename, struct utimbuf *buf); + +TSRM_API int shmget(int key, int size, int flags); +TSRM_API void *shmat(int key, const void *shmaddr, int flags); +TSRM_API int shmdt(const void *shmaddr); +TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf); + +TSRM_API char *realpath(char *orig_path, char *buffer); +#endif -- cgit v1.2.1