1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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 "tsrm_virtual_cwd.h"
36
37#ifdef ZTS
38static ts_rsrc_id win32_globals_id;
39#else
40static tsrm_win32_globals win32_globals;
41#endif
42
43static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC)
44{
45    globals->process = NULL;
46    globals->shm     = NULL;
47    globals->process_size = 0;
48    globals->shm_size     = 0;
49    globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com");
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
62static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC)
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
90TSRM_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 TSRMLS_CC);
96#endif
97}
98
99TSRM_API void tsrm_win32_shutdown(void)
100{
101#ifndef ZTS
102    tsrm_win32_dtor(&win32_globals TSRMLS_CC);
103#endif
104}
105
106char * tsrm_win32_get_path_sid_key(const char *pathname TSRMLS_DC)
107{
108    PSID pSid = TWG(impersonation_token_sid);
109    DWORD sid_len = pSid ? GetLengthSid(pSid) : 0;
110    TCHAR *ptcSid = NULL;
111    char *bucket_key = NULL;
112
113    if (!pSid) {
114        bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + 1);
115        if (!bucket_key) {
116            return NULL;
117        }
118        memcpy(bucket_key, pathname, strlen(pathname));
119        return bucket_key;
120    }
121
122    if (!ConvertSidToStringSid(pSid, &ptcSid)) {
123        return NULL;
124    }
125
126    bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + strlen(ptcSid) + 1);
127    if (!bucket_key) {
128        LocalFree(ptcSid);
129        return NULL;
130    }
131
132    memcpy(bucket_key, ptcSid, strlen(ptcSid));
133    memcpy(bucket_key + strlen(ptcSid), pathname, strlen(pathname) + 1);
134
135    LocalFree(ptcSid);
136    return bucket_key;
137}
138
139
140PSID tsrm_win32_get_token_sid(HANDLE hToken)
141{
142    BOOL bSuccess = FALSE;
143    DWORD dwLength = 0;
144    PTOKEN_USER pTokenUser = NULL;
145    PSID sid;
146    PSID *ppsid = &sid;
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
182Finished:
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
193TSRM_API int tsrm_win32_access(const char *pathname, int mode TSRMLS_DC)
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    BOOL bucket_key_alloc = FALSE;
208    realpath_cache_bucket * bucket = NULL;
209    char * real_path = NULL;
210
211    if (mode == 1 /*X_OK*/) {
212        DWORD type;
213        return GetBinaryType(pathname, &type) ? 0 : -1;
214    } else {
215        if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
216            real_path = (char *)malloc(MAX_PATH);
217            if(tsrm_realpath(pathname, real_path TSRMLS_CC) == NULL) {
218                goto Finished;
219            }
220            pathname = real_path;
221        }
222
223        if(access(pathname, mode)) {
224            free(real_path);
225            return errno;
226        }
227
228        /* If only existence check is made, return now */
229        if (mode == 0) {
230            free(real_path);
231            return 0;
232        }
233
234/* Only in NTS when impersonate==1 (aka FastCGI) */
235
236        /*
237         AccessCheck() requires an impersonation token.  We first get a primary
238         token and then create a duplicate impersonation token.  The
239         impersonation token is not actually assigned to the thread, but is
240         used in the call to AccessCheck.  Thus, this function itself never
241         impersonates, but does use the identity of the thread.  If the thread
242         was impersonating already, this function uses that impersonation context.
243        */
244        if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
245            DWORD err = GetLastError();
246            if (GetLastError() == ERROR_NO_TOKEN) {
247                if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
248                     TWG(impersonation_token) = NULL;
249                     goto Finished;
250                 }
251            }
252        }
253
254        /* token_sid will be freed in tsrmwin32_dtor */
255        token_sid = tsrm_win32_get_token_sid(thread_token);
256        if (!token_sid) {
257            if (TWG(impersonation_token_sid)) {
258                free(TWG(impersonation_token_sid));
259            }
260            TWG(impersonation_token_sid) = NULL;
261            goto Finished;
262        }
263
264        /* Different identity, we need a new impersontated token as well */
265        if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
266            if (TWG(impersonation_token_sid)) {
267                free(TWG(impersonation_token_sid));
268            }
269            TWG(impersonation_token_sid) = token_sid;
270
271            /* Duplicate the token as impersonated token */
272            if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
273                goto Finished;
274            }
275        } else {
276            /* we already have it, free it then */
277            free(token_sid);
278        }
279
280        if (CWDG(realpath_cache_size_limit)) {
281            t = time(0);
282            bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
283            if(bucket == NULL && real_path == NULL) {
284                /* We used the pathname directly. Call tsrm_realpath */
285                /* so that entry is created in realpath cache */
286                real_path = (char *)malloc(MAX_PATH);
287                if(tsrm_realpath(pathname, real_path TSRMLS_CC) != NULL) {
288                    pathname = real_path;
289                    bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
290                }
291            }
292        }
293
294        /* Do a full access check because access() will only check read-only attribute */
295        if(mode == 0 || mode > 6) {
296            if(bucket != NULL && bucket->is_rvalid) {
297                fAccess = bucket->is_readable;
298                goto Finished;
299            }
300            desired_access = FILE_GENERIC_READ;
301        } else if(mode <= 2) {
302            if(bucket != NULL && bucket->is_wvalid) {
303                fAccess = bucket->is_writable;
304                goto Finished;
305            }
306            desired_access = FILE_GENERIC_WRITE;
307        } else if(mode <= 4) {
308            if(bucket != NULL && bucket->is_rvalid) {
309                fAccess = bucket->is_readable;
310                goto Finished;
311            }
312            desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
313        } else { // if(mode <= 6)
314            if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
315                fAccess = bucket->is_readable & bucket->is_writable;
316                goto Finished;
317            }
318            desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
319        }
320
321        if(TWG(impersonation_token) == NULL) {
322            goto Finished;
323        }
324
325        /* Get size of security buffer. Call is expected to fail */
326        if(GetFileSecurity(pathname, sec_info, NULL, 0, &sec_desc_length)) {
327            goto Finished;
328        }
329
330        psec_desc = (BYTE *)malloc(sec_desc_length);
331        if(psec_desc == NULL ||
332             !GetFileSecurity(pathname, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
333            goto Finished;
334        }
335
336        MapGenericMask(&desired_access, &gen_map);
337
338        if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
339            goto Finished_Impersonate;
340        }
341
342        /* Keep the result in realpath_cache */
343        if(bucket != NULL) {
344            if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
345                bucket->is_rvalid = 1;
346                bucket->is_readable = fAccess;
347            }
348            else if(desired_access == FILE_GENERIC_WRITE) {
349                bucket->is_wvalid = 1;
350                bucket->is_writable = fAccess;
351            } else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
352                bucket->is_rvalid = 1;
353                bucket->is_readable = fAccess;
354                bucket->is_wvalid = 1;
355                bucket->is_writable = fAccess;
356            }
357        }
358
359Finished_Impersonate:
360        if(psec_desc != NULL) {
361            free(psec_desc);
362            psec_desc = NULL;
363        }
364
365Finished:
366        if(thread_token != NULL) {
367            CloseHandle(thread_token);
368        }
369        if(real_path != NULL) {
370            free(real_path);
371            real_path = NULL;
372        }
373
374        if(fAccess == FALSE) {
375            errno = EACCES;
376            return errno;
377        } else {
378            return 0;
379        }
380    }
381}
382
383
384static process_pair *process_get(FILE *stream TSRMLS_DC)
385{
386    process_pair *ptr;
387    process_pair *newptr;
388
389    for (ptr = TWG(process); ptr < (TWG(process) + TWG(process_size)); ptr++) {
390        if (ptr->stream == stream) {
391            break;
392        }
393    }
394
395    if (ptr < (TWG(process) + TWG(process_size))) {
396        return ptr;
397    }
398
399    newptr = (process_pair*)realloc((void*)TWG(process), (TWG(process_size)+1)*sizeof(process_pair));
400    if (newptr == NULL) {
401        return NULL;
402    }
403
404    TWG(process) = newptr;
405    ptr = newptr + TWG(process_size);
406    TWG(process_size)++;
407    return ptr;
408}
409
410static shm_pair *shm_get(int key, void *addr)
411{
412    shm_pair *ptr;
413    shm_pair *newptr;
414    TSRMLS_FETCH();
415
416    for (ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
417        if (!ptr->descriptor) {
418            continue;
419        }
420        if (!addr && ptr->descriptor->shm_perm.key == key) {
421            break;
422        } else if (ptr->addr == addr) {
423            break;
424        }
425    }
426
427    if (ptr < (TWG(shm) + TWG(shm_size))) {
428        return ptr;
429    }
430
431    newptr = (shm_pair*)realloc((void*)TWG(shm), (TWG(shm_size)+1)*sizeof(shm_pair));
432    if (newptr == NULL) {
433        return NULL;
434    }
435
436    TWG(shm) = newptr;
437    ptr = newptr + TWG(shm_size);
438    TWG(shm_size)++;
439    return ptr;
440}
441
442static HANDLE dupHandle(HANDLE fh, BOOL inherit) {
443    HANDLE copy, self = GetCurrentProcess();
444    if (!DuplicateHandle(self, fh, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
445        return NULL;
446    }
447    return copy;
448}
449
450TSRM_API FILE *popen(const char *command, const char *type)
451{
452    TSRMLS_FETCH();
453
454    return popen_ex(command, type, NULL, NULL TSRMLS_CC);
455}
456
457TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env TSRMLS_DC)
458{
459    FILE *stream = NULL;
460    int fno, type_len = strlen(type), read, mode;
461    STARTUPINFO startup;
462    PROCESS_INFORMATION process;
463    SECURITY_ATTRIBUTES security;
464    HANDLE in, out;
465    DWORD dwCreateFlags = 0;
466    BOOL res;
467    process_pair *proc;
468    char *cmd;
469    int i;
470    char *ptype = (char *)type;
471    HANDLE thread_token = NULL;
472    HANDLE token_user = NULL;
473    BOOL asuser = TRUE;
474
475    if (!type) {
476        return NULL;
477    }
478
479    /*The following two checks can be removed once we drop XP support */
480    type_len = strlen(type);
481    if (type_len <1 || type_len > 2) {
482        return NULL;
483    }
484
485    for (i=0; i < type_len; i++) {
486        if (!(*ptype == 'r' || *ptype == 'w' || *ptype == 'b' || *ptype == 't')) {
487            return NULL;
488        }
489        ptype++;
490    }
491
492    security.nLength                = sizeof(SECURITY_ATTRIBUTES);
493    security.bInheritHandle         = TRUE;
494    security.lpSecurityDescriptor   = NULL;
495
496    if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
497        return NULL;
498    }
499
500    memset(&startup, 0, sizeof(STARTUPINFO));
501    memset(&process, 0, sizeof(PROCESS_INFORMATION));
502
503    startup.cb          = sizeof(STARTUPINFO);
504    startup.dwFlags     = STARTF_USESTDHANDLES;
505    startup.hStdError   = GetStdHandle(STD_ERROR_HANDLE);
506
507    read = (type[0] == 'r') ? TRUE : FALSE;
508    mode = ((type_len == 2) && (type[1] == 'b')) ? O_BINARY : O_TEXT;
509
510    if (read) {
511        in = dupHandle(in, FALSE);
512        startup.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
513        startup.hStdOutput = out;
514    } else {
515        out = dupHandle(out, FALSE);
516        startup.hStdInput  = in;
517        startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
518    }
519
520    dwCreateFlags = NORMAL_PRIORITY_CLASS;
521    if (strcmp(sapi_module.name, "cli") != 0) {
522        dwCreateFlags |= CREATE_NO_WINDOW;
523    }
524
525    /* Get a token with the impersonated user. */
526    if(OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
527        DuplicateTokenEx(thread_token, MAXIMUM_ALLOWED, &security, SecurityImpersonation, TokenPrimary, &token_user);
528    } else {
529        DWORD err = GetLastError();
530        if (err == ERROR_NO_TOKEN) {
531            asuser = FALSE;
532        }
533    }
534
535    cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")+2);
536    if (!cmd) {
537        return NULL;
538    }
539
540    sprintf(cmd, "%s /c \"%s\"", TWG(comspec), command);
541    if (asuser) {
542        res = CreateProcessAsUser(token_user, NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process);
543        CloseHandle(token_user);
544    } else {
545        res = CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process);
546    }
547    free(cmd);
548
549    if (!res) {
550        return NULL;
551    }
552
553    CloseHandle(process.hThread);
554    proc = process_get(NULL TSRMLS_CC);
555
556    if (read) {
557        fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
558        CloseHandle(out);
559    } else {
560        fno = _open_osfhandle((tsrm_intptr_t)out, _O_WRONLY | mode);
561        CloseHandle(in);
562    }
563
564    stream = _fdopen(fno, type);
565    proc->prochnd = process.hProcess;
566    proc->stream = stream;
567    return stream;
568}
569
570TSRM_API int pclose(FILE *stream)
571{
572    DWORD termstat = 0;
573    process_pair *process;
574    TSRMLS_FETCH();
575
576    if ((process = process_get(stream TSRMLS_CC)) == 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
591TSRM_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    sprintf(shm_segment, "TSRM_SHM_SEGMENT:%d", key);
603    sprintf(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            return -1;
616        }
617    } else {
618        if (flags & IPC_EXCL) {
619            return -1;
620        }
621    }
622
623    shm = shm_get(key, NULL);
624    shm->segment = shm_handle;
625    shm->info    = info_handle;
626    shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
627
628    if (NULL != shm->descriptor && created) {
629        shm->descriptor->shm_perm.key   = key;
630        shm->descriptor->shm_segsz      = size;
631        shm->descriptor->shm_ctime      = time(NULL);
632        shm->descriptor->shm_cpid       = getpid();
633        shm->descriptor->shm_perm.mode  = flags;
634
635        shm->descriptor->shm_perm.cuid  = shm->descriptor->shm_perm.cgid= 0;
636        shm->descriptor->shm_perm.gid   = shm->descriptor->shm_perm.uid = 0;
637        shm->descriptor->shm_atime      = shm->descriptor->shm_dtime    = 0;
638        shm->descriptor->shm_lpid       = shm->descriptor->shm_nattch   = 0;
639        shm->descriptor->shm_perm.mode  = shm->descriptor->shm_perm.seq = 0;
640    }
641
642    if (NULL != shm->descriptor && (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz)) {
643        if (NULL != shm->segment) {
644            CloseHandle(shm->segment);
645        }
646        UnmapViewOfFile(shm->descriptor);
647        CloseHandle(shm->info);
648        return -1;
649    }
650
651    return key;
652}
653
654TSRM_API void *shmat(int key, const void *shmaddr, int flags)
655{
656    shm_pair *shm = shm_get(key, NULL);
657
658    if (!shm->segment) {
659        return (void*)-1;
660    }
661
662    shm->descriptor->shm_atime = time(NULL);
663    shm->descriptor->shm_lpid  = getpid();
664    shm->descriptor->shm_nattch++;
665
666    shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
667
668    return shm->addr;
669}
670
671TSRM_API int shmdt(const void *shmaddr)
672{
673    shm_pair *shm = shm_get(0, (void*)shmaddr);
674
675    if (!shm->segment) {
676        return -1;
677    }
678
679    shm->descriptor->shm_dtime = time(NULL);
680    shm->descriptor->shm_lpid  = getpid();
681    shm->descriptor->shm_nattch--;
682
683    return UnmapViewOfFile(shm->addr) ? 0 : -1;
684}
685
686TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf) {
687    shm_pair *shm = shm_get(key, NULL);
688
689    if (!shm->segment) {
690        return -1;
691    }
692
693    switch (cmd) {
694        case IPC_STAT:
695            memcpy(buf, shm->descriptor, sizeof(struct shmid_ds));
696            return 0;
697
698        case IPC_SET:
699            shm->descriptor->shm_ctime      = time(NULL);
700            shm->descriptor->shm_perm.uid   = buf->shm_perm.uid;
701            shm->descriptor->shm_perm.gid   = buf->shm_perm.gid;
702            shm->descriptor->shm_perm.mode  = buf->shm_perm.mode;
703            return 0;
704
705        case IPC_RMID:
706            if (shm->descriptor->shm_nattch < 1) {
707                shm->descriptor->shm_perm.key = -1;
708            }
709            return 0;
710
711        default:
712            return -1;
713    }
714}
715
716TSRM_API char *realpath(char *orig_path, char *buffer)
717{
718    int ret = GetFullPathName(orig_path, _MAX_PATH, buffer, NULL);
719    if(!ret || ret > _MAX_PATH) {
720        return NULL;
721    }
722    return buffer;
723}
724
725#endif
726