xref: /PHP-7.2/TSRM/tsrm_win32.c (revision 954543ce)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Daniel Beulshausen <daniel@php4win.de>                      |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* $Id$ */
20 
21 #include <stdio.h>
22 #include <fcntl.h>
23 #include <io.h>
24 #include <process.h>
25 #include <time.h>
26 #include <errno.h>
27 
28 #define TSRM_INCLUDE_FULL_WINDOWS_HEADERS
29 #include "SAPI.h"
30 #include "TSRM.h"
31 
32 #ifdef TSRM_WIN32
33 #include <Sddl.h>
34 #include "tsrm_win32.h"
35 #include "zend_virtual_cwd.h"
36 #include "win32/ioutil.h"
37 
38 #ifdef ZTS
39 static ts_rsrc_id win32_globals_id;
40 #else
41 static tsrm_win32_globals win32_globals;
42 #endif
43 
tsrm_win32_ctor(tsrm_win32_globals * globals)44 static void tsrm_win32_ctor(tsrm_win32_globals *globals)
45 {/*{{{*/
46 #ifdef ZTS
47 TSRMLS_CACHE_UPDATE();
48 #endif
49 	globals->process = NULL;
50 	globals->shm	 = NULL;
51 	globals->process_size = 0;
52 	globals->shm_size	  = 0;
53 	globals->comspec = _strdup("cmd.exe");
54 
55 	/* Set it to INVALID_HANDLE_VALUE
56 	 * It will be initialized correctly in tsrm_win32_access or set to
57 	 * NULL if no impersonation has been done.
58 	 * the impersonated token can't be set here as the impersonation
59 	 * will happen later, in fcgi_accept_request (or whatever is the
60 	 * SAPI being used).
61 	 */
62 	globals->impersonation_token = INVALID_HANDLE_VALUE;
63 	globals->impersonation_token_sid = NULL;
64 }/*}}}*/
65 
tsrm_win32_dtor(tsrm_win32_globals * globals)66 static void tsrm_win32_dtor(tsrm_win32_globals *globals)
67 {/*{{{*/
68 	shm_pair *ptr;
69 
70 	if (globals->process) {
71 		free(globals->process);
72 	}
73 
74 	if (globals->shm) {
75 		for (ptr = globals->shm; ptr < (globals->shm + globals->shm_size); ptr++) {
76 			UnmapViewOfFile(ptr->addr);
77 			CloseHandle(ptr->segment);
78 			UnmapViewOfFile(ptr->descriptor);
79 			CloseHandle(ptr->info);
80 		}
81 		free(globals->shm);
82 	}
83 
84 	free(globals->comspec);
85 
86 	if (globals->impersonation_token && globals->impersonation_token != INVALID_HANDLE_VALUE	) {
87 		CloseHandle(globals->impersonation_token);
88 	}
89 	if (globals->impersonation_token_sid) {
90 		free(globals->impersonation_token_sid);
91 	}
92 }/*}}}*/
93 
tsrm_win32_startup(void)94 TSRM_API void tsrm_win32_startup(void)
95 {/*{{{*/
96 #ifdef ZTS
97 	ts_allocate_id(&win32_globals_id, sizeof(tsrm_win32_globals), (ts_allocate_ctor)tsrm_win32_ctor, (ts_allocate_ctor)tsrm_win32_dtor);
98 #else
99 	tsrm_win32_ctor(&win32_globals);
100 #endif
101 }/*}}}*/
102 
tsrm_win32_shutdown(void)103 TSRM_API void tsrm_win32_shutdown(void)
104 {/*{{{*/
105 #ifndef ZTS
106 	tsrm_win32_dtor(&win32_globals);
107 #endif
108 }/*}}}*/
109 
tsrm_win32_get_path_sid_key(const char * pathname,size_t pathname_len,size_t * key_len)110 char * tsrm_win32_get_path_sid_key(const char *pathname, size_t pathname_len, size_t *key_len)
111 {/*{{{*/
112 	PSID pSid = TWG(impersonation_token_sid);
113 	char *ptcSid = NULL;
114 	char *bucket_key = NULL;
115 	size_t ptc_sid_len;
116 
117 	if (!pSid) {
118 		*key_len = pathname_len;
119 		return pathname;
120 	}
121 
122 	if (!ConvertSidToStringSid(pSid, &ptcSid)) {
123 		*key_len = 0;
124 		return NULL;
125 	}
126 
127 
128 	ptc_sid_len = strlen(ptcSid);
129 	*key_len = pathname_len + ptc_sid_len;
130 	bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *key_len + 1);
131 	if (!bucket_key) {
132 		LocalFree(ptcSid);
133 		*key_len = 0;
134 		return NULL;
135 	}
136 
137 	memcpy(bucket_key, ptcSid, ptc_sid_len);
138 	memcpy(bucket_key + ptc_sid_len, pathname, pathname_len + 1);
139 
140 	LocalFree(ptcSid);
141 	return bucket_key;
142 }/*}}}*/
143 
144 
tsrm_win32_get_token_sid(HANDLE hToken)145 PSID tsrm_win32_get_token_sid(HANDLE hToken)
146 {/*{{{*/
147 	DWORD dwLength = 0;
148 	PTOKEN_USER pTokenUser = NULL;
149 	DWORD sid_len;
150 	PSID pResultSid = NULL;
151 
152 	/* Get the actual size of the TokenUser structure */
153 	if (!GetTokenInformation(
154 			hToken, TokenUser, (LPVOID) pTokenUser, 0, &dwLength))  {
155 		if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
156 			goto Finished;
157 		}
158 
159 		pTokenUser = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
160 		if (pTokenUser == NULL) {
161 			goto Finished;
162 		}
163 	}
164 
165 	/* and fetch it now */
166 	if (!GetTokenInformation(
167 		hToken, TokenUser, (LPVOID) pTokenUser, dwLength, &dwLength)) {
168 		goto Finished;
169 	}
170 
171 	sid_len = GetLengthSid(pTokenUser->User.Sid);
172 
173 	/* ConvertSidToStringSid(pTokenUser->User.Sid, &ptcSidOwner); */
174 	pResultSid = malloc(sid_len);
175 	if (!pResultSid) {
176 		goto Finished;
177 	}
178 	if (!CopySid(sid_len, pResultSid, pTokenUser->User.Sid)) {
179 		goto Finished;
180 	}
181 	HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
182 	return pResultSid;
183 
184 Finished:
185 	if (pResultSid) {
186 		free(pResultSid);
187 	}
188 	/* Free the buffer for the token groups. */
189 	if (pTokenUser != NULL) {
190 		HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
191 	}
192 	return NULL;
193 }/*}}}*/
194 
tsrm_win32_access(const char * pathname,int mode)195 TSRM_API int tsrm_win32_access(const char *pathname, int mode)
196 {/*{{{*/
197 	time_t t;
198 	HANDLE thread_token = NULL;
199 	PSID token_sid;
200 	SECURITY_INFORMATION sec_info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
201 	GENERIC_MAPPING gen_map = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS };
202 	DWORD priv_set_length = sizeof(PRIVILEGE_SET);
203 
204 	PRIVILEGE_SET privilege_set = {0};
205 	DWORD sec_desc_length = 0, desired_access = 0, granted_access = 0;
206 	BYTE * psec_desc = NULL;
207 	BOOL fAccess = FALSE;
208 
209 	realpath_cache_bucket * bucket = NULL;
210 	char * real_path = NULL;
211 
212 	PHP_WIN32_IOUTIL_INIT_W(pathname)
213 	if (!pathw) {
214 		return -1;
215 	}
216 
217 	if (mode == 1 /*X_OK*/) {
218 		DWORD type;
219 		int ret;
220 
221 		ret = GetBinaryTypeW(pathw, &type) ? 0 : -1;
222 
223 		PHP_WIN32_IOUTIL_CLEANUP_W()
224 
225 		return ret;
226 	} else {
227 		if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
228 			real_path = (char *)malloc(MAXPATHLEN);
229 			if(tsrm_realpath(pathname, real_path) == NULL) {
230 				goto Finished;
231 			}
232 			pathname = real_path;
233 			PHP_WIN32_IOUTIL_REINIT_W(pathname);
234  		}
235 
236 		if(php_win32_ioutil_access(pathname, mode)) {
237 			PHP_WIN32_IOUTIL_CLEANUP_W()
238 			free(real_path);
239 			return errno;
240 		}
241 
242  		/* If only existence check is made, return now */
243  		if (mode == 0) {
244 			PHP_WIN32_IOUTIL_CLEANUP_W()
245 			free(real_path);
246 			return 0;
247 		}
248 
249 /* Only in NTS when impersonate==1 (aka FastCGI) */
250 
251 		/*
252 		 AccessCheck() requires an impersonation token.  We first get a primary
253 		 token and then create a duplicate impersonation token.  The
254 		 impersonation token is not actually assigned to the thread, but is
255 		 used in the call to AccessCheck.  Thus, this function itself never
256 		 impersonates, but does use the identity of the thread.  If the thread
257 		 was impersonating already, this function uses that impersonation context.
258 		*/
259 		if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
260 			if (GetLastError() == ERROR_NO_TOKEN) {
261 				if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
262 					 TWG(impersonation_token) = NULL;
263 					 goto Finished;
264 				 }
265 			}
266 		}
267 
268 		/* token_sid will be freed in tsrmwin32_dtor */
269 		token_sid = tsrm_win32_get_token_sid(thread_token);
270 		if (!token_sid) {
271 			if (TWG(impersonation_token_sid)) {
272 				free(TWG(impersonation_token_sid));
273 			}
274 			TWG(impersonation_token_sid) = NULL;
275 			goto Finished;
276 		}
277 
278 		/* Different identity, we need a new impersontated token as well */
279 		if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
280 			if (TWG(impersonation_token_sid)) {
281 				free(TWG(impersonation_token_sid));
282 			}
283 			TWG(impersonation_token_sid) = token_sid;
284 
285 			/* Duplicate the token as impersonated token */
286 			if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
287 				goto Finished;
288 			}
289 		} else {
290 			/* we already have it, free it then */
291 			free(token_sid);
292 		}
293 
294 		if (CWDG(realpath_cache_size_limit)) {
295 			t = time(0);
296 			bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
297 			if(bucket == NULL && real_path == NULL) {
298 				/* We used the pathname directly. Call tsrm_realpath */
299 				/* so that entry is created in realpath cache */
300 				real_path = (char *)malloc(MAXPATHLEN);
301 				if(tsrm_realpath(pathname, real_path) != NULL) {
302 					pathname = real_path;
303 					bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
304 					PHP_WIN32_IOUTIL_REINIT_W(pathname);
305 				}
306 			}
307  		}
308 
309  		/* Do a full access check because access() will only check read-only attribute */
310  		if(mode == 0 || mode > 6) {
311 			if(bucket != NULL && bucket->is_rvalid) {
312 				fAccess = bucket->is_readable;
313 				goto Finished;
314 			}
315  			desired_access = FILE_GENERIC_READ;
316  		} else if(mode <= 2) {
317 			if(bucket != NULL && bucket->is_wvalid) {
318 				fAccess = bucket->is_writable;
319 				goto Finished;
320 			}
321 			desired_access = FILE_GENERIC_WRITE;
322  		} else if(mode <= 4) {
323 			if(bucket != NULL && bucket->is_rvalid) {
324 				fAccess = bucket->is_readable;
325 				goto Finished;
326 			}
327 			desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
328  		} else { // if(mode <= 6)
329 			if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
330 				fAccess = bucket->is_readable & bucket->is_writable;
331 				goto Finished;
332 			}
333 			desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
334  		}
335 
336 		if(TWG(impersonation_token) == NULL) {
337 			goto Finished;
338 		}
339 
340 		/* Get size of security buffer. Call is expected to fail */
341 		if(GetFileSecurityW(pathw, sec_info, NULL, 0, &sec_desc_length)) {
342 			goto Finished;
343 		}
344 
345 		psec_desc = (BYTE *)malloc(sec_desc_length);
346 		if(psec_desc == NULL ||
347 			 !GetFileSecurityW(pathw, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
348 			goto Finished;
349 		}
350 
351 		MapGenericMask(&desired_access, &gen_map);
352 
353 		if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
354 			goto Finished_Impersonate;
355 		}
356 
357 		/* Keep the result in realpath_cache */
358 		if(bucket != NULL) {
359 			if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
360 				bucket->is_rvalid = 1;
361 				bucket->is_readable = fAccess;
362 			}
363 			else if(desired_access == FILE_GENERIC_WRITE) {
364 				bucket->is_wvalid = 1;
365 				bucket->is_writable = fAccess;
366 			} else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
367 				bucket->is_rvalid = 1;
368 				bucket->is_readable = fAccess;
369 				bucket->is_wvalid = 1;
370 				bucket->is_writable = fAccess;
371 			}
372 		}
373 
374 Finished_Impersonate:
375 		if(psec_desc != NULL) {
376 			free(psec_desc);
377 			psec_desc = NULL;
378 		}
379 
380 Finished:
381 		if(thread_token != NULL) {
382 			CloseHandle(thread_token);
383 		}
384 		if(real_path != NULL) {
385 			free(real_path);
386 			real_path = NULL;
387 		}
388 
389 		PHP_WIN32_IOUTIL_CLEANUP_W()
390 		if(fAccess == FALSE) {
391 			errno = EACCES;
392 			return errno;
393 		} else {
394 			return 0;
395 		}
396 	}
397 }/*}}}*/
398 
399 
process_get(FILE * stream)400 static process_pair *process_get(FILE *stream)
401 {/*{{{*/
402 	process_pair *ptr;
403 	process_pair *newptr;
404 
405 	for (ptr = TWG(process); ptr < (TWG(process) + TWG(process_size)); ptr++) {
406 		if (ptr->stream == stream) {
407 			break;
408 		}
409 	}
410 
411 	if (ptr < (TWG(process) + TWG(process_size))) {
412 		return ptr;
413 	}
414 
415 	newptr = (process_pair*)realloc((void*)TWG(process), (TWG(process_size)+1)*sizeof(process_pair));
416 	if (newptr == NULL) {
417 		return NULL;
418 	}
419 
420 	TWG(process) = newptr;
421 	ptr = newptr + TWG(process_size);
422 	TWG(process_size)++;
423 	return ptr;
424 }/*}}}*/
425 
shm_get(key_t key,void * addr)426 static shm_pair *shm_get(key_t key, void *addr)
427 {/*{{{*/
428 	shm_pair *ptr;
429 	shm_pair *newptr;
430 
431 	for (ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
432 		if (!ptr->descriptor) {
433 			continue;
434 		}
435 		if (!addr && ptr->descriptor->shm_perm.key == key) {
436 			break;
437 		} else if (ptr->addr == addr) {
438 			break;
439 		}
440 	}
441 
442 	if (ptr < (TWG(shm) + TWG(shm_size))) {
443 		return ptr;
444 	}
445 
446 	newptr = (shm_pair*)realloc((void*)TWG(shm), (TWG(shm_size)+1)*sizeof(shm_pair));
447 	if (newptr == NULL) {
448 		return NULL;
449 	}
450 
451 	TWG(shm) = newptr;
452 	ptr = newptr + TWG(shm_size);
453 	TWG(shm_size)++;
454 	memset(ptr, 0, sizeof(*ptr));
455 	return ptr;
456 }/*}}}*/
457 
dupHandle(HANDLE fh,BOOL inherit)458 static HANDLE dupHandle(HANDLE fh, BOOL inherit)
459 {/*{{{*/
460 	HANDLE copy, self = GetCurrentProcess();
461 	if (!DuplicateHandle(self, fh, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
462 		return NULL;
463 	}
464 	return copy;
465 }/*}}}*/
466 
popen(const char * command,const char * type)467 TSRM_API FILE *popen(const char *command, const char *type)
468 {/*{{{*/
469 
470 	return popen_ex(command, type, NULL, NULL);
471 }/*}}}*/
472 
popen_ex(const char * command,const char * type,const char * cwd,char * env)473 TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env)
474 {/*{{{*/
475 	FILE *stream = NULL;
476 	int fno, type_len, read, mode;
477 	STARTUPINFOW startup;
478 	PROCESS_INFORMATION process;
479 	SECURITY_ATTRIBUTES security;
480 	HANDLE in, out;
481 	DWORD dwCreateFlags = 0;
482 	BOOL res;
483 	process_pair *proc;
484 	char *cmd = NULL;
485 	wchar_t *cmdw = NULL, *cwdw = NULL, *envw = NULL;
486 	int i;
487 	char *ptype = (char *)type;
488 	HANDLE thread_token = NULL;
489 	HANDLE token_user = NULL;
490 	BOOL asuser = TRUE;
491 
492 	if (!type) {
493 		return NULL;
494 	}
495 
496 	/*The following two checks can be removed once we drop XP support */
497 	type_len = (int)strlen(type);
498 	if (type_len <1 || type_len > 2) {
499 		return NULL;
500 	}
501 
502 	for (i=0; i < type_len; i++) {
503 		if (!(*ptype == 'r' || *ptype == 'w' || *ptype == 'b' || *ptype == 't')) {
504 			return NULL;
505 		}
506 		ptype++;
507 	}
508 
509 	cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")+2);
510 	if (!cmd) {
511 		return NULL;
512 	}
513 
514 	sprintf(cmd, "%s /c \"%s\"", TWG(comspec), command);
515 	cmdw = php_win32_cp_any_to_w(cmd);
516 	if (!cmdw) {
517 		free(cmd);
518 		return NULL;
519 	}
520 
521 	if (cwd) {
522 		cwdw = php_win32_ioutil_any_to_w(cwd);
523 		if (!cwdw) {
524 			free(cmd);
525 			free(cmdw);
526 			return NULL;
527 		}
528 	}
529 
530 	security.nLength				= sizeof(SECURITY_ATTRIBUTES);
531 	security.bInheritHandle			= TRUE;
532 	security.lpSecurityDescriptor	= NULL;
533 
534 	if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
535 		free(cmdw);
536 		free(cwdw);
537 		free(cmd);
538 		return NULL;
539 	}
540 
541 	memset(&startup, 0, sizeof(STARTUPINFOW));
542 	memset(&process, 0, sizeof(PROCESS_INFORMATION));
543 
544 	startup.cb			= sizeof(STARTUPINFOW);
545 	startup.dwFlags		= STARTF_USESTDHANDLES;
546 	startup.hStdError	= GetStdHandle(STD_ERROR_HANDLE);
547 
548 	read = (type[0] == 'r') ? TRUE : FALSE;
549 	mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;
550 
551 	if (read) {
552 		in = dupHandle(in, FALSE);
553 		startup.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
554 		startup.hStdOutput = out;
555 	} else {
556 		out = dupHandle(out, FALSE);
557 		startup.hStdInput  = in;
558 		startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
559 	}
560 
561 	dwCreateFlags = NORMAL_PRIORITY_CLASS;
562 	if (strcmp(sapi_module.name, "cli") != 0) {
563 		dwCreateFlags |= CREATE_NO_WINDOW;
564 	}
565 
566 	/* Get a token with the impersonated user. */
567 	if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
568 		DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user);
569 	} else {
570 		DWORD err = GetLastError();
571 		if (err == ERROR_NO_TOKEN) {
572 			asuser = FALSE;
573 		}
574 	}
575 
576 	envw = php_win32_cp_env_any_to_w(env);
577 	if (envw) {
578 		dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
579 	} else {
580 		if (env) {
581 			free(cmd);
582 			free(cmdw);
583 			free(cwdw);
584 			return NULL;
585 		}
586 	}
587 
588 	if (asuser) {
589 		res = CreateProcessAsUserW(token_user, NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
590 		CloseHandle(token_user);
591 	} else {
592 		res = CreateProcessW(NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
593 	}
594 	free(cmd);
595 	free(cmdw);
596 	free(cwdw);
597 	free(envw);
598 
599 	if (!res) {
600 		return NULL;
601 	}
602 
603 	CloseHandle(process.hThread);
604 	proc = process_get(NULL);
605 
606 	if (read) {
607 		fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
608 		CloseHandle(out);
609 	} else {
610 		fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode);
611 		CloseHandle(in);
612 	}
613 
614 	stream = _fdopen(fno, type);
615 	proc->prochnd = process.hProcess;
616 	proc->stream = stream;
617 	return stream;
618 }/*}}}*/
619 
pclose(FILE * stream)620 TSRM_API int pclose(FILE *stream)
621 {/*{{{*/
622 	DWORD termstat = 0;
623 	process_pair *process;
624 
625 	if ((process = process_get(stream)) == NULL) {
626 		return 0;
627 	}
628 
629 	fflush(process->stream);
630 	fclose(process->stream);
631 
632 	WaitForSingleObject(process->prochnd, INFINITE);
633 	GetExitCodeProcess(process->prochnd, &termstat);
634 	process->stream = NULL;
635 	CloseHandle(process->prochnd);
636 
637 	return termstat;
638 }/*}}}*/
639 
shmget(key_t key,size_t size,int flags)640 TSRM_API int shmget(key_t key, size_t size, int flags)
641 {/*{{{*/
642 	shm_pair *shm;
643 	char shm_segment[26], shm_info[29];
644 	HANDLE shm_handle, info_handle;
645 	BOOL created = FALSE;
646 
647 	snprintf(shm_segment, sizeof(shm_segment), "TSRM_SHM_SEGMENT:%d", key);
648 	snprintf(shm_info, sizeof(shm_info), "TSRM_SHM_DESCRIPTOR:%d", key);
649 
650 	shm_handle  = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
651 	info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
652 
653 	if (!shm_handle && !info_handle) {
654 		if (flags & IPC_CREAT) {
655 #if SIZEOF_SIZE_T == 8
656 			DWORD high = size >> 32;
657 			DWORD low = (DWORD)size;
658 #else
659 			DWORD high = 0;
660 			DWORD low = size;
661 #endif
662 			shm_handle	= CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, high, low, shm_segment);
663 			info_handle	= CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), shm_info);
664 			created		= TRUE;
665 		}
666 		if (!shm_handle || !info_handle) {
667 			if (shm_handle) {
668 				CloseHandle(shm_handle);
669 			}
670 			if (info_handle) {
671 				CloseHandle(info_handle);
672 			}
673 			return -1;
674 		}
675 	} else {
676 		if (flags & IPC_EXCL) {
677 			if (shm_handle) {
678 				CloseHandle(shm_handle);
679 			}
680 			if (info_handle) {
681 				CloseHandle(info_handle);
682 			}
683 			return -1;
684 		}
685 	}
686 
687 	shm = shm_get(key, NULL);
688 	if (!shm) {
689 		CloseHandle(shm_handle);
690 		CloseHandle(info_handle);
691 		return -1;
692 	}
693 	shm->segment = shm_handle;
694 	shm->info	 = info_handle;
695 	shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
696 
697 	if (NULL != shm->descriptor && created) {
698 		shm->descriptor->shm_perm.key	= key;
699 		shm->descriptor->shm_segsz		= size;
700 		shm->descriptor->shm_ctime		= time(NULL);
701 		shm->descriptor->shm_cpid		= getpid();
702 		shm->descriptor->shm_perm.mode	= flags;
703 
704 		shm->descriptor->shm_perm.cuid	= shm->descriptor->shm_perm.cgid= 0;
705 		shm->descriptor->shm_perm.gid	= shm->descriptor->shm_perm.uid = 0;
706 		shm->descriptor->shm_atime		= shm->descriptor->shm_dtime	= 0;
707 		shm->descriptor->shm_lpid		= shm->descriptor->shm_nattch	= 0;
708 		shm->descriptor->shm_perm.mode	= shm->descriptor->shm_perm.seq	= 0;
709 	}
710 
711 	if (NULL != shm->descriptor && (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz)) {
712 		if (NULL != shm->segment) {
713 			CloseHandle(shm->segment);
714 		}
715 		UnmapViewOfFile(shm->descriptor);
716 		CloseHandle(shm->info);
717 		return -1;
718 	}
719 
720 	return key;
721 }/*}}}*/
722 
shmat(int key,const void * shmaddr,int flags)723 TSRM_API void *shmat(int key, const void *shmaddr, int flags)
724 {/*{{{*/
725 	shm_pair *shm = shm_get(key, NULL);
726 
727 	if (!shm->segment) {
728 		return (void*)-1;
729 	}
730 
731 	shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
732 
733 	if (NULL == shm->addr) {
734 		int err = GetLastError();
735 		SET_ERRNO_FROM_WIN32_CODE(err);
736 		return (void*)-1;
737 	}
738 
739 	shm->descriptor->shm_atime = time(NULL);
740 	shm->descriptor->shm_lpid  = getpid();
741 	shm->descriptor->shm_nattch++;
742 
743 	return shm->addr;
744 }/*}}}*/
745 
shmdt(const void * shmaddr)746 TSRM_API int shmdt(const void *shmaddr)
747 {/*{{{*/
748 	shm_pair *shm = shm_get(0, (void*)shmaddr);
749 
750 	if (!shm->segment) {
751 		return -1;
752 	}
753 
754 	shm->descriptor->shm_dtime = time(NULL);
755 	shm->descriptor->shm_lpid  = getpid();
756 	shm->descriptor->shm_nattch--;
757 
758 	return UnmapViewOfFile(shm->addr) ? 0 : -1;
759 }/*}}}*/
760 
shmctl(int key,int cmd,struct shmid_ds * buf)761 TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
762 {/*{{{*/
763 	shm_pair *shm = shm_get(key, NULL);
764 
765 	if (!shm->segment) {
766 		return -1;
767 	}
768 
769 	switch (cmd) {
770 		case IPC_STAT:
771 			memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
772 			return 0;
773 
774 		case IPC_SET:
775 			shm->descriptor->shm_ctime		= time(NULL);
776 			shm->descriptor->shm_perm.uid	= buf->shm_perm.uid;
777 			shm->descriptor->shm_perm.gid	= buf->shm_perm.gid;
778 			shm->descriptor->shm_perm.mode	= buf->shm_perm.mode;
779 			return 0;
780 
781 		case IPC_RMID:
782 			if (shm->descriptor->shm_nattch < 1) {
783 				shm->descriptor->shm_perm.key = -1;
784 			}
785 			return 0;
786 
787 		default:
788 			return -1;
789 	}
790 }/*}}}*/
791 
realpath(char * orig_path,char * buffer)792 TSRM_API char *realpath(char *orig_path, char *buffer)
793 {/*{{{*/
794 	int ret = GetFullPathName(orig_path, _MAX_PATH, buffer, NULL);
795 	if(!ret || ret > _MAX_PATH) {
796 		return NULL;
797 	}
798 	return buffer;
799 }/*}}}*/
800 
801 #if HAVE_UTIME
UnixTimeToFileTime(time_t t,LPFILETIME pft)802 static zend_always_inline void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
803 {
804 	// Note that LONGLONG is a 64-bit value
805 	LONGLONG ll;
806 
807 	ll = t * 10000000LL + 116444736000000000LL;
808 	pft->dwLowDateTime = (DWORD)ll;
809 	pft->dwHighDateTime = ll >> 32;
810 }
811 /* }}} */
812 
win32_utime(const char * filename,struct utimbuf * buf)813 TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
814 {
815 	FILETIME mtime, atime;
816 	HANDLE hFile;
817 	PHP_WIN32_IOUTIL_INIT_W(filename)
818 
819 	if (!pathw) {
820 		return -1;
821 	}
822 
823 	hFile = CreateFileW(pathw, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
824 				 OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
825 
826 	PHP_WIN32_IOUTIL_CLEANUP_W()
827 
828 	/* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
829 	   the CreateFile operation succeeds */
830 	if (GetLastError() == ERROR_ALREADY_EXISTS) {
831 		SetLastError(0);
832 	}
833 
834 	if ( hFile == INVALID_HANDLE_VALUE ) {
835 		return -1;
836 	}
837 
838 	if (!buf) {
839 		SYSTEMTIME st;
840 		GetSystemTime(&st);
841 		SystemTimeToFileTime(&st, &mtime);
842 		atime = mtime;
843 	} else {
844 		UnixTimeToFileTime(buf->modtime, &mtime);
845 		UnixTimeToFileTime(buf->actime, &atime);
846 	}
847 	if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
848 		CloseHandle(hFile);
849 		return -1;
850 	}
851 	CloseHandle(hFile);
852 	return 1;
853 }
854 /* }}} */
855 #endif
856 #endif
857 
858 /*
859  * Local variables:
860  * tab-width: 4
861  * c-basic-offset: 4
862  * End:
863  * vim600: sw=4 ts=4 fdm=marker
864  * vim<600: sw=4 ts=4
865  */
866