/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    srvmacro.h

Abstract:

    This module defines miscellaneous macros for the LAN Manager server. 

Author:

    Chuck Lenzmeier (chuckl) 2-Mar-90

Revision History:

    19-Nov-1990 mannyw


--*/

#ifndef _SRVMACRO_
#define _SRVMACRO_

//
// Simple MIN and MAX macros.   Watch out for side effects! 
//

#define MIN(a,b) ( ((a) < (b)) ? ( a) : (b) )
#define MAX(a,b) ( ((a) < (b)) ? ( b) : (a) )

//
// Compute a hash value for the string which is invairant to case
//
#define COMPUTE_STRING_HASH( _pus, _phash ) {           \
    PWCHAR _p = (_pus)->Buffer;                         \
    PWCHAR _ep = _p + ((_pus)->Length/sizeof(PWCHAR));  \
    ULONG _hashvalue = 0;                               \
                                                        \
    while( _p < _ep ) {                                 \
        _hashvalue <<= 1;                               \
        _hashvalue ^= RtlUpcaseUnicodeChar( *_p++ );    \
    }                                                   \
                                                        \
    *(_phash) = _hashvalue;                             \
}

//
// Convert the output of one of the above hash functions to an index into
//  a hash table
//
#define HASH_TO_MFCB_INDEX( _hash )    ((_hash) & (NMFCB_HASH_TABLE-1))

#define HASH_TO_SHARE_INDEX( _hash )   ((_hash) & (NSHARE_HASH_TABLE-1))

//
// GET_SERVER_TIME retrieves the server's concept of the current system time. 
//

#define GET_SERVER_TIME(_queue, a) (*(a) = (_queue)->stats. SystemTime)

//
// SET_SERVER_TIME updates the server's concept of the current system time. 
//

#define SET_SERVER_TIME( _queue ) {         \
    LARGE_INTEGER currentTime;              \
    KeQueryTickCount( &currentTime );       \
    (_queue)->stats. SystemTime = currentTime. LowPart; \
}

//++
//
// VOID
// IMPERSONATE (
//     IN PWORK_CONTEXT WorkContext
//     )
//
// Routine Description:
//
//     This macro calls NtSetInformationThread to impersonate a client. 
//     This should be called before attempting any open on behalf of
//     a remote client. 
//
//     !!!  should this be made a function to save code size?   A function
//         would be a little slower. 
//
// Arguments:
//
//     WorkContext - a pointer to a work context block.   It must have
//         a valid, referenced session pointer, from which the token
//         handle is obtained. 
//
// Return Value:
//
//     None. 
//
//--

#define IMPERSONATE( WorkContext ) SrvImpersonate( WorkContext )

//++
//
// VOID
// REVERT (
//     IN PWORK_CONTEXT WorkContext
//     )
//
// Routine Description:
//
//     This macro calls NtSetInformationThread with a NULL token in order
//     to revert to a thread's original context.   This should be called
//     after the IMPERSONATE macro and an open attempt. 
//
//     !!!  should this be made a function to save code size?   A function
//         would be a little slower. 
//
// Arguments:
//
//     None. 
//
// Return Value:
//
//     None. 
//
//--

#define REVERT( ) SrvRevert( )

//++
//
// VOID
// CHECK_FUNCTION_ACCESS (
//     IN ACCESS_MASK GrantedAccess,
//     IN UCHAR MajorFunction,
//     IN UCHAR MinorFunction,
//     IN ULONG IoControlCode,
//     OUT PNTSTATUS Status
//     )
//
// Routine Description:
//
//     This macro calls IoCheckFunctionAccess the check the client's
//     access to an I/O function identified by major and minor function
//     codes. 
//
//     *** This macro is here only because CHECK_FILE_INFORMATION_ACCESS
//         and CHECK_FS_INFORMATION_ACCESS are here. 
//
// Arguments:
//
//     GrantedAccess - The access granted to the client for the target
//         target file object. 
//
//     MajorFunction - The major function code of the requested
//         operation. 
//
//     MinorFunction - The minor function code of the requested
//         operation. 
//
//     IoControlCode - The control code for device or file system control. 
//
//     Status - Indicates whether the client has the requested access. 
//
// Return Value:
//
//     None. 
//
//--

#define CHECK_FUNCTION_ACCESS( GrantedAccess, MajorFunction, MinorFunction, \
                               IoControlCode, Status ) {                    \
            *(Status) = IoCheckFunctionAccess(                              \
                            (GrantedAccess),                                \
                            (MajorFunction),                                \
                            (MinorFunction),                                \
                            IoControlCode,                                  \
                            NULL,                                           \
                            NULL                                            \
                            );                                              \
        }

//++
//
// VOID
// CHECK_PAGING_IO_ACCESS (
//     IN PWORK_CONTEXT WorkContext
//     IN ACCESS_MASK GrantedAccess,
//     OUT PNTSTATUS Status
//     )
//
// Routine Description:
//
//     This macro checks to see if the client opened the file for execute. 
//     If so, then we allow the redirector to read the file.   If this is
//     an NT redirector, it must set the FLAGS2_PAGING_IO bit for access
//     to be allowed. 
//
// Arguments:
//
//     GrantedAccess - The access granted to the client for the target
//         target file object. 
//
//     WorkContext - A pointer to a work context block. 
//
//     Status - Indicates whether the client has the requested access. 
//
// Return Value:
//
//     None. 
//
//--

#define CHECK_PAGING_IO_ACCESS( WorkContext, GrantedAccess, Status ) {           \
                                                                            \
            if ( ((GrantedAccess) & FILE_EXECUTE) &&                        \
                 ( ! IS_NT_DIALECT( WorkContext->Connection->SmbDialect ) || \
                   WorkContext->RequestHeader->Flags2 &                     \
                       SMB_FLAGS2_PAGING_IO ) ) {                           \
                *Status = STATUS_SUCCESS;                                   \
            } else {                                                        \
                *Status = STATUS_ACCESS_DENIED;                             \
            }                                                               \
        }

//++
//
// VOID
// CHECK_FILE_INFORMATION_ACCESS (
//     IN ACCESS_MASK GrantedAccess,
//     IN UCHAR MajorFunction,
//     IN FILE_INFORMATION_CLASS FileInformationClass
//     OUT PNTSTATUS Status
//     )
//
// Routine Description:
//
//     This macro calls IoCheckFunctionAccess the check the client's
//     access to a query or set file information function identified by
//     major function code and information class. 
//
//     *** This macro is here because IoCheckFunctionAccess takes an
//         OPTIONAL FileInformationClass argument; this is argument is
//         therefore passed by reference.   Rather than force the caller
//         to allocate local storage so that it can pass a constant by
//         reference, we do it in the macro. 
//
// Arguments:
//
//     GrantedAccess - The access granted to the client for the target
//         target file object. 
//
//     MajorFunction - The major function code of the requested
//         operation. 
//
//     FileInformationClass - The type of file information being queried
//         or set. 
//
//     Status - Indicates whether the client has the requested access. 
//
// Return Value:
//
//     None. 
//
//--

#define CHECK_FILE_INFORMATION_ACCESS( GrantedAccess, MajorFunction,        \
                                        FileInformationClass, Status ) {    \
            FILE_INFORMATION_CLASS fileInfoClass = FileInformationClass;    \
            *(Status) = IoCheckFunctionAccess(                              \
                            (GrantedAccess),                                \
                            (MajorFunction),                                \
                            0,                                              \
                            0,                                              \
                            &fileInfoClass,                                 \
                            NULL                                            \
                            );                                              \
        }

//++
//
// VOID
// CHECK_FS_INFORMATION_ACCESS (
//     IN ACCESS_MASK GrantedAccess,
//     IN UCHAR MajorFunction,
//     IN FS_INFORMATION_CLASS FsInformationClass
//     OUT PNTSTATUS Status
//     )
//
// Routine Description:
//
//     This macro calls IoCheckFunctionAccess the check the client's
//     access to a query or set file system information function
//     identified by major function code and information class. 
//
//     *** This macro is here because IoCheckFunctionAccess takes an
//         OPTIONAL FsInformationClass argument; this is argument is
//         therefore passed by reference.   Rather than force the caller
//         to allocate local storage so that it can pass a constant by
//         reference, we do it in the macro. 
//
// Arguments:
//
//     GrantedAccess - The access granted to the client for the target
//         target file object. 
//
//     MajorFunction - The major function code of the requested
//         operation. 
//
//     FsInformationClass - The type of file system information being
//         queried or set. 
//
//     Status - Indicates whether the client has the requested access. 
//
// Return Value:
//
//     None. 
//
//--

#define CHECK_FS_INFORMATION_ACCESS( GrantedAccess, MajorFunction,          \
                                        FsInformationClass, Status ) {      \
            FS_INFORMATION_CLASS fsInfoClass = FsInformationClass;          \
            *(Status) = IoCheckFunctionAccess(                              \
                            (GrantedAccess),                                \
                            (MajorFunction),                                \
                            0,                                              \
                            0,                                              \
                            NULL,                                           \
                            &fsInfoClass                                     \
                            );                                              \
        }

//++
//
// PCHAR
// END_OF_REQUEST_SMB (
//     IN PWORK_CONTEXT WorkContext
//     )
//
// Routine Description:
//
//     This routine returns the address of the last valid location in
//     the request SMB associated with the specified work context
//     block. 
//
// Arguments:
//
//     WorkContext - Pointer to the work context block that owns the
//         request SMB. 
//
// Return Value:
//
//     PCHAR - Address of the last valid location in the request SMB. 
//
//--

#define END_OF_REQUEST_SMB( WorkContext )                       \
            ( (PCHAR)( (WorkContext)->RequestBuffer->Buffer ) + \
                (WorkContext)->RequestBuffer->DataLength - 1 )

//++
//
// PCHAR
// END_OF_TRANSACTION_PARAMETERS (
//     IN PTRANSACTION Transaction
//     )
//
// Routine Description:
//
//     This routine returns the address of the last valid location in
//     the InParameters buffer of the transaction block. 
//
// Arguments:
//
//     Transaction - a pointer to the transaction block to check. 
//
// Return Value:
//
//     PCHAR - Address of the last valid location in the InParameters
//         buffer of the transaction. 
//
//--

#define END_OF_TRANSACTION_PARAMETERS( Transaction )   \
            ( (PCHAR)( (Transaction)->InParameters ) + \
                (Transaction)->ParameterCount - 1 )

//++
//
// VOID
// INTERNAL_ERROR (
//     IN ULONG ErrorLevel,
//     IN PSZ Message,
//     IN PVOID Arg1 OPTIONAL,
//     IN PVOID Arg2 OPTIONAL
//     )
//
// Routine Description:
//
//     This routine handles logging of a server internal error. 
//
//     *** This macro must be usable in the FSD, at DPC level. 
//
// Arguments:
//
//     ErrorLevel - The severity of the error
//
//     Message    - An error message string in DbgPrint() format
//
//     Arg1       - Argument 1 for the error message
//
//     Arg2       - Argument 2 for the error message
//
//--

#define INTERNAL_ERROR( _level, _msg, _arg1, _arg2 ) {          \
    IF_DEBUG(ERRORS) {                                          \
        KdPrint(( (_msg), (_arg1), (_arg2) ));                  \
        KdPrint(( "\n" ));                                      \
        if ( (_level) >= ERROR_LEVEL_UNEXPECTED ) {             \
            IF_DEBUG(STOP_ON_ERRORS) {                          \
                DbgBreakPoint();                                \
            }                                                   \
        }                                                       \
    }                                                           \
    if ( (_level) == ERROR_LEVEL_EXPECTED ) {                   \
        ;                                                       \
    } else if ( (_level) == ERROR_LEVEL_UNEXPECTED ) {          \
        SrvStatistics. SystemErrors++;                           \
    } else {                                                    \
        ASSERT( (_level) > ERROR_LEVEL_UNEXPECTED );            \
        KeBugCheckEx(                                           \
            LM_SERVER_INTERNAL_ERROR,                           \
            BugCheckFileId | __LINE__,                          \
            (ULONG)(_arg1),                                     \
            (ULONG)(_arg2),                                     \
            0                                                   \
            );                                                  \
    }                                                           \
}

#define SRV_FILE_ACCESS     0x00010000
#define SRV_FILE_BLKCOMM    0x00020000
#define SRV_FILE_BLKCONN    0x00030000
#define SRV_FILE_BLKDEBUG   0x00040000
#define SRV_FILE_BLKENDP    0x00050000
#define SRV_FILE_BLKFILE    0x00060000
#define SRV_FILE_BLKSESS    0x00070000
#define SRV_FILE_BLKSHARE   0x00080000
#define SRV_FILE_BLKSRCH    0x00090000
#define SRV_FILE_BLKTABLE   0x000A0000
#define SRV_FILE_BLKTRANS   0x000B0000
#define SRV_FILE_BLKTREE    0x000C0000
#define SRV_FILE_BLKWORK    0x000D0000
#define SRV_FILE_COPY       0x000E0000
#define SRV_FILE_EA         0x000F0000
#define SRV_FILE_ERRORLOG   0x00100000
#define SRV_FILE_FSD        0x00110000
#define SRV_FILE_FSDDISP    0x00120000
#define SRV_FILE_FSDRAW     0x00130000
#define SRV_FILE_FSDSMB     0x00140000
#define SRV_FILE_FSPINIT    0x00150000
#define SRV_FILE_HEAPMGR    0x00160000
#define SRV_FILE_INFO       0x00170000
#define SRV_FILE_IPX        0x00180000
#define SRV_FILE_IO         0x00190000
#define SRV_FILE_LOCK       0x001A0000
#define SRV_FILE_LOCKCODE   0x001B0000
#define SRV_FILE_MOVE       0x001C0000
#define SRV_FILE_NETWORK    0x001D0000
#define SRV_FILE_OPEN       0x001E0000
#define SRV_FILE_OPLOCK     0x001F0000
#define SRV_FILE_PIPE       0x00200000
#define SRV_FILE_PRNSUPP    0x00210000
#define SRV_FILE_SCAVENGR   0x00220000
#define SRV_FILE_SHARE      0x00230000
#define SRV_FILE_SLMCHECK   0x00240000
#define SRV_FILE_SMBADMIN   0x00250000
#define SRV_FILE_SMBATTR    0x00260000
#define SRV_FILE_SMBCLOSE   0x00270000
#define SRV_FILE_SMBDIR     0x00280000
#define SRV_FILE_SMBFILE    0x00290000
#define SRV_FILE_SMBFIND    0x002A0000
#define SRV_FILE_SMBIOCTL   0x002B0000
#define SRV_FILE_SMBLOCK    0x002C0000
#define SRV_FILE_SMBMISC    0x002D0000
#define SRV_FILE_SMBMPX     0x002E0000
#define SRV_FILE_SMBNOTFY   0x002F0000
#define SRV_FILE_SMBOPEN    0x00300000
#define SRV_FILE_SMBPRINT   0x00310000
#define SRV_FILE_SMBPROC    0x00320000
#define SRV_FILE_SMBRAW     0x00330000
#define SRV_FILE_SMBRDWRT   0x00340000
#define SRV_FILE_SMBSRCH    0x00350000
#define SRV_FILE_SMBSUPP    0x00360000
#define SRV_FILE_SMBTRANS   0x00370000
#define SRV_FILE_SMBTREE    0x00380000
#define SRV_FILE_SRVCONFG   0x00390000
#define SRV_FILE_SRVDATA    0x003A0000
#define SRV_FILE_SRVSTAT    0x003B0000
#define SRV_FILE_SRVSTRNG   0x003C0000
#define SRV_FILE_SVCCDEV    0x003D0000
#define SRV_FILE_SVCCDEVQ   0x003E0000
#define SRV_FILE_SVCCONN    0x003F0000
#define SRV_FILE_SVCFILE    0x00400000
#define SRV_FILE_SVCSESS    0x00410000
#define SRV_FILE_SVCSHARE   0x00420000
#define SRV_FILE_SVCSRV     0x00430000
#define SRV_FILE_SVCSTATS   0x00440000
#define SRV_FILE_SVCSUPP    0x00450000
#define SRV_FILE_SVCXPORT   0x00460000
#define SRV_FILE_WORKER     0x00470000
#define SRV_FILE_XSSUPP     0x00480000
#define SRV_FILE_BLKDIR     0x00490000
#define SRV_FILE_DFS        0x004A0000

//
// Error levels used with INTERNAL_ERROR
//

#define ERROR_LEVEL_EXPECTED    0
#define ERROR_LEVEL_UNEXPECTED  1
#define ERROR_LEVEL_IMPOSSIBLE  2
#define ERROR_LEVEL_FATAL       3


//
// Helper macros for dealing with unqiue identifiers (UID, PID, TID,
// FID, SID).   In these macros, id, index, and sequence should all be
// USHORTs. 
//

#define TID_INDEX(id) (CSHORT)( (id) & 0x07FF )
#define TID_SEQUENCE(id) (CSHORT)( (id) >> 11 )
#define MAKE_TID(index, sequence) (CSHORT)( ((sequence) << 11) | (index) )
#define INCREMENT_TID_SEQUENCE(id) (id) = (CSHORT)(( (id) + 1 ) & 0x1F);

#define UID_INDEX(id) (CSHORT)( (id) & 0x07FF )
#define UID_SEQUENCE(id) (CSHORT)( (id) >> 11 )
#define MAKE_UID(index, sequence) (CSHORT)(( (sequence) << 11) | (index) )
#define INCREMENT_UID_SEQUENCE(id) (id) = (CSHORT)(( (id) + 1 ) & 0x1F);

#define FID_INDEX(id) (CSHORT)( (id) & 0x07FF )
#define FID_SEQUENCE(id) (CSHORT)( (id) >> 11 )
#define MAKE_FID(index, sequence) (CSHORT)( ((sequence) << 11) | (index) )
#define INCREMENT_FID_SEQUENCE(id) (id) = (CSHORT)(( (id) + 1 ) & 0x1F);

//
// *** Note that the macros relating to search IDs are somewhat
//     different from those for other kinds of IDs.   The SID is stored
//     in a Resume Key (see smb.h for its definition), in discontiguous
//     fields.   The macros for getting the SID therefore take a pointer
//     to a resume key. 
//

#define SID_INDEX(ResumeKey)                                                 \
            (CSHORT)( ( ((ResumeKey)->Reserved & 0x7) << 8 ) |               \
                      (ResumeKey)->Sid )
#define SID_SEQUENCE(ResumeKey)                                              \
            (CSHORT)( ((ResumeKey)->Reserved & 0x18) >> 3 )
#define SID(ResumeKey)                                                       \
            (USHORT)( ( ((ResumeKey)->Reserved & 0x1F) << 8 ) |              \
                      (ResumeKey)->Sid )
#define INCREMENT_SID_SEQUENCE(id) (id) = (CSHORT)(( (id) + 1 ) & 0x3);
#define SET_RESUME_KEY_SEQUENCE(ResumeKey,Sequence) {                       \
            (ResumeKey)->Reserved &= ~0x18;                                 \
            (ResumeKey)->Reserved |= (Sequence) << 3;                       \
        }
#define SET_RESUME_KEY_INDEX(ResumeKey,Index) {                             \
            (ResumeKey)->Reserved = (UCHAR)( (ULONG)(Index) >> 8 );         \
            (ResumeKey)->Reserved &= (UCHAR)0x7;                            \
            (ResumeKey)->Sid = (UCHAR)( (Index) & (USHORT)0xFF );           \
        }

//
// The following SID macros are used in the same way as the macros for
// other IDs (see above, TID, FID, UID).   The Find2 protocols (Transaction2)
// use a USHORT as a SID, rather than various fields in a resume key. 
//

#define SID_INDEX2(Sid)                                                      \
            (USHORT)( (Sid) & 0x7FF )
#define SID_SEQUENCE2(Sid)                                                   \
            (USHORT)( ((Sid) & 0x1800) >> 11 )
#define MAKE_SID(Index,Sequence)                                             \
            (USHORT)( ((Sequence) << 11) | (Index) )

//
// InitializeObjectAttributes, with security. 
//

#define SrvInitializeObjectAttributes(ObjectAttributes,p1,p2,p3,p4)   \
            InitializeObjectAttributes(ObjectAttributes,p1,p2,p3,p4); \
            (ObjectAttributes)->SecurityQualityOfService = (PVOID)&SrvSecurityQOS;

#define SrvInitializeObjectAttributes_U(ObjectAttributes,p1,p2,p3,p4)   \
            InitializeObjectAttributes(ObjectAttributes,p1,p2,p3,p4); \
            (ObjectAttributes)->SecurityQualityOfService = (PVOID)&SrvSecurityQOS;


//
// Macro used to map from NT attributes to SMB attributes.   The output is placed
//   in *_SmbAttributes
//
#define SRV_NT_ATTRIBUTES_TO_SMB( _NtAttributes, _Directory, _SmbAttributes ) {\
    if ( _NtAttributes == FILE_ATTRIBUTE_NORMAL ) {             \
        *(_SmbAttributes) = 0;                                  \
    } else {                                                    \
        *(_SmbAttributes) = (USHORT)(_NtAttributes &            \
                            ( FILE_ATTRIBUTE_READONLY |         \
                              FILE_ATTRIBUTE_HIDDEN   |         \
                              FILE_ATTRIBUTE_SYSTEM   |         \
                              FILE_ATTRIBUTE_ARCHIVE  |         \
                              FILE_ATTRIBUTE_DIRECTORY )) ;    \
    }                                                           \
    if ( _Directory ) {                                         \
        *(_SmbAttributes) |= SMB_FILE_ATTRIBUTE_DIRECTORY;     \
    }                                                           \
}


//    This macro converts attributes from SMB format to NT format. 
//
//   The attribute bits in the SMB protocol (same as OS/2) have the
//    following meanings:
//
//      bit 0 - read only file
//      bit 1 - hidden file
//      bit 2 - system file
//      bit 3 - reserved
//      bit 4 - directory
//      bit 5 - archive file
//
//    NT file attributes are similar, but have a bit set for a "normal"
//    file (no other bits set) and do not have a bit set for directories. 
//    Instead, directory information is passed to and from APIs as a
//    BOOLEAN parameter. 

#define SRV_SMB_ATTRIBUTES_TO_NT( _SmbAttributes, _Directory, _NtAttributes ) {\
    ULONG _attr = (_SmbAttributes);                                     \
    *(_NtAttributes) = _attr &                                          \
                            ( SMB_FILE_ATTRIBUTE_READONLY |             \
                              SMB_FILE_ATTRIBUTE_HIDDEN   |             \
                              SMB_FILE_ATTRIBUTE_SYSTEM   |             \
                              SMB_FILE_ATTRIBUTE_ARCHIVE  |             \
                              SMB_FILE_ATTRIBUTE_DIRECTORY );           \
    if ( _attr == 0 ) {                                                 \
        *(_NtAttributes) = FILE_ATTRIBUTE_NORMAL;                       \
    }                                                                   \
    if( _Directory ) {                                                  \
        if ( (_attr & SMB_FILE_ATTRIBUTE_DIRECTORY) ! = 0 ) {            \
            *(PBOOLEAN)(_Directory) = TRUE;                             \
        } else {                                                        \
            *(PBOOLEAN)(_Directory) = FALSE;                            \
        }                                                               \
    }                                                                   \
}

//
// ULONG
// MAP_SMB_INFO_TYPE_TO_NT (
//     IN PULONG Map,
//     IN ULONG SmbInformationLevel
//     )
//
// Routine description:
//
//     This macro maps SMB_INFO level to Nt info level. 
//
// Arguments:
//
//     Map - An array of ULONGS.   The first ulong is the base SMB info level
//          the seconds through Nth are NT mappings of the corresponding
//          SMB info levels. 
//
//     Level - The SMB info level to map. 
//
// Return Value:
//
//     NtInfoLevel - The NT info level. 
//

#define MAP_SMB_INFO_TYPE_TO_NT( Map, Level )   Map[Level - Map[0] + 1]

//
// ULONG
// MAP_SMB_INFO_TO_MIN_NT_SIZE (
//     IN PULONG Map,
//     IN ULONG SmbINformationLevel
//     )
//
// Routine Description:
//
//  This macro maps SMB_INFO level to the minimum buffer size needed to make the
//    NtQueryInformationFile call
//
// Arguments:
//     Map - An array of ULONGS.   The first ulong is the base SMB info level,
//          the second is the NT info level, and the third through Nth are the
//          NT mapings for the sizes of the NT info levels. 
//
//     Level - The SMB info level to find the buffer size
//
// Return Value:
//
//    NtMinumumBufferSIze - the minumum buffer size for the request

#define MAP_SMB_INFO_TO_MIN_NT_SIZE( Map, Level )  Map[ Level - Map[0] + 2]

//
// BOOLEAN
// SMB_IS_UNICODE(
//     IN PWORK_CONTEXT WorkContext
//     )
//
// Routine description:
//
//     This macro discovers whether or not an SMB contains Unicode
//     ANSI strings. 
//
// Arguments:
//
//     WorkContext - A pointer to the active work context
//
// Return Value:
//
//     TRUE - The SMB strings are unicode. 
//     FALSE - The SMB strings are ANSI. 
//

#define SMB_IS_UNICODE( WorkContext )  \
            (BOOLEAN)( ((WorkContext)->RequestHeader->Flags2 & SMB_FLAGS2_UNICODE ) ! = 0 )

//
// BOOLEAN
// SMB_CONTAINS_DFS_NAME(
//      IN PWORK_CONTEXT WorkContext
//      )
//
// Routine description:
//
//      This macro discovers whether or not an SMB contains a pathname
//      referring to the DFS namespace. 
//
// Arguments:
//
//      WorkContext - A pointer to the active work context
//
// Return Value:
//
//      TRUE  - The SMB has a DFS name in it
//      FALSE - The SMB does not have a DFS name in it
//

#define SMB_CONTAINS_DFS_NAME( WorkContext ) \
            (BOOLEAN)( ((WorkContext)->RequestHeader->Flags2 & SMB_FLAGS2_DFS ) ! = 0 )

//
// BOOLEAN
// SMB_MARK_AS_DFS_NAME(
//      IN PWORK_CONTEXT WorkContext
//      )
//
// Routine description:
//
//      This macro marks the WorkContext as containing a Dfs name.  This is
//      used when processing SMBs that contain two path names; after the first
//      path name has been canonicalized, the SMB is marked as being
//      Dfs-Translated by SrvCanonicalizePathName, so the attempt to
//      canonicalize the second path in the SMB will fail to do the
//      Dfs translation.  Calling this macro will ensure that the next call
//      to SrvCanonicalizePathName will go through Dfs translation
//
// Arguments:
//
//      WorkContext - A pointer to the active work context
//
// Return Value:
//
//      None
//

#define SMB_MARK_AS_DFS_NAME( WorkContext ) \
        (WorkContext)->RequestHeader->Flags2 |= SMB_FLAGS2_DFS

//
// BOOLEAN
// SMB_MARK_AS_DFS_TRANSLATED(
//      IN PWORK_CONTEXT WorkContext
//      )
//
// Routine description:
//
//      This macro marks the WorkContext as having been through a Dfs
//      translation for the express purpose of preventing a second attempt
//      at Dfs translation on the translated name. 
//
// Arguments:
//
//      WorkContext - A pointer to the active work context
//
// Return Value:
//
//      None
//

#define SMB_MARK_AS_DFS_TRANSLATED( WorkContext ) \
        (WorkContext)->RequestHeader->Flags2 &= (~SMB_FLAGS2_DFS)

//
// BOOLEAN
// CLIENT_CAPABLE_OF(
//     IN ULONG Capability,
//     IN PCONNECTION Connection
//     )
//
// Routine description:
//
//     This macro discovers whether or not a client is supports a
//     certain capability. 
//
//     *Warning* This macro assumes that only one capability is being tested. 
//
// Arguments:
//
//     Connection - A pointer to the active connection
//
// Return Value:
//
//     TRUE - Capability supported. 
//     FALSE - otherwise. 
//

#define CLIENT_CAPABLE_OF( Capability, Connection ) \
            (BOOLEAN) ( ((Connection)->ClientCapabilities & (CAP_ ## Capability)) ! = 0 )

//
// BOOLEAN
// SMB_IS_PIPE_PREFIX(
//     IN PWORK_CONTEXT WorkContext
//     IN PVOID Name
//     )
//
// Routine description:
//
//     This macro discovers whether or not a path prefix is named pipe prefix
//     for a transaction SMB. 
//
// Arguments:
//
//     WorkContext - A pointer to the active work context
//     Name - A pointer to a name string.   This may be ANSI or Unicode
//
// Return Value:
//
//     TRUE - The name is a pipe prefix. 
//     FALSE - The name is not a pipe prefix. 
//

#define SMB_NAME_IS_PIPE_PREFIX( WorkContext, Name )                    \
                                                                        \
       ( ( ! SMB_IS_UNICODE( WorkContext ) &&                            \
           strnicmp(                                                    \
                (PCHAR)Name,                                            \
                SMB_PIPE_PREFIX,                                        \
                SMB_PIPE_PREFIX_LENGTH                                  \
                ) == 0                                                  \
         )                                                              \
        ||                                                              \
         ( SMB_IS_UNICODE( WorkContext ) &&                             \
           wcsnicmp(                                                    \
                (PWCH)Name,                                             \
                UNICODE_SMB_PIPE_PREFIX,                                \
                UNICODE_SMB_PIPE_PREFIX_LENGTH / sizeof(WCHAR)          \
                ) == 0                                                  \
         )                                                              \
       )

//
// BOOLEAN
// SMB_IS_PIPE_API(
//     IN PWORK_CONTEXT WorkContext
//     IN PVOID Name
//     )
//
// Routine description:
//
//     This macro discovers whether or not a transaction name indicates
//     that the transaction is for a LM remote API request. 
//
// Arguments:
//
//     WorkContext - A pointer to the active work context
//     Name - A pointer to a name string.   This may be ANSI or Unicode
//
// Return Value:
//
//     TRUE - The name is a remote API request. 
//     FALSE - The name is not a remote API request. 
//

#define SMB_NAME_IS_PIPE_API( WorkContext, Name )                       \
                                                                        \
       ( ( ! SMB_IS_UNICODE( WorkContext ) &&                            \
           stricmp(                                                     \
                (PCHAR)Name,                                            \
                StrPipeApiOem                                           \
                ) == 0                                                  \
         )                                                              \
                        ||                                              \
         ( SMB_IS_UNICODE( WorkContext ) &&                             \
           wcsicmp(                                                     \
                (PWCH)Name,                                             \
                StrPipeApi                                              \
                ) == 0                                                  \
         )                                                              \
       )

//
// VOID
// SrvReferenceConnection (
//     PCONNECTION Connection
//     )
//
// Routine Description:
//
//     This macro increments the reference count on a connection block. 
//
//     !!!  Users of this macro must be nonpageable. 
//
// Arguments:
//
//     Connection - Address of connection
//
// Return Value:
//
//     None. 
//

#define SrvReferenceConnection( _conn_ )    {                           \
            ASSERT( GET_BLOCK_TYPE(_conn_) ==                           \
                                    BlockTypeConnection );              \
            UPDATE_REFERENCE_HISTORY( (_conn_), FALSE );                \
            (VOID) ExInterlockedAddUlong(                               \
                        &(_conn_)->BlockHeader. ReferenceCount,          \
                        1,                                              \
                        (_conn_)->EndpointSpinLock                      \
                        );                                              \
            IF_DEBUG(REFCNT) {                                          \
                SrvPrint2(                                              \
                "Referencing connection %lx; new refcnt %lx\n",         \
                (_conn_), (_conn_)->BlockHeader. ReferenceCount);        \
            }                                                           \
        }

//
// VOID
// SrvReferenceConnectionLocked (
//     PCONNECTION Connection
//     )
//
// Routine Description:
//
//     This macro increments the reference count on a connection block. 
//     Invokers of this macro must hold the SrvFsdSpinLock. 
//
// Arguments:
//
//     Connection - Address of connection
//
// Return Value:
//
//     None. 
//

#define SrvReferenceConnectionLocked( _conn_ )    {                     \
            ASSERT( GET_BLOCK_TYPE(_conn_) ==                           \
                                    BlockTypeConnection );              \
            UPDATE_REFERENCE_HISTORY( (_conn_), FALSE );                \
            (_conn_)->BlockHeader. ReferenceCount++;                     \
            IF_DEBUG(REFCNT) {                                          \
                SrvPrint2(                                              \
                    "Referencing connection %lx; new refcnt %lx\n",     \
                    (_conn_), (_conn_)->BlockHeader. ReferenceCount );   \
            }                                                           \
        }

//
// VOID
// SrvReferenceSession (
//     PSESSION Session
//     )
//
// Routine Description:
//
//     This macro increments the reference count on a session block. 
//
// Arguments:
//
//     Session - Address of session
//
// Return Value:
//
//     None. 
//

#define SrvReferenceSession( _sess_ )    {                              \
            ASSERT( (_sess_)->NonpagedHeader->ReferenceCount > 0 );     \
            ASSERT( GET_BLOCK_TYPE(_sess_) == BlockTypeSession );       \
            UPDATE_REFERENCE_HISTORY( (_sess_), FALSE );                \
            InterlockedIncrement(                                       \
                &(_sess_)->NonpagedHeader->ReferenceCount               \
                );                                                      \
            IF_DEBUG(REFCNT) {                                          \
                SrvPrint2(                                              \
                    "Referencing session %lx; new refcnt %lx\n",        \
                  (_sess_), (_sess_)->NonpagedHeader->ReferenceCount ); \
            }                                                           \
        }

//
// VOID
// SrvReferenceTransaction (
//     PTRANSACTION Transaction
//     )
//
// Routine Description:
//
//     This macro increments the reference count on a transaction block. 
//
// Arguments:
//
//     Transaction - Address of transaction
//
// Return Value:
//
//     None. 
//

#define SrvReferenceTransaction( _trans_ )    {                         \
            ASSERT( (_trans_)->NonpagedHeader->ReferenceCount > 0 );    \
            ASSERT( GET_BLOCK_TYPE(_trans_) == BlockTypeTransaction );  \
            UPDATE_REFERENCE_HISTORY( (_trans_), FALSE );               \
            InterlockedIncrement(                                       \
                &(_trans_)->NonpagedHeader->ReferenceCount              \
                );                                                      \
            IF_DEBUG(REFCNT) {                                          \
                SrvPrint2(                                              \
                    "Referencing transaction %lx; new refcnt %lx\n",    \
                (_trans_), (_trans_)->NonpagedHeader->ReferenceCount ); \
            }                                                           \
        }

//
// VOID
// SrvReferenceTreeConnect (
//     PTREE_CONNECT TreeConnect
//     )
//
// Routine Description:
//
//     This macro increments the reference count on a tree connect block. 
//     Invokers of this macro must hold TreeConnect->Connection->Lock. 
//
// Arguments:
//
//     TreeConnect - Address of tree connect
//
// Return Value:
//
//     None. 
//

#define SrvReferenceTreeConnect( _tree_ )    {                          \
            ASSERT( (_tree_)->NonpagedHeader->ReferenceCount > 0 );     \
            ASSERT( GET_BLOCK_TYPE(_tree_) == BlockTypeTreeConnect );   \
            UPDATE_REFERENCE_HISTORY( (_tree_), FALSE );                \
            InterlockedIncrement(                                       \
                &(_tree_)->NonpagedHeader->ReferenceCount               \
                );                                                      \
            IF_DEBUG(REFCNT) {                                          \
                SrvPrint2(                                              \
                    "Referencing tree connect %lx; new refcnt %lx\n",   \
                  (_tree_), (_tree_)->NonpagedHeader->ReferenceCount ); \
            }                                                           \
        }

//
// VOID
// SrvReferenceWorkItem (
//     IN PWORK_CONTEXT WorkContext
//     )
//
// Routine Description:
//
//     This function increments the reference count of a work context block. 
//     Invokers of this macro must hold WorkContext->SpinLock. 
//
// Arguments:
//
//     WORK_CONTEXT - Pointer to the work context block to reference. 
//
// Return Value:
//
//     None. 
//

#define SrvReferenceWorkItem( _wc_ ) {                                      \
        ASSERT( (LONG)(_wc_)->BlockHeader. ReferenceCount >= 0 );            \
        ASSERT( (GET_BLOCK_TYPE(_wc_) == BlockTypeWorkContextInitial) ||    \
                (GET_BLOCK_TYPE(_wc_) == BlockTypeWorkContextNormal) ||     \
                (GET_BLOCK_TYPE(_wc_) == BlockTypeWorkContextRaw) );        \
        UPDATE_REFERENCE_HISTORY( (_wc_), FALSE );                          \
        (_wc_)->BlockHeader. ReferenceCount++;                               \
        IF_DEBUG(REFCNT) {                                                  \
            SrvPrint2(                                                      \
              "Referencing WorkContext 0x%lx; new refcnt 0x%lx\n",          \
              (_wc_), (_wc_)->BlockHeader. ReferenceCount );                 \
        }                                                                   \
    }

//
// VOID
// SRV_START_SEND (
//     IN OUT PWORK_CONTEXT WorkContext,
//     IN PMDL Mdl OPTIONAL,
//     IN ULONG SendOptions,
//     IN PRESTART_ROUTINE FsdRestartRoutine,
//     IN PRESTART_ROUTINE FspRestartRoutine
//     )
//
// Routine Description:
//
//     This macro calls the SrvStartSend routine.   It sets the fsd and
//     fsp restart routines before calling it. 
//
// Arguments:
//
//     WorkContext - Supplies a pointer to a Work Context block. 
//
//     Mdl - Supplies a pointer to the first (or only) MDL describing the
//         data that is to be sent. 
//
//     SendOptions - Supplied TDI send options. 
//
//     FsdRestartRoutine - Supplies the address of the FSD routine that is
//         to be called when the I/O completes.   (Often, this is
//         SrvQueueWorkToFspAtDpcLevel.) 
//
//     FspRestartRoutine - Supplies the address of the FSP routine that is
//         to be called when the FSD queues the work item to the FSP. 
//

#define SRV_START_SEND( _wc, _mdl, _opt, _compl, _fsdRestart, _fspRestart ) { \
        ASSERT( !(_ wc)->Endpoint->IsConnectionless );                 \
        if ( (_fspRestart) ! = NULL ) {                                \
            (_wc)->FspRestartRoutine = (_fspRestart);                 \
        }                                                             \
        if ( (_fsdRestart) ! = NULL ) {                                \
            (_wc)->FsdRestartRoutine = (_fsdRestart);                 \
        }                                                             \
        SrvStartSend( (_wc), (_compl), (_mdl), (_opt) );              \
    }

#define SRV_START_SEND_2( _wc, _compl, _fsdRestart, _fspRestart ) {   \
        (_wc)->ResponseBuffer->Mdl->ByteCount =                       \
                            (_wc)->ResponseBuffer->DataLength;        \
        if ( (_fspRestart) ! = NULL ) {                                \
            (_wc)->FspRestartRoutine = (_fspRestart);                 \
        }                                                             \
        if ( (_fsdRestart) ! = NULL ) {                                \
            (_wc)->FsdRestartRoutine = (_fsdRestart);                 \
        }                                                             \
        if ( !(_ wc)->Endpoint->IsConnectionless ) {                   \
            SrvStartSend2( (_wc), (_compl) );                         \
        } else {                                                      \
            SrvIpxStartSend( (_wc), (_compl) );                       \
        }                                                             \
    }

//
// VOID
// SrvUpdateErrorCount(
//     PSRV_ERROR_RECORD ErrorRecord,
//     BOOLEAN IsError
//     )
// /*++
//
// Routine Description:
//
//     This routine updates the server's record of successful / unsuccesful
//     operations. 
//
// Arguments:
//
//     IsError - TRUE - A server error occured
//               FALSE - A server operation was attempted
//
// Return Value:
//
//    None. 
//

#if 0
#define SrvUpdateErrorCount( ErrorRecord, IsError )                     \
        if ( IsError ) {                                                \
            (ErrorRecord)->FailedOperations++;                          \
        } else {                                                        \
            (ErrorRecord)->SuccessfulOperations++;                      \
        }
#else
#define SrvUpdateErrorCount( ErrorRecord, IsError )
#endif

//
// VOID
// SrvUpdateStatistics (
//     PWORK_CONTEXT WorkContext,
//     ULONG BytesSent,
//     UCHAR SmbCommand
//     )
//
// Routine Description:
//
//     Macro to update the server statistics database to reflect the
//     work item that is being completed. 
//
// Arguments:
//
//     WorkContext - Pointer to the workcontext block containing
//         the statistics for this request. 
//
//     BytesSent - Supplies a count of the number of bytes of response data
//         sent as a result of the current SMB. 
//
//     SmbCommand - The SMB command code of the current operation. 
//
//
// Return Value:
//
//    None. 
//

#if SRVDBG_STATS
VOID SRVFASTCALL
SrvUpdateStatistics2 (
    PWORK_CONTEXT WorkContext,
    UCHAR SmbCommand
    );
#define UPDATE_STATISTICS2(_work,_cmd) SrvUpdateStatistics2((_work),(_cmd))
#else
#define UPDATE_STATISTICS2(_work,_cmd)
#endif

#define UPDATE_STATISTICS(_work,_sent,_cmd ) {                 \
    _work->CurrentWorkQueue->stats. BytesSent += (_sent);       \
    UPDATE_STATISTICS2((_work),(_cmd));                        \
}

#define UPDATE_READ_STATS( _work, _count) {                    \
    _work->CurrentWorkQueue->stats. ReadOperations++;           \
    _work->CurrentWorkQueue->stats. BytesRead += (_count);      \
}

#define UPDATE_WRITE_STATS(_work, _count) {                    \
    _work->CurrentWorkQueue->stats. WriteOperations++;          \
    _work->CurrentWorkQueue->stats. BytesWritten += (_count);   \
}

//
// VOID
// SrvFsdSendResponse (
//     IN OUT PWORK_CONTEXT WorkContext
//     )
//
// Routine Description:
//
//     This routine is called when all request processing on an SMB is
//     complete and a response is to be sent.   It starts the sending of
//     that response.   The work item will be queued for final cleanup when
//     the send completes. 
//
// Arguments:
//
//     WorkContext - Supplies a pointer to the work context block
//         containing information about the SMB. 
//
// Return Value:
//
//    None. 
//

#define SrvFsdSendResponse( _wc ) {                               \
                                                                  \
    (_wc)->ResponseBuffer->DataLength =                           \
                    (CLONG)( (PCHAR)(_wc)->ResponseParameters -   \
                                (PCHAR)(_wc)->ResponseHeader );   \
    (_wc)->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;    \
    SRV_START_SEND_2( (_wc), SrvFsdRestartSmbAtSendCompletion, NULL, NULL );    \
    }

//
// VOID
// SrvFsdSendResponse2 (
//     IN OUT PWORK_CONTEXT WorkContext,
//     IN PRESTART_ROUTINE FspRestartRoutine
//     )
//
// Routine Description:
//
//     This routine is identical to SrvFsdSendResponse, except that
//     processing restarts after the send in the FSP, not the FSD. 
//
//     *** If you change either SrvFsdSendResponse or SrvFsdSendResponse2,
//         CHANGE BOTH OF THEM! 
//
// Arguments:
//
//     WorkContext - Supplies a pointer to the work context block
//         containing information about the SMB. 
//
//     FspRestartRoutine - Supplies the address of the restart routine in
//         the FSP that is to be called when the TdiSend completes. 
//
// Return Value:
//
//     None. 
//

#define SrvFsdSendResponse2( _wc, _fspRestart ) {                       \
                                                                        \
    (_wc)->ResponseBuffer->DataLength =                                 \
                    (CLONG)( (PCHAR)(_wc)->ResponseParameters -         \
                                (PCHAR)(_wc)->ResponseHeader );         \
    (_wc)->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;          \
    SRV_START_SEND_2((_wc), SrvQueueWorkToFspAtSendCompletion, NULL, (_fspRestart));\
    }

//
// VOID
// ParseLockData (
//     IN BOOLEAN LargeFileLock,
//     IN PLOCKING_ANDX_RANGE SmallRange,
//     IN PNTLOCKING_ANDX_RANGE LargeRange,
//     OUT PUSHORT Pid,
//     OUT PLARGE_INTEGER Offset,
//     OUT PLARGE_INTEGER Length
//     )
// {
//

#define ParseLockData( _largeLock, _sr, _lr, _pid, _offset, _len ) {    \
                                                                        \
        if ( _largeLock ) {                                             \
            *(_pid) = SmbGetUshort( &(_lr)->Pid );                      \
            (_offset)->LowPart = SmbGetUlong( &(_lr)->OffsetLow );      \
            (_offset)->HighPart = SmbGetUlong( &(_lr)->OffsetHigh );    \
            (_len)->LowPart = SmbGetUlong( &(_lr)->LengthLow );         \
            (_len)->HighPart = SmbGetUlong( &(_lr)->LengthHigh );       \
        } else {                                                        \
            *(_pid) = SmbGetUshort( &(_sr)->Pid );                      \
            (_offset)->QuadPart = SmbGetUlong( &(_sr)->Offset );        \
            (_len)->QuadPart = SmbGetUlong( &(_sr)->Length );           \
        }                                                               \
    }

//
// CHECK_SEND_COMPLETION_STATUS( _status ) will log errors
// that occurs during send completion. 
//

#define CHECK_SEND_COMPLETION_STATUS( _status ) {                       \
    if ( ! NT_SUCCESS( _status ) ) {                                     \
        SrvCheckSendCompletionStatus( _status, __LINE__ );              \
    } else {                                                            \
        SrvUpdateErrorCount( &SrvNetworkErrorRecord, FALSE );           \
    }                                                                   \
}

//
// Definitions for unlockable code sections. 
//

#define SRV_CODE_SECTION_1AS  0
#define SRV_CODE_SECTION_8FIL 1
#define SRV_CODE_SECTION_MAX  2

extern SRV_LOCK SrvUnlockableCodeLock;

typedef struct _SECTION_DESCRIPTOR {
    PVOID Base;
    PVOID Handle;
    ULONG ReferenceCount;
} SECTION_DESCRIPTOR, *PSECTION_DESCRIPTOR;

extern SECTION_DESCRIPTOR SrvSectionInfo[SRV_CODE_SECTION_MAX];

#define UNLOCKABLE_CODE( _section )                                     \
    ASSERTMSG( "Unlockable code called while section not locked",       \
        SrvSectionInfo[SRV_CODE_SECTION_##_section##].Handle ! = NULL )

VOID
SrvReferenceUnlockableCodeSection (
    IN ULONG CodeSection
    );

VOID
SrvDereferenceUnlockableCodeSection (
    IN ULONG CodeSection
    );

//
// We only need to lock these sections on the workstation product,
//  since we lock them down in InitializeServer() if we're NTAS
//
#define REFERENCE_UNLOCKABLE_CODE( _section ) \
    if( ! SrvProductTypeServer ) SrvReferenceUnlockableCodeSection( SRV_CODE_SECTION_##_section## )

#define DEREFERENCE_UNLOCKABLE_CODE( _section ) \
    if( ! SrvProductTypeServer) SrvDereferenceUnlockableCodeSection( SRV_CODE_SECTION_##_section## )


//
// VOID
// SrvInsertWorkQueueTail (
//     IN OUT PWORK_QUEUE WorkQueue,
//     IN PQUEUEABLE_BLOCK_HEADER WorkItem
//     )

#if SRVDBG_STATS2
#define SrvInsertWorkQueueTail( _workQ, _workItem ) {                   \
    ULONG depth;                                                        \
    GET_SERVER_TIME( _workQ, &(_workItem)->Timestamp );                 \
    depth = KeInsertQueue( &(_workQ)->Queue, &(_workItem)->ListEntry ); \
    (_workQ)->ItemsQueued++;                                            \
    if ( (LONG)depth > (_workQ)->MaximumDepth ) {                       \
        (_workQ)->MaximumDepth = (LONG)depth;                           \
    }                                                                   \
}
#else
#define SrvInsertWorkQueueTail( _workQ, _workItem ) {                   \
    GET_SERVER_TIME( _workQ, &(_workItem)->Timestamp );                 \
    (VOID)KeInsertQueue( &(_workQ)->Queue, &(_workItem)->ListEntry );   \
}
#endif // SRVDBG_STATS2

//
// VOID
// SrvInsertWorkQueueHead (
//     IN OUT PWORK_QUEUE WorkQueue,
//     IN PQUEUEABLE_BLOCK_HEADER WorkItem
//     )
#define SrvInsertWorkQueueHead( _workQ, _workItem ) {                    \
    GET_SERVER_TIME( _workQ, &(_workItem)->Timestamp );                  \
    (VOID)KeInsertHeadQueue( &(_workQ)->Queue, &(_workItem)->ListEntry );\
}



#if DBG_STUCK
#define SET_OPERATION_START_TIME( _context ) \
    if( *(_context) ! = NULL ) KeQuerySystemTime( &((*(_context))->OpStartTime) );
#else
#define SET_OPERATION_START_TIME( _context )
#endif

//
// Allocate a WORK_CONTEXT structure. 
//
#define INITIALIZE_WORK_CONTEXT( _queue, _context ) {\
    (_context)->BlockHeader. ReferenceCount = 1; \
    GET_SERVER_TIME( _queue, &(_context)->Timestamp ); \
    RtlZeroMemory( &(_context)->Endpoint, sizeof( struct _WorkContextZeroBeforeReuse ) ); \
}

#define ALLOCATE_WORK_CONTEXT( _queue, _context ) {             \
    *(_context) = NULL;                                         \
    *(_context) = (PWORK_CONTEXT)InterlockedExchange( (PLONG)&(_queue)->FreeContext, (LONG)(*_context) ); \
    if( *(_context) ! = NULL ) {                                 \
        INITIALIZE_WORK_CONTEXT( _queue, *(_context) );         \
    } else {                                                    \
        *(_context) = SrvFsdGetReceiveWorkItem( _queue );       \
    }                                                           \
    SET_OPERATION_START_TIME( _context ) \
}

//
// Returns the work item to the free list. 
//

#define RETURN_FREE_WORKITEM( _wc ) \
    do {                                                                \
        PWORK_QUEUE _queue  = _wc->CurrentWorkQueue;                    \
        ASSERT( _queue >= SrvWorkQueues && _queue < eSrvWorkQueues );   \
        ASSERT( _wc->BlockHeader. ReferenceCount == 0 );                 \
        ASSERT( _wc->FreeList ! = NULL );                                \
        if( _queue->NeedWorkItem ) {                                    \
            if( InterlockedDecrement( &(_queue->NeedWorkItem) ) >= 0 ){ \
                _wc->FspRestartRoutine = SrvServiceWorkItemShortage;    \
                SrvInsertWorkQueueHead( _queue, _wc );                  \
                break;                                                  \
            } else {                                                    \
                InterlockedIncrement( &(_queue->NeedWorkItem) );        \
            }                                                           \
        }                                                               \
        _wc = (PWORK_CONTEXT)InterlockedExchange( (PLONG)&_queue->FreeContext, (LONG)_wc ); \
        if( _wc ) {                                                     \
            ExInterlockedPushEntrySList( _wc->FreeList, &_wc->SingleListEntry, &_queue->SpinLock );\
            InterlockedIncrement( &_queue->FreeWorkItems );             \
        }                                                               \
    } while (0);

//
// Our current work queue, based on our current processor
//

#if MULTIPROCESSOR

#define PROCESSOR_TO_QUEUE()  (&SrvWorkQueues[ KeGetCurrentProcessorNumber() ])

#else

#define PROCESSOR_TO_QUEUE() (&SrvWorkQueues[0])

#endif

#endif // def _SRVMACRO_
