diff options
Diffstat (limited to 'winsup/cygwin/cygserver_shm.cc')
-rwxr-xr-x | winsup/cygwin/cygserver_shm.cc | 1258 |
1 files changed, 801 insertions, 457 deletions
diff --git a/winsup/cygwin/cygserver_shm.cc b/winsup/cygwin/cygserver_shm.cc index c29938bb9..18b1c3d83 100755 --- a/winsup/cygwin/cygserver_shm.cc +++ b/winsup/cygwin/cygserver_shm.cc @@ -1,552 +1,896 @@ -/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin +/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin. - Copyright 2001 Red Hat, Inc. + Copyright 2002 Red Hat, Inc. - Originally written by Robert Collins <robert.collins@hotmail.com> + Written by Conrad Scott <conrad.scott@dsl.pipex.com>. + Based on code by Robert Collins <robert.collins@hotmail.com>. - This file is part of Cygwin. +This file is part of Cygwin. - This software is a copyrighted work licensed under the terms of the - Cygwin license. Please consult the file "CYGWIN_LICENSE" for - details. */ +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ +#include "woutsup.h" -#ifdef __OUTSIDE_CYGWIN__ -#undef __INSIDE_CYGWIN__ -#else -#include "winsup.h" -#endif - -#ifndef __INSIDE_CYGWIN__ -#define DEBUG 0 -#define system_printf printf -#define debug_printf if (DEBUG) printf -#define api_fatal printf -#include <stdio.h> -#include <windows.h> -#endif - -#include <sys/stat.h> #include <errno.h> -#include "cygerrno.h" -#include <unistd.h> -#include "security.h" -//#include "fhandler.h" -//#include "dtable.h" -//#include "cygheap.h" +#include <pthread.h> #include <stdio.h> -//#include "thread.h" -#ifndef __INSIDE_CYGWIN__ -#define __INSIDE_CYGWIN__ -#include <sys/shm.h> -#undef __INSIDE_CYGWIN__ -#else -#include <sys/shm.h> -#endif -//#include "perprocess.h" -#include <threaded_queue.h> -#include <cygwin/cygserver_process.h> +#include <string.h> +#include <time.h> + +#include "cygserver_ipc.h" #include "cygserver_shm.h" +#include "security.h" + +#include "cygwin/cygserver.h" +#include "cygwin/cygserver_process.h" +#include "cygwin/cygserver_transport.h" + +/*---------------------------------------------------------------------------* + * class server_shmmgr + * + * A singleton class. + *---------------------------------------------------------------------------*/ -// FIXME IS THIS CORRECT -/* Implementation notes: We use two shared memory regions per key: - * One for the control structure, and one for the shared memory. - * While this has a higher overhead tham a single shared area, - * It allows more flexability. As the entire code is transparent to the user - * We can merge these in the future should it be needed. - * Also, IPC_PRIVATE keys create unique mappings each time. The shm_ids just - * keep monotonically incrementing - system wide. - */ -size_t -getsystemallocgranularity () +#define shmmgr (server_shmmgr::instance ()) + +class server_shmmgr { - SYSTEM_INFO sysinfo; - static size_t buffer_offset = 0; - if (buffer_offset) - return buffer_offset; - GetSystemInfo (&sysinfo); - buffer_offset = sysinfo.dwAllocationGranularity; - return buffer_offset; -} +private: + class attach_t + { + public: + class process *const _client; + unsigned int _refcnt; + + attach_t *_next; + + attach_t (class process *const client) + : _client (client), + _refcnt (0), + _next (NULL) + {} + }; + + class segment_t + { + private: + // Bits for the _flg field. + enum { IS_DELETED = 0x01 }; + + public: + const int _intid; + const int _shmid; + struct shmid_ds _ds; + + segment_t *_next; + + segment_t (const key_t key, const int intid, const HANDLE hFileMap); + ~segment_t (); + + bool is_deleted () const + { + return _flg & IS_DELETED; + } + bool is_pending_delete () const + { + return !_ds.shm_nattch && is_deleted (); + } -client_request_shm::client_request_shm ():client_request (CYGSERVER_REQUEST_SHM_GET, sizeof (parameters)) + void mark_deleted () + { + assert (!is_deleted ()); + + _flg |= IS_DELETED; + } + + int attach (class process *, HANDLE & hFileMap); + int detach (class process *); + + private: + static long _sequence; + + int _flg; + const HANDLE _hFileMap; + attach_t *_attach_head; // A list sorted by winpid; + + attach_t *find (const class process *, attach_t **previous = NULL); + }; + + class cleanup_t : public cleanup_routine + { + public: + cleanup_t (const segment_t *const segptr) + : cleanup_routine (reinterpret_cast<void *> (segptr->_shmid)) + { + assert (key ()); + } + + int shmid () const { return reinterpret_cast<int> (key ()); } + + virtual void cleanup (class process *const client) + { + const int res = shmmgr.shmdt (shmid (), client); + + if (res != 0) + debug_printf ("process cleanup failed [shmid = %d]: %s", + shmid (), strerror (-res)); + } + }; + +public: + static server_shmmgr & instance (); + + int shmat (HANDLE & hFileMap, + int shmid, int shmflg, class process *); + int shmctl (int & out_shmid, struct shmid_ds & out_ds, + struct shminfo & out_shminfo, struct shm_info & out_shm_info, + const int shmid, int cmd, const struct shmid_ds &, + class process *); + int shmdt (int shmid, class process *); + int shmget (int & out_shmid, key_t, size_t, int shmflg, uid_t, gid_t, + class process *); + +private: + static server_shmmgr *_instance; + static pthread_once_t _instance_once; + + static void initialise_instance (); + + CRITICAL_SECTION _segments_lock; + segment_t *_segments_head; // A list sorted by int_id. + + int _shm_ids; // Number of shm segments (for ipcs(8)). + int _shm_tot; // Total bytes of shm segments (for ipcs(8)). + int _shm_atts; // Number of attached segments (for ipcs(8)). + int _intid_max; // Highest intid yet allocated (for ipcs(8)). + + server_shmmgr (); + ~server_shmmgr (); + + // Undefined (as this class is a singleton): + server_shmmgr (const server_shmmgr &); + server_shmmgr & operator= (const server_shmmgr &); + + segment_t *find_by_key (key_t); + segment_t *find (int intid, segment_t **previous = NULL); + + int new_segment (key_t, size_t, int shmflg, pid_t, uid_t, gid_t); + + segment_t *new_segment (key_t, size_t, HANDLE); + void delete_segment (segment_t *); +}; + +/* static */ long server_shmmgr::segment_t::_sequence = 0; + +/* static */ server_shmmgr *server_shmmgr::_instance = NULL; +/* static */ pthread_once_t server_shmmgr::_instance_once = PTHREAD_ONCE_INIT; + +/*---------------------------------------------------------------------------* + * server_shmmgr::segment_t::segment_t () + *---------------------------------------------------------------------------*/ + +server_shmmgr::segment_t::segment_t (const key_t key, + const int intid, + const HANDLE hFileMap) + : _intid (intid), + _shmid (ipc_int2ext (intid, IPC_SHMOP, _sequence)), + _next (NULL), + _flg (0), + _hFileMap (hFileMap), + _attach_head (NULL) { - buffer = (char *) ¶meters; + assert (0 <= _intid && _intid < SHMMNI); + + memset (&_ds, '\0', sizeof (_ds)); + _ds.shm_perm.key = key; } -/* FIXME: If building on a 64-bit compiler, the address->int typecast will fail. - * Solution: manually calculate the next id value - */ +/*---------------------------------------------------------------------------* + * server_shmmgr::segment_t::~segment_t () + *---------------------------------------------------------------------------*/ -#if 0 -extern "C" void * -shmat (int shmid, const void *shmaddr, int parameters.in.shmflg) +server_shmmgr::segment_t::~segment_t () { - class shmid_ds *shm = (class shmid_ds *) shmid; //FIXME: verifyable object test + assert (!_attach_head); + + if (!CloseHandle (_hFileMap)) + syscall_printf ("failed to close file map [handle = 0x%x]: %E", _hFileMap); +} - if (shmaddr) +/*---------------------------------------------------------------------------* + * server_shmmgr::segment_t::attach () + *---------------------------------------------------------------------------*/ + +int +server_shmmgr::segment_t::attach (class process *const client, + HANDLE & hFileMap) +{ + assert (client); + + if (!DuplicateHandle (GetCurrentProcess (), + _hFileMap, + client->handle (), + &hFileMap, + 0, + FALSE, // bInheritHandle + DUPLICATE_SAME_ACCESS)) { - //FIXME: requested base address ?! - set_errno (EINVAL); - return (void *) -1; - } + syscall_printf (("failed to duplicate handle for client " + "[key = 0x%016llx, shmid = %d, handle = 0x%x]: %E"), + _ds.shm_perm.key, _shmid, _hFileMap); - void *rv = MapViewOfFile (shm->attachmap, + return -EACCES; // FIXME: Case analysis? + } + _ds.shm_lpid = client->cygpid (); + _ds.shm_nattch += 1; + _ds.shm_atime = time (NULL); // FIXME: sub-second times. - (parameters.in.shmflg & SHM_RDONLY) ? - FILE_MAP_READ : FILE_MAP_WRITE, 0, - 0, 0); + attach_t *previous = NULL; + attach_t *attptr = find (client, &previous); - if (!rv) + if (!attptr) { - //FIXME: translate GetLastError() - set_errno (EACCES); - return (void *) -1; + attptr = safe_new (attach_t, client); + + if (previous) + { + attptr->_next = previous->_next; + previous->_next = attptr; + } + else + { + attptr->_next = _attach_head; + _attach_head = attptr; + } } -/* FIXME: this needs to be globally protected to prevent a mismatch betwen - * attach count and attachees list - */ + attptr->_refcnt += 1; + + cleanup_t *const cleanup = safe_new (cleanup_t, this); - InterlockedIncrement (&shm->shm_nattch); - _shmattach *attachnode = new _shmattach; + // FIXME: ::add should only fail if the process object is already + // cleaning up; but it can't be doing that since this thread has it + // locked. - attachnode->data = rv; - attachnode->next = - (_shmattach *) InterlockedExchangePointer ((LONG *) & shm->attachhead, - (long int) attachnode); - return rv; + const bool result = client->add (cleanup); + + assert (result); + + return 0; } -#endif - -/* FIXME: evaluate getuid() and getgid() against the requested mode. Then - * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ - * appropriately - */ - -/* Test result from openbsd: shm ids are persistent cross process if a handle is left - * open. This could lead to resource starvation: we're not copying that behaviour - * unless we have to. (It will involve acygwin1.dll gloal shared list :[ ). - */ -/* FIXME: shmid should be a verifyable object - */ - -/* FIXME: on NT we should check everything against the SD. On 95 we just emulate. - */ - -extern GENERIC_MAPPING access_mapping; - -extern int -check_and_dup_handle (HANDLE from_process, HANDLE to_process, - HANDLE from_process_token, - DWORD access, - HANDLE from_handle, - HANDLE * to_handle_ptr, BOOL bInheritHandle); - -//FIXME: where should this live -static shmnode *shm_head = NULL; -/* must be long for InterlockedIncrement */ -static long new_id = 0; -static long new_private_key = 0; -void -client_request_shm::serve (transport_layer_base * conn, - process_cache * cache) +/*---------------------------------------------------------------------------* + * server_shmmgr::segment_t::detach () + *---------------------------------------------------------------------------*/ + +int +server_shmmgr::segment_t::detach (class process *const client) { -// DWORD sd_size = 4096; -// char sd_buf[4096]; - PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf; -// /* create a sd for our open requests based on shmflag & 0x01ff */ -// psd = alloc_sd (getuid (), getgid (), cygheap->user.logsrv (), -// parameters.in.shmflg & 0x01ff, psd, &sd_size); - - HANDLE from_process_handle = NULL; - HANDLE token_handle = NULL; - DWORD rc; - - from_process_handle = cache->process (parameters.in.pid)->handle (); - /* possible TODO: reduce the access on the handle before we use it */ - /* Note that unless we do this, we don't need to call CloseHandle - it's kept open - * by the process cache until the process terminates. - * We may need a refcount on the cache however... - */ - if (!from_process_handle) + attach_t *previous = NULL; + attach_t *const attptr = find (client, &previous); + + if (!attptr) + return -EINVAL; + + if (client->is_active ()) { - debug_printf ("error opening process (%lu)\n", GetLastError ()); - header.error_code = EACCES; - return; + const cleanup_t key (this); + + if (!client->remove (&key)) + syscall_printf (("failed to remove cleanup routine for %d(%lu) " + "[shmid = %d]"), + client->cygpid (), client->winpid (), + _shmid); } - conn->impersonate_client (); + attptr->_refcnt -= 1; - rc = OpenThreadToken (GetCurrentThread (), - TOKEN_QUERY, TRUE, &token_handle); + if (!attptr->_refcnt) + { + assert (previous ? previous->_next == attptr : _attach_head == attptr); - conn->revert_to_self (); + if (previous) + previous->_next = attptr->_next; + else + _attach_head = attptr->_next; - if (!rc) - { - debug_printf ("error opening thread token (%lu)\n", GetLastError ()); - header.error_code = EACCES; - CloseHandle (from_process_handle); - return; + safe_delete (attptr); } + assert (_ds.shm_nattch > 0); - /* we trust the clients request - we will be doing it as them, and - * the worst they can do is open their own permissions - */ + _ds.shm_lpid = client->cygpid (); + _ds.shm_nattch -= 1; + _ds.shm_dtime = time (NULL); // FIXME: sub-second times. + return 0; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::segment_t::find () + *---------------------------------------------------------------------------*/ + +server_shmmgr::attach_t * +server_shmmgr::segment_t::find (const class process *const client, + attach_t **previous) +{ + if (previous) + *previous = NULL; + + // Nb. The _attach_head list is sorted by winpid. + + for (attach_t *attptr = _attach_head; attptr; attptr = attptr->_next) + if (attptr->_client == client) + return attptr; + else if (attptr->_client->winpid () > client->winpid ()) + return NULL; + else if (previous) + *previous = attptr; + + return NULL; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::instance () + *---------------------------------------------------------------------------*/ - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof (sa); - sa.lpSecurityDescriptor = psd; - sa.bInheritHandle = TRUE; /* the memory structures inherit ok */ +/* static */ server_shmmgr & +server_shmmgr::instance () +{ + pthread_once (&_instance_once, &initialise_instance); - char *shmname = NULL, *shmaname = NULL; - char stringbuf[29], stringbuf1[29]; + assert (_instance); - /* TODO: make this code block a function! */ - if (parameters.in.type == SHM_REATTACH) + return *_instance; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::shmat () + *---------------------------------------------------------------------------*/ + +int +server_shmmgr::shmat (HANDLE & hFileMap, + const int shmid, const int shmflg, + class process *const client) +{ + syscall_printf ("shmat (shmid = %d, shmflg = 0%o) for %d(%lu)", + shmid, shmflg, client->cygpid (), client->winpid ()); + + int result = 0; + EnterCriticalSection (&_segments_lock); + + segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP)); + + if (!segptr) + result = -EINVAL; + else + result = segptr->attach (client, hFileMap); + + if (!result) + _shm_atts += 1; + + LeaveCriticalSection (&_segments_lock); + + if (result < 0) + syscall_printf (("-1 [%d] = shmat (shmid = %d, shmflg = 0%o) " + "for %d(%lu)"), + -result, shmid, shmflg, + client->cygpid (), client->winpid ()); + else + syscall_printf (("0x%x = shmat (shmid = %d, shmflg = 0%o) " + "for %d(%lu)"), + hFileMap, shmid, shmflg, + client->cygpid (), client->winpid ()); + + return result; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::shmctl () + *---------------------------------------------------------------------------*/ + +int +server_shmmgr::shmctl (int & out_shmid, + struct shmid_ds & out_ds, + struct shminfo & out_shminfo, + struct shm_info & out_shm_info, + const int shmid, const int cmd, + const struct shmid_ds & ds, + class process *const client) +{ + syscall_printf ("shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)", + shmid, cmd, client->cygpid (), client->winpid ()); + + int result = 0; + EnterCriticalSection (&_segments_lock); + + switch (cmd) { - /* just find and fill out the existing shm_id */ - shmnode *tempnode = shm_head; - while (tempnode) - { - if (tempnode->shm_id == parameters.in.shm_id) + case IPC_STAT: + case SHM_STAT: // Uses intids rather than shmids. + case IPC_SET: + case IPC_RMID: + { + int intid; + + if (cmd == SHM_STAT) + intid = shmid; + else + intid = ipc_ext2int (shmid, IPC_SHMOP); + + segment_t *const segptr = find (intid); + + if (!segptr) + result = -EINVAL; + else + switch (cmd) { - parameters.out.shm_id = tempnode->shm_id; - parameters.out.key = tempnode->key; - if (check_and_dup_handle - (GetCurrentProcess (), from_process_handle, token_handle, - DUPLICATE_SAME_ACCESS, tempnode->filemap, - ¶meters.out.filemap, TRUE) != 0) - { - debug_printf ("error duplicating filemap handle (%lu)\n", - GetLastError ()); - header.error_code = EACCES; - } - if (check_and_dup_handle - (GetCurrentProcess (), from_process_handle, token_handle, - DUPLICATE_SAME_ACCESS, tempnode->attachmap, - ¶meters.out.attachmap, TRUE) != 0) + case IPC_STAT: + out_ds = segptr->_ds; + break; + + case IPC_SET: + segptr->_ds.shm_perm.uid = ds.shm_perm.uid; + segptr->_ds.shm_perm.gid = ds.shm_perm.gid; + segptr->_ds.shm_perm.mode = ds.shm_perm.mode & 0777; + segptr->_ds.shm_lpid = client->cygpid (); + segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times. + break; + + case IPC_RMID: + if (segptr->is_deleted ()) + result = -EIDRM; + else { - debug_printf ("error duplicating attachmap handle (%lu)\n", - GetLastError ()); - header.error_code = EACCES; + segptr->mark_deleted (); + if (segptr->is_pending_delete ()) + delete_segment (segptr); } - CloseHandle (token_handle); - return; + break; + + case SHM_STAT: // ipcs(8) i'face. + out_ds = segptr->_ds; + out_shmid = segptr->_shmid; + break; } - tempnode = tempnode->next; - } - header.error_code = EINVAL; - CloseHandle (token_handle); - return; + } + break; + + case IPC_INFO: + out_shminfo.shmmax = SHMMAX; + out_shminfo.shmmin = SHMMIN; + out_shminfo.shmmni = SHMMNI; + out_shminfo.shmseg = SHMSEG; + out_shminfo.shmall = SHMALL; + break; + + case SHM_INFO: // ipcs(8) i'face. + out_shmid = _intid_max; + out_shm_info.shm_ids = _shm_ids; + out_shm_info.shm_tot = _shm_tot; + out_shm_info.shm_atts = _shm_atts; + break; + + default: + result = -EINVAL; + break; } - /* someone attached */ - /* someone can send shm_id's they don't have and currently we will increment those - * attach counts. If someone wants to fix that, please go ahead. - * The problem is that shm_get has nothing to do with the ability to attach. Attach - * requires a permission check, which we get the OS to do in MapViewOfFile. - */ - if (parameters.in.type == SHM_ATTACH) - { - shmnode *tempnode = shm_head; - while (tempnode) - { - if (tempnode->shm_id == parameters.in.shm_id) - { - InterlockedIncrement (&tempnode->shmds->shm_nattch); - header.error_code = 0; - CloseHandle (token_handle); - return; - } - tempnode = tempnode->next; - } - header.error_code = EINVAL; - CloseHandle (token_handle); - return; - } + LeaveCriticalSection (&_segments_lock); - /* it's a original request from the users */ + if (result < 0) + syscall_printf (("-1 [%d] = " + "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"), + -result, + shmid, cmd, client->cygpid (), client->winpid ()); + else + syscall_printf (("%d = " + "shmctl (shmid = %d, cmd = 0x%x) for %d(%lu)"), + ((cmd == SHM_STAT || cmd == SHM_INFO) + ? out_shmid + : result), + shmid, cmd, client->cygpid (), client->winpid ()); + + return result; +} - /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide - * to prevent races on shmget. - */ +/*---------------------------------------------------------------------------* + * server_shmmgr::shmdt () + *---------------------------------------------------------------------------*/ - if (parameters.in.key == IPC_PRIVATE) +int +server_shmmgr::shmdt (const int shmid, class process *const client) +{ + syscall_printf ("shmdt (shmid = %d) for %d(%lu)", + shmid, client->cygpid (), client->winpid ()); + + int result = 0; + EnterCriticalSection (&_segments_lock); + + segment_t *const segptr = find (ipc_ext2int (shmid, IPC_SHMOP)); + + if (!segptr) + result = -EINVAL; + else + result = segptr->detach (client); + + if (!result) + _shm_atts -= 1; + + if (!result && segptr->is_pending_delete ()) + delete_segment (segptr); + + LeaveCriticalSection (&_segments_lock); + + if (result < 0) + syscall_printf ("-1 [%d] = shmdt (shmid = %d) for %d(%lu)", + -result, shmid, client->cygpid (), client->winpid ()); + else + syscall_printf ("%d = shmdt (shmid = %d) for %d(%lu)", + result, shmid, client->cygpid (), client->winpid ()); + + return result; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::shmget () + *---------------------------------------------------------------------------*/ + +int +server_shmmgr::shmget (int & out_shmid, + const key_t key, const size_t size, const int shmflg, + const uid_t uid, const gid_t gid, + class process *const client) +{ + syscall_printf (("shmget (key = 0x%016llx, size = %u, shmflg = 0%o) " + "for %d(%lu)"), + key, size, shmflg, + client->cygpid (), client->winpid ()); + + int result = 0; + EnterCriticalSection (&_segments_lock); + + if (key == IPC_PRIVATE) + result = new_segment (key, size, shmflg, + client->cygpid (), uid, gid); + else { - /* create the mapping name (CYGWINSHMKPRIVATE_0x01234567 */ - /* The K refers to Key, the actual mapped area has D */ - long private_key = (int) InterlockedIncrement (&new_private_key); - snprintf (stringbuf, 29, "CYGWINSHMKPRIVATE_0x%0x", private_key); - shmname = stringbuf; - snprintf (stringbuf1, 29, "CYGWINSHMDPRIVATE_0x%0x", private_key); - shmaname = stringbuf1; + segment_t *const segptr = find_by_key (key); + + if (!segptr) + if (shmflg & IPC_CREAT) + result = new_segment (key, size, shmflg, + client->cygpid (), uid, gid); + else + result = -ENOENT; + else if (segptr->is_deleted ()) + result = -EIDRM; + else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) + result = -EEXIST; + else if ((shmflg & ~(segptr->_ds.shm_perm.mode)) & 0777) + result = -EACCES; + else if (size && segptr->_ds.shm_segsz < size) + result = -EINVAL; + else + result = segptr->_shmid; } - else + + LeaveCriticalSection (&_segments_lock); + + if (result >= 0) { - /* create the mapping name (CYGWINSHMK0x0123456789abcdef */ - /* The K refers to Key, the actual mapped area has D */ - - snprintf (stringbuf, 29, "CYGWINSHMK0x%0qx", parameters.in.key); - shmname = stringbuf; - snprintf (stringbuf1, 29, "CYGWINSHMD0x%0qx", parameters.in.key); - shmaname = stringbuf1; - debug_printf ("system id strings are \n%s\n%s\n", shmname, shmaname); - debug_printf ("key input value is 0x%0qx\n", parameters.in.key); + out_shmid = result; + result = 0; } - /* attempt to open the key */ + if (result < 0) + syscall_printf (("-1 [%d] = " + "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) " + "for %d(%lu)"), + -result, + key, size, shmflg, + client->cygpid (), client->winpid ()); + else + syscall_printf (("%d = " + "shmget (key = 0x%016llx, size = %u, shmflg = 0%o) " + "for %d(%lu)"), + out_shmid, + key, size, shmflg, + client->cygpid (), client->winpid ()); + + return result; +} - /* get an existing key */ - /* On unix the same shmid identifier is returned on multiple calls to shm_get - * with the same key and size. Different modes is a ?. - */ +/*---------------------------------------------------------------------------* + * server_shmmgr::initialise_instance () + *---------------------------------------------------------------------------*/ +/* static */ void +server_shmmgr::initialise_instance () +{ + assert (!_instance); + _instance = safe_new0 (server_shmmgr); - /* walk the list of known keys and return the id if found. remember, we are - * authoritative... - */ + assert (_instance); +} - shmnode *tempnode = shm_head; - while (tempnode) - { - if (tempnode->key == parameters.in.key - && parameters.in.key != IPC_PRIVATE) - { - // FIXME: free the mutex - if (parameters.in.size - && tempnode->shmds->shm_segsz < parameters.in.size) - { - header.error_code = EINVAL; - CloseHandle (token_handle); - return; - } - /* FIXME: can the same process call this twice without error ? test - * on unix - */ - if ((parameters.in.shmflg & IPC_CREAT) - && (parameters.in.shmflg & IPC_EXCL)) - { - header.error_code = EEXIST; - debug_printf - ("attempt to exclusively create already created shm_area with key 0x%0qx\n", - parameters.in.key); - // FIXME: free the mutex - CloseHandle (token_handle); - return; - } - // FIXME: do we need to other tests of the requested mode with the - // tempnode->shm_id mode ? testcase on unix needed. - // FIXME how do we do the security test? or - // do we wait for shmat to bother with that? - /* One possibly solution: impersonate the client, and then test we can - * reopen the area. In fact we'll probably have to do that to get - * handles back to them, alternatively just tell them the id, and then - * let them attempt the open. - */ - parameters.out.shm_id = tempnode->shm_id; - if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, - token_handle, - DUPLICATE_SAME_ACCESS, - tempnode->filemap, - ¶meters.out.filemap, TRUE) != 0) - { - printf ("error duplicating filemap handle (%lu)\n", - GetLastError ()); - header.error_code = EACCES; -/*mutex*/ - CloseHandle (token_handle); - return; - } - if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, - token_handle, - DUPLICATE_SAME_ACCESS, - tempnode->attachmap, - ¶meters.out.attachmap, TRUE) != 0) - { - printf ("error duplicating attachmap handle (%lu)\n", - GetLastError ()); - header.error_code = EACCES; -/*mutex*/ - CloseHandle (token_handle); - return; - } +/*---------------------------------------------------------------------------* + * server_shmmgr::server_shmmgr () + *---------------------------------------------------------------------------*/ - CloseHandle (token_handle); - return; - } - tempnode = tempnode->next; - } - /* couldn't find a currently open shm area. */ +server_shmmgr::server_shmmgr () + : _segments_head (NULL), + _shm_ids (0), + _shm_tot (0), + _shm_atts (0), + _intid_max (0) +{ + InitializeCriticalSection (&_segments_lock); +} - /* create one */ - /* do this as the client */ - conn->impersonate_client (); - /* This may need sh_none... it's only a control structure */ - HANDLE filemap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile. - &sa, - PAGE_READWRITE, // protection - 0x00000000, - getsystemallocgranularity (), - shmname // object name - ); - int lasterr = GetLastError (); - conn->revert_to_self (); +/*---------------------------------------------------------------------------* + * server_shmmgr::~server_shmmgr () + *---------------------------------------------------------------------------*/ + +server_shmmgr::~server_shmmgr () +{ + DeleteCriticalSection (&_segments_lock); +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::find_by_key () + *---------------------------------------------------------------------------*/ + +server_shmmgr::segment_t * +server_shmmgr::find_by_key (const key_t key) +{ + for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next) + if (segptr->_ds.shm_perm.key == key) + return segptr; + + return NULL; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::find () + *---------------------------------------------------------------------------*/ + +server_shmmgr::segment_t * +server_shmmgr::find (const int intid, segment_t **previous) +{ + if (previous) + *previous = NULL; + + for (segment_t *segptr = _segments_head; segptr; segptr = segptr->_next) + if (segptr->_intid == intid) + return segptr; + else if (segptr->_intid > intid) // The list is sorted by intid. + return NULL; + else if (previous) + *previous = segptr; + + return NULL; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::new_segment () + *---------------------------------------------------------------------------*/ + +int +server_shmmgr::new_segment (const key_t key, + const size_t size, + const int shmflg, + const pid_t cygpid, + const uid_t uid, + const gid_t gid) +{ + if (size < SHMMIN || size > SHMMAX) + return -EINVAL; + + const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE, + NULL, PAGE_READWRITE, + 0, size, + NULL); - if (filemap == NULL) + if (!hFileMap) { - /* We failed to open the filemapping ? */ - system_printf ("failed to open file mapping: %lu\n", GetLastError ()); - // free the mutex - // we can assume that it exists, and that it was an access problem. - header.error_code = EACCES; - CloseHandle (token_handle); - return; + syscall_printf ("failed to create file mapping [size = %lu]: %E", size); + return -ENOMEM; // FIXME } - /* successfully opened the control region mapping */ - /* did we create it ? */ - int oldmapping = lasterr == ERROR_ALREADY_EXISTS; - if (oldmapping) + segment_t *const segptr = new_segment (key, size, hFileMap); + + if (!segptr) { - /* should never happen - we are the global daemon! */ -#if 0 - if ((parameters.in.shmflg & IPC_CREAT) - && (parameters.in.shmflg & IPC_EXCL)) -#endif - { - /* FIXME free mutex */ - CloseHandle (filemap); - header.error_code = EEXIST; - CloseHandle (token_handle); - return; - } + (void) CloseHandle (hFileMap); + return -ENOSPC; } - /* we created a new mapping */ - if (parameters.in.key != IPC_PRIVATE && - (parameters.in.shmflg & IPC_CREAT) == 0) + segptr->_ds.shm_perm.cuid = segptr->_ds.shm_perm.uid = uid; + segptr->_ds.shm_perm.cgid = segptr->_ds.shm_perm.gid = gid; + segptr->_ds.shm_perm.mode = shmflg & 0777; + segptr->_ds.shm_segsz = size; + segptr->_ds.shm_cpid = cygpid; + segptr->_ds.shm_ctime = time (NULL); // FIXME: sub-second times. + + return segptr->_shmid; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::new_segment () + * + * Allocate a new segment for the given key and file map with the + * lowest available intid and insert into the segment map. + *---------------------------------------------------------------------------*/ + +server_shmmgr::segment_t * +server_shmmgr::new_segment (const key_t key, const size_t size, + const HANDLE hFileMap) +{ + // FIXME: Overflow risk. + if (_shm_tot + size > SHMALL) + return NULL; + + int intid = 0; // Next expected intid value. + segment_t *previous = NULL; // Insert pointer. + + // Find first unallocated intid. + for (segment_t *segptr = _segments_head; + segptr && segptr->_intid == intid; + segptr = segptr->_next, intid++) { - CloseHandle (filemap); - /* FIXME free mutex */ - header.error_code = ENOENT; - CloseHandle (token_handle); - return; + previous = segptr; } - conn->impersonate_client (); - void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0); - conn->revert_to_self (); + /* By the time this condition is reached (given the default value of + * SHMMNI), the linear searches should all replaced by something + * just a *little* cleverer . . . + */ + if (intid >= SHMMNI) + return NULL; + + segment_t *const segptr = safe_new (segment_t, key, intid, hFileMap); - if (!mapptr) + assert (segptr); + + if (previous) { - CloseHandle (filemap); - //FIXME: close filemap and free the mutex - /* we couldn't access the mapped area with the requested permissions */ - header.error_code = EACCES; - CloseHandle (token_handle); - return; + segptr->_next = previous->_next; + previous->_next = segptr; + } + else + { + segptr->_next = _segments_head; + _segments_head = segptr; } - conn->impersonate_client (); - /* Now get the user data */ - HANDLE attachmap = CreateFileMapping (INVALID_HANDLE_VALUE, // system pagefile - &sa, - PAGE_READWRITE, // protection (FIXME) - 0x00000000, - parameters.in.size + - parameters.in.size % - getsystemallocgranularity (), - shmaname // object name - ); - conn->revert_to_self (); + _shm_ids += 1; + _shm_tot += size; + if (intid > _intid_max) + _intid_max = intid; + + return segptr; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::delete_segment () + *---------------------------------------------------------------------------*/ + +void +server_shmmgr::delete_segment (segment_t *const segptr) +{ + assert (segptr); + assert (segptr->is_pending_delete ()); + + segment_t *previous = NULL; + + const segment_t *const tmp = find (segptr->_intid, &previous); + + assert (tmp == segptr); + assert (previous ? previous->_next == segptr : _segments_head == segptr); + + if (previous) + previous->_next = segptr->_next; + else + _segments_head = segptr->_next; + + assert (_shm_ids > 0); + _shm_ids -= 1; + _shm_tot -= segptr->_ds.shm_segsz; + + safe_delete (segptr); +} + +/*---------------------------------------------------------------------------* + * client_request_shm::client_request_shm () + *---------------------------------------------------------------------------*/ + +client_request_shm::client_request_shm () + : client_request (CYGSERVER_REQUEST_SHM, + &_parameters, sizeof (_parameters)) +{ + // verbose: syscall_printf ("created"); +} + +/*---------------------------------------------------------------------------* + * client_request_shm::serve () + *---------------------------------------------------------------------------*/ - if (attachmap == NULL) +void +client_request_shm::serve (transport_layer_base *const conn, + process_cache *const cache) +{ + assert (conn); + + assert (!error_code ()); + + if (msglen () != sizeof (_parameters.in)) { - system_printf ("failed to get shm attachmap\n"); - header.error_code = ENOMEM; - UnmapViewOfFile (mapptr); - CloseHandle (filemap); - /* FIXME exit the mutex */ - CloseHandle (token_handle); + syscall_printf ("bad request body length: expecting %lu bytes, got %lu", + sizeof (_parameters), msglen ()); + error_code (EINVAL); + msglen (0); return; } - shmid_ds *shmtemp = new shmid_ds; - if (!shmtemp) + // FIXME: Get a return code out of this and don't continue on error. + conn->impersonate_client (); + + class process *const client = cache->process (_parameters.in.cygpid, + _parameters.in.winpid); + + if (!client) { - system_printf ("failed to malloc shm node\n"); - header.error_code = ENOMEM; - UnmapViewOfFile (mapptr); - CloseHandle (filemap); - CloseHandle (attachmap); - /* FIXME exit mutex */ - CloseHandle (token_handle); + error_code (EAGAIN); + msglen (0); return; } - /* fill out the node data */ - shmtemp->shm_perm.cuid = getuid (); - shmtemp->shm_perm.uid = shmtemp->shm_perm.cuid; - shmtemp->shm_perm.cgid = getgid (); - shmtemp->shm_perm.gid = shmtemp->shm_perm.cgid; - shmtemp->shm_perm.mode = parameters.in.shmflg & 0x01ff; - shmtemp->shm_lpid = 0; - shmtemp->shm_nattch = 0; - shmtemp->shm_atime = 0; - shmtemp->shm_dtime = 0; - shmtemp->shm_ctime = time (NULL); - shmtemp->shm_segsz = parameters.in.size; - *(shmid_ds *) mapptr = *shmtemp; - shmtemp->mapptr = mapptr; - - /* no need for InterlockedExchange here, we're serialised by the global mutex */ - tempnode = new shmnode; - tempnode->shmds = shmtemp; - tempnode->shm_id = (int) InterlockedIncrement (&new_id); - tempnode->key = parameters.in.key; - tempnode->filemap = filemap; - tempnode->attachmap = attachmap; - tempnode->next = shm_head; - shm_head = tempnode; - - /* we now have the area in the daemon list, opened. - - FIXME: leave the system wide shm mutex */ - - parameters.out.shm_id = tempnode->shm_id; - if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, - token_handle, - DUPLICATE_SAME_ACCESS, - tempnode->filemap, ¶meters.out.filemap, - TRUE) != 0) + int result = -EINVAL; + + switch (_parameters.in.shmop) { - printf ("error duplicating filemap handle (%lu)\n", GetLastError ()); - header.error_code = EACCES; - CloseHandle (token_handle); -/* mutex et al */ - return; + case SHMOP_shmget: + result = shmmgr.shmget (_parameters.out.shmid, + _parameters.in.key, _parameters.in.size, + _parameters.in.shmflg, + _parameters.in.uid, _parameters.in.gid, + client); + break; + + case SHMOP_shmat: + result = shmmgr.shmat (_parameters.out.hFileMap, + _parameters.in.shmid, _parameters.in.shmflg, + client); + break; + + case SHMOP_shmdt: + result = shmmgr.shmdt (_parameters.in.shmid, client); + break; + + case SHMOP_shmctl: + result = shmmgr.shmctl (_parameters.out.shmid, + _parameters.out.ds, _parameters.out.shminfo, + _parameters.out.shm_info, + _parameters.in.shmid, _parameters.in.cmd, + _parameters.in.ds, + client); + break; } - if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, - token_handle, - DUPLICATE_SAME_ACCESS, - tempnode->attachmap, ¶meters.out.attachmap, - TRUE) != 0) + + client->release (); + conn->revert_to_self (); + + if (result < 0) { - printf ("error duplicating attachmap handle (%lu)\n", GetLastError ()); - header.error_code = EACCES; - CloseHandle (from_process_handle); - CloseHandle (token_handle); -/* more cleanup... yay! */ - return; + error_code (-result); + msglen (0); } - CloseHandle (token_handle); - return; + else + msglen (sizeof (_parameters.out)); } |