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