/*static char *SCCSID = "@(#)semverif.c 6.6 92/01/12";*/
#define SCCSID  "@(#)semverif.c 6.6 92/01/12"

/*
 *      Semaphores - 32-bit Semaphore Verification Routines
 *
 *      This is an OS/2 2.x specific file
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Created: June 1989 - Julie Bennett
 *
 *      Feb 1994, R. Cadima - VR32RequestMutexSem and VR32ReleaseMutexSem
 *                            are never being called. Code commented out.
 *                            Look for #ifddef KMUTEX.
 */


#define INCL_TYPES
#include <basedef.h>
#define FNO_NUM FNO_SEMVERIF_C          /* for assertion macros */
#include <fnotask.h>                    /* MUST BE BEFORE osmac.h */
#include <osmac.h>
#include <osconst.h>
#include <ostypes.h>
#include <infoseg.h>
#include <mi.h>
#include <proc.h>
#include <error.h>
#include <sigdef.h>
#include <except.h>
#include <dossymc.h>
#include <seldesc.h>
#include <pgexport.h>
#include <vmexport.h>
#include <vmowner.h>
#include <selxport.h>
#include <tkexport.h>
#include <sem.h>
#include <screen.h>
#include <curdir.h>
#include <dirent.h>
#include <find.h>
#include <semxport.h>
#include <tkptda.h>
#include <ioexp.h>
#include <bsetib.h>
#include <tklocal.h>
#include <kmode.h>
#include <semlocal.h>
#include <string.h>

#pragma code_seg("LOAD_TUNE9_SWP","CODE")
/***EP  VR32CreateEventSem - Create an Event Semaphore
 *
 *      VR32CreateEventSem creates and opens an event semaphore.  If pszName
 *      is not null, then the semaphore will be a named shared semaphore.
 *      If the pszName is null and the DC_SEM_SHARED flag is set, then the
 *      semaphore will be an anonymous shared semaphore.  Otherwise, the
 *      semaphore will be a private semaphore.  DC_SEM_SHARED will be ignored
 *      if the pszName is not null.  If the semaphore already exists, then
 *      ERROR_DUPLICATE_NAME will be returned.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      The fState flag sets the initial state of the event semaphore.  If the
 *      fState flag is set to false, then an implicit DosResetEventSem will be
 *      executed before returning.  This means that if a thread follows this
 *      call with a DosWaitEventSem, the thread will block.  If the fState flag
 *      is set to true, then an implicit DosPostEventSem will be executed.
 *      For the same scenario outlined above, this means that the thread will
 *      be able to run.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name of Event, or NULL for anonymous Event
 *              phev    - ptr to receive event handle
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *              fState  - TRUE  => execute implicit DosPostEventSem before
 *                                 returning
 *                        FALSE => execute implicit DosResetEventSem before
 *                                 returning
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_NAME
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_TOO_MANY_HANDLES
 *
 *      PSEUDO CODE
 *
 *      If (flAttr has more than 1 bit set) ||
 *         (fState is other than TRUE or FALSE)
 *        return (ERROR_INVALID_PARAMETER)
 *      Endif
 *      If (pszName != NULL)
 *        validate pszName
 *        call semStoreStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiCreateEventSem with pSemName and hevTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hevTmp to phev
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32CreateEventSem(THUNKSEF sef,       /* system entry frame */
                               PSZ pszName,        /* semaphore name */
                               PHEV phev,          /* ptr to event handle */
                               ULONG flAttr,       /* attributes */
                               BOOL fState)        /* initial state of event */

{
    PSZ pSemName = NULL;       /* ptr to resident buffer for pszName */
    HEV hevTmp;                /* temporary handle */
    PHEV pStrEvt;              /* ptr to event handle in string structure */
    ULONG rc;                  /* return code */


    /*
     *  Check flag attributes.
     *  Check fState is either TRUE or FALSE.
     */
    if (((flAttr | (DC_SEM_SHARED | DC_SEM_PM)) != (DC_SEM_SHARED | DC_SEM_PM)) ||
        ((fState != TRUE) && (fState != FALSE)))
      return (ERROR_INVALID_PARAMETER);

    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phev);

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        return (rc);
      }
      /*
       *  Set shared flag and store the string node in the string table.
       */
      flAttr |= DC_SEM_SHARED;
      if ((rc = semStoreStr(pSemName,SSToDS(&pStrEvt))) != NO_ERROR) {
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
        return (rc);
      }
    }
    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiCreateEventSem(pSemName,SSToDS(&hevTmp),flAttr,fState);

    if (rc == NO_ERROR) {
      if (pSemName)
        *pStrEvt = semGetIndex(hevTmp);
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hevTmp,phev)) != NO_ERROR) {
        SEMApiCloseEventSem(hevTmp,pPTDACur);
        /*
         *  The return code from CloseEvent is not checked here.
         *  The process is dying, so if the close returns error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process.  This could only happen in a race condition where
         *  a named semaphore was opened, set, and waited on by
         *  another process while this process was blocked on the
         *  TKSuDWord (very unlikely).
         */
        return (rc);
      }
    }
    else {
      if (pSemName) {
        /*
         *  Error.  Free string node from string table and string name
         *  from semaphore structure.
         */
        semStrTblDelete(pSemName);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
      }
    }

#ifdef PMDEBUG
    if (rc == NO_ERROR) {
      if (semIfShared(hevTmp))
        ((PSEVENT)pShSemTbl[semGetIndex(hevTmp)])->pulCreatAddr = phev;
      else
        ((PPEVENT)((pPTDACur->pPrSemTbl)[semGetIndex(hevTmp)]))
                  ->pulCreatAddr = phev;
    }
#endif

    return (rc);
}
#pragma code_seg()
#pragma code_seg("LOAD_TUNE8_SWP","CODE")
/***EP  VR32OpenEventSem - Open an Event Semaphore
 *
 *      VR32OpenEventSem opens an event semaphore.  DosCreateEventSem must
 *      create the semaphore before VR32OpenEventSem can open it.  If the
 *      pszName is not null, then the semaphore is a named shared event
 *      semaphore.  Otherwise, the semaphore will be referenced by the
 *      semaphore handle passed in phev.  VR32OpenEventSem only returns the
 *      handle of the semaphore.  It does not test or change any of the values
 *      of the semaphore.  If phev is not used, then it should be zero.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      The event semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes that semaphore, the semaphore disappears and must
 *      be recreated by its next user with DosCreateEventSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseEventSem calls must equal the number of
 *      DosOpenEventSem and DosCreateEventSem calls.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, NULL => anonymous shared
 *              phev    - if psz is NULL, phev contains the handle of an
 *                        anonymous event created by another process
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_NOT_FOUND
 *                        ERROR_TOO_MANY_OPENS
 *
 *      PSEUDO CODE
 *
 *      If (pszName != NULL)
 *        validate pszName
 *        call semSaveStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiOpenEventSem with pSemName and hevTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hevTmp to phev
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32OpenEventSem(THUNKSEF sef,       /* system entry frame */
                             PSZ pszName,        /* semaphore name */
                             PHEV phev)         /* ptr to event handle */

{
    PSZ pSemName = NULL;      /* ptr to resident buffer for pszName */
    HEV hevTmp;               /* temporary handle */
    ULONG rc;                 /* return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phev);

    /*
     *  Copy handle into kernel space.
     */
    if ((rc = TKFuDWord(SSToDS(&hevTmp),phev)) != NO_ERROR)
      return (rc);

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        return (rc);
      }
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiOpenEventSem(pSemName,SSToDS(&hevTmp),pPTDACur);

    if (pSemName != NULL)
      VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
    if (rc == NO_ERROR) {
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hevTmp,phev)) != NO_ERROR) {
        SEMApiCloseEventSem(hevTmp,pPTDACur);
        /*
         *  The return code from CloseEvent is not checked here.
         *  The process is dying, so if the close returns error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process.  This could only happen in a race condition where
         *  a named semaphore was opened, set, and waited on by
         *  another process while this process was blocked on the
         *  TKSuDWord (very unlikely).
         */
        return (rc);
      }
    }
    return (rc);
}

#pragma code_seg()
#pragma code_seg("LOAD_EXITDOS1_SWP","CODE")

/***EP  VR32CloseEventSem - Close an Event Semaphore
 *
 *      VR32CloseEventSem closes an event semaphore that was previously opened
 *      via DosOpenEventSem or DosCreateEventSem.  If this is the last
 *      reference to this event, then the event semaphore is destroyed.
 *
 *      The ERROR_SEM_BUSY return code will be returned if the thread is trying
 *      to make the final close of a semaphore that has another thread in the
 *      same process still waiting on it.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes that semaphore, the semaphore disappears and must
 *      be recreated by its next user with DosCreateEventSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseEventSem calls must equal the number of
 *      DosOpenEventSem and DosCreateEventSem calls.
 *
 *      ENTRY   sef - system entry frame
 *              hev - handle of event to close
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiCloseEventSem(hev,pPTDACur) )
 */

ULONG cdecl VR32CloseEventSem(THUNKSEF sef,     /* system entry frame */
                              HEV hev)          /* event handle */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiCloseEventSem(hev,pPTDACur) );
}
#pragma code_seg()
#pragma code_seg("LOAD_TUNE4_SWP","CODE")

/***EP  VR32ResetEventSem - Reset an Event Semaphore
 *
 *      VR32ResetEventSem resets an event semaphore that has been previously
 *      created (via DosCreateEventSem) and opened (via DosOpenEventSem).
 *      The number of posts performed on the event before it was reset is
 *      returned to the caller in the pulPostCt parameter.  If the event
 *      was already reset, the ERROR_ALREADY_RESET return code is returned
 *      to the calling thread.  It is not reset a second time.
 *
 *      To reverse this operation, simply call DosPostEventSem.  This will
 *      post the event, so that any threads that were waiting for the event
 *      semaphore to be posted (via DosWaitEventSem) will be allowed to run.
 *
 *      ENTRY   sef       - system entry frame
 *              hev       - handle of event to reset
 *              pulPostCt - pointer to receive current number of posts
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_RESET
 *              Failure - ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      validate pulPostCt
 *      rc = call SEMApiResetEventSem with ulPost
 *      If (rc == NO_ERROR)
 *        call TKSuWord to copy ulPost to pulPostCt
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32ResetEventSem(THUNKSEF sef,     /* system entry frame */
                              HEV hev,          /* event handle */
                              PULONG pulPostCt) /* ptr to number of posts */

{
    ULONG ulPost;                /* temp storage for number of posts */
    ULONG rc;                    /* return code */
    ULONG rc2;                   /* another return code */


    /*
     *  Verify that user pointer is not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pulPostCt);

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiResetEventSem(hev,SSToDS(&ulPost),pPTDACur);

    /*
     *  Copy the post count back to user space.
     */
    if ((rc2 = TKSuDWord(ulPost,pulPostCt)) != NO_ERROR)
      return (rc2);
    return (rc);
}

#pragma code_seg()
#pragma code_seg("LOAD_TUNE9_SWP","CODE")
/***EP  VR32PostEventSem - Post an Event Semaphore
 *
 *      VR32PostEventSem posts an event semaphore that was previously reset
 *      via DosResetEventSem.  If the event is already posted, the post
 *      count is incremented and the ERROR_ALREADY_POSTED return code is
 *      returned to the calling thread.  It is not posted a second time.
 *      Otherwise, the event state is posted, the post count is incremented,
 *      and all threads that called DosWaitEventSem are made runnable.
 *
 *      ENTRY   sef - system entry frame
 *              hev - handle of event to post
 *
 *      EXIT    Success - NO_ERROR
 *                        ERROR_ALREADY_POSTED
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_TOO_MANY_POSTS
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiPostEventSem(hev) )
 */

ULONG cdecl VR32PostEventSem(THUNKSEF sef,       /* system entry frame */
                             HEV hev)            /* event handle */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiPostEventSem(hev,pPTDACur) );
}

/***EP  VR32WaitEventSem - Wait on an Event Semaphore
 *
 *      VR32WaitEventSem allows a thread to wait on an event semaphore that
 *      was previously created (via DosCreateEventSem) and opened (via
 *      DosOpenEventSem).  When an event semaphore is reset (via
 *      DosResetEventSem), any thread that calls DosWaitEventSem will be
 *      blocked.  When the event semaphore is posted (via DosPostEventSem),
 *      all threads waiting on the event are made runnable.  If the semaphore
 *      is posted when "wait" is called, the thread will continue to run and
 *      the routine will return immediately.
 *
 *      The ulTimeout parameter places an upper boundary on the amount of time
 *      to block before returning, even though the semaphore is still owned.
 *      If the semaphore is still in use at timeout, the ERROR_TIMEOUT return
 *      code will be returned.  If a timeout of zero is passed in,
 *      VR32WaitEventSem will return without blocking.  If a timeout of -1 is
 *      passed in, it will block indefinitely.
 *
 *      ENTRY   sef       - system entry frame
 *              hev       - handle of event to wait upon for posting
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_TIMEOUT
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiWaitEventSem(hev,ulTimeout) )
 */

ULONG cdecl VR32WaitEventSem(THUNKSEF sef,         /* system entry frame */
                             HEV hev,              /* event handle */
                             ULONG ulTimeout)      /* timeout in milliseconds */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiWaitEventSem(hev,ulTimeout) );
}
#pragma code_seg()
#pragma code_seg("SEMSWAP_TEXT", "CODE")

/***EP  VR32QueryEventSem - Query an Event Semaphore
 *
 *      VR32QueryEventSem will query information about a given event semaphore
 *      if the semaphore was previously opened by the process with a
 *      DosOpenEventSem.  If the event semaphore exists, the number of posts
 *      performed on the event is obtained and returned to the caller in the
 *      pulPostCt parameter.  If the event semaphore does not exist, then
 *      the ERROR_INVALID_HANDLE return code is returned to the caller.
 *
 *      ENTRY   sef       - system entry frame
 *              hev       - handle of event to query
 *              pulPostCt - pointer to receive the current number of
 *                          posts performed on the event
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *
 *      PSEUDO CODE
 *
 *      validate pulPostCt
 *      rc = call SEMApiQueryEventSem with ulPost
 *      If (rc == NO_ERROR)
 *        call TKSuWord to copy ulPost to pulPostCt
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32QueryEventSem(THUNKSEF sef,     /* system entry frame */
                              HEV hev,          /* event handle */
                              PULONG pulPostCt) /* ptr to number of posts */

{
    ULONG ulPost;                /* temp storage for number of posts */
    ULONG rc;                    /* return code */


    /*
     *  Verify that user pointer is not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pulPostCt);

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiQueryEventSem(hev,SSToDS(&ulPost));

    if (rc == NO_ERROR) {
      /*
       *  Copy the post count back to user space.
       */
      rc = TKSuDWord(ulPost,pulPostCt);
    }
    return (rc);
}

#pragma code_seg()
#pragma code_seg("LOAD_TUNE9_SWP","CODE")
/***EP  VR32CreateMutexSem - Create a Mutex Semaphore
 *
 *      VR32CreateMutexSem creates and opens a mutex semaphore.  If pszName
 *      is not null, then the semaphore will be a named shared semaphore.
 *      If the pszName is null and the DC_SEM_SHARED flag is set, then the
 *      semaphore will be an anonymous shared semaphore.  Otherwise, the
 *      semaphore will be a private semaphore.  DC_SEM_SHARED will be
 *      ignored if the pszName is not null.  If the semaphore already exists,
 *      then ERROR_DUPLICATE_NAME will be returned.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      The fState flag sets the initial state of the mutex semaphore.  If the
 *      fState flag is set to false, then the mutex is unowned.  If the fState
 *      flag is set to true, then the calling thread becomes the owner of the
 *      mutex and a request is executed.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, or NULL for an anonymous mutex
 *              phmtx   - pointer to receive a mutex handle
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *              fState  - TRUE  => initial state of mutex owned
 *                        FALSE => initial state of mutex unowned
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_NAME
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_TOO_MANY_HANDLES
 *
 *      PSEUDO CODE
 *
 *      If (flAttr has more than 1 bit set)
 *        return (ERROR_INVALID_PARAMETER)
 *      Endif
 *      If (pszName != NULL)
 *        validate pszName
 *        call semStoreStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiCreateMutexSem with pSemName and hmtxTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hmtxTmp to phmtx
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32CreateMutexSem(THUNKSEF sef,       /* system entry frame */
                               PSZ pszName,        /* semaphore name */
                               PHMTX phmtx,        /* ptr to handle */
                               ULONG flAttr,       /* attributes */
                               BOOL fState)        /* initial state */

{
    PSZ pSemName = NULL;     /* ptr to resident buffer for pszName */
    HMTX hmtxTmp;            /* temporary handle */
    PHMTX pStrMtx;           /* ptr to mutex handle in string structure */
    ULONG rc;                /* return code */


    /*
     *  Check flag attributes.
     *  Check fState is either TRUE or FALSE.
     */
    if (((flAttr | DC_SEM_SHARED) != DC_SEM_SHARED) ||
        ((fState != TRUE) && (fState != FALSE)))
      return (ERROR_INVALID_PARAMETER);

    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phmtx);

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        return (rc);
      }
      /*
       *  Set shared flag and store the string node in the string table.
       */
      flAttr |= DC_SEM_SHARED;
      if ((rc = semStoreStr(pSemName,SSToDS(&pStrMtx))) != NO_ERROR) {
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
        return (rc);
      }
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiCreateMutexSem(pSemName,SSToDS(&hmtxTmp),flAttr,fState);

    if (rc == NO_ERROR) {
      if (pSemName)
        *pStrMtx = semGetIndex(hmtxTmp);
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmtxTmp,phmtx)) != NO_ERROR) {
        SEMApiCloseMutexSem(hmtxTmp);
        /*
         *  The return code from CloseMutex is not checked here.
         *  The process is dying, so if the close gets error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process and return error_owner_died to any process
         *  waiting on the mutex if the current process owned it.
         *  This could only happen in a race condition where
         *  a named semaphore was opened and waited on by another
         *  process while this process was blocked on the TKSuDWord
         *  (very unlikely).
         */
        return (rc);
      }
    }
    else {
      if (pSemName) {
        /*
         *  Error.  Free string node from string table and string name
         *  from semaphore structure.
         */
        /*
          Be sure to delete the string table entry before releasing the
          storage.   defect 67077.
        */
        semStrTblDelete(pSemName);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
      }
    }
#ifdef PMDEBUG
    if (rc == NO_ERROR) {
      if (semIfShared(hmtxTmp))
        ((PSMUTEX)pShSemTbl[semGetIndex(hmtxTmp)])->pulCreatAddr = phmtx;
      else
        ((PPMUTEX)((pPTDACur->pPrSemTbl)[semGetIndex(hmtxTmp)]))
                  ->pulCreatAddr = phmtx;
    }
#endif
    return (rc);
}
#pragma code_seg()
#pragma code_seg("LOAD_FTUNE1_SWP","CODE")

/***EP  VR32OpenMutexSem - Open a Mutex Semaphore
 *
 *      VR32OpenMutexSem opens a mutex semaphore.  DosCreateMutexSem must
 *      create the semaphore before VR32OpenMutexSem can open it.  If the
 *      pszName is not null, then the semaphore is a named shared mutex
 *      semaphore.  Otherwise, the semaphore will be referenced by the
 *      semaphore handle passed in phmtx.  VR32OpenMutexSem only returns the
 *      handle of the semaphore.  It does not test or change any of the values
 *      of the semaphore.  If phmtx is not used, then it should be zero.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes that semaphore, the semaphore disappears and must
 *      be recreated by its next user with DosCreateMutexSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseMutexSem calls must equal the number of
 *      DosOpenMutexSem and DosCreateMutexSem calls.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, NULL => anonymous shared
 *              phmtx   - if pszName is NULL, phmtx contains the handle of
 *                        an anonymous mutex created by another process
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_NOT_FOUND
 *                        ERROR_TOO_MANY_OPENS
 *
 *      PSEUDO CODE
 *
 *      If (pszName != NULL)
 *        validate pszName
 *        call semSaveStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiOpenMutexSem with pSemName and hmtxTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hmtxTmp to phmtx
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32OpenMutexSem(THUNKSEF sef,       /* system entry frame */
                             PSZ pszName,        /* semaphore name */
                             PHMTX phmtx)        /* ptr to handle */

{
    PSZ pSemName = NULL;     /* ptr to resident buffer for pszName */
    HMTX hmtxTmp;            /* temporary handle */
    ULONG rc;                /* return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phmtx);

    /*
     *  Copy handle into kernel space.
     */
    if ((rc = TKFuDWord(SSToDS(&hmtxTmp),phmtx)) != NO_ERROR)
      return (rc);

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        return (rc);
      }
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiOpenMutexSem(pSemName,SSToDS(&hmtxTmp));

    if (pSemName != NULL)
      VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
    if (rc == NO_ERROR) {
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmtxTmp,phmtx)) != NO_ERROR) {
        SEMApiCloseMutexSem(hmtxTmp);
        /*
         *  The return code from CloseMutex is not checked here.
         *  The process is dying, so if the close gets error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process and return error_owner_died to any process
         *  waiting on the mutex if the current process owned it.
         *  This could only happen in a race condition where
         *  a named semaphore was opened and waited on by another
         *  process while this process was blocked on the TKSuDWord
         *  (very unlikely).
         */
        return (rc);
      }
    }
    return (rc);
}
#pragma code_seg()
#pragma code_seg("LOAD_EXITDOS1_SWP","CODE")

/***EP  VR32CloseMutexSem - Close a Mutex Semaphore
 *
 *      VR32CloseMutexSem closes a mutex semaphore that was previously opened
 *      via DosOpenMutexSem or DosCreateMutexSem.  If this is the last
 *      reference to this mutex, then the mutex is destroyed.
 *
 *      When all processes using the semaphore issue a DosCloseMutexSem, the
 *      semaphore is deleted.  If a process terminates with open semaphores,
 *      the system closes the semaphores.  If any of the semaphores are owned
 *      by the current process, all waiters on the semaphore are awakened and
 *      any subsequent calls on the semaphore will get the ERROR_SEM_OWNER_DIED
 *      return code.  This means that the owner of the semaphore terminated
 *      without releasing it, so the resources may be in an indeterminate
 *      state.
 *
 *      The ERROR_SEM_BUSY return code will be returned if (1) the mutex is owned
 *      by this thread, (2) the thread is trying to make the final close of a
 *      semaphore currently owned by another thread in the same process, or
 *      (3) the thread is trying to make the final close of a semaphore while
 *      another thread in the same process is still waiting on the semaphore.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes that semaphore, the semaphore disappears and must
 *      be recreated by its next user with DosCreateMutexSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseMutexSem calls must equal the number of
 *      DosOpenMutexSem and DosCreateMutexSem calls.
 *
 *      ENTRY   sef  - system entry frame
 *              hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiCloseMutexSem(hmtx) )
 */

ULONG cdecl VR32CloseMutexSem(THUNKSEF sef,       /* system entry frame */
                              HMTX hmtx)          /* mutex handle */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiCloseMutexSem(hmtx) );
}

#pragma code_seg()

// #ifdef KMUTEX   /* These routimes are not being called, place ifdef around them
//                 * so no code is generated.  */
#pragma code_seg("SEMSWAP_TEXT", "CODE")
/***EP  VR32RequestMutexSem - Request a Mutex Semaphore
 *
 *      VR32RequestMutexSem requests a mutex semaphore that has been previously
 *      created (via DosCreateMutexSem) and opened (via DosOpenMutexSem).  If
 *      a semaphore is unowned, VR32RequestMutexSem sets it to be owned and
 *      returns immediately to the caller.  If the semaphore is owned,
 *      VR32RequestMutexSem will block the thread until it is unowned, then try
 *      again.  The ulTimeout parameter places an upper boundary on the amount
 *      of time to block before returning, even though the semaphore is still
 *      owned.  If the semaphore is still in use at timeout, the ERROR_TIMEOUT
 *      return code will be returned.  If a timeout of zero is passed in,
 *      VR32RequestMutexSem will return without blocking.  If a timeout of -1
 *      is passed in, it will block indefinitely.
 *
 *      If a thread terminates while it owns a mutex semaphore, the
 *      ERROR_SEM_OWNER_DIED return code is returned to the next thread that
 *      calls DosRequestMutexSem.  That thread must then call DosCloseMutexSem
 *      so that the semaphore may be destroyed.
 *
 *      When a thread no longer requires the protected resource, it issues
 *      DosReleaseMutexSem to set the semaphore to be unowned.  Any threads
 *      that were blocked waiting for that semaphore can try to gain access
 *      to the semaphore (priority is assigned in the order of the requests).
 *
 *      The request/release calls may be nested.  A maximum of 64K requests is
 *      allowed per semaphore at any one time.  However, when a thread is
 *      finished using a semaphore (completes the critical section), the
 *      number of DosReleaseMutexSem calls must equal the number of
 *      DosRequestMutexSem calls.
 *
 *      ENTRY   sef       - system entry frame
 *              hmtx      - handle of a mutex
 *              ulTimeout - timeout in milliseconds
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *                        ERROR_TOO_MANY_SEM_REQUESTS
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiRequestMutexSem(hmtx,ulTimeout) )
 */

ULONG cdecl VR32RequestMutexSem(THUNKSEF sef,     /* system entry frame */
                                HMTX hmtx,        /* mutex handle */
                                ULONG ulTimeout)  /* timeout in milliseconds */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiRequestMutexSem(hmtx,ulTimeout) );
}


/***EP  VR32ReleaseMutexSem - Release a Mutex Semaphore
 *
 *      VR32ReleaseMutexSem releases a mutex semaphore that was previously
 *      requested by DosRequestMutexSem.  The request count is decremented.
 *      If the count goes to zero (no more requests were made by the current
 *      thread), then another thread is given the opportunity to gain
 *      ownership of the mutex.  DosReleaseMutexSem cannot be issued against a
 *      mutex semaphore by any thread other than the owner.
 *
 *      If two threads have the same priority, then a thread waiting on a
 *      muxwait semaphore has priority over a thread waiting on a single
 *      mutex (if the requirements of the muxwait semaphore have been met).
 *      For example, suppose the released mutex is part of a muxwait
 *      semaphore.  One thread is waiting on the single mutex and another
 *      thread is waiting on the muxwait containing the same mutex.  The
 *      mutex has been released.  All other mutexes in the muxwait are
 *      unowned and ready to be given to the muxwait.  Both threads have the
 *      same priority, so either one of them could run.  In this case, the
 *      thread waiting on the muxwait semaphore will run.
 *
 *      The request/release calls may be nested.  A maximum of 64K requests is
 *      allowed per semaphore at any one time.  However, when a thread is
 *      finished using a semaphore (completes the critical section), the
 *      number of DosReleaseMutexSem calls must equal the number of
 *      DosRequestMutexSem calls.
 *
 *      ENTRY   sef  - system entry frame
 *              hmtx - handle of a mutex
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_NOT_OWNER
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiReleaseMutexSem(hmtx) )
 */

ULONG cdecl VR32ReleaseMutexSem(THUNKSEF sef,       /* system entry frame */
                                HMTX hmtx)          /* mutex handle */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiReleaseMutexSem(hmtx) );
}

#pragma code_seg()
// #endif /* KMUTEX */

#pragma code_seg("LOAD_TUNE9_SWP","CODE")

/***EP  VR32QueryMutexSem - Query a Mutex Semaphore
 *
 *      VR32QueryMutexSem will query information about a given mutex semaphore
 *      if the semaphore was previously opened by the process with a
 *      DosOpenMutexSem.  If the mutex semaphore exists, the relative
 *      information is obtained and returned to the caller in the given
 *      parameters.  If the semaphore is owned, then owner information is
 *      returned.  If the semaphore is unowned, then zero is returned in the
 *      pulCount parameter.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if the owner has died
 *      and the semaphore has not yet been destroyed by a call to
 *      DosCloseMutexSem.  In this case, all of the parameters are filled in
 *      with the information of the dead mutex owner.  That thread must then
 *      call DosCloseMutexSem so the semaphore may be destroyed.
 *
 *      The user's PID and TID are 32-bit values, while the PID and TID that
 *      the semaphore worker routines are using are 16-bit values.  The high
 *      words of the 32-bit values must therefore be zeroed if the low words
 *      are to be filled in with 16-bit values.
 *
 *      ENTRY   sef       - system entry frame
 *              hmtx      - handle of a mutex
 *              ppidOwner - filled in with the process ID of a mutex owner
 *                          (not filled in if semaphore is unowned)
 *              ptidOwner - filled in with the thread ID of a mutex owner
 *                          (not filled in if semaphore is unowned)
 *              pulCount  - filled in with the current number of
 *                          DosRequestMutexSem calls made by the owning
 *                          thread (ZERO if semaphore is unowned)
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_OWNER_DIED
 *
 *      PSEUDO CODE
 *
 *      validate ppidOwner, ptidOwner, and pulCount
 *      rc = call SEMApiQueryMutexSem with pidTmp, tidTmp, ulCt
 *      If (rc == NO_ERROR)
 *        call TKSuDWord to copy ulpid to ppidOwner
 *        call TKSuDWord to copy ultid to ptidOwner
 *        call TKSuDWord to copy ulCt to pulCount
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32QueryMutexSem(THUNKSEF sef,       /* system entry frame */
                              HMTX hmtx,          /* mutex handle */
                              PULONG ppidOwner,   /* process id */
                              PULONG ptidOwner,   /* thread id */
                              PULONG pulCount)    /* number of requests */

{
    PID pidTmp;               /* temp 16-bit storage for process id */
    TID tidTmp;               /* temp 16-bit storage for thread id */
    ULONG ulpid;              /* temp 32-bit storage for process id */
    ULONG ultid;              /* temp 32-bit storage for thread id */
    ULONG ulCt;               /* temp storage for state of event */
    ULONG rc;                 /* return code */
    ULONG rc2;                /* another return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)ppidOwner);
    semChkNotKrnlSpace((PVOID)ptidOwner);
    semChkNotKrnlSpace((PVOID)pulCount);

    /*
     *  Call the allocation/validation layer.
     */
    pidTmp = tidTmp = 0;
    rc = SEMApiQueryMutexSem(hmtx,SSToDS(&pidTmp),SSToDS(&tidTmp),
                             SSToDS(&ulCt));

    if ((rc == NO_ERROR) || (rc == ERROR_SEM_OWNER_DIED)) {
      /*
       *  Copy the pid, tid, and request count of the owner back to
       *  user space.
       */
      if (ulCt != 0) {
        ulpid = (ULONG)pidTmp;
        ultid = (ULONG)tidTmp;
        if (((rc2 = TKSuDWord(ulpid,ppidOwner)) != NO_ERROR) ||
            ((rc2 = TKSuDWord(ultid,ptidOwner)) != NO_ERROR))
          return (rc2);
      }
      if ((rc2 = TKSuDWord(ulCt,pulCount)) != NO_ERROR)
        return (rc2);
    }
    return (rc);
}

#pragma code_seg()
#pragma code_seg("SEMSWAP_TEXT", "CODE")

/***EP  VR32CreateMuxWaitSem - Create a MuxWait Semaphore
 *
 *      VR32CreateMuxWaitSem creates and opens a muxwait semaphore.  If the
 *      pszName is not null, then the semaphore will be a named shared muxwait
 *      semaphore.  If the pszName is null and the DC_SEM_SHARED flag is set,
 *      then the semaphore will be an anonymous shared semaphore.  Otherwise,
 *      the semaphore will be an anonymous private semaphore.  DC_SEM_SHARED
 *      will be ignored if the pszName is not null.  If the semaphore already
 *      exists, then ERROR_DUPLICATE_NAME will be returned.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      All muxwait semaphores must contain either ALL mutexes or ALL events.
 *      If the creation of a muxwait is being attempted with a combination of
 *      events and mutexes, the ERROR_WRONG_TYPE return code is returned to
 *      the calling routine.  Both shared and private semaphores may be used
 *      in the same muxwait.  However, the private semaphores will not be
 *      valid between processes, so the muxwait will only be valid in the one
 *      process.
 *
 *      If the DCMW_WAIT_ANY flag is set, then the semaphore is to wait for
 *      any of the event/mutex semaphores in its list to be posted/released.
 *      Once any ONE of the semaphores is posted/released, the thread waiting
 *      on the muxwait can run.  If there is more than one mutex in the list
 *      and one of the mutex semaphores was the one semaphore that cleared,
 *      then the thread will only gain ownership of the one mutex that became
 *      available, NOT all of them.  The semaphores will be checked in the
 *      order in which they are defined in the muxwait's list.  If the
 *      DCMW_WAIT_ALL flag is set, then the semaphore is to wait for ALL of
 *      the event/mutex semaphores in its list to be posted/released.  Once
 *      ALL of the semaphores have been posted/released, the thread waiting
 *      on the muxwait can run.
 *
 *      A muxwait can have a maximum of 64 event/mutex semaphores in its list.
 *      If this maximum is reached, then the ERROR_TOO_MANY_SEMAPHORES
 *      return code is returned to the calling thread.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name of MuxWait, or NULL if
 *                        creating an anonymous MuxWait.
 *              phmux   - pointer to be filled in with handle of new muxwait
 *              cSemRec - count of SEMRECORD entries in pSemRec
 *              pSemRec - array of SEMRECORD entries to put into muxwait
 *              flAttr  - attributes:
 *                          DC_SEM_SHARED
 *                          DCMW_WAIT_ANY
 *                          DCMW_WAIT_ALL
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_DUPLICATE_NAME
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TOO_MANY_HANDLES
 *                        ERROR_TOO_MANY_SEMAPHORES
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      If (flAttr has more than 2 bits set)
 *        return (ERROR_INVALID_PARAMETER)
 *      Endif
 *      If (cSemRec > SEM_MAXSEMS)
 *        return (ERROR_TOO_MANY_SEMAPHORES)
 *      Endif
 *      pSemRecord = NULL
 *      If (cSemRec > 0)                                // semrecord
 *        pSemRecord = allocate array of cSemRec + 1 number of SEMRECORDs
 *        pSemRecord[0].hsemCur = SEM_EMPTY_SEMREC      // copy info into it
 *        pSemRecord[0].ulUser = cSemRec + 1
 *        call TKFuBuff to copy pSemRec into pSemRecord
 *      Endif
 *      If (pszName != NULL)
 *        validate pszName
 *        call semStoreStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiCreateMuxWaitSem with pSemName,pSemRecord,hmuxTmp
 *      If (rc == error)
 *        free pSemName and pSemRecord
 *      Else
 *        call TKSuDWord to copy hmuxTmp to phmux
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32CreateMuxWaitSem(THUNKSEF sef,       /* system entry frame */
                                 PSZ pszName,        /* semaphore name */
                                 PHMUX phmux,        /* ptr to muxwait handle */
                                 ULONG cSemRec,      /* count of SEMRECORDs */
                                 PSEMRECORD pSemRec, /* array of SEMRECORDs */
                                 ULONG flAttr)       /* attributes */

{
    PSEMRECORD pSemRecord = NULL;  /* ptr to resident semaphore record */
    PSZ pSemName = NULL;           /* ptr to resident buffer for pszName */
    HMUX hmuxTmp;                  /* temporary handle */
    PHMUX pStrMux;                 /* ptr to mux handle in string structure */
    ULONG rc;                      /* return code */


    /*
     *  Check flag attributes.
     *  Check 64 limit of semrecord entries.
     */
    if (((flAttr | SEM_MUXFLAGS) != SEM_MUXFLAGS) ||
        (!(((flAttr & DCMW_WAIT_ANY) << 1) ^ (flAttr & DCMW_WAIT_ALL))))
      return (ERROR_INVALID_PARAMETER);
    if (cSemRec > SEM_MAXSEMS)
      return (ERROR_TOO_MANY_SEMAPHORES);

    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phmux);
    semChkNotKrnlSpace((PVOID)pSemRec);

    if (cSemRec > 0) {
      /*
       *  Allocate kernel space for semrecord entries.
       *  Copy entries from user space into newly allocated kernel space.
       */
      if ((rc = allocKRHB(((cSemRec + 1)*sizeof(SEMRECORD)),(VMHOB)SEMSEMRECOWNER,
                          SSToDS(&pSemRecord))) != NO_ERROR)
        return (rc);
      pSemRecord[0].hsemCur = SEM_EMPTY_SEMREC;    /* copy info into it */
      pSemRecord[0].ulUser = cSemRec + 1;
      if ((rc = TKFuBuff((PCHAR)(&(pSemRecord[1])),(PCHAR)pSemRec,
                     (cSemRec * sizeof(SEMRECORD)),TK_UM_FATAL)) != NO_ERROR) {
        if (pSemRecord != NULL)
          freeKRHB(pSemRecord);
        return (rc);
      }
    }

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        if (pSemRecord != NULL)
          freeKRHB(pSemRecord);
        return (rc);
      }
      /*
       *  Set shared flag and store the string node in the string table.
       */
      flAttr |= DC_SEM_SHARED;
      if ((rc = semStoreStr(pSemName,SSToDS(&pStrMux))) != NO_ERROR) {
        if (pSemRecord != NULL)
           freeKRHB(pSemRecord);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
        return (rc);
      }
    }
    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiCreateMuxWaitSem(pSemName,SSToDS(&hmuxTmp),cSemRec,
                                  pSemRecord,flAttr);

    if (rc != NO_ERROR) {
      if (pSemRecord != NULL)
        freeKRHB(pSemRecord);
      if (pSemName != NULL) {
        /*
         *  Error.  Free string node from string table and string name
         *  from semaphore structure.
         */
        semStrTblDelete(pSemName);
        VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
      }
    }
    else {
      if (pSemName)
        *pStrMux = semGetIndex(hmuxTmp);
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmuxTmp,phmux)) != NO_ERROR) {
        SEMApiCloseMuxWaitSem(hmuxTmp);
        /*
         *  The return code from CloseMuxWait is not checked here.
         *  The process is dying, so if the close returns error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process.  This could only happen in a race condition where
         *  a named semaphore was opened and waited on by another
         *  process while this process was blocked on the TKSuDWord
         *  (very unlikely).
         */
        return (rc);
      }
    }
#ifdef PMDEBUG
    if (rc == NO_ERROR) {
      if (semIfShared(hmuxTmp))
        ((PSMUX)pShSemTbl[semGetIndex(hmuxTmp)])->pulCreatAddr = phmux;
      else
        ((PPMUX)((pPTDACur->pPrSemTbl)[semGetIndex(hmuxTmp)]))
                ->pulCreatAddr = phmux;
    }
#endif
    return (rc);
}


/***EP  VR32OpenMuxWaitSem - Open a MuxWait Semaphore
 *
 *      VR32OpenMuxWaitSem opens a muxwait semaphore.  DosCreateMuxWaitSem must
 *      create the semaphore before VR32OpenMuxWaitSem can open it.  If the
 *      pszName is not null, then the semaphore is a named shared muxwait
 *      semaphore.  Otherwise, the semaphore will be referenced by the
 *      semaphore handle passed in phmux.  VR32OpenMuxWaitSem only returns the
 *      handle of the semaphore.  It does not test or change any of the
 *      values of the semaphore.  If phmux is not used, then it should be
 *      zero.
 *
 *      The named semaphores will be validated by the file system.
 *      Therefore, any name that makes up a valid file name will be a valid
 *      semaphore name.  There is a limit of 255 characters per semaphore
 *      name.  When specifying the name for a system semaphore, the ASCIIZ
 *      name string must include the prefix \SEM32\.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes (DosCloseMuxWaitSem) that semaphore, the
 *      semaphore disappears and must be recreated by its next user with
 *      DosCreateMuxWaitSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseMuxWaitSem calls must equal the number of
 *      DosOpenMuxWaitSem and DosCreateMuxWaitSem calls.
 *
 *      ENTRY   sef     - system entry frame
 *              pszName - asciiz name, NULL => anonymous
 *              phmux   - if psz is NULL, phmux contains the handle of an
 *                        anonymous muxwait created by another process
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_NAME
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_SEM_NOT_FOUND
 *                        ERROR_TOO_MANY_OPENS
 *
 *      PSEUDO CODE
 *
 *      If (shared)
 *        validate
 *        call semSaveStr to copy pszName into pSemName
 *      Endif
 *      rc = call SEMApiOpenMuxWaitSem with pSemName and hmuxTmp
 *      If (rc == error)
 *        free pSemName
 *      Else
 *        call TKSuDWord to copy hmuxTmp to phmux
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32OpenMuxWaitSem(THUNKSEF sef,       /* system entry frame */
                               PSZ pszName,        /* semaphore name */
                               PHMUX phmux)        /* ptr to muxwait handle */

{
    PSZ pSemName = NULL;     /* ptr to resident buffer for pszName */
    HMUX hmuxTmp;            /* temporary handle */
    ULONG rc;                /* return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pszName);
    semChkNotKrnlSpace((PVOID)phmux);

    /*
     *  Copy handle into kernel space.
     */
    if ((rc = TKFuDWord(SSToDS(&hmuxTmp),phmux)) != NO_ERROR)
      return (rc);

    if (pszName != NULL) {
      /*
       *  Validate name string passed in.
       */
      if ((rc = semValidateName(pszName,SSToDS(&pSemName))) != NO_ERROR) {
        return (rc);
      }
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiOpenMuxWaitSem(pSemName,SSToDS(&hmuxTmp));

    if (pSemName != NULL)
      VMFreeKHB(VM_HKH_PUB_SWAPRW,pSemName);
    if (rc == NO_ERROR) {
      /*
       *  Copy handle back to user space.
       */
      if ((rc = TKSuDWord(hmuxTmp,phmux)) != NO_ERROR) {
        SEMApiCloseMuxWaitSem(hmuxTmp);
        /*
         *  The return code from CloseMuxWait is not checked here.
         *  The process is dying, so if the close returns error_sem_busy,
         *  semCleanProcDying will close the semaphore in the current
         *  process.  This could only happen in a race condition where
         *  a named semaphore was opened and waited on by another
         *  process while this process was blocked on the TKSuDWord
         *  (very unlikely).
         */
        return (rc);
      }
    }
    return (rc);
}


/***EP  VR32CloseMuxWaitSem - Close a MuxWait Semaphore
 *
 *      VR32CloseMuxWaitSem closes a muxwait semaphore that was previously
 *      opened via DosOpenMuxWaitSem or DosCreateMuxWaitSem.  If this is the
 *      last reference to this muxwait, then the muxwait semaphore is
 *      destroyed.
 *
 *      The ERROR_SEM_BUSY return code will be returned if the thread is trying
 *      to make the final close of a semaphore that has another thread in the
 *      same process still waiting on it.
 *
 *      The system semaphores reside in a memory buffer rather than on a disk
 *      file.  This means that when the last process which has a semaphore
 *      open exits or closes that semaphore, the semaphore disappears and
 *      must be recreated by its next user with DosCreateMuxWaitSem.
 *
 *      The open/close operations may be nested.  A maximum of 64K opens per
 *      process is allowed for each semaphore at any one time.  However, for a
 *      process to intentionally destroy a semaphore prior to termination, the
 *      number of DosCloseMuxWaitSem calls must equal the number of
 *      DosOpenMuxWaitSem and DosCreateMuxWaitSem calls.
 *
 *      ENTRY   sef  - system entry frame
 *              hmux - handle of a muxwait to close
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_SEM_BUSY
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiCloseMuxWaitSem(hmux) )
 */

ULONG cdecl VR32CloseMuxWaitSem(THUNKSEF sef,      /* system entry frame */
                                HMUX hmux)         /* muxwait handle */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiCloseMuxWaitSem(hmux) );
}


/***EP  VR32WaitMuxWaitSem - Wait on a MuxWait Semaphore
 *
 *      VR32WaitMuxWaitSem allows a thread to wait on a muxwait semaphore that
 *      was previously created (via DosCreateMuxWaitSem) and opened (via
 *      DosOpenMuxWaitSem).  A muxwait can be waiting on multiple numbers of
 *      either all events or all mutexes.
 *
 *      In order to avoid deadlock in the system, if the DCMW_WAIT_ALL flag is
 *      set, a thread will not claim ownership of any mutex until ALL of the
 *      mutexes in the semaphore list have been released.  If the
 *      DCMW_WAIT_ANY flag is set and a mutex is released, the thread will
 *      only gain ownership of the ONE mutex that was released.  The ownership
 *      of all other mutex semaphores in the muxwait's list will remain
 *      unchanged.  The semaphores will be checked in the order in which they
 *      are defined in the muxwait's list.  Also, if any of the mutexes in the
 *      list are owned by the calling thread, ERROR_MUTEX_OWNED will be
 *      returned immediately.
 *
 *      If two threads have the same priority, then a thread waiting on a
 *      muxwait semaphore has priority over a thread waiting on a single mutex
 *      (if the requirements of the muxwait semaphore have been met).  For
 *      example, suppose the released mutex is part of a muxwait semaphore.
 *      One thread is waiting on the single mutex and another thread is
 *      waiting on the muxwait containing the same mutex.  The mutex is then
 *      released.  Asume also that all other mutexes in the muxwait are
 *      unowned and ready to be given to the muxwait.  Both threads have the
 *      same priority, so either one of them could run.  In this case, the
 *      thread waiting on the muxwait semaphore will run.
 *
 *      The event semaphores in a muxwait are all edge-triggered.  This means
 *      that if an event is posted and then reset while the thread is waiting
 *      on the semaphore, then the event will be considered to be posted for
 *      the rest of the thread's waiting period.  This is important for a
 *      muxwait semaphore with the DCMW_WAIT_ALL flag set.  When the
 *      DCMW_WAIT_ANY flag is set, the semaphores will be checked in the
 *      order in which they are defined in the muxwait's list.
 *
 *      The ulTimeout parameter places an upper boundary on the amount of
 *      time to block before returning, even though the semaphore is still
 *      owned.  If any of the semaphores in the muxwait are still in use at
 *      timeout, the ERROR_TIMEOUT return code will be returned.  If a timeout
 *      of zero is passed in, VR32WaitMuxWaitSem will return immediately.  If
 *      a timeout of -1 is passed in, it will block indefinitely.  When the
 *      ERROR_TIMEOUT error code is returned, it should be assumed that the
 *      thread did NOT get ownership of anything.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if any of the mutex
 *      semaphores in the muxwait semaphore have been placed in the owner
 *      died state.  This means that a thread died while it owned at least
 *      one mutex, and that mutex is part of the muxwait.  When this
 *      error code is returned, the calling thread should do a
 *      DosQueryMutexSem on each mutex in the muxwait's list to find out which
 *      ones are corrupt.  Each mutex that gets the ERROR_SEM_OWNER_DIED return
 *      code from the query should be closed via calls to DosCloseMutexSem.
 *      Semaphore handles may be reused, so the mutex semaphores that are
 *      closed MUST be deleted from the muxwait semaphore.      When the
 *      ERROR_SEM_OWNER_DIED return code is returned from this routine, a
 *      subsequent call must be made to DosWaitMuxWaitSem after the
 *      appropriate mutex semaphores have been closed and deleted from the
 *      muxwait in order to successfully wait on the muxwait semaphore.
 *
 *      ENTRY   sef       - system entry frame
 *              hmux      - handle of a muxwait
 *              ulTimeout - timeout in milliseconds
 *              pulUser   - For DCMW_WAIT_ANY muxwait, it is filled in with
 *                            the ULONG of the semaphore that occured.  If
 *                            it was a mutex, then it's owned by the caller.
 *                        - For DCMW_WAIT_ALL muxwait, it is filled in with
 *                            the ULONG of the LAST semaphore that occurred.
 *                            All mutex semaphores are owned by the caller.
 *                            Note that, for events, this means that all of
 *                            them were cleared at some point since the wait
 *                            call.  This does not mean that they are all
 *                            clear now.
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INTERRUPT
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_MUTEX_OWNED
 *                        ERROR_SEM_OWNER_DIED
 *                        ERROR_TIMEOUT
 *
 *      PSEUDO CODE
 *
 *      rc = call SEMApiWaitMuxWaitSem with ulTmp
 *      If (rc == NO_ERROR)
 *        call TKSuDWord to copy ulTmp to pulUser
 *      Endif
 *      return (rc)
 */

ULONG cdecl VR32WaitMuxWaitSem(THUNKSEF sef,      /* system entry frame */
                               HMUX hmux,         /* muxwait handle */
                               ULONG ulTimeout,   /* timeout in milliseconds */
                               PULONG pulUser)    /* user ulong */

{
    ULONG ulTmp;                 /* temp storage for user ulong */
    ULONG rc;                    /* return code */


    /*
     *  Verify that user pointer is not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pulUser);

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiWaitMuxWaitSem(hmux,ulTimeout,SSToDS(&ulTmp));

    if (rc == NO_ERROR) {
      /*
       *  Copy the ulUser value back to user space.
       */
      if ((rc = TKSuDWord(ulTmp,pulUser)) != NO_ERROR) {
        semUndoWaitMuxMtx(hmux);
        return (rc);
      }
    }
    return (rc);
}


/***EP  VR32AddMuxWaitSem - Add an Event/Mutex to a Muxwait Semaphore
 *
 *      VR32AddMuxWaitSem adds a mutex or an event to the existing list of
 *      semaphores in a given muxwait semaphore.  Adding a new semaphore to
 *      this list means that the thread waiting on the muxwait semaphore will
 *      have to wait for an additional semaphore to be cleared before it can
 *      run.  VR32AddMuxWaitSem will allow semaphores to be added to a muxwait
 *      while threads are currently waiting on the semaphore.  If a mutex is
 *      being added, then the other semaphores that are part of the muxwait
 *      must also be mutex semaphores.  If an event is being added, then the
 *      other semaphores in the muxwait must be event semaphores.   If not,
 *      then the ERROR_WRONG_TYPE return code is returned to the calling
 *      routine.
 *
 *      A muxwait can have a maximum of 64 event/mutex semaphores in its list.
 *      If this maximum is reached, then the ERROR_TOO_MANY_SEMAPHORES
 *      return code is returned to the calling thread.
 *
 *      ENTRY   sef     - system entry frame
 *              hmux    - handle of a muxwait
 *              pSemRec - pointer to the new SEMRECORD to add
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_DUPLICATE_HANDLE
 *                        ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_TOO_MANY_SEMAPHORES
 *                        ERROR_WRONG_TYPE
 *
 *      PSEUDO CODE
 *
 *      pNewSR = allocate new SemRecord node
 *      call TKFuBuff with pSemRec and pNewSR
 *      rc = call SEMApiAddMuxWaitSem with pNewSR
 *      free pNewSR
 *      return (rc)
 */

ULONG cdecl VR32AddMuxWaitSem(THUNKSEF sef,        /* system entry frame */
                              HMUX hmux,           /* muxwait handle */
                              PSEMRECORD pSemRec)  /* ptr to new SEMRECORD */

{
    PSEMRECORD pNewSR;           /* ptr to resident semrecord */
    ULONG rc;                    /* return code */


    /*
     *  Verify that user pointer is not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pSemRec);

    /*
     *  Allocate kernel space for semrecord entry.
     *  Copy the user space semrecord into the kernel space semrecord.
     */
    if ((rc = allocKRHB(2 * sizeof(SEMRECORD),(VMHOB)SEMSEMRECOWNER,
                        SSToDS(&pNewSR))) != NO_ERROR)
      return (rc);
    pNewSR[0].hsemCur = SEM_SRFLAG;
    pNewSR[0].ulUser = 2;
    if ((rc = TKFuBuff((PCHAR)(pNewSR+1),(PCHAR)pSemRec,sizeof(SEMRECORD),
                       TK_UM_FATAL)) != NO_ERROR) {
      freeKRHB(pNewSR);
      return (rc);
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiAddMuxWaitSem(hmux,pNewSR);

    if (pNewSR[0].hsemCur == SEM_SRFLAG)
      /*
       *  Allocated semrecord was not used, so free it.
       */
      freeKRHB(pNewSR);
    return (rc);
}


/***EP  VR32DeleteMuxWaitSem - Delete an Event/Mutex from a MuxWait Semaphore
 *
 *      VR32DeleteMuxWaitSem deletes a mutex or an event from the existing list
 *      of semaphores in a given muxwait semaphore.  Removing a semaphore from
 *      this list means that the thread waiting on the muxwait semaphore will
 *      have to wait for fewer semaphores to be cleared before it can run.
 *      VR32DeleteMuxWaitSem will allow semaphores to be deleted from a muxwait
 *      while threads are currently waiting on the semaphore.  If the
 *      semaphore that was deleted happened to be the last one in the muxwait
 *      that the thread was waiting for, then the thread can automatically
 *      run.
 *
 *      ENTRY   sef  - system entry frame
 *              hmux - handle of a muxwait
 *              hsem - handle of semaphore to be deleted from muxwait
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_EMPTY_MUXWAIT
 *                        ERROR_INVALID_HANDLE
 *
 *      PSEUDO CODE
 *
 *      return ( SEMApiDeleteMuxWaitSem(hmux,hsem) )
 */

ULONG cdecl VR32DeleteMuxWaitSem(THUNKSEF sef,     /* system entry frame */
                                 HMUX hmux,        /* muxwait handle */
                                 HSEM hsem)        /* semaphore handle */

{
    /*
     *  Call the allocation/validation layer.
     */
    return ( SEMApiDeleteMuxWaitSem(hmux,hsem) );
}


/***EP  VR32QueryMuxWaitSem - Query a MuxWait Semaphore
 *
 *      VR32QueryMuxWaitSem will query information about a given muxwait
 *      semaphore if the semaphore was previously opened by the process
 *      with a DosOpenMuxWaitSem.  If the muxwait semaphore does not exist,
 *      then the ERROR_INVALID_HANDLE return code is returned to the calling
 *      thread.  If the pSemRecord parameter is not large enough to hold all
 *      of the semaphore records in the muxwait, then the pcSemRec parameter
 *      is filled in with the count of semaphore records needed for the given
 *      muxwait and the ERROR_PARAM_TOO_SMALL return code is returned
 *      to the calling thread.  This is done so that the calling thread may
 *      do another DosQueryMuxWaitSem with the correct amount of space for the
 *      list.  If the muxwait semaphore does exist, then the appropriate
 *      information is returned to the calling thread.
 *
 *      The ERROR_SEM_OWNER_DIED return code is returned if any of the mutex
 *      semaphores in the muxwait semaphore have been placed in the owner
 *      died state.  This means that a thread died while it owned at least
 *      one mutex, and at least one mutex is part of the muxwait.  Also, the
 *      semaphore has not yet been destroyed by DosCloseMutexSem.
 *      When this error code is returned, the calling thread should do a
 *      DosQueryMutexSem on each mutex in the muxwait's list to find out
 *      which ones are corrupt.  Each mutex that gets the ERROR_SEM_OWNER_DIED
 *      return code from the query should be closed via a call to
 *      DosCloseMutexSem.  Semaphore handles may be reused, so the mutex
 *      semaphores that are closed MUST be deleted from the muxwait
 *      semaphore.
 *
 *      ENTRY   sef      - system entry frame
 *              hmux     - handle of a muxwait
 *              pcSemRec - number of SEMRECORDs that psemrec can accept
 *                       - filled in with count of SEMRECORDs returned
 *              pSemRec  - filled in with SEMRECORDs
 *              pflAttr  - filled in with flAttr passed on Create call
 *
 *      EXIT    Success - NO_ERROR
 *              Failure - ERROR_INVALID_HANDLE
 *                        ERROR_INVALID_PARAMETER
 *                        ERROR_PARAM_TOO_SMALL
 *                        ERROR_SEM_OWNER_DIED
 *
 *      PSEUDO CODE
 *
 *      validate pcSemRec, pSemrec, and pflAttr
 *      call TKFuWord for pcSemRec
 *      pNEWSemRec = allocate SEMRECORD array of size pcSemRec
 *      rc = call SEMApiQueryMuxWaitSem with usCtTmp, pNEWSemRec, ulAttrTmp
 *      If (rc == NO_ERROR)
 *        call TKSuWord to copy usCtTmp to pcSemRec
 *        call TKSuBuff to copy pNEWSemRec into pSemRec
 *        call TKSuDWord to copy ulAttrTmp to pflAttr
 *      Endif
 *      free pNEWSemRec
 *      return (rc)
 */

ULONG cdecl VR32QueryMuxWaitSem(THUNKSEF sef,        /* system entry frame */
                                HMUX hmux,           /* muxwait handle */
                                PULONG pcSemRec,     /* count of SEMRECORDs */
                                PSEMRECORD pSemRec,  /* array of SEMRECORDs */
                                PULONG pflAttr)      /* attributes */

{
    PSEMRECORD pNewSemRec = NULL;  /* ptr to resident SEMRECORDs */
    ULONG ulCtTmp;                 /* temp storage for count of SEMRECORDs */
    ULONG ulAttrTmp;               /* temp storage for attributes */
    ULONG rc;                      /* return code */
    ULONG rc2;                     /* return code */


    /*
     *  Verify that user pointers are not in kernel space.
     */
    semChkNotKrnlSpace((PVOID)pcSemRec);
    semChkNotKrnlSpace((PVOID)pSemRec);
    semChkNotKrnlSpace((PVOID)pflAttr);

    /*
     *  Copy semrecord count from user space to kernel space.
     */
    if ((rc = TKFuDWord(SSToDS(&ulCtTmp),pcSemRec)) != NO_ERROR)
      return (rc);
    if (ulCtTmp > 0) {
      /*
       *  Allocate kernel space for semrecord of same size as
       *  reported from user space.
       *  Only allocate kernel heap for up to the maximum number of
       *  semaphores in the MUXSEM list.                               *B728370
       */
      if ( ulCtTmp > SEM_MAXSEMS) ulCtTmp = SEM_MAXSEMS;
      if ((rc = allocKRHB((ulCtTmp * sizeof(SEMRECORD)),(VMHOB)SEMSEMRECOWNER,
                          SSToDS(&pNewSemRec))) != NO_ERROR)
        return (rc);
    }

    /*
     *  Call the allocation/validation layer.
     */
    rc = SEMApiQueryMuxWaitSem(hmux,SSToDS(&ulCtTmp),pNewSemRec,
                               SSToDS(&ulAttrTmp));

    if ((rc != ERROR_PARAM_TOO_SMALL) && (ulCtTmp != 0)) {
      /*
       *  Copy semrecord back to user space.
       */
      if ((rc2 = TKSuBuff((char*)pSemRec,(char*)pNewSemRec,
                (ulCtTmp * sizeof(SEMRECORD)),TK_UM_FATAL)) != NO_ERROR) {
        if (pNewSemRec != NULL)
          freeKRHB(pNewSemRec);
        return (rc2);
      }
    }
    if (pNewSemRec != NULL) {
      /*
       *  Free kernel space semrecord.
       */
      freeKRHB(pNewSemRec);
    }

    /*
     *  Copy count of semrecords and attributes flags back to user space.
     */
    if (((rc2 = TKSuDWord(ulCtTmp,pcSemRec)) != NO_ERROR) ||
        ((rc2 = TKSuDWord(ulAttrTmp,pflAttr)) != NO_ERROR)) {
      return (rc2);
    }
    return (rc);
}
