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, ©, 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