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