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