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