diff options
author | Conrad Scott <conrad.scott@dsl.pipex.com> | 2002-06-28 22:04:02 +0400 |
---|---|---|
committer | Conrad Scott <conrad.scott@dsl.pipex.com> | 2002-06-28 22:04:02 +0400 |
commit | eaedba9ac4665b81a284281cce2b63dab3f9941d (patch) | |
tree | cf70c3a96763287920b5e07eafb5ab12a70432c2 /winsup/cygwin | |
parent | 74a1317a08be0c435a58e61eda8a28a4b015d5f4 (diff) |
* cygserver_ipc.h: New file.
* cygserver_shm.h: Re-written from scratch.
* cygserver_shm.cc: Ditto.
* shm.cc: Ditto.
Diffstat (limited to 'winsup/cygwin')
-rw-r--r-- | winsup/cygwin/ChangeLog | 7 | ||||
-rw-r--r-- | winsup/cygwin/cygserver_ipc.h | 71 | ||||
-rwxr-xr-x | winsup/cygwin/cygserver_shm.cc | 1014 | ||||
-rw-r--r-- | winsup/cygwin/cygserver_shm.h | 138 | ||||
-rw-r--r-- | winsup/cygwin/shm.cc | 915 |
5 files changed, 1117 insertions, 1028 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 03f4dece3..88f24726a 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,5 +1,12 @@ 2002-06-28 Conrad Scott <conrad.scott@dsl.pipex.com> + * cygserver_ipc.h: New file. + * cygserver_shm.h: Re-written from scratch. + * cygserver_shm.cc: Ditto. + * shm.cc: Ditto. + +2002-06-28 Conrad Scott <conrad.scott@dsl.pipex.com> + * threaded_queue.h (class queue_request): Re-write. (threaded_queue_thread_function): Remove. (class queue_process_param): Remove. diff --git a/winsup/cygwin/cygserver_ipc.h b/winsup/cygwin/cygserver_ipc.h new file mode 100644 index 000000000..a69dd9c88 --- /dev/null +++ b/winsup/cygwin/cygserver_ipc.h @@ -0,0 +1,71 @@ +/* cygserver_ipc.h
+
+ Copyright 2002 Red Hat, Inc.
+
+ Originally written by Conrad Scott <conrad.scott@dsl.pipex.com>
+
+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. */
+
+#ifndef __CYGSERVER_IPC_H__
+#define __CYGSERVER_IPC_H__
+
+#include <limits.h> /* For OPEN_MAX. */
+
+/*
+ * The sysv ipc id's (msgid, semid, shmid) are small integers arranged
+ * such that they no subsystem will generate the same id as some other
+ * subsystem, and nor do these ids overlap file descriptors (the other
+ * common small integer ids). Since Cygwin can allocate more than
+ * OPEN_MAX file descriptor, it can't be guarenteed not to overlap,
+ * but it should help catch some errors.
+ *
+ * msgid's: OPEN_MAX, OPEN_MAX + 3, OPEN_MAX + 6, . . .
+ * semid's: OPEN_MAX + 1, OPEN_MAX + 4, OPEN_MAX + 7, . . .
+ * shmid's: OPEN_MAX + 2, OPEN_MAX + 5, OPEN_MAX + 8, . . .
+ *
+ * Internal ipc id's, which are 0, 1, ... within each subsystem, are
+ * used solely by the ipcs(8) interface.
+ */
+
+enum ipc_subsys_t {
+ IPC_MSGOP = 0,
+ IPC_SEMOP = 1,
+ IPC_SHMOP = 2,
+ IPC_SUBSYS_COUNT
+};
+
+inline int
+ipc_int2ext (const int id, const ipc_subsys_t subsys)
+{
+ return OPEN_MAX + (id * IPC_SUBSYS_COUNT) + subsys;
+}
+
+inline int
+ipc_ext2int (const int id)
+{
+ return (id - OPEN_MAX) / IPC_SUBSYS_COUNT;
+}
+
+inline int
+ipc_ext2int_subsys (const int id)
+{
+ return (id - OPEN_MAX) % IPC_SUBSYS_COUNT;
+}
+
+inline int
+ipc_inc_id (const int id, const ipc_subsys_t subsys)
+{
+ return id + IPC_SUBSYS_COUNT;
+}
+
+inline int
+ipc_dec_id (const int id, const ipc_subsys_t subsys)
+{
+ return id - IPC_SUBSYS_COUNT;
+}
+
+#endif /* __CYGSERVER_IPC_H__ */
diff --git a/winsup/cygwin/cygserver_shm.cc b/winsup/cygwin/cygserver_shm.cc index 0d30616b0..2df6522ed 100755 --- a/winsup/cygwin/cygserver_shm.cc +++ b/winsup/cygwin/cygserver_shm.cc @@ -1,8 +1,9 @@ -/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin +/* cygserver_shm.cc: Single unix specification IPC interface for Cygwin. - Copyright 2001, 2002 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. @@ -12,13 +13,13 @@ details. */ #include "woutsup.h" -#include <assert.h> #include <errno.h> +#include <pthread.h> #include <stdio.h> +#include <string.h> #include <time.h> -#include <unistd.h> -#include "cygerrno.h" +#include "cygserver_ipc.h" #include "cygserver_shm.h" #include "security.h" @@ -26,581 +27,550 @@ details. */ #include "cygwin/cygserver_process.h" #include "cygwin/cygserver_transport.h" -// 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 () +/*---------------------------------------------------------------------------* + * with_strerr () + *---------------------------------------------------------------------------*/ + +#define with_strerr(MSG, ACTION) \ + do \ + { \ + const DWORD lasterr = GetLastError (); \ + char *MSG = NULL; \ + if (!FormatMessage ((FORMAT_MESSAGE_ALLOCATE_BUFFER \ + | FORMAT_MESSAGE_FROM_SYSTEM \ + | FORMAT_MESSAGE_IGNORE_INSERTS), \ + NULL, \ + lasterr, \ + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), \ + reinterpret_cast<char *>(&MSG), \ + 0, \ + NULL)) \ + { \ + MSG = static_cast<char *> \ + (LocalAlloc (LMEM_FIXED, 24)); /* Big enough. */ \ + if (!MSG) \ + { \ + system_printf (("failure in LocalAlloc(LMEM_FIXED, 16): " \ + "error = %lu"), \ + GetLastError ()); \ + } \ + else \ + { \ + snprintf (MSG, 24, "error = %lu", lasterr); \ + } \ + } \ + SetLastError (lasterr); \ + { ACTION; } \ + if (MSG && !LocalFree (MSG)) \ + { \ + system_printf ("failed to free memory at %p, error = %lu", \ + MSG, GetLastError ()); \ + } \ + SetLastError (lasterr); \ + } while (false) + +/*---------------------------------------------------------------------------* + * class server_shmmgr + * + * A singleton class. + *---------------------------------------------------------------------------*/ + +#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 segment_t + { + public: + // Bits for the _flg field. + enum { REMOVED = 0x01 }; + + const key_t key; + const int shmid; + struct shmid_ds ds; + int flg; + const HANDLE hFileMap; + + segment_t *next; + + segment_t (const key_t key, const int shmid, const HANDLE hFileMap) + : key (key), shmid (shmid), flg (0), hFileMap (hFileMap), next (NULL) + { + bzero (&ds, sizeof (ds)); + } + }; -client_request_shm::client_request_shm () - : client_request (CYGSERVER_REQUEST_SHM, ¶meters, sizeof (parameters)) +public: + static server_shmmgr & instance (); + + int shmat (int shmid, int shmflg, + pid_t, HANDLE & hFileMap, + process_cache *, DWORD winpid); + int shmctl (int shmid, int cmd, struct shmid_ds &, pid_t); + int shmdt (int shmid, pid_t); + int shmget (key_t, size_t, int shmflg, pid_t, uid_t, gid_t); + +private: + CRITICAL_SECTION _segments_lock; + segment_t *_segments_head; // A list sorted by shmid. + + int _shmid_cnt; // Number of shmid's allocated (for ipcs(8)). + int _shmid_max; // Highest shmid 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 shmid, 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, HANDLE); + void delete_segment (segment_t *); +}; + +/*---------------------------------------------------------------------------* + * server_shmmgr::instance () + *---------------------------------------------------------------------------*/ + +server_shmmgr & +server_shmmgr::instance () { - syscall_printf ("created"); + static server_shmmgr instance; + + return instance; } -/* 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; -//FIXME: ditto. -static shmnode * - deleted_head = NULL; -/* must be long for InterlockedIncrement */ -static long - new_id = - 0; -static long - new_private_key = - 0; - -static void -delete_shmnode (shmnode **nodeptr) +/*---------------------------------------------------------------------------* + * server_shmmgr::shmat () + *---------------------------------------------------------------------------*/ + +int +server_shmmgr::shmat (const int shmid, const int shmflg, + const pid_t pid, HANDLE & hFileMap, + process_cache *const cache, const DWORD winpid) { - shmnode *node = *nodeptr; + process *const client = cache->process (winpid); + + if (!client) + { + return -EAGAIN; + } + + int result; + EnterCriticalSection (&_segments_lock); - // remove from the list - if (node == shm_head) - shm_head = shm_head->next; + segment_t *const segptr = find (shmid); + + if (!segptr) + result = -EINVAL; + else if (!DuplicateHandle (GetCurrentProcess (), + segptr->hFileMap, + client->handle (), + &hFileMap, + 0, + FALSE, // bInheritHandle + DUPLICATE_SAME_ACCESS)) + { + with_strerr (msg, + syscall_printf (("failed to duplicate handle for client " + "[key = %lld, shmid = %d, handle = %p]:" + "%s"), + segptr->key, segptr->shmid, + segptr->hFileMap, msg)); + + result = -EACCES; // FIXME + } else { - shmnode *tempnode = shm_head; - while (tempnode && tempnode->next != node) - tempnode = tempnode->next; - if (tempnode) - tempnode->next = node->next; - // else log the unexpected ! + segptr->ds.shm_lpid = pid; + segptr->ds.shm_nattch += 1; + segptr->ds.shm_atime = time (NULL); // FIXME: sub-second times. + + result = 0; } - // release the shared data view - UnmapViewOfFile (node->shmds->mapptr); - delete node->shmds; - CloseHandle (node->filemap); - CloseHandle (node->attachmap); + LeaveCriticalSection (&_segments_lock); - // free the memory - delete node; - nodeptr = NULL; + client->release (); + return result; } -void -client_request_shm::serve (transport_layer_base * const conn, - process_cache * const cache) -{ - assert (conn); - assert (cache); +/*---------------------------------------------------------------------------* + * server_shmmgr::shmctl () + *---------------------------------------------------------------------------*/ - assert (!error_code ()); +int +server_shmmgr::shmctl (const int shmid, const int cmd, struct shmid_ds & ds, + const pid_t pid) +{ + int result; + EnterCriticalSection (&_segments_lock); - // The minimum possible request length. SHM_CREATE requests should - // be longer than this. - const size_t min_req_msglen = - (sizeof (parameters.in) - sizeof (parameters.in.sd_buf)); + segment_t *const segptr = find (shmid); - if (msglen () < min_req_msglen) + if (!segptr) + result = -EINVAL; + else { - syscall_printf (("bad request body length: " - "expecting at least %lu bytes, got %lu"), - min_req_msglen, msglen ()); - error_code (EINVAL); - msglen (0); - return; + switch (cmd) + { + case IPC_STAT: + ds = segptr->ds; + result = 0; + 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 = pid; + segptr->ds.shm_ctime = time (NULL); // FIXME: sub-second times. + result = 0; + break; + + case IPC_RMID: + if (segptr->flg & segment_t::REMOVED) + result = -EIDRM; + else + { + segptr->flg |= segment_t::REMOVED; + if (!segptr->ds.shm_nattch) + delete_segment (segptr); + result = 0; + } + break; + + case IPC_INFO: + result = -EINVAL; + break; + + case SHM_STAT: + result = -EINVAL; + break; + + default: + result = -EINVAL; + break; + } } - // FIXME: check length of sd_buf contents for SHM_CREATE? + LeaveCriticalSection (&_segments_lock); + return result; +} - msglen (sizeof (parameters.out)); +/*---------------------------------------------------------------------------* + * server_shmmgr::shmdt () + *---------------------------------------------------------------------------*/ - HANDLE from_process_handle = NULL; - HANDLE token_handle = NULL; - DWORD rc; +int +server_shmmgr::shmdt (const int shmid, const pid_t pid) +{ + int result; + EnterCriticalSection (&_segments_lock); - class process *const process = cache->process (parameters.in.winpid); - assert (process); - from_process_handle = process->handle (); - process->release (); + segment_t *const segptr = find (shmid); - /* 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) + if (!segptr) + result = -EINVAL; + else { - system_printf ("error opening process (%lu)", GetLastError ()); - error_code (EACCES); - return; + assert (segptr->ds.shm_nattch > 0); + + segptr->ds.shm_lpid = pid; + segptr->ds.shm_nattch -= 1; + segptr->ds.shm_dtime = time (NULL); // FIXME: sub-second times. + + if (!segptr->ds.shm_nattch && (segptr->flg & segment_t::REMOVED)) + delete_segment (segptr); + + result = 0; } - if (wincap.has_security ()) - { - conn->impersonate_client (); + LeaveCriticalSection (&_segments_lock); + return result; +} - rc = OpenThreadToken (GetCurrentThread (), - TOKEN_QUERY, TRUE, &token_handle); +/*---------------------------------------------------------------------------* + * server_shmmgr::shmget () + *---------------------------------------------------------------------------*/ - conn->revert_to_self (); +int +server_shmmgr::shmget (const key_t key, const size_t size, const int shmflg, + const pid_t pid, const uid_t uid, const gid_t gid) +{ + int result; + EnterCriticalSection (&_segments_lock); - if (!rc) - { - system_printf ("error opening thread token (%lu)", GetLastError ()); - error_code (EACCES); - CloseHandle (from_process_handle); - return; - } + /* Does a segment already exist with that key? */ + if (key == IPC_PRIVATE) + result = new_segment (key, size, shmflg, pid, uid, gid); + else + { + segment_t *const segptr = find_by_key (key); + + if (!segptr) + if (shmflg & IPC_CREAT) + result = new_segment (key, size, shmflg, pid, uid, gid); + else + result = -ENOENT; + else if (segptr->flg & segment_t::REMOVED) + result = -EIDRM; + else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) + result = -EEXIST; + else if (size && segptr->ds.shm_segsz < size) + result = -EINVAL; + else + result = segptr->shmid; } - char *shmname = NULL, *shmaname = NULL; - char stringbuf[29], stringbuf1[29]; + LeaveCriticalSection (&_segments_lock); + return result; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::server_shmmgr () + *---------------------------------------------------------------------------*/ + +server_shmmgr::server_shmmgr () + : _segments_head (NULL) +{ + InitializeCriticalSection (&_segments_lock); +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::~server_shmmgr () + *---------------------------------------------------------------------------*/ - /* TODO: make this code block a function! */ - if (parameters.in.type == SHM_REATTACH) +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->key == key) + return segptr; + + return NULL; +} + +/*---------------------------------------------------------------------------* + * server_shmmgr::find () + *---------------------------------------------------------------------------*/ + +server_shmmgr::segment_t * +server_shmmgr::find (const int shmid, segment_t **previous) +{ + if (previous) + *previous = NULL; + + for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next) + if (segptr->shmid == shmid) + return segptr; + else if (segptr->shmid > shmid) // The list is sorted by shmid. + 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 pid, + const uid_t uid, + const gid_t gid) +{ + const HANDLE hFileMap = CreateFileMapping (INVALID_HANDLE_VALUE, + NULL, PAGE_READWRITE, + 0, size, + NULL); + + if (!hFileMap) { - /* just find and fill out the existing shm_id */ - shmnode *tempnode = shm_head; - while (tempnode) - { - if (tempnode->shm_id == parameters.in.shm_id) - { - 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) - { - system_printf ("error duplicating filemap handle (%lu)", - GetLastError ()); - error_code (EACCES); - } - if (check_and_dup_handle - (GetCurrentProcess (), from_process_handle, token_handle, - DUPLICATE_SAME_ACCESS, tempnode->attachmap, - ¶meters.out.attachmap, TRUE) != 0) - { - system_printf ("error duplicating attachmap handle (%lu)", - GetLastError ()); - error_code (EACCES); - } - CloseHandle (token_handle); - return; - } - tempnode = tempnode->next; - } - error_code (EINVAL); - CloseHandle (token_handle); - return; + with_strerr (msg, + syscall_printf (("failed to create file mapping " + "[size = %lu]: %s"), + size, msg)); + + return -EINVAL; // FIXME } - /* 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) + segment_t *const segptr = new_segment (key, hFileMap); + + 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 = pid; + 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 shmid and insert into the segment map. + *---------------------------------------------------------------------------*/ + +server_shmmgr::segment_t * +server_shmmgr::new_segment (const key_t key, const HANDLE hFileMap) +{ + int shmid = ipc_int2ext (0, IPC_SHMOP); // Next expected shmid value. + segment_t *previous = NULL; // Insert pointer. + + // Find first unallocated shmid. + for ( segment_t *segptr = _segments_head; + segptr && segptr->shmid == shmid; + segptr = segptr->next, shmid = ipc_inc_id (shmid, IPC_SHMOP)) { - shmnode *tempnode = shm_head; - while (tempnode) - { - if (tempnode->shm_id == parameters.in.shm_id) - { - InterlockedIncrement (&tempnode->shmds->ds.shm_nattch); - CloseHandle (token_handle); - return; - } - tempnode = tempnode->next; - } - error_code (EINVAL); - CloseHandle (token_handle); - return; + previous = segptr; } - /* Someone detached */ - if (parameters.in.type == SHM_DETACH) + segment_t *const segptr = new segment_t (key, shmid, hFileMap); + + assert (segptr); + + if (previous) { - shmnode *tempnode = shm_head; - while (tempnode) - { - if (tempnode->shm_id == parameters.in.shm_id) - { - InterlockedDecrement (&tempnode->shmds->ds.shm_nattch); - CloseHandle (token_handle); - return; - } - tempnode = tempnode->next; - } - error_code (EINVAL); - CloseHandle (token_handle); - return; + segptr->next = previous->next; + previous->next = segptr; } - - /* Someone wants the ID removed. */ - if (parameters.in.type == SHM_DEL) + else { - shmnode **tempnode = &shm_head; - while (*tempnode) - { - if ((*tempnode)->shm_id == parameters.in.shm_id) - { - // unlink from the accessible node list - shmnode *temp2 = *tempnode; - *tempnode = temp2->next; - // link into the deleted list - temp2->next = deleted_head; - deleted_head = temp2; - - // FIXME: when/where do we delete the handles? - if (temp2->shmds->ds.shm_nattch) - { - // FIXME: add to a pending queue? - } - else - { - delete_shmnode (&temp2); - } - - CloseHandle (token_handle); - return; - } - tempnode = &(*tempnode)->next; - } - error_code (EINVAL); - CloseHandle (token_handle); - return; + segptr->next = _segments_head; + _segments_head = segptr; } + _shmid_cnt += 1; + if (shmid > _shmid_max) + _shmid_max = shmid; - if (parameters.in.type == SHM_CREATE) - { - /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide - * to prevent races on shmget. - */ + return segptr; +} - if (parameters.in.key == IPC_PRIVATE) - { - /* 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; - } - else - { - /* 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: key = %s", shmname); - debug_printf ("system id strings: data = %s", shmaname); - debug_printf ("key input value is 0x%0qx", parameters.in.key); - } +/*---------------------------------------------------------------------------* + * server_shmmgr::delete_segment () + *---------------------------------------------------------------------------*/ - /* attempt to open the key */ +void +server_shmmgr::delete_segment (segment_t *const segptr) +{ + assert (segptr); + assert (!segptr->ds.shm_nattch); + assert (segptr->flg & segment_t::REMOVED); - /* 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 ?. - */ + segment_t *previous = NULL; - /* walk the list of known keys and return the id if found. remember, we are - * authoritative... - */ + const segment_t *const tmp = find (segptr->shmid, &previous); - 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->ds.shm_segsz < parameters.in.size) - { - 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)) - { - error_code (EEXIST); - debug_printf - ("attempt to exclusively create already created shm_area with key 0x%0qx", - 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) - { - system_printf ("error duplicating filemap handle (%lu)", - GetLastError ()); - 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) - { - system_printf ("error duplicating attachmap handle (%lu)", - GetLastError ()); - error_code (EACCES); - /*mutex*/ - CloseHandle (token_handle); - return; - } - - CloseHandle (token_handle); - return; - } - tempnode = tempnode->next; - } - /* couldn't find a currently open shm area. */ - - /* create one */ - - /* we trust the clients request - we will be doing it as them, and - * the worst they can do is open their own permissions - */ - - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof (sa); - sa.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf; - sa.bInheritHandle = TRUE; /* the memory structures inherit ok */ - - /* 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 - ); - DWORD lasterr = GetLastError (); - conn->revert_to_self (); - - if (filemap == NULL) - { - /* We failed to open the filemapping ? */ - system_printf ("failed to open file mapping: %lu", - GetLastError ()); - // free the mutex - // we can assume that it exists, and that it was an access problem. - error_code (EACCES); - CloseHandle (token_handle); - return; - } + assert (tmp == segptr); + assert (previous ? previous->next == segptr : _segments_head == segptr); - /* successfully opened the control region mapping */ - /* did we create it ? */ - int oldmapping = lasterr == ERROR_ALREADY_EXISTS; - if (oldmapping) - { - /* 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); - error_code (EEXIST); - CloseHandle (token_handle); - return; - } - } + if (previous) + previous->next = segptr->next; + else + _segments_head = segptr->next; - /* we created a new mapping */ - if (parameters.in.key != IPC_PRIVATE && - (parameters.in.shmflg & IPC_CREAT) == 0) - { - CloseHandle (filemap); - /* FIXME free mutex */ - error_code (ENOENT); - CloseHandle (token_handle); - return; - } + if (!CloseHandle (segptr->hFileMap)) + with_strerr (msg, + syscall_printf (("failed to close file map " + "[handle = %p]: %s"), + segptr->hFileMap, msg)); - conn->impersonate_client (); - void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0); - conn->revert_to_self (); + assert (_shmid_cnt > 0); + _shmid_cnt -= 1; - if (!mapptr) - { - CloseHandle (filemap); - //FIXME: close filemap and free the mutex - /* we couldn't access the mapped area with the requested permissions */ - error_code (EACCES); - CloseHandle (token_handle); - return; - } + delete 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 (); - - if (attachmap == NULL) - { - system_printf ("failed to get shm attachmap"); - error_code (ENOMEM); - UnmapViewOfFile (mapptr); - CloseHandle (filemap); - /* FIXME exit the mutex */ - CloseHandle (token_handle); - return; - } +/*---------------------------------------------------------------------------* + * client_request_shm::client_request_shm () + *---------------------------------------------------------------------------*/ - int_shmid_ds *shmtemp = new int_shmid_ds; - if (!shmtemp) - { - system_printf ("failed to malloc shm node"); - error_code (ENOMEM); - UnmapViewOfFile (mapptr); - CloseHandle (filemap); - CloseHandle (attachmap); - /* FIXME exit mutex */ - CloseHandle (token_handle); - return; - } +client_request_shm::client_request_shm () + : client_request (CYGSERVER_REQUEST_SHM, + &_parameters, sizeof (_parameters)) +{ + syscall_printf ("created"); +} - /* fill out the node data */ - shmtemp->ds.shm_perm.cuid = getuid (); - shmtemp->ds.shm_perm.uid = shmtemp->ds.shm_perm.cuid; - shmtemp->ds.shm_perm.cgid = getgid (); - shmtemp->ds.shm_perm.gid = shmtemp->ds.shm_perm.cgid; - shmtemp->ds.shm_perm.mode = parameters.in.shmflg & 0x01ff; - shmtemp->ds.shm_lpid = 0; - shmtemp->ds.shm_nattch = 0; - shmtemp->ds.shm_atime = 0; - shmtemp->ds.shm_dtime = 0; - shmtemp->ds.shm_ctime = time (NULL); - shmtemp->ds.shm_segsz = parameters.in.size; - *(shmid_ds *) mapptr = shmtemp->ds; - 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) - { - system_printf ("error duplicating filemap handle (%lu)", - GetLastError ()); - error_code (EACCES); - CloseHandle (token_handle); - /* mutex et al */ - return; - } - if (check_and_dup_handle (GetCurrentProcess (), from_process_handle, - token_handle, - DUPLICATE_SAME_ACCESS, - tempnode->attachmap, - ¶meters.out.attachmap, TRUE) != 0) - { - system_printf ("error duplicating attachmap handle (%lu)", - GetLastError ()); - error_code (EACCES); - CloseHandle (from_process_handle); - CloseHandle (token_handle); - /* more cleanup... yay! */ - return; - } - CloseHandle (token_handle); +/*---------------------------------------------------------------------------* + * client_request_shm::serve () + *---------------------------------------------------------------------------*/ + +void +client_request_shm::serve (transport_layer_base *const conn, + process_cache *const cache) +{ + assert (conn); + + assert (!error_code ()); + + if (msglen () != sizeof (_parameters)) + { + syscall_printf ("bad request body length: expecting %lu bytes, got %lu", + sizeof (_parameters), msglen ()); + error_code (EINVAL); + msglen (0); return; } - error_code (ENOSYS); - CloseHandle (token_handle); + // FIXME: Get a return code out of this and don't continue on error. + conn->impersonate_client (); + + int result = -EINVAL; + + switch (_parameters.in.shmop) + { + case SHMOP_shmget: + result = shmmgr.shmget (_parameters.in.key, _parameters.in.size, + _parameters.in.shmflg, _parameters.in.cygpid, + _parameters.in.uid, _parameters.in.gid); + _parameters.shmid = result; + break; + + case SHMOP_shmat: + result = shmmgr.shmat (_parameters.shmid, _parameters.in.shmflg, + _parameters.in.cygpid, _parameters.out.hFileMap, + cache, _parameters.in.winpid); + break; + + case SHMOP_shmdt: + result = shmmgr.shmdt (_parameters.shmid, _parameters.in.cygpid); + break; + + case SHMOP_shmctl: + result = shmmgr.shmctl (_parameters.shmid, _parameters.in.cmd, + _parameters.ds, _parameters.in.cygpid); + break; + } + + conn->revert_to_self (); - return; + if (result < 0) + { + error_code (-result); + msglen (0); + } } diff --git a/winsup/cygwin/cygserver_shm.h b/winsup/cygwin/cygserver_shm.h index 27a49d7c1..df147f10a 100644 --- a/winsup/cygwin/cygserver_shm.h +++ b/winsup/cygwin/cygserver_shm.h @@ -1,7 +1,9 @@ -/* cygserver_shm.h +/* cygserver_shm.h: Single unix specification IPC interface for Cygwin. - Copyright 2001, 2002 Red Hat Inc. - Written by Robert Collins <rbtcollins@hotmail.com> + Copyright 2002 Red Hat, Inc. + + 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. @@ -9,77 +11,101 @@ This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ +#ifndef __CYGSERVER_SHM_H__ +#define __CYGSERVER_SHM_H__ + #include <sys/types.h> +#include <assert.h> + #include "cygwin_shm.h" #include "cygwin/cygserver.h" -/* Values for the client_request_shm::parameters.in.type field. */ -#define SHM_CREATE 0 -#define SHM_REATTACH 1 -#define SHM_ATTACH 2 -#define SHM_DETACH 3 -#define SHM_DEL 4 - -struct _shmattach -{ - void *data; - int shmflg; - struct _shmattach *next; -}; - -struct int_shmid_ds -{ - struct shmid_ds ds; - void *mapptr; -}; +/*---------------------------------------------------------------------------* + * class client_request_shm + *---------------------------------------------------------------------------*/ -struct shmnode -{ - struct int_shmid_ds *shmds; - int shm_id; - struct shmnode *next; - key_t key; - HANDLE filemap; - HANDLE attachmap; - struct _shmattach *attachhead; -}; +#ifndef __INSIDE_CYGWIN__ +class transport_layer_base; +class process_cache; +#endif class client_request_shm : public client_request { + friend class client_request; + public: + enum shmop_t + { + SHMOP_shmat, + SHMOP_shmctl, + SHMOP_shmdt, + SHMOP_shmget + }; + #ifdef __INSIDE_CYGWIN__ - client_request_shm (key_t ntype, size_t nsize, int shmflg); - client_request_shm (int ntype, int nshmid); -#else - client_request_shm (); + client_request_shm (int shmid, int shmflg); // shmat + client_request_shm (int shmid, int cmd, const struct shmid_ds *); // shmctl + client_request_shm (int shmid); // shmdt + client_request_shm (key_t, size_t, int shmflg); // shmget #endif - union + // Accessors for out parameters. + + int shmid () const { - struct - { - int type; - pid_t cygpid; - DWORD winpid; - int shm_id; - key_t key; - size_t size; - int shmflg; - char sd_buf[4096]; // Must be the last item (variable length). - } in; - struct - { - int shm_id; - HANDLE filemap; - HANDLE attachmap; - key_t key; - } out; - } parameters; + assert (!error_code ()); + return _parameters.shmid; + } + + const struct shmid_ds & ds () const + { + assert (!error_code ()); + return _parameters.ds; + } + + HANDLE hFileMap () const + { + assert (!error_code ()); + return _parameters.out.hFileMap; + } private: + struct + { + int shmid; + struct shmid_ds ds; + + union + { + struct + { + shmop_t shmop; + key_t key; + size_t size; + int shmflg; + int cmd; + pid_t cygpid; + DWORD winpid; + uid_t uid; + gid_t gid; + } in; + + struct + { + HANDLE hFileMap; + } out; + }; + } _parameters; + +#ifndef __INSIDE_CYGWIN__ + client_request_shm (); +#endif + #ifndef __INSIDE_CYGWIN__ virtual void serve (transport_layer_base *, process_cache *); #endif }; + +#endif /* __CYGSERVER_SHM_H__ */ diff --git a/winsup/cygwin/shm.cc b/winsup/cygwin/shm.cc index 80a47318b..75fd04aa9 100644 --- a/winsup/cygwin/shm.cc +++ b/winsup/cygwin/shm.cc @@ -1,8 +1,9 @@ -/* shm.cc: Single unix specification IPC interface for Cygwin +/* shm.cc: Single unix specification IPC interface for Cygwin. - Copyright 2001, 2002 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. @@ -11,545 +12,559 @@ Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" -#include <sys/stat.h> -#include <assert.h> + +#include <sys/types.h> + #include <errno.h> -#include "cygerrno.h" -#include <limits.h> +#include <stdio.h> #include <unistd.h> -#include "security.h" -#include "fhandler.h" -#include "path.h" -#include "dtable.h" -#include "cygheap.h" -#include "thread.h" -#include "cygwin_shm.h" -#include "cygserver_shm.h" -/* - * FIXME: These must be defined somewhere? i.e. get the high and low - * double words of a quad word? They are required here to allow keys - * to be printed out via XXX_printf (i.e. small_printf()). Note that - * even if the key is only 32 bits (the old default), these routine - * should still give sensible results, i.e. hi_ulong() => 0. - */ +#include "cygerrno.h" -inline long int hi_ulong (const key_t key) -{ - return (unsigned long) - (((unsigned long long) key >> 32) & ULONG_MAX); -} +#include "cygserver_ipc.h" +#include "cygserver_shm.h" -inline long int lo_ulong (const key_t key) +/*---------------------------------------------------------------------------* + * with_strerr () + *---------------------------------------------------------------------------*/ + +#define with_strerr(MSG, ACTION) \ + do \ + { \ + const DWORD lasterr = GetLastError (); \ + char *MSG = NULL; \ + if (!FormatMessage ((FORMAT_MESSAGE_ALLOCATE_BUFFER \ + | FORMAT_MESSAGE_FROM_SYSTEM \ + | FORMAT_MESSAGE_IGNORE_INSERTS), \ + NULL, \ + lasterr, \ + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), \ + reinterpret_cast<char *>(&MSG), \ + 0, \ + NULL)) \ + { \ + MSG = static_cast<char *> \ + (LocalAlloc (LMEM_FIXED, 24)); /* Big enough. */ \ + if (!MSG) \ + { \ + system_printf (("failure in LocalAlloc(LMEM_FIXED, 16): " \ + "error = %lu"), \ + GetLastError ()); \ + } \ + else \ + { \ + snprintf (MSG, 24, "error = %lu", lasterr); \ + } \ + } \ + SetLastError (lasterr); \ + { ACTION; } \ + if (MSG && !LocalFree (MSG)) \ + { \ + system_printf ("failed to free memory at %p, error = %lu", \ + MSG, GetLastError ()); \ + } \ + SetLastError (lasterr); \ + } while (false) + +/*---------------------------------------------------------------------------* + * class client_shmmgr + * + * A singleton class. + *---------------------------------------------------------------------------*/ + +#define shmmgr (client_shmmgr::instance ()) + +class client_shmmgr { - return (unsigned long) - ((unsigned long long) key & ULONG_MAX); -} +private: + class segment_t + { + public: + const int shmid; + const void *const shmaddr; + const int shmflg; + HANDLE hFileMap; // Updated by fixup_shms_after_fork (). -// 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. - */ -extern "C" size_t -getsystemallocgranularity () -{ - SYSTEM_INFO sysinfo; - static size_t buffer_offset = 0; - if (buffer_offset) - return buffer_offset; - GetSystemInfo (&sysinfo); - buffer_offset = sysinfo.dwAllocationGranularity; - return buffer_offset; -} + segment_t *next; -/* - * Used for: SHM_ATTACH, SHM_DETACH, SHM_REATTACH, and SHM_DEL. - */ -client_request_shm::client_request_shm (int ntype, int nshmid) - : client_request (CYGSERVER_REQUEST_SHM, ¶meters, sizeof (parameters)) -{ - assert (ntype == SHM_REATTACH \ - || ntype == SHM_ATTACH \ - || ntype == SHM_DETACH \ - || ntype == SHM_DEL); + segment_t (const int shmid, const void *const shmaddr, const int shmflg, + const HANDLE hFileMap) + : shmid (shmid), shmaddr (shmaddr), shmflg (shmflg), hFileMap (hFileMap), + next (NULL) + {} + }; - parameters.in.type = ntype; - parameters.in.cygpid = getpid (); - parameters.in.winpid = GetCurrentProcessId (); - parameters.in.shm_id = nshmid; +public: + static client_shmmgr & instance (); - assert (parameters.in.cygpid > 0); - assert (parameters.in.winpid != 0); + void *shmat (int shmid, const void *, int shmflg); + int shmctl (int shmid, int cmd, struct shmid_ds *); + int shmdt (const void *); + int shmget (key_t, size_t, int shmflg); - msglen (sizeof (parameters.in) - sizeof (parameters.in.sd_buf)); + int fixup_shms_after_fork (); - syscall_printf ("created: type = %d, shmid = %d", - parameters.in.type, parameters.in.shm_id); -} +private: + CRITICAL_SECTION _segments_lock; + static segment_t *_segments_head; // A list sorted by shmaddr. -/* - * Used for: SHM_CREATE. - */ -client_request_shm::client_request_shm (key_t nkey, size_t nsize, int nshmflg) - : client_request (CYGSERVER_REQUEST_SHM, ¶meters, sizeof (parameters)) -{ - assert (nkey != (key_t) -1); - assert (nsize >= 0); + client_shmmgr (); + ~client_shmmgr (); - parameters.in.type = SHM_CREATE; - parameters.in.cygpid = getpid (); - parameters.in.winpid = GetCurrentProcessId (); - parameters.in.key = nkey; - parameters.in.size = nsize; - parameters.in.shmflg = nshmflg; + // Undefined (as this class is a singleton): + client_shmmgr (const client_shmmgr &); + client_shmmgr & operator= (const client_shmmgr &); - assert (parameters.in.cygpid > 0); - assert (parameters.in.winpid != 0); + segment_t *find (const void *, segment_t **previous = NULL); - DWORD sd_buf_size = 0; + void *attach (int shmid, const void *, int shmflg, HANDLE & hFileMap); - if (wincap.has_security ()) - { - SECURITY_ATTRIBUTES sa = sec_none; - sd_buf_size = sizeof (parameters.in.sd_buf); + segment_t *new_segment (int shmid, const void *, int shmflg, HANDLE); +}; - PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) parameters.in.sd_buf; +client_shmmgr::segment_t *client_shmmgr::_segments_head; - InitializeSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION); +/*---------------------------------------------------------------------------* + * client_shmmgr::instance () + *---------------------------------------------------------------------------*/ - alloc_sd (geteuid32 (), getegid32 (), parameters.in.shmflg & 0777, - psd, &sd_buf_size); - } +client_shmmgr & +client_shmmgr::instance () +{ + static NO_COPY client_shmmgr instance; + + return instance; +} - msglen (sizeof (parameters.in) - sizeof (parameters.in.sd_buf) - + sd_buf_size); +/*---------------------------------------------------------------------------* + * client_shmmgr::shmat () + *---------------------------------------------------------------------------*/ - syscall_printf (("created: type = %d, " - "key = 0x%x%08x, size = %ld, shmflg = %o"), - parameters.in.type, - hi_ulong(parameters.in.key), lo_ulong(parameters.in.key), - parameters.in.size, parameters.in.shmflg); +void * +client_shmmgr::shmat (const int shmid, + const void *const shmaddr, + const int shmflg) +{ + HANDLE hFileMap = NULL; + + void *const ptr = attach (shmid, shmaddr, shmflg, hFileMap); + + if (ptr) + new_segment (shmid, ptr, shmflg, hFileMap); + + return (ptr ? ptr : (void *) -1); } -static shmnode *shm_head = NULL; +/*---------------------------------------------------------------------------* + * client_shmmgr::shmctl () + *---------------------------------------------------------------------------*/ -static shmnode * -build_inprocess_shmds (HANDLE hfilemap, HANDLE hattachmap, key_t key, - int shm_id) +int +client_shmmgr::shmctl (const int shmid, + const int cmd, + struct shmid_ds *const buf) { - HANDLE filemap = hfilemap; - void *mapptr = MapViewOfFile (filemap, FILE_MAP_WRITE, 0, 0, 0); + client_request_shm request (shmid, cmd, buf); - if (!mapptr) + if (request.make_request () == -1 || request.error_code ()) { - CloseHandle (hfilemap); - CloseHandle (hattachmap); - //FIXME: close filemap and free the mutex - /* we couldn't access the mapped area with the requested permissions */ - set_errno (EACCES); - return NULL; + set_errno (request.error_code ()); + return -1; } - /* Now get the user data */ - HANDLE attachmap = hattachmap; - int_shmid_ds *shmtemp = new int_shmid_ds; - if (!shmtemp) + // Some commands require special processing, e.g. for out parameters. + + switch (cmd) { - system_printf ("failed to malloc shm node"); - set_errno (ENOMEM); - UnmapViewOfFile (mapptr); - CloseHandle (filemap); - CloseHandle (attachmap); - /* exit mutex */ - return NULL; + case IPC_STAT: + *buf = request.ds (); + break; + } + + return 0; +} + +/*---------------------------------------------------------------------------* + * client_shmmgr::shmdt () + * + * According to Posix, the only error condition for this system call + * is EINVAL if shmaddr is not the address of the start of an attached + * shared memory segment. Given that, all other errors just generate + * tracing noise. + *---------------------------------------------------------------------------*/ + +int +client_shmmgr::shmdt (const void *const shmaddr) +{ + segment_t *previous = NULL; + + segment_t *const segptr = find (shmaddr, &previous); + + if (!segptr) + { + set_errno (EINVAL); + return -1; } - /* get the system node data */ - shmtemp->ds = *(shmid_ds *) mapptr; + assert (previous ? previous->next == segptr : _segments_head == segptr); + + if (previous) + previous->next = segptr->next; + else + _segments_head = segptr->next; + + if (!UnmapViewOfFile ((void *) shmaddr)) + with_strerr (msg, + syscall_printf (("failed to unmap view " + "[shmid = %d, handle = %p, shmaddr = %p]:" + "%s"), + segptr->shmid, segptr->hFileMap, shmaddr, + msg)); + + assert (segptr->hFileMap); - /* process local data */ - shmnode *tempnode = new shmnode; + if (!CloseHandle (segptr->hFileMap)) + with_strerr (msg, + syscall_printf (("failed to close file map handle " + "[shmid = %d, handle = %p]:" + "%s"), + segptr->shmid, segptr->hFileMap, + msg)); - tempnode->filemap = filemap; - tempnode->attachmap = attachmap; - shmtemp->mapptr = mapptr; + client_request_shm request (segptr->shmid); - /* no need for InterlockedExchange here, we're serialised by the global mutex */ - tempnode->shmds = shmtemp; - tempnode->shm_id = shm_id; - tempnode->key = key; - tempnode->next = shm_head; - tempnode->attachhead = NULL; - shm_head = tempnode; + if (request.make_request () == -1 || request.error_code ()) + syscall_printf ("shmdt request failed [shmid = %d, handle = %p]: %s", + segptr->shmid, segptr->hFileMap, + strerror (request.error_code ())); - /* FIXME: leave the system wide shm mutex */ + delete segptr; - return tempnode; + return 0; } -static void -delete_inprocess_shmds (shmnode **nodeptr) +/*---------------------------------------------------------------------------* + * client_shmmgr::shmget () + *---------------------------------------------------------------------------*/ + +int +client_shmmgr::shmget (const key_t key, const size_t size, const int shmflg) { - shmnode *node = *nodeptr; + client_request_shm request (key, size, shmflg); - // remove from the list - if (node == shm_head) - shm_head = shm_head->next; - else + if (request.make_request () == -1 || request.error_code ()) { - shmnode *tempnode = shm_head; - while (tempnode && tempnode->next != node) - tempnode = tempnode->next; - if (tempnode) - tempnode->next = node->next; - // else log the unexpected ! + set_errno (request.error_code ()); + return -1; } - // release the shared data view - UnmapViewOfFile (node->shmds); - CloseHandle (node->filemap); - CloseHandle (node->attachmap); - - // free the memory - delete node; - nodeptr = NULL; + return request.shmid (); } -int __stdcall -fixup_shms_after_fork () +/*---------------------------------------------------------------------------* + * client_shmmgr::fixup_shms_after_fork () + * + * The hFileMap handles are non-inheritable: so the + *---------------------------------------------------------------------------*/ + +int +client_shmmgr::fixup_shms_after_fork () { - shmnode *tempnode = shm_head; - while (tempnode) - { - void *newshmds = - MapViewOfFile (tempnode->filemap, FILE_MAP_WRITE, 0, 0, 0); - if (!newshmds) - { - /* don't worry about handle cleanup, we're dying! */ - system_printf ("failed to reattach to shm control file view %x", - tempnode); - return 1; - } - tempnode->shmds->ds = *(shmid_ds *) newshmds; - tempnode->shmds->mapptr = newshmds; - _shmattach *attachnode = tempnode->attachhead; - while (attachnode) - { - void *newdata = MapViewOfFileEx (tempnode->attachmap, - (attachnode->shmflg & SHM_RDONLY) ? - FILE_MAP_READ : FILE_MAP_WRITE, 0, - 0, 0, attachnode->data); - if (newdata != attachnode->data) - { - /* don't worry about handle cleanup, we're dying! */ - system_printf ("failed to reattach to mapped file view %x", - attachnode->data); - return 1; - } - attachnode = attachnode->next; - } - tempnode = tempnode->next; - } + for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next) + if (!attach (segptr->shmid, + segptr->shmaddr, + segptr->shmflg & ~SHM_RND, + segptr->hFileMap)) + { + system_printf ("fatal error re-attaching to shared memory segments"); + return 1; + } + return 0; } -/* this is ugly. Yes, I know that. - * FIXME: abstract the lookup functionality, - * So that it can be an array, list, whatever without us being worried - */ +/*---------------------------------------------------------------------------* + * client_shmmgr::client_shmmgr () + *---------------------------------------------------------------------------*/ -/* FIXME: after fork, every memory area needs to have the attach count - * incremented. This should be done in the server? - */ +client_shmmgr::client_shmmgr () +{ + InitializeCriticalSection (&_segments_lock); +} -/* FIXME: tell the daemon when we attach, so at process close it can clean up - * the attach count - */ -extern "C" void * -shmat (int shmid, const void *shmaddr, int shmflg) +/*---------------------------------------------------------------------------* + * client_shmmgr::~client_shmmgr () + *---------------------------------------------------------------------------*/ + +client_shmmgr::~client_shmmgr () { - shmnode *tempnode = shm_head; - while (tempnode && tempnode->shm_id != shmid) - tempnode = tempnode->next; + DeleteCriticalSection (&_segments_lock); +} - if (!tempnode) - { - /* couldn't find a currently open shm control area for the key - probably because - * shmget hasn't been called. - * Allocate a new control block - this has to be handled by the daemon */ - client_request_shm req (SHM_REATTACH, shmid); - - if (req.make_request () == -1) - { - set_errno (ENOSYS); /* daemon communication failed */ - return (void *) -1; - } - - if (req.error_code ()) /* shm_get failed in the daemon */ - { - set_errno (req.error_code ()); - return (void *) -1; - } - - /* we've got the id, now we open the memory area ourselves. - * This tests security automagically - * FIXME: make this a method of shmnode ? - */ - tempnode = - build_inprocess_shmds (req.parameters.out.filemap, - req.parameters.out.attachmap, - req.parameters.out.key, - req.parameters.out.shm_id); - if (!tempnode) - return (void *) -1; +/*---------------------------------------------------------------------------* + * client_shmmgr::find () + *---------------------------------------------------------------------------*/ - } +client_shmmgr::segment_t * +client_shmmgr::find (const void *const shmaddr, segment_t **previous) +{ + if (previous) + *previous = NULL; - // class shmid_ds *shm = tempnode->shmds; + for (segment_t *segptr = _segments_head; segptr; segptr = segptr->next) + if (segptr->shmaddr == shmaddr) + return segptr; + else if (segptr->shmaddr > shmaddr) // The list is sorted by shmaddr. + return NULL; + else if (previous) + *previous = segptr; - if (shmaddr) + return NULL; +} + +/*---------------------------------------------------------------------------* + * client_shmmgr::attach () + * + * The body of shmat (), also used by fixup_shms_after_fork (). + *---------------------------------------------------------------------------*/ + +void * +client_shmmgr::attach (const int shmid, + const void *shmaddr, + const int shmflg, + HANDLE & hFileMap) +{ + client_request_shm request (shmid, shmflg); + + if (request.make_request () == -1 || request.error_code ()) { - //FIXME: requested base address ?! (Don't forget to fix the fixup_after_fork too) - set_errno (EINVAL); - return (void *) -1; + set_errno (request.error_code ()); + return NULL; } - void *rv = MapViewOfFile (tempnode->attachmap, - (shmflg & SHM_RDONLY) ? FILE_MAP_READ : - FILE_MAP_WRITE, 0, 0, 0); + int result = 0; + + const DWORD access = (shmflg & SHM_RDONLY) ? FILE_MAP_READ : FILE_MAP_WRITE; + + if (shmaddr && (shmflg & SHM_RND)) + shmaddr = (char *) shmaddr - ((ssize_t) shmaddr % SHMLBA); + + void *const ptr = + MapViewOfFileEx (request.hFileMap (), access, 0, 0, 0, (void *) shmaddr); - if (!rv) + if (!ptr) { - //FIXME: translate GetLastError() - set_errno (EACCES); - return (void *) -1; + with_strerr (msg, + syscall_printf (("failed to map view " + "[shmid = %d, handle = %p, shmaddr = %p]:" + "%s"), + shmid, request.hFileMap (), shmaddr, + msg)); + result = EINVAL; // FIXME } - /* tell the daemon we have attached */ - client_request_shm req (SHM_ATTACH, shmid); - - if (req.make_request () == -1) + else if (shmaddr && ptr != shmaddr) { - debug_printf ("failed to tell daemon that we have attached"); + syscall_printf (("failed to map view at requested address " + "[shmid = %d, handle = %p]: " + "requested address = %p, mapped address = %p"), + shmid, request.hFileMap (), + shmaddr, ptr); + result = EINVAL; // FIXME } - _shmattach *attachnode = new _shmattach; - attachnode->data = rv; - attachnode->shmflg = shmflg; - attachnode->next = - (_shmattach *) InterlockedExchangePointer (&tempnode->attachhead, - attachnode); - + if (result != 0) + { + if (!CloseHandle (request.hFileMap ())) + with_strerr (msg, + syscall_printf (("failed to close file map handle " + "[shmid = %d, handle = %p]:" + "%s"), + shmid, request.hFileMap (), + msg)); + + client_request_shm dt_req (shmid); + + if (dt_req.make_request () == -1 || dt_req.error_code ()) + syscall_printf ("shmdt request failed [shmid = %d, handle = %p]: %s", + shmid, request.hFileMap (), + strerror (dt_req.error_code ())); + + set_errno (result); + return NULL; + } - return rv; + hFileMap = request.hFileMap (); + return ptr; } -/* FIXME: tell the daemon when we detach so it doesn't cleanup incorrectly. - */ -extern "C" int -shmdt (const void *shmaddr) +/*---------------------------------------------------------------------------* + * client_shmmgr::new_segment () + * + * Allocate a new segment for the given shmid, file map and address + * and insert into the segment map. + *---------------------------------------------------------------------------*/ + +client_shmmgr::segment_t * +client_shmmgr::new_segment (const int shmid, + const void *const shmaddr, + const int shmflg, + const HANDLE hFileMap) { - /* this should be "rare" so a hefty search is ok. If this is common, then we - * should alter the data structs to allow more optimisation - */ - shmnode *tempnode = shm_head; - _shmattach *attachnode; - while (tempnode) + assert (ipc_ext2int_subsys (shmid) == IPC_SHMOP); + assert (hFileMap); + assert (shmaddr); + + segment_t *previous = NULL; // Insert pointer. + + const segment_t *const tmp = find (shmaddr, &previous); + + assert (!tmp); + assert (previous \ + ? (!previous->next || previous->next->shmaddr > shmaddr) \ + : (!_segments_head || _segments_head->shmaddr > shmaddr)); + + segment_t *const segptr = new segment_t (shmid, shmaddr, shmflg, hFileMap); + + assert (segptr); + + if (previous) { - // FIXME: Race potential - attachnode = tempnode->attachhead; - while (attachnode && attachnode->data != shmaddr) - attachnode = attachnode->next; - if (attachnode) - break; - tempnode = tempnode->next; + segptr->next = previous->next; + previous->next = segptr; } - if (!tempnode) + else { - // dt cannot be called by an app that hasn't alreadu at'd - set_errno (EINVAL); - return -1; + segptr->next = _segments_head; + _segments_head = segptr; } - UnmapViewOfFile (attachnode->data); - /* tell the daemon we have attached */ - client_request_shm req (SHM_DETACH, tempnode->shm_id); + return segptr; +} - if (req.make_request () == -1) - { - debug_printf ("failed to tell daemon that we have detached"); - } +/*---------------------------------------------------------------------------* + * shmat () + *---------------------------------------------------------------------------*/ - return 0; +extern "C" void * +shmat (const int shmid, const void *const shmaddr, const int shmflg) +{ + return shmmgr.shmat (shmid, shmaddr, shmflg); } -//FIXME: who is allowed to perform STAT? +/*---------------------------------------------------------------------------* + * shmctl () + *---------------------------------------------------------------------------*/ + extern "C" int -shmctl (int shmid, int cmd, struct shmid_ds *buf) +shmctl (const int shmid, const int cmd, struct shmid_ds *const buf) { - shmnode *tempnode = shm_head; - while (tempnode && tempnode->shm_id != shmid) - tempnode = tempnode->next; - if (!tempnode) - { - /* couldn't find a currently open shm control area for the key - probably because - * shmget hasn't been called. - * Allocate a new control block - this has to be handled by the daemon */ - client_request_shm req (SHM_REATTACH, shmid); - - if (req.make_request () == -1) - { - set_errno (ENOSYS); /* daemon communication failed */ - return -1; - } - - if (req.error_code ()) /* shm_get failed in the daemon */ - { - set_errno (req.error_code ()); - return -1; - } - - /* we've got the id, now we open the memory area ourselves. - * This tests security automagically - * FIXME: make this a method of shmnode ? - */ - tempnode = - build_inprocess_shmds (req.parameters.out.filemap, - req.parameters.out.attachmap, - req.parameters.out.key, - req.parameters.out.shm_id); - if (!tempnode) - return -1; - } + return shmmgr.shmctl (shmid, cmd, buf); +} - switch (cmd) - { - case IPC_STAT: - *buf = tempnode->shmds->ds; - break; - case IPC_RMID: - { - /* TODO: check permissions. Or possibly, the daemon gets to be the only - * one with write access to the memory area? - */ - if (tempnode->shmds->ds.shm_nattch) - system_printf - ("call to shmctl with cmd= IPC_RMID when memory area still has" - " attachees"); - /* how does this work? - * we mark the ds area as "deleted", and the at and get calls all fail from now on - * on, when nattch becomes 0, the mapped data area is destroyed. - * and each process, as they touch this area detaches. eventually only the - * daemon has an attach. The daemon gets asked to detach immediately. - */ - //waiting for the daemon to handle terminating process's - client_request_shm req (SHM_DEL, shmid); - - if (req.make_request () == -1) - { - set_errno (ENOSYS); /* daemon communication failed */ - return -1; - } - - if (req.error_code ()) /* shm_del failed in the daemon */ - { - set_errno (req.error_code ()); - return -1; - } - - /* the daemon has deleted it's references */ - /* now for us */ - - // FIXME: create a destructor - delete_inprocess_shmds (&tempnode); +/*---------------------------------------------------------------------------* + * shmdt () + *---------------------------------------------------------------------------*/ - } - break; - case IPC_SET: - default: - set_errno (EINVAL); - return -1; - } - return 0; +extern "C" int +shmdt (const void *const shmaddr) +{ + return shmmgr.shmdt (shmaddr); } -/* FIXME: evaluate getuid32() and getgid32() against the requested mode. Then - * choose PAGE_READWRITE | PAGE_READONLY and FILE_MAP_WRITE | FILE_MAP_READ - * appropriately - */ +/*---------------------------------------------------------------------------* + * shmget () + *---------------------------------------------------------------------------*/ -/* FIXME: shmid should be a verifyable object - */ - -/* FIXME: on NT we should check everything against the SD. On 95 we just emulate. - */ extern "C" int -shmget (key_t key, size_t size, int shmflg) +shmget (const key_t key, const size_t size, const int shmflg) { - if (key == (key_t) - 1) - { - set_errno (ENOENT); - return -1; - } + return shmmgr.shmget (key, size, shmflg); +} - /* FIXME: enter the checking for existing keys mutex. This mutex _must_ be system wide - * to prevent races on shmget. - */ +/*---------------------------------------------------------------------------* + * fixup_shms_after_fork () + *---------------------------------------------------------------------------*/ - /* walk the list of currently open keys and return the id if found - */ - shmnode *tempnode = shm_head; - while (tempnode) - { - if (tempnode->key == key && key != IPC_PRIVATE) - { - // FIXME: free the mutex - if (size && tempnode->shmds->ds.shm_segsz < size) - { - set_errno (EINVAL); - return -1; - } - if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) - { - set_errno (EEXIST); - // FIXME: free the mutex - return -1; - } - // FIXME: do we need to other tests of the requested mode with the - // tempnode->shmid mode ? testcase on unix needed. - // FIXME do we need a security test? We are only examining the keys we already have open. - // FIXME: what are the sec implications for fork () if we don't check here? - return tempnode->shm_id; - } - tempnode = tempnode->next; - } - /* couldn't find a currently open shm control area for the key. - * Allocate a new control block - this has to be handled by the daemon */ - client_request_shm req (key, size, shmflg); +int __stdcall +fixup_shms_after_fork () +{ + return shmmgr.fixup_shms_after_fork (); +} - if (req.make_request () == -1) - { - set_errno (ENOSYS); /* daemon communication failed */ - return -1; - } +/*---------------------------------------------------------------------------* + * client_request_shm::client_request_shm () + *---------------------------------------------------------------------------*/ - if (req.error_code ()) /* shm_get failed in the daemon */ - { - set_errno (req.error_code ()); - return -1; - } +client_request_shm::client_request_shm (const int shmid, const int shmflg) + : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) +{ + _parameters.in.shmop = SHMOP_shmat; + + _parameters.shmid = shmid; + _parameters.in.shmflg = shmflg; + + _parameters.in.cygpid = getpid (); + _parameters.in.winpid = GetCurrentProcessId (); + _parameters.in.uid = geteuid (); + _parameters.in.gid = getegid (); +} + +/*---------------------------------------------------------------------------* + * client_request_shm::client_request_shm () + *---------------------------------------------------------------------------*/ + +client_request_shm::client_request_shm (const int shmid, + const int cmd, + const struct shmid_ds * const buf) + : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) +{ + _parameters.in.shmop = SHMOP_shmctl; + + _parameters.shmid = shmid; + if (cmd == IPC_SET) + _parameters.ds = *buf; + _parameters.in.cmd = cmd; + + _parameters.in.cygpid = getpid (); + _parameters.in.winpid = GetCurrentProcessId (); + _parameters.in.uid = geteuid (); + _parameters.in.gid = getegid (); +} + +/*---------------------------------------------------------------------------* + * client_request_shm::client_request_shm () + *---------------------------------------------------------------------------*/ + +client_request_shm::client_request_shm (const int shmid) + : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) +{ + _parameters.in.shmop = SHMOP_shmdt; + + _parameters.shmid = shmid; + + _parameters.in.cygpid = getpid (); + _parameters.in.winpid = GetCurrentProcessId (); + _parameters.in.uid = geteuid (); + _parameters.in.gid = getegid (); +} + +/*---------------------------------------------------------------------------* + * client_request_shm::client_request_shm () + *---------------------------------------------------------------------------*/ + +client_request_shm::client_request_shm (const key_t key, + const size_t size, + const int shmflg) + : client_request (CYGSERVER_REQUEST_SHM, &_parameters, sizeof (_parameters)) +{ + _parameters.in.shmop = SHMOP_shmget; + + _parameters.in.key = key; + _parameters.in.size = size; + _parameters.in.shmflg = shmflg; - /* we've got the id, now we open the memory area ourselves. - * This tests security automagically - * FIXME: make this a method of shmnode ? - */ - shmnode *shmtemp = build_inprocess_shmds (req.parameters.out.filemap, - req.parameters.out.attachmap, - key, - req.parameters.out.shm_id); - if (shmtemp) - return shmtemp->shm_id; - return -1; + _parameters.in.cygpid = getpid (); + _parameters.in.winpid = GetCurrentProcessId (); + _parameters.in.uid = geteuid (); + _parameters.in.gid = getegid (); } |