/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    smbattr.c

Abstract:

    This module contains routines for processing the following SMBs:

        Query Information
        Set Information
        Query Information2
        Set Information2
        Query Path Information
        Set Path Information
        Query File Information
        Set File Information

Author:

    David Treadwell (davidtr) 27-Dec-1989
    Chuck Lenzmeier (chuckl)

Revision History:

--*/

#include "precomp.h"
#include "smbattr.tmh"
#pragma hdrstop

#define BugCheckFileId SRV_FILE_SMBATTR

#pragma pack(1)

typedef struct _FILESTATUS {
    SMB_DATE CreationDate;
    SMB_TIME CreationTime;
    SMB_DATE LastAccessDate;
    SMB_TIME LastAccessTime;
    SMB_DATE LastWriteDate;
    SMB_TIME LastWriteTime;
    _ULONG( DataSize );
    _ULONG( AllocationSize );
    _USHORT( Attributes );
    _ULONG( EaSize );           // this field intentionally misaligned!
} FILESTATUS, *PFILESTATUS;

#pragma pack()

STATIC
ULONG QueryFileInformation[] = {
         SMB_QUERY_FILE_BASIC_INFO,// Base level
         FileBasicInformation,     // Mapping for base level
         FileStandardInformation,
         FileEaInformation,
         FileNameInformation,
         FileAllocationInformation,
         FileEndOfFileInformation,
         0,                        // FileAllInformation
         FileAlternateNameInformation,
         FileStreamInformation,
         0,                        //Used to be FileOleAllInformation -- OBSOLETE
         FileCompressionInformation
};

STATIC
ULONG QueryFileInformationSize[] = {
        SMB_QUERY_FILE_BASIC_INFO,// Base level
        FileBasicInformation,     // Mapping for base level
        sizeof( FILE_BASIC_INFORMATION),
        sizeof( FILE_STANDARD_INFORMATION ),
        sizeof( FILE_EA_INFORMATION ),
        sizeof( FILE_NAME_INFORMATION ),
        sizeof( FILE_ALLOCATION_INFORMATION ),
        sizeof( FILE_END_OF_FILE_INFORMATION ),
        sizeof( FILE_ALL_INFORMATION ),
        sizeof( FILE_NAME_INFORMATION ),
        sizeof( FILE_STREAM_INFORMATION ),
        0,                      // Used to be sizeof( FILE_OLE_ALL_INFORMATION )
        sizeof( FILE_COMPRESSION_INFORMATION )
};

STATIC
ULONG SetFileInformation[] = {
         SMB_SET_FILE_BASIC_INFO,  // Base level
         FileBasicInformation,     // Mapping for base level
         FileDispositionInformation,
         FileAllocationInformation,
         FileEndOfFileInformation
};

STATIC
ULONG SetFileInformationSize[] = {
        SMB_SET_FILE_BASIC_INFO, // Base level
        FileBasicInformation,    // Mapping for base level
        sizeof( FILE_BASIC_INFORMATION ),
        sizeof( FILE_DISPOSITION_INFORMATION ),
        sizeof( FILE_ALLOCATION_INFORMATION ),
        sizeof( FILE_END_OF_FILE_INFORMATION )
};

STATIC
NTSTATUS
QueryPathOrFileInformation (
    IN PWORK_CONTEXT WorkContext,
    IN PTRANSACTION Transaction,
    IN USHORT InformationLevel,
    IN HANDLE FileHandle,
    OUT PRESP_QUERY_PATH_INFORMATION Response
    );

STATIC
NTSTATUS
SetPathOrFileInformation (
    IN PWORK_CONTEXT WorkContext,
    IN PTRANSACTION Transaction,
    IN USHORT InformationLevel,
    IN HANDLE FileHandle,
    OUT PRESP_SET_PATH_INFORMATION Response
    );

SMB_TRANS_STATUS
GenerateQueryPathInfoResponse (
    IN PWORK_CONTEXT WorkContext,
    IN NTSTATUS OpenStatus
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvSmbQueryInformation )
#pragma alloc_text( PAGE, SrvSmbSetInformation )
#pragma alloc_text( PAGE, SrvSmbQueryInformation2 )
#pragma alloc_text( PAGE, SrvSmbSetInformation2 )
#pragma alloc_text( PAGE, QueryPathOrFileInformation )
#pragma alloc_text( PAGE, SrvSmbQueryFileInformation )
#pragma alloc_text( PAGE, SrvSmbQueryPathInformation )
#pragma alloc_text( PAGE, GenerateQueryPathInfoResponse )
#pragma alloc_text( PAGE, SetPathOrFileInformation )
#pragma alloc_text( PAGE, SrvSmbSetFileInformation )
#pragma alloc_text( PAGE, SrvSmbSetPathInformation )
#endif


SMB_PROCESSOR_RETURN_TYPE
SrvSmbQueryInformation (
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes the QueryInformation SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h

--*/

{
    PREQ_QUERY_INFORMATION request;
    PRESP_QUERY_INFORMATION response;

    NTSTATUS   status    = STATUS_SUCCESS;
    SMB_STATUS SmbStatus = SmbStatusInProgress;
    PSESSION session;
    PTREE_CONNECT treeConnect;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING objectName;
    IO_STATUS_BLOCK ioStatusBlock;
    BOOLEAN isUnicode;
    FILE_NETWORK_OPEN_INFORMATION fileInformation;

    PAGED_CODE( );

    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_INFORMATION;
    SrvWmiStartContext(WorkContext);
    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "QueryInformation request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader,
                    WorkContext->ResponseHeader ));
        KdPrint(( "QueryInformation request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters ));
    }

    request = (PREQ_QUERY_INFORMATION)WorkContext->RequestParameters;
    response = (PRESP_QUERY_INFORMATION)WorkContext->ResponseParameters;

    //
    // If a session block has not already been assigned to the current
    // work context, verify the UID.  If verified, the address of the
    // session block corresponding to this user is stored in the WorkContext
    // block and the session block is referenced.
    //
    // Find tree connect corresponding to given TID if a tree connect
    // pointer has not already been put in the WorkContext block by an
    // AndX command.
    //

    status = SrvVerifyUidAndTid(
                WorkContext,
                &session,
                &treeConnect,
                ShareTypeDisk
                );

    if ( !NT_SUCCESS(status) ) {
        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbQueryInformation: Invalid UID or TID\n" ));
        }
        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // If the session has expired, return that info
    //
    if( session->IsSessionExpired )
    {
        status =  SESSION_EXPIRED_STATUS_CODE;
        SmbStatus = SmbStatusSendResponse;
        SrvSetSmbError( WorkContext, status );
        goto Cleanup;
    }

    //
    // Get the path name of the file to open relative to the share.
    //

    isUnicode = SMB_IS_UNICODE( WorkContext );

    status = SrvCanonicalizePathName(
            WorkContext,
            treeConnect->Share,
            NULL,
            (PVOID)(request->Buffer + 1),
            END_OF_REQUEST_SMB( WorkContext ),
            TRUE,
            isUnicode,
            &objectName
            );

    if( !NT_SUCCESS( status ) ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbQueryInformation: bad path name: %s\n",
                        (PSZ)request->Buffer + 1 ));
        }

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Initialize the object attributes structure.
    //

    SrvInitializeObjectAttributes_U(
        &objectAttributes,
        &objectName,
        (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
         session->UsingUppercasePaths) ? OBJ_CASE_INSENSITIVE : 0L,
        NULL,
        NULL
        );


    //
    // "Be the client" for access checking
    //
    status = IMPERSONATE( WorkContext );

    if( NT_SUCCESS( status ) ) {

        status = SrvGetShareRootHandle( treeConnect->Share );

        if( NT_SUCCESS( status ) ) {
            //
            // The file name is always relative to the share root
            //
            status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
            if( !NT_SUCCESS( status ) )
            {
                goto SnapError;
            }

            //
            // Get the information
            //
            if( IoFastQueryNetworkAttributes(
                &objectAttributes,
                FILE_READ_ATTRIBUTES,
                0,
                &ioStatusBlock,
                &fileInformation
                ) == FALSE ) {

                    SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
                    ioStatusBlock.Status = STATUS_OBJECT_PATH_NOT_FOUND;
            }

            status = ioStatusBlock.Status;

            //
            // If the media was changed and we can come up with a new share root handle,
            //  then we should retry the operation
            //
            if( SrvRetryDueToDismount( treeConnect->Share, status ) ) {

                status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
                if( !NT_SUCCESS( status ) )
                {
                    goto SnapError;
                }

                if( IoFastQueryNetworkAttributes(
                    &objectAttributes,
                    FILE_READ_ATTRIBUTES,
                    0,
                    &ioStatusBlock,
                    &fileInformation
                    ) == FALSE ) {

                        SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
                        ioStatusBlock.Status = STATUS_OBJECT_PATH_NOT_FOUND;
                }

                status = ioStatusBlock.Status;

            }

SnapError:
            SrvReleaseShareRootHandle( treeConnect->Share );
        }

        REVERT();
    }

    if ( !isUnicode ) {
        RtlFreeUnicodeString( &objectName );
    }

    //
    // Build the response SMB.
    //

    if ( !NT_SUCCESS(status) ) {

        if ( status == STATUS_ACCESS_DENIED ) {
            SrvStatistics.AccessPermissionErrors++;
        }

        IF_DEBUG(ERRORS) {
            KdPrint(( "SrvSmbQueryInformation: "
                        "SrvQueryInformationFileAbbreviated failed: %X\n", status ));
        }

        SrvSetSmbError( WorkContext, status );

    } else {

        USHORT smbFileAttributes;
        LARGE_INTEGER newTime;

        response->WordCount = 10;

        SRV_NT_ATTRIBUTES_TO_SMB(
            fileInformation.FileAttributes,
            fileInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY,
            &smbFileAttributes
        );

        SmbPutUshort( &response->FileAttributes, smbFileAttributes );

        //
        // Convert the time to that which the SMB protocol needs
        //
        ExSystemTimeToLocalTime( &fileInformation.LastWriteTime, &newTime );
        newTime.QuadPart += AlmostTwoSeconds;

        if ( !RtlTimeToSecondsSince1970( &newTime, &fileInformation.LastWriteTime.LowPart ) ) {
            fileInformation.LastWriteTime.LowPart = 0;
        }

        //
        // Round to 2 seconds
        //
        fileInformation.LastWriteTime.LowPart &= ~1;

        SmbPutUlong(
            &response->LastWriteTimeInSeconds,
            fileInformation.LastWriteTime.LowPart
            );

        SmbPutUlong( &response->FileSize, fileInformation.EndOfFile.LowPart );
        RtlZeroMemory( (PVOID)&response->Reserved[0], sizeof(response->Reserved) );
        SmbPutUshort( &response->ByteCount, 0 );

        WorkContext->ResponseParameters = NEXT_LOCATION(
                                            response,
                                            RESP_QUERY_INFORMATION,
                                            0
                                            );
    }

    SmbStatus = SmbStatusSendResponse;

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbQueryInformation


SMB_PROCESSOR_RETURN_TYPE
SrvSmbSetInformation (
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes the SetInformation SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h

--*/

{
    PREQ_SET_INFORMATION request;
    PRESP_SET_INFORMATION response;

    NTSTATUS   status    = STATUS_SUCCESS;
    SMB_STATUS SmbStatus = SmbStatusInProgress;
    PSESSION session;
    PTREE_CONNECT treeConnect;
    HANDLE fileHandle;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING objectName;
    IO_STATUS_BLOCK ioStatusBlock;
    BOOLEAN isUnicode;
    FILE_BASIC_INFORMATION fileBasicInformation;
    ULONG lastWriteTimeInSeconds;

    PAGED_CODE( );

    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_SET_INFORMATION;
    SrvWmiStartContext(WorkContext);
    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "SetInformation request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader,
                    WorkContext->ResponseHeader ));
        KdPrint(( "SetInformation request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters ));
    }

    request = (PREQ_SET_INFORMATION)WorkContext->RequestParameters;
    response = (PRESP_SET_INFORMATION)WorkContext->ResponseParameters;

    //
    // If a session block has not already been assigned to the current
    // work context, verify the UID.  If verified, the address of the
    // session block corresponding to this user is stored in the WorkContext
    // block and the session block is referenced.
    //
    // Find tree connect corresponding to given TID if a tree connect
    // pointer has not already been put in the WorkContext block by an
    // AndX command.
    //

    status = SrvVerifyUidAndTid(
                  WorkContext,
                  &session,
                  &treeConnect,
                  ShareTypeDisk
                  );

    if ( !NT_SUCCESS(status) ) {
        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetInformation: Invalid UID and TID\n" ));
        }
        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // If the session has expired, return that info
    //
    if( session->IsSessionExpired )
    {
        status =  SESSION_EXPIRED_STATUS_CODE;
        SmbStatus = SmbStatusSendResponse;
        SrvSetSmbError( WorkContext, status );
        goto Cleanup;
    }

    if ( treeConnect == NULL ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetInformation: Invalid TID: 0x%lx\n",
                SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid ) ));
        }

        SrvSetSmbError( WorkContext, STATUS_SMB_BAD_TID );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Concatenate PathName from the share block and PathName from the
    // incoming SMB to generate the full path name to the file.
    //

    isUnicode = SMB_IS_UNICODE( WorkContext );

    status = SrvCanonicalizePathName(
            WorkContext,
            treeConnect->Share,
            NULL,
            (PVOID)(request->Buffer + 1),
            END_OF_REQUEST_SMB( WorkContext ),
            TRUE,
            isUnicode,
            &objectName
            );

    if( !NT_SUCCESS( status ) ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetInformation: bad path name: %s\n",
                        (PSZ)request->Buffer + 1 ));
        }

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // If the client is trying to delete the root of the share, reject
    // the request.
    //

    if ( objectName.Length < sizeof(WCHAR) ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetInformation: attempting to set info on "
                          "share root\n" ));
        }

        if (!SMB_IS_UNICODE( WorkContext )) {
            RtlFreeUnicodeString( &objectName );
        }
        SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Initialize the object attributes structure.
    //

    SrvInitializeObjectAttributes_U(
        &objectAttributes,
        &objectName,
        (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
         session->UsingUppercasePaths) ? OBJ_CASE_INSENSITIVE : 0L,
        NULL,
        NULL
        );

    IF_SMB_DEBUG(QUERY_SET2) KdPrint(( "Opening file %wZ\n", &objectName ));

    //
    // Open the file--must be opened in order to have a handle to pass
    // to NtSetInformationFile.  We will close it after setting the
    // necessary information.
    //
    INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
    INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );

    //
    // *** FILE_WRITE_ATTRIBUTES does not cause oplock breaks!
    //

    status = SrvIoCreateFile(
                 WorkContext,
                 &fileHandle,
                 FILE_WRITE_ATTRIBUTES,                     // DesiredAccess
                 &objectAttributes,
                 &ioStatusBlock,
                 NULL,                                      // AllocationSize
                 0,                                         // FileAttributes
                 FILE_SHARE_READ | FILE_SHARE_WRITE |
                    FILE_SHARE_DELETE,                      // ShareAccess
                 FILE_OPEN,                                 // Disposition
                 FILE_OPEN_REPARSE_POINT,                   // CreateOptions
                 NULL,                                      // EaBuffer
                 0,                                         // EaLength
                 CreateFileTypeNone,
                 NULL,                                      // ExtraCreateParameters
                 IO_FORCE_ACCESS_CHECK,                     // Options
                 treeConnect->Share
                 );

    if( status == STATUS_INVALID_PARAMETER ) {
        status = SrvIoCreateFile(
                     WorkContext,
                     &fileHandle,
                     FILE_WRITE_ATTRIBUTES,                     // DesiredAccess
                     &objectAttributes,
                     &ioStatusBlock,
                     NULL,                                      // AllocationSize
                     0,                                         // FileAttributes
                     FILE_SHARE_READ | FILE_SHARE_WRITE |
                        FILE_SHARE_DELETE,                      // ShareAccess
                     FILE_OPEN,                                 // Disposition
                     0,                                         // CreateOptions
                     NULL,                                      // EaBuffer
                     0,                                         // EaLength
                     CreateFileTypeNone,
                     NULL,                                      // ExtraCreateParameters
                     IO_FORCE_ACCESS_CHECK,                     // Options
                     treeConnect->Share
                     );
    }

    ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );

    if ( !isUnicode ) {
        RtlFreeUnicodeString( &objectName );
    }

    if ( NT_SUCCESS(status) ) {

        SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 20, 0 );

        //
        // Ensure this client's RFCB cache is empty.  This covers the case
        //  where a client opened a file for writing, closed it, set the
        //  attributes to readonly, and then tried to reopen the file for
        //  writing.  This sequence should fail, but it will succeed if the
        //  file was in the RFCB cache.
        //
        SrvCloseCachedRfcbsOnConnection( WorkContext->Connection );

    } else {

        if ( status == STATUS_ACCESS_DENIED ) {
            SrvStatistics.AccessPermissionErrors++;
        }

        IF_DEBUG(ERRORS) {
            KdPrint(( "SrvSmbSetInformation: SrvIoCreateFile "
                        "failed: %X\n", status ));
        }

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    IF_SMB_DEBUG(QUERY_SET2) {
        KdPrint(( "SrvIoCreateFile succeeded, handle = 0x%p\n", fileHandle ));
    }

    //
    // Set fields of fileBasicInformation to pass to NtSetInformationFile.
    // Note that we zero the creation, last access, and change times so
    // that they are not actually changed.
    //

    RtlZeroMemory( &fileBasicInformation, sizeof(fileBasicInformation) );

    lastWriteTimeInSeconds = SmbGetUlong( &request->LastWriteTimeInSeconds );
    if ( lastWriteTimeInSeconds != 0 ) {
        RtlSecondsSince1970ToTime(
            lastWriteTimeInSeconds,
            &fileBasicInformation.LastWriteTime
            );

        ExLocalTimeToSystemTime(
            &fileBasicInformation.LastWriteTime,
            &fileBasicInformation.LastWriteTime
            );

    }

    //
    // Set the new file attributes.  Note that we don't return an error
    // if the client tries to set the Directory or Volume bits -- we
    // assume that the remote redirector filters such requests.
    //

    SRV_SMB_ATTRIBUTES_TO_NT(
        SmbGetUshort( &request->FileAttributes ),
        NULL,
        &fileBasicInformation.FileAttributes
        );

    //
    // Set the new file information.
    //

    status = NtSetInformationFile(
                 fileHandle,
                 &ioStatusBlock,
                 &fileBasicInformation,
                 sizeof(FILE_BASIC_INFORMATION),
                 FileBasicInformation
                 );

    //
    // Close the file--it was only opened to set the attributes.
    //

    SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 30, 0 );
    SrvNtClose( fileHandle, TRUE );

    if ( !NT_SUCCESS(status) ) {

        INTERNAL_ERROR(
            ERROR_LEVEL_UNEXPECTED,
            "SrvSmbSetInformation: NtSetInformationFile returned %X",
            status,
            NULL
            );

        SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_FILE, status );

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Build the response SMB.
    //

    response->WordCount = 0;
    SmbPutUshort( &response->ByteCount, 0 );

    WorkContext->ResponseParameters = NEXT_LOCATION(
                                        response,
                                        RESP_SET_INFORMATION,
                                        0
                                        );

    IF_DEBUG(TRACE2) KdPrint(( "SrvSmbSetInformation complete.\n" ));
    SmbStatus = SmbStatusSendResponse;

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;
} // SrvSmbSetInformation


SMB_PROCESSOR_RETURN_TYPE
SrvSmbQueryInformation2 (
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes the QueryInformation2 SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h

--*/

{
    PREQ_QUERY_INFORMATION2 request;
    PRESP_QUERY_INFORMATION2 response;

    NTSTATUS   status    = STATUS_SUCCESS;
    SMB_STATUS SmbStatus = SmbStatusInProgress;
    PRFCB rfcb;
    SRV_FILE_INFORMATION fileInformation;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_INFORMATION2;
    SrvWmiStartContext(WorkContext);

    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "QueryInformation2 request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader,
                    WorkContext->ResponseHeader ));
        KdPrint(( "QueryInformation2 request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters ));
    }

    request = (PREQ_QUERY_INFORMATION2)WorkContext->RequestParameters;
    response = (PRESP_QUERY_INFORMATION2)WorkContext->ResponseParameters;

    //
    // Verify the FID.  If verified, the RFCB block is referenced
    // and its addresses is stored in the WorkContext block, and the
    // RFCB address is returned.
    //

    rfcb = SrvVerifyFid(
                WorkContext,
                SmbGetUshort( &request->Fid ),
                TRUE,
                SrvRestartSmbReceived,   // serialize with raw write
                &status
                );

    if ( rfcb == SRV_INVALID_RFCB_POINTER ) {

        if ( !NT_SUCCESS( status ) ) {

            //
            // Invalid file ID or write behind error.  Reject the request.
            //

            IF_DEBUG(ERRORS) {
                KdPrint((
                    "SrvSmbQueryInformation2: Status %X on fid 0x%lx\n",
                    status,
                    SmbGetUshort( &request->Fid )
                    ));
            }

            SrvSetSmbError( WorkContext, status );
            SmbStatus = SmbStatusSendResponse;
            goto Cleanup;
        }

        //
        // The work item has been queued because a raw write is in
        // progress.
        //

        SmbStatus = SmbStatusInProgress;
        goto Cleanup;
    }

    if( rfcb->Lfcb->Session->IsSessionExpired )
    {
        status = SESSION_EXPIRED_STATUS_CODE;
        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Verify that the client has read attributes access to the file via
    // the specified handle.
    //

    CHECK_FILE_INFORMATION_ACCESS(
        rfcb->GrantedAccess,
        IRP_MJ_QUERY_INFORMATION,
        FileBasicInformation,
        &status
        );

    if ( !NT_SUCCESS(status) ) {

        SrvStatistics.GrantedAccessErrors++;

        IF_DEBUG(ERRORS) {
            KdPrint(( "SrvSmbQueryInformation2: IoCheckFunctionAccess failed: "
                        "0x%X, GrantedAccess: %lx\n",
                        status, rfcb->GrantedAccess ));
        }

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Get the necessary information about the file.
    //

    status = SrvQueryInformationFile(
                rfcb->Lfcb->FileHandle,
                rfcb->Lfcb->FileObject,
                &fileInformation,
                (SHARE_TYPE) -1,
                FALSE
                );

    if ( !NT_SUCCESS(status) ) {

        INTERNAL_ERROR(
            ERROR_LEVEL_UNEXPECTED,
            "SrvSmbQueryInformation2: SrvQueryInformationFile returned %X",
            status,
            NULL
            );

        SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Build the response SMB.
    //

    response->WordCount = 11;
    SmbPutDate( &response->CreationDate, fileInformation.CreationDate );
    SmbPutTime( &response->CreationTime, fileInformation.CreationTime );
    SmbPutDate( &response->LastAccessDate, fileInformation.LastAccessDate );
    SmbPutTime( &response->LastAccessTime, fileInformation.LastAccessTime );
    SmbPutDate( &response->LastWriteDate, fileInformation.LastWriteDate );
    SmbPutTime( &response->LastWriteTime, fileInformation.LastWriteTime );
    SmbPutUlong( &response->FileDataSize, fileInformation.DataSize.LowPart );
    SmbPutUlong(
        &response->FileAllocationSize,
        fileInformation.AllocationSize.LowPart
        );
    SmbPutUshort( &response->FileAttributes, fileInformation.Attributes );
    SmbPutUshort( &response->ByteCount, 0 );

    WorkContext->ResponseParameters = NEXT_LOCATION(
                                        response,
                                        RESP_QUERY_INFORMATION2,
                                        0
                                        );
    SmbStatus = SmbStatusSendResponse;
    IF_DEBUG(TRACE2) KdPrint(( "SrvSmbQueryInformation2 complete.\n" ));

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbQueryInformation2


SMB_PROCESSOR_RETURN_TYPE
SrvSmbSetInformation2 (
    SMB_PROCESSOR_PARAMETERS
    )

/*++

Routine Description:

    Processes the Set Information2 SMB.

Arguments:

    SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
        of the parameters to SMB processor routines.

Return Value:

    SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h

--*/

{
    PREQ_SET_INFORMATION2 request;
    PRESP_SET_INFORMATION2 response;

    NTSTATUS   status    = STATUS_SUCCESS;
    SMB_STATUS SmbStatus = SmbStatusInProgress;
    PRFCB rfcb;
    FILE_BASIC_INFORMATION fileBasicInformation;
    IO_STATUS_BLOCK ioStatusBlock;
    SMB_DATE date;
    SMB_TIME time;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_SET_INFORMATION2;
    SrvWmiStartContext(WorkContext);

    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "SetInformation2 request header at 0x%p, response header at 0x%p\n",
                    WorkContext->RequestHeader,
                    WorkContext->ResponseHeader ));
        KdPrint(( "SetInformation2 request parameters at 0x%p, response parameters at 0x%p\n",
                    WorkContext->RequestParameters,
                    WorkContext->ResponseParameters ));
    }

    request = (PREQ_SET_INFORMATION2)WorkContext->RequestParameters;
    response = (PRESP_SET_INFORMATION2)WorkContext->ResponseParameters;

    //
    // Verify the FID.  If verified, the RFCB block is referenced
    // and its addresses is stored in the WorkContext block, and the
    // RFCB address is returned.
    //

    rfcb = SrvVerifyFid(
                WorkContext,
                SmbGetUshort( &request->Fid ),
                TRUE,
                SrvRestartSmbReceived,   // serialize with raw write
                &status
                );

    if ( rfcb == SRV_INVALID_RFCB_POINTER ) {

        if ( !NT_SUCCESS( status ) ) {

            //
            // Invalid file ID or write behind error.  Reject the request.
            //

            IF_DEBUG(ERRORS) {
                KdPrint((
                    "SrvSmbSetInformation2: Status %X on fid 0x%lx\n",
                    status,
                    SmbGetUshort( &request->Fid )
                    ));
            }

            SrvSetSmbError( WorkContext, status );
            SmbStatus = SmbStatusSendResponse;
            goto Cleanup;
        }

        //
        // The work item has been queued because a raw write is in
        // progress.
        //

        SmbStatus = SmbStatusInProgress;
        goto Cleanup;
    }

    if( rfcb->Lfcb->Session->IsSessionExpired )
    {
        status = SESSION_EXPIRED_STATUS_CODE;
        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Verify that the client has write attributes access to the file
    // via the specified handle.
    //

    CHECK_FILE_INFORMATION_ACCESS(
        rfcb->GrantedAccess,
        IRP_MJ_SET_INFORMATION,
        FileBasicInformation,
        &status
        );

    if ( !NT_SUCCESS(status) ) {

        SrvStatistics.GrantedAccessErrors++;

        IF_DEBUG(ERRORS) {
            KdPrint(( "SrvSmbSetInformation2: IoCheckFunctionAccess failed: "
                        "0x%X, GrantedAccess: %lx\n",
                        status, rfcb->GrantedAccess ));
        }

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Convert the DOS dates and times passed in the SMB to NT TIMEs
    // to pass to NtSetInformationFile.  Note that we zero the rest
    // of the fileBasicInformation structure so that the corresponding
    // fields are not changed.
    //

    RtlZeroMemory( &fileBasicInformation, sizeof(fileBasicInformation) );

    SmbMoveDate( &date, &request->CreationDate );
    SmbMoveTime( &time, &request->CreationTime );
    if ( !SmbIsDateZero(&date) || !SmbIsTimeZero(&time) ) {
        SrvDosTimeToTime( &fileBasicInformation.CreationTime, date, time );
    }

    SmbMoveDate( &date, &request->LastAccessDate );
    SmbMoveTime( &time, &request->LastAccessTime );
    if ( !SmbIsDateZero(&date) || !SmbIsTimeZero(&time) ) {
        SrvDosTimeToTime( &fileBasicInformation.LastAccessTime, date, time );
    }

    SmbMoveDate( &date, &request->LastWriteDate );
    SmbMoveTime( &time, &request->LastWriteTime );
    if ( !SmbIsDateZero(&date) || !SmbIsTimeZero(&time) ) {
        SrvDosTimeToTime( &fileBasicInformation.LastWriteTime, date, time );
    }

    //
    // Call NtSetInformationFile to set the information from the SMB.
    //


    status = NtSetInformationFile(
                 rfcb->Lfcb->FileHandle,
                 &ioStatusBlock,
                 &fileBasicInformation,
                 sizeof(FILE_BASIC_INFORMATION),
                 FileBasicInformation
                 );

    if ( !NT_SUCCESS(status) ) {

        INTERNAL_ERROR(
            ERROR_LEVEL_UNEXPECTED,
            "SrvSmbSetInformation2: NtSetInformationFile failed: %X",
            status,
            NULL
            );

        SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_FILE, status );

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

#ifdef INCLUDE_SMB_IFMODIFIED
    rfcb->Lfcb->FileUpdated = TRUE;
#endif

    //
    // reset the WrittenTo flag.  This will allow this rfcb to be cached.
    //

    rfcb->WrittenTo = FALSE;

    //
    // Build the response SMB.
    //

    response->WordCount = 0;
    SmbPutUshort( &response->ByteCount, 0 );

    WorkContext->ResponseParameters = NEXT_LOCATION(
                                        response,
                                        RESP_SET_INFORMATION2,
                                        0
                                        );
    SmbStatus = SmbStatusSendResponse;
    IF_DEBUG(TRACE2) KdPrint(( "SrvSmbSetInformation2 complete.\n" ));

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbSetInformation2


STATIC
NTSTATUS
QueryPathOrFileInformation (
    IN PWORK_CONTEXT WorkContext,
    IN PTRANSACTION Transaction,
    IN USHORT InformationLevel,
    IN HANDLE FileHandle,
    OUT PRESP_QUERY_PATH_INFORMATION Response
    )

{
    NTSTATUS status;
    IO_STATUS_BLOCK ioStatusBlock;
    SRV_FILE_INFORMATION fileInformation;
    BOOLEAN queryEaSize;
    USHORT eaErrorOffset;
    PFILE_ALL_INFORMATION fileAllInformation;
    ULONG nameInformationSize;
    PVOID currentLocation;
    ULONG dataSize;

    PUNICODE_STRING pathName;
    ULONG inputBufferLength;
    PPATHNAME_BUFFER inputBuffer;

    PFILE_NAME_INFORMATION nameInfoBuffer;
    PSHARE share;

    PAGED_CODE( );

    Transaction->SetupCount = 0;
    Transaction->ParameterCount = 0;

    if( InformationLevel < SMB_INFO_PASSTHROUGH ) {
        switch ( InformationLevel ) {
        case SMB_INFO_STANDARD:
        case SMB_INFO_QUERY_EA_SIZE:

            //
            // Information level is either STANDARD or QUERY_EA_SIZE.  Both
            // return normal file information; the latter also returns the
            // length of the file's EAs.
            //

            queryEaSize = (BOOLEAN)(InformationLevel == SMB_INFO_QUERY_EA_SIZE);

            status = SrvQueryInformationFile(
                        FileHandle,
                        NULL,
                        &fileInformation,
                        (SHARE_TYPE) -1, // Don't care
                        queryEaSize
                        );

            if ( NT_SUCCESS(status) ) {

                //
                // Build the output parameter and data structures.
                //

                PFILESTATUS fileStatus = (PFILESTATUS)Transaction->OutData;

                Transaction->ParameterCount = sizeof( RESP_QUERY_FILE_INFORMATION );
                SmbPutUshort( &Response->EaErrorOffset, 0 );
                Transaction->DataCount = queryEaSize ? 26 : 22;

                SmbPutDate(
                    &fileStatus->CreationDate,
                    fileInformation.CreationDate
                    );
                SmbPutTime(
                    &fileStatus->CreationTime,
                    fileInformation.CreationTime
                    );

                SmbPutDate(
                    &fileStatus->LastAccessDate,
                    fileInformation.LastAccessDate
                    );
                SmbPutTime(
                    &fileStatus->LastAccessTime,
                    fileInformation.LastAccessTime
                    );

                SmbPutDate(
                    &fileStatus->LastWriteDate,
                    fileInformation.LastWriteDate
                    );
                SmbPutTime(
                    &fileStatus->LastWriteTime,
                    fileInformation.LastWriteTime
                    );

                SmbPutUlong( &fileStatus->DataSize, fileInformation.DataSize.LowPart );
                SmbPutUlong(
                    &fileStatus->AllocationSize,
                    fileInformation.AllocationSize.LowPart
                    );

                SmbPutUshort(
                    &fileStatus->Attributes,
                    fileInformation.Attributes
                    );

                if ( queryEaSize ) {
                    SmbPutUlong( &fileStatus->EaSize, fileInformation.EaSize );
                }

            } else {

                //
                // Set the data count to zero so that no data is returned to the
                // client.
                //

                Transaction->DataCount = 0;

                INTERNAL_ERROR(
                    ERROR_LEVEL_UNEXPECTED,
                    "QueryPathOrFileInformation: SrvQueryInformationFile"
                        "returned %X",
                    status,
                    NULL
                    );

                SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );

            }

            break;

        case SMB_INFO_QUERY_EAS_FROM_LIST:
        case SMB_INFO_QUERY_ALL_EAS:

            //
            // The request is for EAs, either all of them or a subset.
            //

            status = SrvQueryOs2FeaList(
                         FileHandle,
                         InformationLevel == SMB_INFO_QUERY_EAS_FROM_LIST ?
                             (PGEALIST)Transaction->InData : NULL,
                         NULL,
                         Transaction->DataCount,
                         (PFEALIST)Transaction->OutData,
                         Transaction->MaxDataCount,
                         &eaErrorOffset
                         );

            if ( NT_SUCCESS(status) ) {

                //
                // The first longword of the OutData buffer holds the length
                // of the remaining data written (the cbList field of the
                // FEALIST).  Add four (the longword itself) to get the number
                // of data bytes written.
                //

                Transaction->DataCount =
                       SmbGetAlignedUlong( (PULONG)Transaction->OutData );

#if     0
                //
                // If there were no EAs, convert the error to
                // STATUS_NO_EAS_ON_FILE.  OS/2 clients expect STATUS_SUCCESS.
                //

                if ( (Transaction->DataCount == 4) &&
                     IS_NT_DIALECT( Transaction->Connection->SmbDialect ) ) {

                    status = STATUS_NO_EAS_ON_FILE;
                }
#endif
            } else {

                IF_DEBUG(ERRORS) {
                    KdPrint(( "QueryPathOrFileInformation: "
                                "SrvQueryOs2FeaList failed: %X\n", status ));
                }

                Transaction->DataCount = 0;
            }

            //
            // Build the output parameter and data structures.
            //

            Transaction->ParameterCount = sizeof( RESP_QUERY_FILE_INFORMATION );
            SmbPutUshort( &Response->EaErrorOffset, eaErrorOffset );

            break;

        case SMB_INFO_IS_NAME_VALID:
            status = STATUS_SUCCESS;
            Transaction->DataCount = 0;

            break;

        case SMB_QUERY_FILE_BASIC_INFO:
        case SMB_QUERY_FILE_STANDARD_INFO:
        case SMB_QUERY_FILE_EA_INFO:
        case SMB_QUERY_FILE_ALT_NAME_INFO:
        case SMB_QUERY_FILE_STREAM_INFO:
        case SMB_QUERY_FILE_COMPRESSION_INFO:

            //
            // Pass the data buffer directly to the file system as it
            // is already in NT format.
            //

            if( Transaction->MaxDataCount <
                MAP_SMB_INFO_TO_MIN_NT_SIZE(QueryFileInformationSize, InformationLevel ) ) {

                //
                // The buffer is too small.  Return an error.
                //
                status = STATUS_INFO_LENGTH_MISMATCH;

            } else {

                status = NtQueryInformationFile(
                             FileHandle,
                             &ioStatusBlock,
                             Transaction->OutData,
                             Transaction->MaxDataCount,
                             MAP_SMB_INFO_TYPE_TO_NT(
                                 QueryFileInformation,
                                 InformationLevel
                                 )
                             );
            }

            SmbPutUshort( &Response->EaErrorOffset, 0 );

            Transaction->ParameterCount = sizeof( RESP_QUERY_FILE_INFORMATION );

            if (NT_SUCCESS( status) || (status == STATUS_BUFFER_OVERFLOW)) {
                Transaction->DataCount = (ULONG)ioStatusBlock.Information;
            } else {
                Transaction->DataCount = 0;
            }

            break;

        case SMB_QUERY_FILE_NAME_INFO:

DoFileNameInfo:
            share = Transaction->TreeConnect->Share;

            nameInfoBuffer = (PFILE_NAME_INFORMATION)Transaction->OutData;

            if ( Transaction->MaxDataCount < FIELD_OFFSET(FILE_NAME_INFORMATION,FileName) ) {

                //
                // The buffer is too small to fit even the fixed part.
                // Return an error.
                //

                status = STATUS_INFO_LENGTH_MISMATCH;
                Transaction->DataCount = 0;

            } else if ( share->ShareType != ShareTypeDisk ) {

                //
                // This is not a disk share.  Pass the request straight to
                // the file system.
                //

                status = NtQueryInformationFile(
                             FileHandle,
                             &ioStatusBlock,
                             nameInfoBuffer,
                             Transaction->MaxDataCount,
                             FileNameInformation
                             );

                Transaction->DataCount = (ULONG)ioStatusBlock.Information;

            } else {

                //
                // We need a temporary buffer since the file system will
                // return the share path together with the file name.  The
                // total length might be larger than the max data allowed
                // in the transaction, though the actual name might fit.
                //

                PFILE_NAME_INFORMATION tempBuffer;
                ULONG tempBufferLength;

                ASSERT( share->QueryNamePrefixLength >= 0 );

                tempBufferLength = Transaction->MaxDataCount + share->QueryNamePrefixLength;

                tempBuffer = ALLOCATE_HEAP( tempBufferLength, BlockTypeBuffer );

                if ( tempBuffer == NULL ) {
                    status = STATUS_INSUFF_SERVER_RESOURCES;
                } else {
                    status = NtQueryInformationFile(
                                 FileHandle,
                                 &ioStatusBlock,
                                 tempBuffer,
                                 tempBufferLength,
                                 FileNameInformation
                                 );
                }

                //
                // remove the share part
                //

                if ( (status == STATUS_SUCCESS) || (status == STATUS_BUFFER_OVERFLOW) ) {

                    LONG bytesToMove;
                    PWCHAR source;
                    WCHAR slash = L'\\';

                    //
                    // Calculate how long the name string is, not including the root prefix.
                    //

                    bytesToMove = (LONG)(tempBuffer->FileNameLength - share->QueryNamePrefixLength);

                    if ( bytesToMove <= 0 ) {

                        //
                        // bytesToMove will be zero if this is the root of
                        // the share.  Return just a \ for this case.
                        //

                        bytesToMove = sizeof(WCHAR);
                        source = &slash;

                    } else {

                        source = tempBuffer->FileName + share->QueryNamePrefixLength/sizeof(WCHAR);

                    }

                    //
                    // Store the actual file name length.
                    //

                    SmbPutUlong( &nameInfoBuffer->FileNameLength, bytesToMove );

                    //
                    // If the buffer isn't big enough, return an error and
                    // reduce the amount to be copied.
                    //

                    if ( (ULONG)bytesToMove >
                         (Transaction->MaxDataCount -
                            FIELD_OFFSET(FILE_NAME_INFORMATION,FileName)) ) {

                        status = STATUS_BUFFER_OVERFLOW;
                        bytesToMove = Transaction->MaxDataCount -
                                    FIELD_OFFSET(FILE_NAME_INFORMATION,FileName);

                    } else {
                        status = STATUS_SUCCESS;
                    }

                    //
                    // Copy all but the prefix.
                    //

                    RtlCopyMemory(
                        nameInfoBuffer->FileName,
                        source,
                        bytesToMove
                        );

                    Transaction->DataCount =
                        FIELD_OFFSET(FILE_NAME_INFORMATION,FileName) + bytesToMove;

                } else {
                    Transaction->DataCount = 0;
                }

                if ( tempBuffer != NULL ) {
                    FREE_HEAP( tempBuffer );
                }

            }

            SmbPutUshort( &Response->EaErrorOffset, 0 );
            Transaction->ParameterCount = sizeof( RESP_QUERY_FILE_INFORMATION );

            break;

        case SMB_QUERY_FILE_ALL_INFO:

DoFileAllInfo:
            //
            // Setup early for the response in case the call to the file
            // system fails.
            //

            SmbPutUshort( &Response->EaErrorOffset, 0 );

            Transaction->ParameterCount = sizeof( RESP_QUERY_FILE_INFORMATION );

            //
            // Allocate a buffer large enough to return all the information.
            // The buffer size we request is the size requested by the client
            // plus room for the extra information returned by the file system
            // that the server doesn't return to the client.
            //

            dataSize = Transaction->MaxDataCount +
                           sizeof( FILE_ALL_INFORMATION )
                           - sizeof( FILE_BASIC_INFORMATION )
                           - sizeof( FILE_STANDARD_INFORMATION )
                           - sizeof( FILE_EA_INFORMATION )
                           - FIELD_OFFSET( FILE_NAME_INFORMATION, FileName );

            fileAllInformation = ALLOCATE_HEAP_COLD( dataSize, BlockTypeDataBuffer );

            if ( fileAllInformation == NULL ) {
                status = STATUS_INSUFFICIENT_RESOURCES;
                break;
            }

            status = NtQueryInformationFile(
                         FileHandle,
                         &ioStatusBlock,
                         fileAllInformation,
                         dataSize,
                         FileAllInformation
                         );

            if ( NT_SUCCESS( status ) ) {

                //
                // Calculate the size of data we will return.  We do not
                // return the entire buffer, just specific fields.
                //

                nameInformationSize =
                    FIELD_OFFSET( FILE_NAME_INFORMATION, FileName ) +
                    fileAllInformation->NameInformation.FileNameLength;

                Transaction->DataCount =
                    sizeof( FILE_BASIC_INFORMATION ) +
                    sizeof( FILE_STANDARD_INFORMATION ) +
                    sizeof( FILE_EA_INFORMATION ) +
                    nameInformationSize;

                //
                // Now copy the data into the transaction buffer.  Start with
                // the fixed sized fields.
                //

                currentLocation = Transaction->OutData;

                *((PFILE_BASIC_INFORMATION)currentLocation)++ =
                     fileAllInformation->BasicInformation;
                *((PFILE_STANDARD_INFORMATION)currentLocation)++ =
                     fileAllInformation->StandardInformation;
                *((PFILE_EA_INFORMATION)currentLocation)++ =
                     fileAllInformation->EaInformation;

                RtlCopyMemory(
                    currentLocation,
                    &fileAllInformation->NameInformation,
                    nameInformationSize
                    );

            } else {
                Transaction->DataCount = 0;
            }

            FREE_HEAP( fileAllInformation );

            break;

        default:
            IF_DEBUG(SMB_ERRORS) {
                KdPrint(( "QueryPathOrFileInformation: bad info level %d\n",
                           InformationLevel ));
            }

            status = STATUS_INVALID_SMB;

            break;
        }

    } else {

        InformationLevel -= SMB_INFO_PASSTHROUGH;

        if( InformationLevel == FileNameInformation ) {
            goto DoFileNameInfo;
        } else if( InformationLevel == FileAllInformation ) {
            goto DoFileAllInfo;
        }

        //
        // See if the supplied parameters are correct.
        //
        status = IoCheckQuerySetFileInformation( InformationLevel,
                                                 Transaction->MaxDataCount,
                                                 FALSE );

        if( NT_SUCCESS( status ) ) {

            //
            // Some information levels require us to impersonate the client.  Do it for all.
            //
            status = IMPERSONATE( WorkContext );

            if( NT_SUCCESS( status ) ) {

                status = NtQueryInformationFile(
                                 FileHandle,
                                 &ioStatusBlock,
                                 Transaction->OutData,
                                 Transaction->MaxDataCount,
                                 InformationLevel
                                 );
                REVERT();
            }
        }

        SmbPutUshort( &Response->EaErrorOffset, 0 );

        Transaction->ParameterCount = sizeof( RESP_QUERY_FILE_INFORMATION );

        if (NT_SUCCESS( status) || (status == STATUS_BUFFER_OVERFLOW)) {
            Transaction->DataCount = (ULONG)ioStatusBlock.Information;
        } else {
            Transaction->DataCount = 0;
        }
    }

    return status;

} // QueryPathOrFileInformation


SMB_TRANS_STATUS
SrvSmbQueryFileInformation (
    IN OUT PWORK_CONTEXT WorkContext
    )

/*++

Routine Description:

    Processes the Query File Information request.  This request arrives
    in a Transaction2 SMB.  Query File Information corresponds to the
    OS/2 DosQFileInfo service.

Arguments:

    WorkContext - Supplies the address of a Work Context Block
        describing the current request.  See smbtypes.h for a more
        complete description of the valid fields.

Return Value:

    SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so,
        whether data should be returned to the client.  See smbtypes.h
        for a more complete description.

--*/

{
    PREQ_QUERY_FILE_INFORMATION request;
    PRESP_QUERY_FILE_INFORMATION response;

    NTSTATUS         status    = STATUS_SUCCESS;
    SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
    PTRANSACTION transaction;
    PRFCB rfcb;
    USHORT informationLevel;
    ACCESS_MASK grantedAccess;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_FILE_INFORMATION;
    SrvWmiStartContext(WorkContext);

    transaction = WorkContext->Parameters.Transaction;
    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "Query File Information entered; transaction 0x%p\n",
                    transaction ));
    }

    request = (PREQ_QUERY_FILE_INFORMATION)transaction->InParameters;
    response = (PRESP_QUERY_FILE_INFORMATION)transaction->OutParameters;

    //
    // Verify that enough parameter bytes were sent and that we're allowed
    // to return enough parameter bytes.
    //

    if ( (transaction->ParameterCount <
            sizeof(REQ_QUERY_FILE_INFORMATION)) ||
         (transaction->MaxParameterCount <
            sizeof(RESP_QUERY_FILE_INFORMATION)) ) {

        //
        // Not enough parameter bytes were sent.
        //

        IF_SMB_DEBUG(QUERY_SET1) {
            KdPrint(( "SrvSmbQueryFileInformation: bad parameter byte counts: "
                        "%ld %ld\n",
                        transaction->ParameterCount,
                        transaction->MaxParameterCount ));
        }

        SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
        status    = STATUS_INVALID_SMB;
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Verify the FID.  If verified, the RFCB block is referenced
    // and its addresses is stored in the WorkContext block, and the
    // RFCB address is returned.
    //

    rfcb = SrvVerifyFid(
                WorkContext,
                SmbGetUshort( &request->Fid ),
                TRUE,
                SrvRestartExecuteTransaction,   // serialize with raw write
                &status
                );

    if ( rfcb == SRV_INVALID_RFCB_POINTER ) {

        if ( !NT_SUCCESS( status ) ) {

            //
            // Invalid file ID or write behind error.  Reject the request.
            //

            IF_DEBUG(ERRORS) {
                KdPrint((
                    "SrvSmbQueryFileInformation: Status %X on FID: 0x%lx\n",
                    status,
                    SmbGetUshort( &request->Fid )
                    ));
            }

            SrvSetSmbError( WorkContext, status );
            SmbStatus = SmbTransStatusErrorWithoutData;
            goto Cleanup;
        }

        //
        // The work item has been queued because a raw write is in
        // progress.
        //

        SmbStatus = SmbTransStatusInProgress;
        goto Cleanup;
    }

    //
    //
    // Verify the information level and the number of input and output
    // data bytes available.
    //

    informationLevel = SmbGetUshort( &request->InformationLevel );
    grantedAccess = rfcb->GrantedAccess;

    status = STATUS_SUCCESS;

    if( informationLevel < SMB_INFO_PASSTHROUGH ) {

        switch ( informationLevel ) {

        case SMB_INFO_STANDARD:

            if ( transaction->MaxDataCount < 22 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbQueryFileInformation: invalid MaxDataCount "
                                "%ld\n", transaction->MaxDataCount ));
                }
                status = STATUS_INVALID_SMB;
                break;
            }

            //
            // Verify that the client has read attributes access to the file
            // via the specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileBasicInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_INFO_QUERY_EA_SIZE:

            if ( transaction->MaxDataCount < 26 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbQueryFileInformation: invalid MaxDataCount "
                                "%ld\n", transaction->MaxDataCount ));
                }
                status = STATUS_INVALID_SMB;
                break;
            }

            //
            // Verify that the client has read EA access to the file via the
            // specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileEaInformation,
                &status
                );

            IF_DEBUG(SMB_ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_INFO_QUERY_EAS_FROM_LIST:
        case SMB_INFO_QUERY_ALL_EAS:


            //
            // Verify that the client has read EA access to the file via the
            // specified handle.
            //

            CHECK_FUNCTION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_EA,
                0,
                0,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;


        case SMB_QUERY_FILE_BASIC_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileBasicInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_STANDARD_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileStandardInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_EA_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileEaInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_NAME_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileNameInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_ALL_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileAllInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_ALT_NAME_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileAlternateNameInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_STREAM_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileStreamInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_COMPRESSION_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileCompressionInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        default:

            IF_DEBUG(SMB_ERRORS) {
                KdPrint(( "SrvSmbQueryFileInformation: invalid info level %ld\n",
                            informationLevel ));
            }

            status = STATUS_OS2_INVALID_LEVEL;
            break;
        }

    } else {

        if( informationLevel - SMB_INFO_PASSTHROUGH >= FileMaximumInformation ) {
            status = STATUS_INVALID_INFO_CLASS;
        }

        if( NT_SUCCESS( status ) ) {
            status = IoCheckQuerySetFileInformation( informationLevel - SMB_INFO_PASSTHROUGH,
                                                 0xFFFFFFFF,
                                                 FALSE
                                                );
        }

        if( NT_SUCCESS( status ) ) {
            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                informationLevel - SMB_INFO_PASSTHROUGH,
                &status
            );
        }
    }

    if ( !NT_SUCCESS(status) ) {

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Get the necessary information about the file.
    //

    status = QueryPathOrFileInformation(
                 WorkContext,
                 transaction,
                 informationLevel,
                 rfcb->Lfcb->FileHandle,
                 (PRESP_QUERY_PATH_INFORMATION)response
                 );

    //
    // Map STATUS_BUFFER_OVERFLOW for OS/2 clients.
    //

    if ( status == STATUS_BUFFER_OVERFLOW &&
         !IS_NT_DIALECT( WorkContext->Connection->SmbDialect ) ) {

        status = STATUS_BUFFER_TOO_SMALL;

    }

    //
    // If an error occurred, return an appropriate response.
    //

    if ( !NT_SUCCESS(status) ) {

        //
        // QueryPathOrFileInformation already filled in the response
        // information, so just set the error and return.
        //

        SrvSetSmbError2( WorkContext, status, TRUE );
        SmbStatus = SmbTransStatusErrorWithData;
        goto Cleanup;
    }
    SmbStatus = SmbTransStatusSuccess;
    IF_DEBUG(TRACE2) KdPrint(( "SrvSmbQueryFileInformation complete.\n" ));

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbQueryFileInformation


SMB_TRANS_STATUS
SrvSmbQueryPathInformation (
    IN OUT PWORK_CONTEXT WorkContext
    )
/*++

Routine Description:

    Processes the Query Path Information request.  This request arrives
    in a Transaction2 SMB.  Query Path Information corresponds to the
    OS/2 DosQPathInfo service.

Arguments:

    WorkContext - Supplies the address of a Work Context Block
        describing the current request.  See smbtypes.h for a more
        complete description of the valid fields.

Return Value:

    SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so,
        whether data should be returned to the client.  See smbtypes.h
        for a more complete description.

--*/
{
    PTRANSACTION transaction;
    PREQ_QUERY_PATH_INFORMATION request;
    PRESP_QUERY_PATH_INFORMATION response;
    USHORT informationLevel;
    NTSTATUS         status    = STATUS_SUCCESS;
    HANDLE fileHandle;
    IO_STATUS_BLOCK ioStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING objectName;
    BOOLEAN isUnicode;

    SMB_TRANS_STATUS smbStatus = SmbTransStatusInProgress;
    ACCESS_MASK desiredAccess;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_QUERY_PATH_INFORMATION;
    SrvWmiStartContext(WorkContext);

    transaction = WorkContext->Parameters.Transaction;

    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "Query Path Information entered; transaction 0x%p\n",
                    transaction ));
    }

    //
    // Verify that enough parameter bytes were sent and that we're allowed
    // to return enough parameter bytes.
    //
    if ( (transaction->ParameterCount <
            sizeof(REQ_QUERY_PATH_INFORMATION)) ||
         (transaction->MaxParameterCount <
            sizeof(RESP_QUERY_PATH_INFORMATION)) ) {

        //
        // Not enough parameter bytes were sent.
        //

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbQueryPathInformation: bad parameter byte "
                        "counts: %ld %ld\n",
                        transaction->ParameterCount,
                        transaction->MaxParameterCount ));
        }

        SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
        status    = STATUS_INVALID_SMB;
        smbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    request = (PREQ_QUERY_PATH_INFORMATION)transaction->InParameters;
    informationLevel = SmbGetUshort( &request->InformationLevel );

    //
    // The response formats for Query Path and Query File and identical,
    // so just use the RESP_QUERY_PATH_INFORMATION structure for both.
    // The request formats differ, so conditionalize access to them.
    //
    response = (PRESP_QUERY_PATH_INFORMATION)transaction->OutParameters;

    switch( informationLevel ) {
    case SMB_INFO_QUERY_EA_SIZE:
    case SMB_INFO_QUERY_EAS_FROM_LIST:
    case SMB_INFO_QUERY_ALL_EAS:

        //
        // For these info levels, we must be in a blocking thread because we
        // might end up waiting for an oplock break.
        //
        if( WorkContext->UsingBlockingThread == 0 ) {
            WorkContext->FspRestartRoutine = SrvRestartExecuteTransaction;
            SrvQueueWorkToBlockingThread( WorkContext );
            smbStatus = SmbTransStatusInProgress;
            goto Cleanup;
        }
        desiredAccess = FILE_READ_EA;
        break;

    default:
        desiredAccess = FILE_READ_ATTRIBUTES;
        break;
    }

    //
    // Make sure the client is allowed to do this, if we have an Admin share
    //
    status = SrvIsAllowedOnAdminShare( WorkContext, WorkContext->TreeConnect->Share );
    if( !NT_SUCCESS( status ) ) {
        SrvSetSmbError( WorkContext, status );
        smbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Get the path name of the file to open relative to the share.
    //

    isUnicode = SMB_IS_UNICODE( WorkContext );

    status = SrvCanonicalizePathName(
            WorkContext,
            WorkContext->TreeConnect->Share,
            NULL,
            request->Buffer,
            END_OF_TRANSACTION_PARAMETERS( transaction ),
            TRUE,
            isUnicode,
            &objectName
            );

    if( !NT_SUCCESS( status ) ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbQueryPathInformation: bad path name: %s\n",
                        request->Buffer ));
        }

        SrvSetSmbError( WorkContext, status );
        smbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Special case: If this is the IS_PATH_VALID information level, then
    // the user just wants to know if the path syntax is correct.  Do not
    // attempt to open the file.
    //

    informationLevel = SmbGetUshort( &request->InformationLevel );

    if ( informationLevel == SMB_INFO_IS_NAME_VALID ) {

        transaction->InData = (PVOID)&objectName;

        //
        // Get the Share root handle.
        //
        smbStatus = SrvGetShareRootHandle( WorkContext->TreeConnect->Share );

        if ( !NT_SUCCESS(smbStatus) ) {

            IF_DEBUG(ERRORS) {
                KdPrint(( "SrvSmbQueryPathInformation: SrvGetShareRootHandle failed %x.\n",
                            smbStatus ));
            }

            if (!isUnicode) {
                RtlFreeUnicodeString( &objectName );
            }

            SrvSetSmbError( WorkContext, smbStatus );
            status    = smbStatus;
            smbStatus = SmbTransStatusErrorWithoutData;
            goto Cleanup;
        }

        status = SrvSnapGetRootHandle( WorkContext, &WorkContext->Parameters2.FileInformation.FileHandle );
        if( !NT_SUCCESS(status) )
        {
            SrvSetSmbError( WorkContext, status );
            smbStatus = SmbTransStatusErrorWithoutData;
            goto Cleanup;
        }


        smbStatus = GenerateQueryPathInfoResponse(
                       WorkContext,
                       SmbTransStatusSuccess
                       );

        //
        // Release the root handle for removable devices
        //

        SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );

        if ( !isUnicode ) {
            RtlFreeUnicodeString( &objectName );
        }
        goto Cleanup;
    }

    //
    // Initialize the object attributes structure.
    //

    SrvInitializeObjectAttributes_U(
        &objectAttributes,
        &objectName,
        (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
         transaction->Session->UsingUppercasePaths) ?
            OBJ_CASE_INSENSITIVE : 0L,
        NULL,
        NULL
        );

    //
    // Take the fast path for this if we can
    //
    if( informationLevel == SMB_QUERY_FILE_BASIC_INFO ) {

        FILE_NETWORK_OPEN_INFORMATION fileInformation;
        UNALIGNED FILE_BASIC_INFORMATION *pbInfo = (PFILE_BASIC_INFORMATION)transaction->OutData;

        if( transaction->MaxDataCount < sizeof( FILE_BASIC_INFORMATION ) ) {
            SrvSetSmbError( WorkContext, STATUS_INFO_LENGTH_MISMATCH );
            status    = STATUS_INFO_LENGTH_MISMATCH;
            smbStatus = SmbTransStatusErrorWithoutData;
            goto Cleanup;
        }

        status = IMPERSONATE( WorkContext );

        if( NT_SUCCESS( status ) ) {

            status = SrvGetShareRootHandle( transaction->TreeConnect->Share );

            if( NT_SUCCESS( status ) ) {

                //
                // The file name is always relative to the share root
                //
                status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
                if( !NT_SUCCESS(status) )
                {
                    goto SnapError;
                }

                //
                // Get the information
                //
                if( IoFastQueryNetworkAttributes(
                        &objectAttributes,
                        FILE_READ_ATTRIBUTES,
                        0,
                        &ioStatusBlock,
                        &fileInformation
                        ) == FALSE ) {

                    SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
                    ioStatusBlock.Status = STATUS_OBJECT_PATH_NOT_FOUND;
                }

                status = ioStatusBlock.Status;

                //
                // If the media was changed and we can come up with a new share root handle,
                //  then we should retry the operation
                //
                if( SrvRetryDueToDismount( transaction->TreeConnect->Share, status ) ) {

                    status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
                    if( !NT_SUCCESS(status) )
                    {
                        goto SnapError;
                    }

                    //
                    // Get the information
                    //
                    if( IoFastQueryNetworkAttributes(
                            &objectAttributes,
                            FILE_READ_ATTRIBUTES,
                            0,
                            &ioStatusBlock,
                            &fileInformation
                            ) == FALSE ) {

                        SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
                        ioStatusBlock.Status = STATUS_OBJECT_PATH_NOT_FOUND;
                    }

                    status = ioStatusBlock.Status;
                }

SnapError:

                SrvReleaseShareRootHandle( transaction->TreeConnect->Share );
            }

            REVERT();
        }

        if( status == STATUS_BUFFER_OVERFLOW ) {
            goto hard_way;
        }

        if ( !isUnicode ) {
            RtlFreeUnicodeString( &objectName );
        }

        if ( !NT_SUCCESS( status ) ) {
            if ( status == STATUS_ACCESS_DENIED ) {
                SrvStatistics.AccessPermissionErrors++;
            }

            IF_DEBUG(ERRORS) {
                KdPrint(( "SrvSmbQueryPathInformation: IoFastQueryNetworkAttributes "
                    "failed: %X\n", status ));
            }

            SrvSetSmbError( WorkContext, status );
            smbStatus = SmbTransStatusErrorWithoutData;
            goto Cleanup;
        }

        // FORMULATE THE RESPONSE

        transaction->SetupCount = 0;
        transaction->DataCount = sizeof( *pbInfo );
        transaction->ParameterCount = sizeof( RESP_QUERY_FILE_INFORMATION );

        SmbPutUshort( &response->EaErrorOffset, 0 );

        pbInfo->CreationTime =   fileInformation.CreationTime;
        pbInfo->LastAccessTime = fileInformation.LastAccessTime;
        pbInfo->LastWriteTime =  fileInformation.LastWriteTime;
        pbInfo->ChangeTime =     fileInformation.ChangeTime;
        pbInfo->FileAttributes = fileInformation.FileAttributes;

        smbStatus = SmbTransStatusSuccess;
        goto Cleanup;
    }

hard_way:

    IF_SMB_DEBUG(QUERY_SET2) KdPrint(( "Opening file %wZ\n", &objectName ));

    //
    // Open the file -- must be opened in order to have a handle to pass
    // to NtQueryInformationFile.  We will close it after getting the
    // necessary information.
    //
    INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
    INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );

    //
    // !!! We may block if the file is oplocked.  We must do this, because
    //     it is required to get the FS to break a batch oplock.
    //     We should figure out a way to do this without blocking.
    //

    status = SrvIoCreateFile(
                 WorkContext,
                 &fileHandle,
                 desiredAccess,
                 &objectAttributes,
                 &ioStatusBlock,
                 NULL,                                      // AllocationSize
                 0,                                         // FileAttributes
                 FILE_SHARE_READ | FILE_SHARE_WRITE |
                    FILE_SHARE_DELETE,                      // ShareAccess
                 FILE_OPEN,                                 // Disposition
                 FILE_OPEN_REPARSE_POINT,                   // CreateOptions
                 NULL,                                      // EaBuffer
                 0,                                         // EaLength
                 CreateFileTypeNone,
                 NULL,                                      // ExtraCreateParameters
                 IO_FORCE_ACCESS_CHECK,                     // Options
                 transaction->TreeConnect->Share
                 );

    if( status == STATUS_INVALID_PARAMETER ) {
        status = SrvIoCreateFile(
                     WorkContext,
                     &fileHandle,
                     desiredAccess,
                     &objectAttributes,
                     &ioStatusBlock,
                     NULL,                                      // AllocationSize
                     0,                                         // FileAttributes
                     FILE_SHARE_READ | FILE_SHARE_WRITE |
                        FILE_SHARE_DELETE,                      // ShareAccess
                     FILE_OPEN,                                 // Disposition
                     0,                                         // CreateOptions
                     NULL,                                      // EaBuffer
                     0,                                         // EaLength
                     CreateFileTypeNone,
                     NULL,                                      // ExtraCreateParameters
                     IO_FORCE_ACCESS_CHECK,                     // Options
                     transaction->TreeConnect->Share
                     );
    }

    if ( NT_SUCCESS(status) ) {
        SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 21, 0 );
    }
    else {
        SrvSetSmbError( WorkContext, status );
        smbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    if ( !isUnicode ) {
        RtlFreeUnicodeString( &objectName );
    }

    //
    // Save a copy of the file handle for the restart routine.
    //

    WorkContext->Parameters2.FileInformation.FileHandle = fileHandle;

    ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );

    smbStatus = GenerateQueryPathInfoResponse( WorkContext, status );

Cleanup:
    SrvWmiEndContext(WorkContext);
    return smbStatus;

} // SrvSmbQueryPathInformation


SMB_TRANS_STATUS
GenerateQueryPathInfoResponse (
    IN PWORK_CONTEXT WorkContext,
    IN NTSTATUS OpenStatus
    )

/*++

Routine Description:

    This function completes processing for and generates a response to a
    query path information response SMB.

Arguments:

    WorkContext - A pointer to the work context block for this SMB
    OpenStatus - The completion status of the open.

Return Value:

    The status of the SMB processing.

--*/

{
    PREQ_QUERY_PATH_INFORMATION request;
    PRESP_QUERY_PATH_INFORMATION response;
    PTRANSACTION transaction;

    NTSTATUS status;
    BOOLEAN error;
    HANDLE fileHandle;
    USHORT informationLevel;

    PFILE_OBJECT fileObject;
    OBJECT_HANDLE_INFORMATION handleInformation;

    PAGED_CODE( );

    transaction = WorkContext->Parameters.Transaction;
    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "Query Path Information entered; transaction 0x%p\n",
                    transaction ));
    }

    request = (PREQ_QUERY_PATH_INFORMATION)transaction->InParameters;
    response = (PRESP_QUERY_PATH_INFORMATION)transaction->OutParameters;

    fileHandle = WorkContext->Parameters2.FileInformation.FileHandle;

    //
    // If the user didn't have this permission, update the
    // statistics database.
    //

    if ( OpenStatus == STATUS_ACCESS_DENIED ) {
        SrvStatistics.AccessPermissionErrors++;
    }

    if ( !NT_SUCCESS( OpenStatus ) ) {

        IF_DEBUG(ERRORS) {
            KdPrint(( "GenerateQueryPathInfoResponse: SrvIoCreateFile failed: %X\n", OpenStatus ));
        }

        SrvSetSmbError( WorkContext, OpenStatus );

        return SmbTransStatusErrorWithoutData;
    }

    IF_SMB_DEBUG(QUERY_SET2) {
        KdPrint(( "SrvIoCreateFile succeeded, handle = 0x%p\n", fileHandle ));
    }

    //
    // Find out the access the user has.
    //

    status = ObReferenceObjectByHandle(
                fileHandle,
                0,
                NULL,
                KernelMode,
                (PVOID *)&fileObject,
                &handleInformation
                );

    if ( !NT_SUCCESS(status) ) {

        SrvLogServiceFailure( SRV_SVC_OB_REF_BY_HANDLE, status );

        //
        // This internal error bugchecks the system.
        //

        INTERNAL_ERROR(
            ERROR_LEVEL_IMPOSSIBLE,
            "GenerateQueryPathInfoResponse: unable to reference file handle 0x%lx",
            fileHandle,
            NULL
            );

        SrvSetSmbError( WorkContext, OpenStatus );
        return SmbTransStatusErrorWithoutData;

    }

    ObDereferenceObject( fileObject );

    //
    // Verify the information level and the number of input and output
    // data bytes available.
    //

    informationLevel = SmbGetUshort( &request->InformationLevel );

    error = FALSE;

    if( informationLevel < SMB_INFO_PASSTHROUGH ) {

        switch ( informationLevel ) {

        case SMB_INFO_STANDARD:
            if ( transaction->MaxDataCount < 22 ) {
                IF_SMB_DEBUG(QUERY_SET1) {
                    KdPrint(( "GenerateQueryPathInfoResponse: invalid "
                                "MaxDataCount %ld\n", transaction->MaxDataCount ));
                }
                error = TRUE;
            }
            break;

        case SMB_INFO_QUERY_EA_SIZE:
            if ( transaction->MaxDataCount < 26 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "GenerateQueryPathInfoResponse: invalid "
                                "MaxDataCount %ld\n", transaction->MaxDataCount ));
                }
                error = TRUE;
            }
            break;

        case SMB_INFO_QUERY_EAS_FROM_LIST:
        case SMB_INFO_QUERY_ALL_EAS:
            if ( transaction->MaxDataCount < 4 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "GenerateQueryPathInfoResponse: invalid "
                                "MaxDataCount %ld\n", transaction->MaxDataCount ));
                }
                error = TRUE;
            }
            break;

        case SMB_INFO_IS_NAME_VALID:
            break;

        case SMB_QUERY_FILE_BASIC_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileBasicInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_STANDARD_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileStandardInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_EA_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileEaInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_NAME_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileNameInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_ALL_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileAllInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_ALT_NAME_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileAlternateNameInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_STREAM_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileStreamInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        case SMB_QUERY_FILE_COMPRESSION_INFO:

            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                FileCompressionInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbQueryFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, handleInformation.GrantedAccess ));
                }
            }

            break;

        default:
            IF_DEBUG(SMB_ERRORS) {
                KdPrint(( "GenerateQueryPathInfoResponse: invalid info level"
                          "%ld\n", informationLevel ));
            }
            error = TRUE;
            break;
        }

    } else {

        if( informationLevel - SMB_INFO_PASSTHROUGH >= FileMaximumInformation ) {
            status = STATUS_INVALID_INFO_CLASS;
        }

        if( NT_SUCCESS( status ) ) {
            status = IoCheckQuerySetFileInformation( informationLevel - SMB_INFO_PASSTHROUGH,
                                                 0xFFFFFFFF,
                                                 FALSE
                                                );
        }

        if( NT_SUCCESS( status ) ) {
            CHECK_FILE_INFORMATION_ACCESS(
                handleInformation.GrantedAccess,
                IRP_MJ_QUERY_INFORMATION,
                informationLevel - SMB_INFO_PASSTHROUGH,
                &status
            );

        } else {

            error = TRUE;

        }
    }

    if ( error ) {

        SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 32, 0 );
        SrvNtClose( fileHandle, TRUE );
        SrvSetSmbError( WorkContext, STATUS_OS2_INVALID_LEVEL );
        return SmbTransStatusErrorWithoutData;
    }

    //
    // Get the necessary information about the file.
    //

    status = QueryPathOrFileInformation(
                 WorkContext,
                 transaction,
                 informationLevel,
                 fileHandle,
                 (PRESP_QUERY_PATH_INFORMATION)response
                 );

    //
    // Map STATUS_BUFFER_OVERFLOW for OS/2 clients.
    //

    if ( status == STATUS_BUFFER_OVERFLOW &&
         !IS_NT_DIALECT( WorkContext->Connection->SmbDialect ) ) {

        status = STATUS_BUFFER_TOO_SMALL;

    }

    //
    // Close the file--it was only opened to read the attributes.
    //

    if ( informationLevel != SMB_INFO_IS_NAME_VALID ) {
        SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 33, 0 );
        SrvNtClose( fileHandle, TRUE );
    }

    //
    // If an error occurred, return an appropriate response.
    //

    if ( !NT_SUCCESS(status) ) {

        //
        // QueryPathOrFileInformation already set the response parameters,
        // so just return an error condition.
        //

        SrvSetSmbError2( WorkContext, status, TRUE );
        return SmbTransStatusErrorWithData;
    }

    IF_DEBUG(TRACE2) KdPrint(( "GenerateQueryPathInfoResponse complete.\n" ));
    return SmbTransStatusSuccess;

} // GenerateQueryPathInfoResponse


STATIC
NTSTATUS
SetPathOrFileInformation (
    IN PWORK_CONTEXT WorkContext,
    IN PTRANSACTION Transaction,
    IN USHORT InformationLevel,
    IN HANDLE FileHandle,
    OUT PRESP_SET_PATH_INFORMATION Response
    )

{
    NTSTATUS status = STATUS_SUCCESS;
    IO_STATUS_BLOCK ioStatusBlock;
    SMB_DATE date;
    SMB_TIME time;
    PWCHAR p, ep;

    PFILESTATUS fileStatus = (PFILESTATUS)Transaction->InData;
    FILE_BASIC_INFORMATION fileBasicInformation;

    USHORT eaErrorOffset;

    PAGED_CODE( );

    if( InformationLevel < SMB_INFO_PASSTHROUGH ) {
        switch( InformationLevel ) {

        case SMB_INFO_STANDARD:

            //
            // Information level is STANDARD.  Set normal file information.
            // Convert the DOS dates and times passed in the SMB to NT TIMEs
            // to pass to NtSetInformationFile.  Note that we zero the rest
            // of the fileBasicInformation structure so that the corresponding
            // fields are not changed.  Note also that the file attributes
            // are not changed.
            //

            RtlZeroMemory( &fileBasicInformation, sizeof(fileBasicInformation) );

            if ( !SmbIsDateZero(&fileStatus->CreationDate) ||
                 !SmbIsTimeZero(&fileStatus->CreationTime) ) {

                SmbMoveDate( &date, &fileStatus->CreationDate );
                SmbMoveTime( &time, &fileStatus->CreationTime );

                SrvDosTimeToTime( &fileBasicInformation.CreationTime, date, time );
            }

            if ( !SmbIsDateZero(&fileStatus->LastAccessDate) ||
                 !SmbIsTimeZero(&fileStatus->LastAccessTime) ) {

                SmbMoveDate( &date, &fileStatus->LastAccessDate );
                SmbMoveTime( &time, &fileStatus->LastAccessTime );

                SrvDosTimeToTime( &fileBasicInformation.LastAccessTime, date, time );
            }

            if ( !SmbIsDateZero(&fileStatus->LastWriteDate) ||
                 !SmbIsTimeZero(&fileStatus->LastWriteTime) ) {

                SmbMoveDate( &date, &fileStatus->LastWriteDate );
                SmbMoveTime( &time, &fileStatus->LastWriteTime );

                SrvDosTimeToTime( &fileBasicInformation.LastWriteTime, date, time );
            }

            //
            // Call NtSetInformationFile to set the information from the SMB.
            //

            status = NtSetInformationFile(
                         FileHandle,
                         &ioStatusBlock,
                         &fileBasicInformation,
                         sizeof(FILE_BASIC_INFORMATION),
                         FileBasicInformation
                         );

            if ( !NT_SUCCESS(status) ) {
                INTERNAL_ERROR(
                    ERROR_LEVEL_UNEXPECTED,
                    "SetPathOrFileInformation: SrvSetInformationFile returned: %X",
                    status,
                    NULL
                    );

                SrvLogServiceFailure( SRV_SVC_NT_SET_INFO_FILE, status );
            }

            //
            // No EAs to deal with.  Set EA error offset to zero.
            //

            SmbPutUshort( &Response->EaErrorOffset, 0 );

            break;

        case SMB_INFO_QUERY_EA_SIZE:

            //
            // The request is to set the file's EAs.
            //

            status = SrvSetOs2FeaList(
                         FileHandle,
                         (PFEALIST)Transaction->InData,
                         Transaction->DataCount,
                         &eaErrorOffset
                         );

            if ( !NT_SUCCESS(status) ) {
                IF_DEBUG(ERRORS) {
                    KdPrint(( "SetPathOrFileInformation: SrvSetOs2FeaList "
                                "failed: %X\n", status ));
                }
            }

            //
            // Return the EA error offset in the response.
            //

            SmbPutUshort( &Response->EaErrorOffset, eaErrorOffset );

            break;


        case SMB_SET_FILE_BASIC_INFO:
        case SMB_SET_FILE_DISPOSITION_INFO:
        case SMB_SET_FILE_ALLOCATION_INFO:
        case SMB_SET_FILE_END_OF_FILE_INFO:

            //
            // The data buffer is in NT format.  Pass it directly to the
            // filesystem.
            //
            if( Transaction->DataCount <
                MAP_SMB_INFO_TO_MIN_NT_SIZE(SetFileInformationSize, InformationLevel ) ) {

                //
                // The buffer is too small.  Return an error.
                //
                status = STATUS_INFO_LENGTH_MISMATCH;

            } else {

                status = NtSetInformationFile(
                             FileHandle,
                             &ioStatusBlock,
                             Transaction->InData,
                             Transaction->DataCount,
                             MAP_SMB_INFO_TYPE_TO_NT(
                                 SetFileInformation,
                                 InformationLevel
                                 )
                             );

            }

            //
            // No EAs to deal with.  Set EA error offset to zero.
            //

            SmbPutUshort( &Response->EaErrorOffset, 0 );

            break;

        default:
            status = STATUS_OS2_INVALID_LEVEL;
            break;
        }

    } else {
        PFILE_RENAME_INFORMATION setInfo = NULL;
        ULONG setInfoLength;
#ifdef _WIN64
        PFILE_RENAME_INFORMATION32 pRemoteInfo;
#endif

        InformationLevel -= SMB_INFO_PASSTHROUGH;

        setInfo = (PFILE_RENAME_INFORMATION)Transaction->InData;
        setInfoLength = Transaction->DataCount;

        //
        // There are some info levels which we do not allow in this path.  Unless we
        // put in special handling, we can not allow any that pass handles.  And we
        // would need to be careful on any that allow renaming or linking (to prevent
        // escaping the share).  These are the ones we restrict or disallow,
        // which the I/O subsystem may otherwise allow:
        //
        switch( InformationLevel ) {
        case FileLinkInformation:
        case FileMoveClusterInformation:
        case FileTrackingInformation:
        case FileCompletionInformation:
        case FileMailslotSetInformation:
            status = STATUS_NOT_SUPPORTED;
            break;

        case FileRenameInformation: {

            PWCHAR s, es;

#ifdef _WIN64
            pRemoteInfo = (PFILE_RENAME_INFORMATION32)Transaction->InData;
            setInfoLength = Transaction->DataCount + sizeof(PVOID)-sizeof(ULONG);
            setInfo = (PFILE_RENAME_INFORMATION)ALLOCATE_NONPAGED_POOL( setInfoLength, BlockTypeMisc );
            if( !setInfo )
            {
                status = STATUS_INSUFFICIENT_RESOURCES;
                break;
            }
            // Thunk most of the structure but wait to copy until we validate the file
            // name length is correct
            setInfo->ReplaceIfExists = pRemoteInfo->ReplaceIfExists;
            setInfo->RootDirectory = UlongToHandle( pRemoteInfo->RootDirectory );
            setInfo->FileNameLength = pRemoteInfo->FileNameLength;
#endif

            //
            // See if the structure is internally consistent
            //
            if( setInfoLength < sizeof( FILE_RENAME_INFORMATION ) ||
                setInfo->RootDirectory != NULL ||
                setInfo->FileNameLength > setInfoLength ||
                (setInfo->FileNameLength & (sizeof(WCHAR)-1)) ||
                setInfo->FileNameLength +
                    FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ) >
                    setInfoLength ) {

                status = STATUS_INVALID_PARAMETER;
                break;
            }

#ifdef _WIN64
            // We've validated the original buffer, so lets copy the filename
            RtlCopyMemory( setInfo->FileName, pRemoteInfo->FileName, setInfo->FileNameLength );
#endif

            //
            // If there are any path separaters in the name, then we do not support
            //   this operation.
            //
            es = &setInfo->FileName[ setInfo->FileNameLength / sizeof( WCHAR ) ];
            for( s = setInfo->FileName; s < es; s++ ) {
                if( IS_UNICODE_PATH_SEPARATOR( *s ) ) {
                    status = STATUS_NOT_SUPPORTED;
                    break;
                }
            }
        }

        }

        if( NT_SUCCESS( status ) ) {

            //
            // See if the supplied parameters are correct.
            //
            status = IoCheckQuerySetFileInformation( InformationLevel,
                                                     setInfoLength,
                                                     TRUE
                                                    );
            if( NT_SUCCESS( status ) ) {

                //
                // Some information levels require us to impersonate the client.
                //
                status = IMPERSONATE( WorkContext );

                if( NT_SUCCESS( status ) ) {
                    status = NtSetInformationFile(
                                                 FileHandle,
                                                 &ioStatusBlock,
                                                 setInfo,
                                                 setInfoLength,
                                                 InformationLevel
                                                 );
                    REVERT();

                    //
                    // No EAs to deal with.  Set EA error offset to zero.
                    //
                    SmbPutUshort( &Response->EaErrorOffset, 0 );
                }
            }
        }

#ifdef _WIN64
        if( (FileRenameInformation == InformationLevel) && setInfo )
        {
            DEALLOCATE_NONPAGED_POOL( setInfo );
            setInfo = NULL;
        }
#endif

    }

    //
    // Build the output parameter and data structures.  It is basically
    // the same for all info levels reguardless of the completion status.
    //

    Transaction->SetupCount = 0;
    Transaction->ParameterCount = 2;
    Transaction->DataCount = 0;

    return status;

} // SetPathOrFileInformation


SMB_TRANS_STATUS
SrvSmbSetFileInformation (
    IN OUT PWORK_CONTEXT WorkContext
    )

/*++

Routine Description:

    Processes the Set File Information request.  This request arrives
    in a Transaction2 SMB.  Set File Information corresponds to the
    OS/2 DosSetFileInfo service.

Arguments:

    WorkContext - Supplies the address of a Work Context Block
        describing the current request.  See smbtypes.h for a more
        complete description of the valid fields.

Return Value:

    SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so,
        whether data should be returned to the client.  See smbtypes.h
        for a more complete description.

--*/

{
    PREQ_SET_FILE_INFORMATION request;
    PRESP_SET_FILE_INFORMATION response;

    NTSTATUS         status    = STATUS_SUCCESS;
    SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
    PTRANSACTION transaction;
    PRFCB rfcb;
    USHORT informationLevel;
    USHORT NtInformationLevel;
    ACCESS_MASK grantedAccess;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_SET_FILE_INFORMATION;
    SrvWmiStartContext(WorkContext);

    transaction = WorkContext->Parameters.Transaction;
    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "Set File Information entered; transaction 0x%p\n",
                    transaction ));
    }

    request = (PREQ_SET_FILE_INFORMATION)transaction->InParameters;
    response = (PRESP_SET_FILE_INFORMATION)transaction->OutParameters;

    //
    // Verify that enough parameter bytes were sent and that we're allowed
    // to return enough parameter bytes.
    //

    if ( (transaction->ParameterCount <
            sizeof(REQ_SET_FILE_INFORMATION)) ||
         (transaction->MaxParameterCount <
            sizeof(RESP_SET_FILE_INFORMATION)) ) {

        //
        // Not enough parameter bytes were sent.
        //

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetFileInformation: bad parameter byte counts: "
                        "%ld %ld\n",
                        transaction->ParameterCount,
                        transaction->MaxParameterCount ));
        }

        SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
        status    = STATUS_INVALID_SMB;
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Verify the FID.  If verified, the RFCB block is referenced
    // and its addresses is stored in the WorkContext block, and the
    // RFCB address is returned.
    //

    rfcb = SrvVerifyFid(
                WorkContext,
                SmbGetUshort( &request->Fid ),
                TRUE,
                SrvRestartExecuteTransaction,   // serialize with raw write
                &status
                );

    if ( rfcb == SRV_INVALID_RFCB_POINTER ) {

        if ( !NT_SUCCESS( status ) ) {

            //
            // Invalid file ID or write behind error.  Reject the request.
            //

            IF_DEBUG(ERRORS) {
                KdPrint((
                    "SrvSmbSetFileInformation: Status %X on FID: 0x%lx\n",
                    status,
                    SmbGetUshort( &request->Fid )
                    ));
            }

            SrvSetSmbError( WorkContext, status );
            SmbStatus = SmbTransStatusErrorWithoutData;
            goto Cleanup;
        }

        //
        // The work item has been queued because a raw write is in
        // progress.
        //

        SmbStatus = SmbTransStatusInProgress;
        goto Cleanup;
    }

    //
    // Verify the information level and the number of input and output
    // data bytes available.
    //

    informationLevel = SmbGetUshort( &request->InformationLevel );
    grantedAccess = rfcb->GrantedAccess;

    status = STATUS_SUCCESS;

    if( informationLevel < SMB_INFO_PASSTHROUGH ) {
        switch ( informationLevel ) {

        case SMB_INFO_STANDARD:

            if ( transaction->DataCount < 22 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetFileInformation: invalid DataCount %ld\n",
                                transaction->DataCount ));
                }
                status = STATUS_INVALID_SMB;
            }

            //
            // Verify that the client has write attributes access to the
            // file via the specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_INFORMATION,
                FileBasicInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbSetFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_INFO_QUERY_EA_SIZE:

            if ( transaction->DataCount < 4 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetFileInformation: invalid DataCount %ld\n",
                                transaction->MaxParameterCount ));
                }
                status = STATUS_INVALID_SMB;
            }

            //
            // Verify that the client has write EA access to the file via
            // the specified handle.
            //

            CHECK_FUNCTION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_EA,
                0,
                0,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbSetFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_SET_FILE_BASIC_INFO:

            if ( transaction->DataCount != sizeof( FILE_BASIC_INFORMATION ) ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetFileInformation: invalid DataCount %ld\n",
                                transaction->DataCount ));
                }
                status = STATUS_INVALID_SMB;
            }

            //
            // Verify that the client has write attributes access to the
            // file via the specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_INFORMATION,
                FileBasicInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbSetFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

#if     0 // No longer supported
        case SMB_SET_FILE_RENAME_INFO:

            //
            // The data must contain rename information plus a non-zero
            // length name.
            //

            if ( transaction->DataCount <=
                        FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName  ) ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetFileInformation: invalid DataCount %ld\n",
                                transaction->DataCount ));
                }
                status = STATUS_INVALID_SMB;
            }

            //
            // Verify that the client has write attributes access to the
            // file via the specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_INFORMATION,
                FileRenameInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbSetFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;
#endif

        case SMB_SET_FILE_DISPOSITION_INFO:

            if ( transaction->DataCount !=
                            sizeof( FILE_DISPOSITION_INFORMATION ) ){
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetFileInformation: invalid DataCount %ld\n",
                                transaction->DataCount ));
                }
                status = STATUS_INVALID_SMB;
            }

            //
            // Verify that the client has write attributes access to the
            // file via the specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_INFORMATION,
                FileDispositionInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbSetFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_SET_FILE_ALLOCATION_INFO:

            if ( transaction->DataCount !=
                            sizeof( FILE_ALLOCATION_INFORMATION ) ){
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetFileInformation: invalid DataCount %ld\n",
                                transaction->DataCount ));
                }
                status = STATUS_INVALID_SMB;
            }

            //
            // Verify that the client has write attributes access to the
            // file via the specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_INFORMATION,
                FileAllocationInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbSetFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        case SMB_SET_FILE_END_OF_FILE_INFO:

            if ( transaction->DataCount !=
                            sizeof( FILE_END_OF_FILE_INFORMATION ) ){
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetFileInformation: invalid DataCount %ld\n",
                                transaction->DataCount ));
                }
                status = STATUS_INVALID_SMB;
            }

            //
            // Verify that the client has write attributes access to the
            // file via the specified handle.
            //

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_INFORMATION,
                FileEndOfFileInformation,
                &status
                );

            IF_DEBUG(ERRORS) {
                if ( !NT_SUCCESS(status) ) {
                    KdPrint(( "SrvSmbSetFileInformation: IoCheckFunctionAccess "
                                "failed: 0x%X, GrantedAccess: %lx\n",
                                status, grantedAccess ));
                }
            }

            break;

        default:

            IF_DEBUG(SMB_ERRORS) {
                KdPrint(( "SrvSmbSetFileInformation: invalid info level %ld\n",
                            informationLevel ));
            }
            status = STATUS_OS2_INVALID_LEVEL;

        }

    } else {

        if( informationLevel - SMB_INFO_PASSTHROUGH >= FileMaximumInformation ) {
            status = STATUS_INVALID_INFO_CLASS;

        } else {

            CHECK_FILE_INFORMATION_ACCESS(
                grantedAccess,
                IRP_MJ_SET_INFORMATION,
                informationLevel - SMB_INFO_PASSTHROUGH,
                &status
            );
        }

        IF_DEBUG(ERRORS) {
            if ( !NT_SUCCESS(status) ) {
                KdPrint(( "SrvSmbSetFileInformation level %u: IoCheckFunctionAccess "
                            "failed: 0x%X, GrantedAccess: %lx\n",
                            informationLevel, status, grantedAccess ));
            }
        }
    }

    if ( !NT_SUCCESS(status) ) {

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Set the appropriate information about the file.
    //

    status = SetPathOrFileInformation(
                 WorkContext,
                 transaction,
                 informationLevel,
                 rfcb->Lfcb->FileHandle,
                 (PRESP_SET_PATH_INFORMATION)response
                 );

    //
    // If an error occurred, return an appropriate response.
    //

    if ( !NT_SUCCESS(status) ) {

        //
        // SetPathOrFileInformation already set the response parameters,
        // so just return an error condition.
        //

        SrvSetSmbError2( WorkContext, status, TRUE );
        SmbStatus = SmbTransStatusErrorWithData;
        goto Cleanup;
    }

#ifdef INCLUDE_SMB_IFMODIFIED
    rfcb->Lfcb->FileUpdated = TRUE;
#endif

    //
    // reset this boolean so that the rfcb will not be cached after client close
    //
    rfcb->IsCacheable = FALSE;
    SmbStatus = SmbTransStatusSuccess;
    IF_DEBUG(TRACE2) KdPrint(( "SrvSmbSetFileInformation complete.\n" ));

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbSetFileInformation


SMB_TRANS_STATUS
SrvSmbSetPathInformation (
    IN OUT PWORK_CONTEXT WorkContext
    )

/*++

Routine Description:

    Processes the Set Path Information request.  This request arrives
    in a Transaction2 SMB.  Set Path Information corresponds to the
    OS/2 DosSetPathInfo service.

Arguments:

    WorkContext - Supplies the address of a Work Context Block
        describing the current request.  See smbtypes.h for a more
        complete description of the valid fields.

Return Value:

    SMB_TRANS_STATUS - Indicates whether an error occurred, and, if so,
        whether data should be returned to the client.  See smbtypes.h
        for a more complete description.

--*/

{
    PTRANSACTION transaction;
    PREQ_SET_PATH_INFORMATION request;
    USHORT informationLevel;
    NTSTATUS         status    = STATUS_SUCCESS;
    SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
    IO_STATUS_BLOCK ioStatusBlock;
    HANDLE fileHandle;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING objectName;
    BOOLEAN isUnicode;
    ACCESS_MASK desiredAccess;

    PAGED_CODE( );
    if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
        WorkContext->PreviousSMB = EVENT_TYPE_SMB_SET_PATH_INFORMATION;
    SrvWmiStartContext(WorkContext);

    transaction = WorkContext->Parameters.Transaction;

    IF_SMB_DEBUG(QUERY_SET1) {
        KdPrint(( "SrvSmbSetPathInformation entered; transaction 0x%p\n",
                    transaction ));
    }

    request = (PREQ_SET_PATH_INFORMATION)transaction->InParameters;
    informationLevel = SmbGetUshort( &request->InformationLevel );

    switch( informationLevel ) {
    case SMB_SET_FILE_ALLOCATION_INFO:
    case SMB_SET_FILE_END_OF_FILE_INFO:
        desiredAccess = FILE_WRITE_DATA;
        break;

    case SMB_SET_FILE_DISPOSITION_INFO:
        desiredAccess = DELETE;
        break;

    case SMB_INFO_SET_EAS:
        desiredAccess = FILE_WRITE_EA;
        break;

    default:
        desiredAccess = FILE_WRITE_ATTRIBUTES;
        break;
    }

    if( desiredAccess != FILE_WRITE_ATTRIBUTES &&
        WorkContext->UsingBlockingThread == 0 ) {

        //
        // We can't process the SMB in a nonblocking thread because this
        // info level requires opening the file, which may be oplocked, so
        // the open operation may block.
        //

        WorkContext->FspRestartRoutine = SrvRestartExecuteTransaction;
        SrvQueueWorkToBlockingThread( WorkContext );
        SmbStatus = SmbTransStatusInProgress;
        goto Cleanup;
    }

    //
    // Verify that enough parameter bytes were sent and that we're allowed
    // to return enough parameter bytes.
    //

    request = (PREQ_SET_PATH_INFORMATION)transaction->InParameters;

    if ( (transaction->ParameterCount <
            sizeof(REQ_SET_PATH_INFORMATION)) ||
         (transaction->MaxParameterCount <
            sizeof(RESP_SET_PATH_INFORMATION)) ) {

        //
        // Not enough parameter bytes were sent.
        //

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetPathInformation: bad parameter byte "
                        "counts: %ld %ld\n",
                        transaction->ParameterCount,
                        transaction->MaxParameterCount ));
        }

        SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
        status    = STATUS_INVALID_SMB;
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Make sure the client is allowed to do this, if we have an Admin share
    //
    status = SrvIsAllowedOnAdminShare( WorkContext, transaction->TreeConnect->Share );
    if( !NT_SUCCESS( status ) ) {
        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // Get the path name of the file to open relative to the share.
    //

    isUnicode = SMB_IS_UNICODE( WorkContext );

    status = SrvCanonicalizePathName(
            WorkContext,
            transaction->TreeConnect->Share,
            NULL,
            request->Buffer,
            END_OF_TRANSACTION_PARAMETERS( transaction ),
            TRUE,
            isUnicode,
            &objectName
            );

    if( !NT_SUCCESS( status ) ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetPathInformation: bad path name: %s\n",
                        request->Buffer ));
        }

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    //
    // If the client is trying to operate on the root of the share, reject
    // the request.
    //

    if ( objectName.Length < sizeof(WCHAR) ) {

        IF_DEBUG(SMB_ERRORS) {
            KdPrint(( "SrvSmbSetPathInformation: attempting to set info on "
                          "share root\n" ));
        }

        SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
        status = STATUS_ACCESS_DENIED;
        if ( !isUnicode ) {
            RtlFreeUnicodeString( &objectName );
        }
        SmbStatus = SmbStatusSendResponse;
        goto Cleanup;
    }

    //
    // Initialize the object attributes structure.
    //

    SrvInitializeObjectAttributes_U(
        &objectAttributes,
        &objectName,
        (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
         transaction->Session->UsingUppercasePaths) ?
            OBJ_CASE_INSENSITIVE : 0L,
        NULL,
        NULL
        );

    IF_SMB_DEBUG(QUERY_SET2) {
        KdPrint(( "Opening file %wZ\n", &objectName ));
    }

    //
    // Open the file -- must be opened in order to have a handle to pass
    // to NtSetInformationFile.  We will close it after getting the
    // necessary information.
    //
    // The DosQPathInfo API insures that EAs are written directly to
    // the disk rather than cached, so if EAs are being written, open
    // with FILE_WRITE_THROUGH.  See OS/2 1.2 DCR 581 for more
    // information.
    //
    INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
    INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );

    status = SrvIoCreateFile(
                 WorkContext,
                 &fileHandle,
                 desiredAccess,
                 &objectAttributes,
                 &ioStatusBlock,
                 NULL,                                      // AllocationSize
                 0,                                         // FileAttributes
                 FILE_SHARE_READ | FILE_SHARE_WRITE |
                     FILE_SHARE_DELETE,                     // ShareAccess
                 FILE_OPEN,                                 // Disposition
                 FILE_OPEN_REPARSE_POINT,                   // CreateOptions
                 NULL,                                      // EaBuffer
                 0,                                         // EaLength
                 CreateFileTypeNone,
                 NULL,                                      // ExtraCreateParameters
                 IO_FORCE_ACCESS_CHECK,                     // Options
                 transaction->TreeConnect->Share
                 );

    if( status == STATUS_INVALID_PARAMETER ) {
        status = SrvIoCreateFile(
                     WorkContext,
                     &fileHandle,
                     desiredAccess,
                     &objectAttributes,
                     &ioStatusBlock,
                     NULL,                                      // AllocationSize
                     0,                                         // FileAttributes
                     FILE_SHARE_READ | FILE_SHARE_WRITE |
                         FILE_SHARE_DELETE,                     // ShareAccess
                     FILE_OPEN,                                 // Disposition
                     0,                                         // CreateOptions
                     NULL,                                      // EaBuffer
                     0,                                         // EaLength
                     CreateFileTypeNone,
                     NULL,                                      // ExtraCreateParameters
                     IO_FORCE_ACCESS_CHECK,                     // Options
                     transaction->TreeConnect->Share
                     );
    }

    ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );

    if ( NT_SUCCESS(status) ) {
        SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 22, 0 );
    }

    if ( !isUnicode ) {
        RtlFreeUnicodeString( &objectName );
    }

    if ( !NT_SUCCESS( status ) ) {

        //
        // If the user didn't have this permission, update the
        // statistics database.
        //
        if ( status == STATUS_ACCESS_DENIED ) {
            SrvStatistics.AccessPermissionErrors++;
        }

        IF_DEBUG(ERRORS) {
            KdPrint(( "SrvSmbSetPathInformation: SrvIoCreateFile failed: "
                        "%X\n", status ));
        }

        SrvSetSmbError( WorkContext, status );
        SmbStatus = SmbTransStatusErrorWithoutData;
        goto Cleanup;
    }

    IF_SMB_DEBUG(QUERY_SET2) {
        KdPrint(( "SrvIoCreateFile succeeded, handle = 0x%p\n", fileHandle ));
    }

    if( informationLevel < SMB_INFO_PASSTHROUGH ) {

        //
        // Verify the information level and the number of input and output
        // data bytes available.
        //

        BOOLEAN error = FALSE;

        switch ( informationLevel ) {

        case SMB_INFO_STANDARD:
            if ( transaction->DataCount < 22 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetPathInformation: invalid DataCount %ld\n",
                                transaction->DataCount ));
                }
                error = TRUE;
            }
            break;

        case SMB_INFO_QUERY_EA_SIZE:
        case SMB_INFO_QUERY_ALL_EAS:
            if ( transaction->DataCount < 4 ) {
                IF_DEBUG(SMB_ERRORS) {
                    KdPrint(( "SrvSmbSetPathInformation: invalid DataCount %ld\n",
                                transaction->MaxParameterCount ));
                }
                error = TRUE;
            }
            break;

        default:
            IF_DEBUG(SMB_ERRORS) {
                KdPrint(( "SrvSmbSetPathInformation: invalid info level %ld\n",
                            informationLevel ));
            }
            error = TRUE;

        }

        if ( error ) {

            //
            // Just return an error condition.
            //

            SrvSetSmbError2( WorkContext, STATUS_OS2_INVALID_LEVEL, TRUE );
            status    = STATUS_OS2_INVALID_LEVEL;
            SmbStatus = SmbTransStatusErrorWithoutData;
            goto Cleanup;
        }
    }

    //
    // Set the appropriate information about the file.
    //

    status = SetPathOrFileInformation(
                 WorkContext,
                 transaction,
                 informationLevel,
                 fileHandle,
                 (PRESP_SET_PATH_INFORMATION)transaction->OutParameters
                 );

    //
    // Close the file--it was only opened to write the attributes.
    //

    SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 35, 0 );
    SrvNtClose( fileHandle, TRUE );

    //
    // If an error occurred, return an appropriate response.
    //

    if ( !NT_SUCCESS(status) ) {

        //
        // SetPathOrFileInformation already set the response parameters,
        // so just return an error condition.
        //

        SrvSetSmbError2( WorkContext, status, TRUE );
        SmbStatus = SmbTransStatusErrorWithData;
        goto Cleanup;
    }
    SmbStatus = SmbTransStatusSuccess;
    IF_DEBUG(TRACE2) KdPrint(( "SrvSmbSetPathInformation complete.\n" ));

Cleanup:
    SrvWmiEndContext(WorkContext);
    return SmbStatus;

} // SrvSmbSetPathInformation