xref: /PHP-7.3/TSRM/tsrm_win32.c (revision f33cf52f)
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 #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 	int i;
462 	char *ptype = (char *)type;
463 	HANDLE thread_token = NULL;
464 	HANDLE token_user = NULL;
465 	BOOL asuser = TRUE;
466 
467 	if (!type) {
468 		return NULL;
469 	}
470 
471 	/*The following two checks can be removed once we drop XP support */
472 	type_len = (int)strlen(type);
473 	if (type_len <1 || type_len > 2) {
474 		return NULL;
475 	}
476 
477 	for (i=0; i < type_len; i++) {
478 		if (!(*ptype == 'r' || *ptype == 'w' || *ptype == 'b' || *ptype == 't')) {
479 			return NULL;
480 		}
481 		ptype++;
482 	}
483 
484 	cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")+2);
485 	if (!cmd) {
486 		return NULL;
487 	}
488 
489 	sprintf(cmd, "%s /c \"%s\"", TWG(comspec), command);
490 	cmdw = php_win32_cp_any_to_w(cmd);
491 	if (!cmdw) {
492 		free(cmd);
493 		return NULL;
494 	}
495 
496 	if (cwd) {
497 		cwdw = php_win32_ioutil_any_to_w(cwd);
498 		if (!cwdw) {
499 			free(cmd);
500 			free(cmdw);
501 			return NULL;
502 		}
503 	}
504 
505 	security.nLength				= sizeof(SECURITY_ATTRIBUTES);
506 	security.bInheritHandle			= TRUE;
507 	security.lpSecurityDescriptor	= NULL;
508 
509 	if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
510 		free(cmdw);
511 		free(cwdw);
512 		free(cmd);
513 		return NULL;
514 	}
515 
516 	memset(&startup, 0, sizeof(STARTUPINFOW));
517 	memset(&process, 0, sizeof(PROCESS_INFORMATION));
518 
519 	startup.cb			= sizeof(STARTUPINFOW);
520 	startup.dwFlags		= STARTF_USESTDHANDLES;
521 	startup.hStdError	= GetStdHandle(STD_ERROR_HANDLE);
522 
523 	read = (type[0] == 'r') ? TRUE : FALSE;
524 	mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;
525 
526 	if (read) {
527 		in = dupHandle(in, FALSE);
528 		startup.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
529 		startup.hStdOutput = out;
530 	} else {
531 		out = dupHandle(out, FALSE);
532 		startup.hStdInput  = in;
533 		startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
534 	}
535 
536 	dwCreateFlags = NORMAL_PRIORITY_CLASS;
537 	if (strcmp(sapi_module.name, "cli") != 0) {
538 		dwCreateFlags |= CREATE_NO_WINDOW;
539 	}
540 
541 	/* Get a token with the impersonated user. */
542 	if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
543 		DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user);
544 	} else {
545 		DWORD err = GetLastError();
546 		if (err == ERROR_NO_TOKEN) {
547 			asuser = FALSE;
548 		}
549 	}
550 
551 	envw = php_win32_cp_env_any_to_w(env);
552 	if (envw) {
553 		dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
554 	} else {
555 		if (env) {
556 			free(cmd);
557 			free(cmdw);
558 			free(cwdw);
559 			return NULL;
560 		}
561 	}
562 
563 	if (asuser) {
564 		res = CreateProcessAsUserW(token_user, NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
565 		CloseHandle(token_user);
566 	} else {
567 		res = CreateProcessW(NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
568 	}
569 	free(cmd);
570 	free(cmdw);
571 	free(cwdw);
572 	free(envw);
573 
574 	if (!res) {
575 		return NULL;
576 	}
577 
578 	CloseHandle(process.hThread);
579 	proc = process_get(NULL);
580 
581 	if (read) {
582 		fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
583 		CloseHandle(out);
584 	} else {
585 		fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode);
586 		CloseHandle(in);
587 	}
588 
589 	stream = _fdopen(fno, type);
590 	proc->prochnd = process.hProcess;
591 	proc->stream = stream;
592 	return stream;
593 }/*}}}*/
594 
pclose(FILE * stream)595 TSRM_API int pclose(FILE *stream)
596 {/*{{{*/
597 	DWORD termstat = 0;
598 	process_pair *process;
599 
600 	if ((process = process_get(stream)) == NULL) {
601 		return 0;
602 	}
603 
604 	fflush(process->stream);
605 	fclose(process->stream);
606 
607 	WaitForSingleObject(process->prochnd, INFINITE);
608 	GetExitCodeProcess(process->prochnd, &termstat);
609 	process->stream = NULL;
610 	CloseHandle(process->prochnd);
611 
612 	return termstat;
613 }/*}}}*/
614 
shmget(key_t key,size_t size,int flags)615 TSRM_API int shmget(key_t key, size_t size, int flags)
616 {/*{{{*/
617 	shm_pair *shm;
618 	char shm_segment[26], shm_info[29];
619 	HANDLE shm_handle = NULL, info_handle = NULL;
620 	BOOL created = FALSE;
621 
622 	if (key != IPC_PRIVATE) {
623 		snprintf(shm_segment, sizeof(shm_segment), "TSRM_SHM_SEGMENT:%d", key);
624 		snprintf(shm_info, sizeof(shm_info), "TSRM_SHM_DESCRIPTOR:%d", key);
625 
626 		shm_handle  = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
627 		info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
628 	}
629 
630 	if (!shm_handle && !info_handle) {
631 		if (flags & IPC_CREAT) {
632 #if SIZEOF_SIZE_T == 8
633 			DWORD high = size >> 32;
634 			DWORD low = (DWORD)size;
635 #else
636 			DWORD high = 0;
637 			DWORD low = size;
638 #endif
639 			shm_handle	= CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, high, low, key == IPC_PRIVATE ? NULL : shm_segment);
640 			info_handle	= CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), key == IPC_PRIVATE ? NULL : shm_info);
641 			created		= TRUE;
642 		}
643 		if (!shm_handle || !info_handle) {
644 			if (shm_handle) {
645 				CloseHandle(shm_handle);
646 			}
647 			if (info_handle) {
648 				CloseHandle(info_handle);
649 			}
650 			return -1;
651 		}
652 	} else {
653 		if (flags & IPC_EXCL) {
654 			if (shm_handle) {
655 				CloseHandle(shm_handle);
656 			}
657 			if (info_handle) {
658 				CloseHandle(info_handle);
659 			}
660 			return -1;
661 		}
662 	}
663 
664 	shm = shm_get(key, NULL);
665 	if (!shm) {
666 		CloseHandle(shm_handle);
667 		CloseHandle(info_handle);
668 		return -1;
669 	}
670 	shm->segment = shm_handle;
671 	shm->info	 = info_handle;
672 	shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
673 
674 	if (NULL != shm->descriptor && created) {
675 		shm->descriptor->shm_perm.key	= key;
676 		shm->descriptor->shm_segsz		= size;
677 		shm->descriptor->shm_ctime		= time(NULL);
678 		shm->descriptor->shm_cpid		= getpid();
679 		shm->descriptor->shm_perm.mode	= flags;
680 
681 		shm->descriptor->shm_perm.cuid	= shm->descriptor->shm_perm.cgid= 0;
682 		shm->descriptor->shm_perm.gid	= shm->descriptor->shm_perm.uid = 0;
683 		shm->descriptor->shm_atime		= shm->descriptor->shm_dtime	= 0;
684 		shm->descriptor->shm_lpid		= shm->descriptor->shm_nattch	= 0;
685 		shm->descriptor->shm_perm.mode	= shm->descriptor->shm_perm.seq	= 0;
686 	}
687 
688 	if (NULL != shm->descriptor && (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz)) {
689 		if (NULL != shm->segment) {
690 			CloseHandle(shm->segment);
691 		}
692 		UnmapViewOfFile(shm->descriptor);
693 		CloseHandle(shm->info);
694 		return -1;
695 	}
696 
697 	return key;
698 }/*}}}*/
699 
shmat(int key,const void * shmaddr,int flags)700 TSRM_API void *shmat(int key, const void *shmaddr, int flags)
701 {/*{{{*/
702 	shm_pair *shm = shm_get(key, NULL);
703 
704 	if (!shm->segment) {
705 		return (void*)-1;
706 	}
707 
708 	shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
709 
710 	if (NULL == shm->addr) {
711 		int err = GetLastError();
712 		SET_ERRNO_FROM_WIN32_CODE(err);
713 		return (void*)-1;
714 	}
715 
716 	shm->descriptor->shm_atime = time(NULL);
717 	shm->descriptor->shm_lpid  = getpid();
718 	shm->descriptor->shm_nattch++;
719 
720 	return shm->addr;
721 }/*}}}*/
722 
shmdt(const void * shmaddr)723 TSRM_API int shmdt(const void *shmaddr)
724 {/*{{{*/
725 	shm_pair *shm = shm_get(0, (void*)shmaddr);
726 	int ret;
727 
728 	if (!shm->segment) {
729 		return -1;
730 	}
731 
732 	shm->descriptor->shm_dtime = time(NULL);
733 	shm->descriptor->shm_lpid  = getpid();
734 	shm->descriptor->shm_nattch--;
735 
736 	ret = UnmapViewOfFile(shm->addr) ? 0 : -1;
737 	if (!ret  && shm->descriptor->shm_nattch <= 0) {
738 		ret = UnmapViewOfFile(shm->descriptor) ? 0 : -1;
739 		shm->descriptor = NULL;
740 	}
741 	return ret;
742 }/*}}}*/
743 
shmctl(int key,int cmd,struct shmid_ds * buf)744 TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
745 {/*{{{*/
746 	shm_pair *shm = shm_get(key, NULL);
747 
748 	if (!shm->segment) {
749 		return -1;
750 	}
751 
752 	switch (cmd) {
753 		case IPC_STAT:
754 			memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
755 			return 0;
756 
757 		case IPC_SET:
758 			shm->descriptor->shm_ctime		= time(NULL);
759 			shm->descriptor->shm_perm.uid	= buf->shm_perm.uid;
760 			shm->descriptor->shm_perm.gid	= buf->shm_perm.gid;
761 			shm->descriptor->shm_perm.mode	= buf->shm_perm.mode;
762 			return 0;
763 
764 		case IPC_RMID:
765 			if (shm->descriptor->shm_nattch < 1) {
766 				shm->descriptor->shm_perm.key = -1;
767 			}
768 			return 0;
769 
770 		default:
771 			return -1;
772 	}
773 }/*}}}*/
774 
775 #if HAVE_UTIME
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 #endif
831 
832 /*
833  * Local variables:
834  * tab-width: 4
835  * c-basic-offset: 4
836  * End:
837  * vim600: sw=4 ts=4 fdm=marker
838  * vim<600: sw=4 ts=4
839  */
840