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