/*++

Copyright (c) 1991-1992 Microsoft Corporation

Module Name:

    SsSubs.c

Abstract:

    This module contains support routines for the NT server service.

Author:

    David Treadwell (davidtr)    10-Jan-1991

Revision History:

--*/

#include "srvsvcp.h"
#include "ssreg.h"

#include <lmerr.h>
#include <lmsname.h>
#include <netlibnt.h>
#include <tstr.h>

#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>

#include <ntddnfs.h>

#define MRXSMB_DEVICE_NAME TEXT("\\Device\\LanmanRedirector")


PSERVER_REQUEST_PACKET
SsAllocateSrp (
    VOID
    )

/*++

Routine Description:

    This routine allocates a serer request packet so that an API can
    communicate with the kernel-mode server.  Any general initialization
    in performed here.

Arguments:

    None.

Return Value:

    PSERVER_REQUEST_PACKET - a pointer to the allocated SRP.

--*/

{
    PSERVER_REQUEST_PACKET srp;

    srp = MIDL_user_allocate( sizeof(SERVER_REQUEST_PACKET) );
    if ( srp != NULL ) {
        RtlZeroMemory( srp, sizeof(SERVER_REQUEST_PACKET) );
    }

    return srp;

}  // SsAllocateSrp

#if DBG

VOID
SsAssert(
    IN PVOID FailedAssertion,
    IN PVOID FileName,
    IN ULONG LineNumber
    )
{
    BOOL ok;
    CHAR choice[16];
    DWORD bytes;
    DWORD error;

    SsPrintf( "\nAssertion failed: %s\n  at line %ld of %s\n",
                FailedAssertion, LineNumber, FileName );
    do {
        HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);

        SsPrintf( "Break or Ignore [bi]? " );

        if (hStdIn && (hStdIn != INVALID_HANDLE_VALUE))
        {
            bytes = sizeof(choice);
            ok = ReadFile(
                    hStdIn,
                    &choice,
                    bytes,
                    &bytes,
                    NULL
                    );
        }
        else
        {
            // default to "break"
            ok = TRUE;
            choice[0] = TEXT('B');
        }

        if ( ok ) {
            if ( toupper(choice[0]) == 'I' ) {
                break;
            }
            if ( toupper(choice[0]) == 'B' ) {
                DbgUserBreakPoint( );
            }
        } else {
            error = GetLastError( );
        }
    } while ( TRUE );

    return;

} // SsAssert
#endif


VOID
SsCloseServer (
    VOID
    )

/*++

Routine Description:

    This routine closes the server file system device, if it has been
    opened.

Arguments:

    None.

Return Value:

    None.

--*/

{
    //
    // Close the server device, if it has been opened.
    //

    if ( SsData.SsServerDeviceHandle != NULL ) {
        NtClose( SsData.SsServerDeviceHandle );
        SsData.SsServerDeviceHandle = NULL;
    }

} // SsCloseServer


VOID
SsControlCHandler (
    IN ULONG CtrlType
    )

/*++

Routine Description:

    Captures and ignores a kill signal.  Without this, any ^C pressed in
    the window that started the server service will result in this
    process being killed, and then the server can't function properly.

Arguments:

    None.

Return Value:

    None.

--*/

{
    CtrlType;

    return;

} // SsControlCHandler


VOID
SsFreeSrp (
    IN PSERVER_REQUEST_PACKET Srp
    )

/*++

Routine Description:

    Frees an SRP allocated by SsAllocateSrp.

Arguments:

    Srp - a pointer to the SRP to free.

Return Value:

    None.

--*/

{
    MIDL_user_free( Srp );

}  // SsFreeSrp


VOID
SsLogEvent(
    IN DWORD MessageId,
    IN DWORD NumberOfSubStrings,
    IN LPWSTR *SubStrings,
    IN DWORD ErrorCode
    )
{
    HANDLE logHandle;
    DWORD dataSize = 0;
    LPVOID rawData = NULL;
    USHORT eventType = EVENTLOG_ERROR_TYPE;

    logHandle = RegisterEventSource(
                    NULL,
                    SERVER_DISPLAY_NAME
                    );

    if ( logHandle == NULL ) {
        SS_PRINT(( "SRVSVC: RegisterEventSource failed: %lu\n",
                    GetLastError() ));
        return;
    }

    if ( ErrorCode != NERR_Success ) {

        //
        // An error code was specified.
        //

        dataSize = sizeof(ErrorCode);
        rawData = (LPVOID)&ErrorCode;

    }

    //
    //  If the message is is only a warning, then set the event type as such.
    //  This is doc'd in netevent.h.
    //

    if ((ULONG)(MessageId & 0xC0000000) == (ULONG) 0x80000000 ) {

        eventType = EVENTLOG_WARNING_TYPE;
    }

    //
    // Log the error.
    //

    if ( !ReportEventW(
            logHandle,
            eventType,
            0,                  // event category
            MessageId,
            NULL,               // user SID
            (WORD)NumberOfSubStrings,
            dataSize,
            SubStrings,
            rawData
            ) ) {
        SS_PRINT(( "SRVSVC: ReportEvent failed: %lu\n",
                    GetLastError() ));
    }

    if ( !DeregisterEventSource( logHandle ) ) {
        SS_PRINT(( "SRVSVC: DeregisterEventSource failed: %lu\n",
                    GetLastError() ));
    }

    return;

} // SsLogEvent


NET_API_STATUS
SsOpenServer ()

/*++

Routine Description:

    This routine opens the server file system device, allowing the
    server service to send FS controls to it.

Arguments:

    None.

Return Value:

    NET_API_STATUS - results of operation.

--*/

{
    NTSTATUS status;
    UNICODE_STRING unicodeServerName;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK ioStatusBlock;

    //
    // Open the server device.
    //

    RtlInitUnicodeString( &unicodeServerName, SERVER_DEVICE_NAME );

    InitializeObjectAttributes(
        &objectAttributes,
        &unicodeServerName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
        );

    //
    // Opening the server with desired access = SYNCHRONIZE and open
    // options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have
    // to worry about waiting for the NtFsControlFile to complete--this
    // makes all IO system calls that use this handle synchronous.
    //

    status = NtOpenFile(
                 &SsData.SsServerDeviceHandle,
                 FILE_ALL_ACCESS & ~SYNCHRONIZE,
                 &objectAttributes,
                 &ioStatusBlock,
                 0,
                 0
                 );

    if ( NT_SUCCESS(status) ) {
        status = ioStatusBlock.Status;
    }

    if ( !NT_SUCCESS(status) ) {
        IF_DEBUG(INITIALIZATION_ERRORS) {
            SS_PRINT(( "SsOpenServer: NtOpenFile (server device object) "
                          "failed: %X\n", status ));
        }
        return NetpNtStatusToApiStatus( status );
    }

    //
    // We're now ready to talk to the server.
    //

    return NO_ERROR;

} // SsOpenServer

#if DBG

VOID
SsPrintf (
    char *Format,
    ...
    )

{
    va_list arglist;
    char OutputBuffer[1024];
    ULONG length;
    HANDLE hStdOut;

    va_start( arglist, Format );

    vsprintf( OutputBuffer, Format, arglist );

    va_end( arglist );

    length = strlen( OutputBuffer );

    hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    
    if (hStdOut && (hStdOut != INVALID_HANDLE_VALUE))
    {
        WriteFile(hStdOut, (LPVOID )OutputBuffer, length, &length, NULL );
    }

} // SsPrintf
#endif


NET_API_STATUS
SsServerFsControlGetInfo (
    IN ULONG ServerControlCode,
    IN PSERVER_REQUEST_PACKET Srp,
    IN OUT PVOID *OutputBuffer,
    IN ULONG PreferredMaximumLength
    )

/*++

Routine Description:

    This routine sends an SRP to the server for an API that retrieves
    information from the server and takes a PreferredMaximumLength
    parameter.

Arguments:

    ServerControlCode - the FSCTL code for the operation.

    Srp - a pointer to the SRP for the operation.

    OutputBuffer - a pointer to receive a pointer to the buffer
        allocated by this routine to hold the output information.

    PreferredMaximumLength - the PreferredMaximumLength parameter.

Return Value:

    NET_API_STATUS - results of operation.

--*/

{
    NET_API_STATUS status = STATUS_SUCCESS;
    ULONG resumeHandle = Srp->Parameters.Get.ResumeHandle;
    ULONG BufLen = min( INITIAL_BUFFER_SIZE, PreferredMaximumLength );
    ULONG i;

    *OutputBuffer = NULL;

    //
    // Normally, we should only go through this loop at most 2 times.  But
    //   if the amount of data the server needs to return is growing, then
    //   we might be forced to run through it a couple of more times.  '5'
    //   is an arbitrary number just to ensure we don't get stuck.
    //

    for( i=0; i < 5; i++ ) {

        if( *OutputBuffer ) {
            MIDL_user_free( *OutputBuffer );
        }

        *OutputBuffer = MIDL_user_allocate( BufLen );

        if( *OutputBuffer == NULL ) {
            status = ERROR_NOT_ENOUGH_MEMORY;
            break;
        }

        //
        // Make the request of the server.
        //

        Srp->Parameters.Get.ResumeHandle = resumeHandle;

        status = SsServerFsControl(
                    ServerControlCode,
                    Srp,
                    *OutputBuffer,
                    BufLen
                    );

        //
        // If we were successful, or we got an error other than our buffer
        //   being too small, break out.
        //
        if ( status != ERROR_MORE_DATA && status != NERR_BufTooSmall ) {
            break;
        }

        //
        // We've been told that our buffer isn't big enough.  But if we've hit
        //  the caller's PreferredMaximumLength, break out.
        //
        if( BufLen >= PreferredMaximumLength ) {
            break;
        }

        //
        // Let's try again.  EXTRA_ALLOCATION is here to cover the case where the
        //   amount of space required grows between the previous FsControl call and
        //   the next one.
        //
        BufLen = min( Srp->Parameters.Get.TotalBytesNeeded + EXTRA_ALLOCATION,
                    PreferredMaximumLength );

    }

    if ( *OutputBuffer && Srp->Parameters.Get.EntriesRead == 0 ) {
        MIDL_user_free( *OutputBuffer );
        *OutputBuffer = NULL;
    }

    return status;

} // SsServerFsControlGetInfo


NET_API_STATUS
SsServerFsControl (
    IN ULONG ServerControlCode,
    IN PSERVER_REQUEST_PACKET Srp OPTIONAL,
    IN PVOID Buffer,
    IN ULONG BufferLength
    )

/*++

Routine Description:

    This routine sends an FSCTL to the server using the previously opened
    server handle

Arguments:

    ServerControlCode - the FSCTL code to send to the server.

    Srp - a pointer to the SRP for the operation.

    Buffer - a pointer to the buffer to pass to the server as the
        "OutputBuffer" parameter of NtFsControlFile.

    BufferLength - the size of this buffer.

Return Value:

    NET_API_STATUS - results of the operation.

--*/

{
    NTSTATUS status;
    NET_API_STATUS error;
    IO_STATUS_BLOCK ioStatusBlock;
    PSERVER_REQUEST_PACKET sendSrp;
    ULONG sendSrpLength;
    PWCH name1Buffer, name2Buffer;
    HANDLE eventHandle;

    if( SsData.SsServerDeviceHandle == NULL ) {
        DbgPrint( "SRVSVC: SsData.SsServerDeviceHandle == NULL\n" );
        return ERROR_BAD_NET_RESP;
    }

    //
    // If a name was specified, we must capture the SRP along with the
    // name in order to avoid sending embedded input pointers.
    //

    if ( Srp != NULL ) {

        name1Buffer = Srp->Name1.Buffer;
        name2Buffer = Srp->Name2.Buffer;

        if ( Srp->Name1.Buffer != NULL || Srp->Name2.Buffer != NULL ) {

            PCHAR nextStringLocation;

            //
            // Allocate enough space to hold the SRP + name.
            //

            sendSrpLength = sizeof(SERVER_REQUEST_PACKET);

            if ( Srp->Name1.Buffer != NULL ) {
                sendSrpLength += Srp->Name1.MaximumLength;
            }

            if ( Srp->Name2.Buffer != NULL ) {
                sendSrpLength += Srp->Name2.MaximumLength;
            }

            sendSrp =  MIDL_user_allocate( sendSrpLength );

            if ( sendSrp == NULL ) {
                return ERROR_NOT_ENOUGH_MEMORY;
            }

            //
            // Copy over the SRP.
            //

            RtlCopyMemory( sendSrp, Srp, sizeof(SERVER_REQUEST_PACKET) );

            //
            // Set up the names in the new SRP.
            //

            nextStringLocation = (PCHAR)( sendSrp + 1 );

            if ( Srp->Name1.Buffer != NULL ) {

                sendSrp->Name1.Length = Srp->Name1.Length;
                sendSrp->Name1.MaximumLength = Srp->Name1.MaximumLength;
                sendSrp->Name1.Buffer = (PWCH)nextStringLocation;

                RtlCopyMemory(
                    sendSrp->Name1.Buffer,
                    Srp->Name1.Buffer,
                    Srp->Name1.MaximumLength
                    );

                nextStringLocation += Srp->Name1.MaximumLength;

                POINTER_TO_OFFSET( sendSrp->Name1.Buffer, sendSrp );
            }

            if ( Srp->Name2.Buffer != NULL ) {

                sendSrp->Name2.Length = Srp->Name2.Length;
                sendSrp->Name2.MaximumLength = Srp->Name2.MaximumLength;
                sendSrp->Name2.Buffer = (PWCH)nextStringLocation;

                RtlCopyMemory(
                    sendSrp->Name2.Buffer,
                    Srp->Name2.Buffer,
                    Srp->Name2.MaximumLength
                    );

                POINTER_TO_OFFSET( sendSrp->Name2.Buffer, sendSrp );
            }

        } else {

            //
            // There was no name in the SRP, so just send the SRP that was
            // passed in.
            //

            sendSrp = Srp;
            sendSrpLength = sizeof(SERVER_REQUEST_PACKET);
        }

    } else {

        //
        // This request has no SRP.
        //

        sendSrp = NULL;
        sendSrpLength = 0;

    }

    //
    // Create an event to synchronize with the driver
    //
    status = NtCreateEvent(
                &eventHandle,
                FILE_ALL_ACCESS,
                NULL,
                NotificationEvent,
                FALSE
                );

    //
    // Send the request to the server FSD.
    //
    if( NT_SUCCESS( status ) ) {

        status = NtFsControlFile(
                 SsData.SsServerDeviceHandle,
                 eventHandle,
                 NULL,
                 NULL,
                 &ioStatusBlock,
                 ServerControlCode,
                 sendSrp,
                 sendSrpLength,
                 Buffer,
                 BufferLength
                 );

        if( status == STATUS_PENDING ) {
            NtWaitForSingleObject( eventHandle, FALSE, NULL );
        }

        NtClose( eventHandle );
    }

    //
    // If an error code was set in the SRP, use it.  Otherwise, if
    // an error was returned or set in the IO status block, use that.
    //

    if ( (sendSrp != NULL) && (sendSrp->ErrorCode != NO_ERROR) ) {
        error = sendSrp->ErrorCode;
        IF_DEBUG(API_ERRORS) {
            SS_PRINT(( "SsServerFsControl: (1) API call %lx to srv failed, "
                        "err = %ld\n", ServerControlCode, error ));
        }
    } else {
        if ( NT_SUCCESS(status) ) {
            status = ioStatusBlock.Status;
        }
        if ( status == STATUS_SERVER_HAS_OPEN_HANDLES ) {
            error = ERROR_SERVER_HAS_OPEN_HANDLES;
        } else {
            error = NetpNtStatusToApiStatus( status );
        }
        if ( error != NO_ERROR ) {
            IF_DEBUG(API_ERRORS) {
                SS_PRINT(( "SsServerFsControl: (2) API call %lx to srv "
                            "failed, err = %ld, status = %X\n",
                            ServerControlCode, error, status ));
            }
        }
    }

    //
    // If a separate buffer was allocated to capture the name, copy
    // over the new SRP and free it.
    //

    if ( sendSrp != Srp ) {
        RtlCopyMemory( Srp, sendSrp, sizeof(SERVER_REQUEST_PACKET) );
        Srp->Name1.Buffer = name1Buffer;
        Srp->Name2.Buffer = name2Buffer;
        MIDL_user_free( sendSrp );
    }

    return error;

} // SsServerFsControl


DWORD
SsGetServerType (
    VOID
    )
/*++

Routine Description:

    Return the ServiceBits of all the services implemented by this service.

    Enter with SsData.SsServerInfoResource locked.

Arguments:

    None

Return Value:

    SV_TYPE service bits.

--*/
{
    DWORD serviceBits;

    serviceBits = SV_TYPE_SERVER | SV_TYPE_NT | SsData.ServiceBits;

    if( SsData.IsDfsRoot ) {
        serviceBits |= SV_TYPE_DFS;
    }

    if ( SsData.ServerInfo599.sv599_timesource ) {
        serviceBits |= SV_TYPE_TIME_SOURCE;
    }

    if ( SsData.ServerInfo598.sv598_producttype == NtProductServer ) {
        serviceBits |= SV_TYPE_SERVER_NT;
    }

    if ( SsData.NumberOfPrintShares != 0 ) {
        serviceBits |= SV_TYPE_PRINTQ_SERVER;
    }

    return serviceBits;

}


VOID
SsSetExportedServerType (
    IN PNAME_LIST_ENTRY service        OPTIONAL,
    IN BOOL ExternalBitsAlreadyChanged,
    IN BOOL UpdateImmediately
    )
{
    DWORD serviceBits;
    DWORD newServiceBits;
    BOOL changed = ExternalBitsAlreadyChanged;

    //
    // The value returned in the sv102_type field is an amalgam of the
    // following:
    //
    // 1) The internal server type bits SV_TYPE_SERVER (always set),
    //    SV_TYPE_NT (always set), SV_TYPE_TIME_SOURCE (set if the
    //    parameter TimeSource is TRUE), and SV_TYPE_PRINTQ_SERVER (set
    //    if there are any print shares).
    //
    // 2) SV_TYPE_DFS if this machine is the root of a DFS tree
    //
    // 3) The bits set by the service controller calling I_NetServerSetServiceBits.
    //      SV_TYPE_TIME_SOURCE is a pseudo internal bit.  It can be set
    //      internally or it can be set by the w32time service.
    //
    // 4) The logical OR of all per-transport server type bits set by
    //    the Browser calling I_NetServerSetServiceBits.
    //

    (VOID)RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE );

    serviceBits = SsGetServerType();

    if( ARGUMENT_PRESENT( service ) ) {
        //
        // Change the bits for the passed-in NAME_LIST_ENTRY only
        //

        newServiceBits = service->ServiceBits;
        newServiceBits &= ~(SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS);
        newServiceBits |= serviceBits;

        if( service->ServiceBits != newServiceBits ) {
            service->ServiceBits |= newServiceBits;
            changed = TRUE;
        }

    } else {
        //
        // Change the bits for each NAME_LIST_ENTRY
        //
        for ( service = SsData.SsServerNameList; service != NULL; service = service->Next ) {

            newServiceBits = service->ServiceBits;
            newServiceBits &= ~(SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS );
            newServiceBits |= serviceBits;

            if( service->ServiceBits != newServiceBits ) {
                service->ServiceBits |= newServiceBits;
                changed = TRUE;
            }
        }
    }

    RtlReleaseResource( &SsData.SsServerInfoResource );

    if ( changed && UpdateImmediately ) {
        if( SsData.SsStatusChangedEvent )
        {
            if ( !SetEvent( SsData.SsStatusChangedEvent ) ) {
                SS_PRINT(( "SsSetExportedServerType: SetEvent failed: %ld\n",
                        GetLastError( ) ));
            }
        }
    }

    return;

} // SsSetExportedServerType


NET_API_STATUS
SsSetField (
    IN PFIELD_DESCRIPTOR Field,
    IN PVOID Value,
    IN BOOLEAN WriteToRegistry,
    OUT BOOLEAN *AnnouncementInformationChanged OPTIONAL
    )
{
    PCHAR structure;

    //
    // *** We do not initialize *AnnouncementInformationChanged to
    //     FALSE!  We leave it alone, unless interesting information is
    //     changed, in which case we set it to TRUE.  This is to allow a
    //     caller to initialize it itself, then call this function
    //     multiple times, with the resulting value in the parameter
    //     being TRUE if at least one of the calls changed an
    //     interesting parameter.
    //

    //
    // Determine the structure that will be set.
    //

    if ( Field->Level / 100 == 5 ) {
        if ( Field->Level != 598 ) {
            structure = (PCHAR)&SsData.ServerInfo599;
        } else {
            structure = (PCHAR)&SsData.ServerInfo598;
        }
    } else {
        structure = (PCHAR)&SsData.ServerInfo102;
    }

    //
    // Set the value in the field based on the field type.
    //

    switch ( Field->FieldType ) {

    case BOOLEAN_FIELD: {

        BOOLEAN value = *(PBOOLEAN)Value;
        PBOOLEAN valueLocation;

        //
        // BOOLEANs may only be TRUE (1) or FALSE (0).
        //

        if ( value != TRUE && value != FALSE ) {
            return ERROR_INVALID_PARAMETER;
        }

        valueLocation = (PBOOLEAN)( structure + Field->FieldOffset );

        //
        // If we're turning off Hidden (i.e., making the server public),
        // indicate that an announcment-related parameter has changed.
        // This will cause an announcement to be sent immediately.
        //

        if ( (Field->FieldOffset ==
                        FIELD_OFFSET( SERVER_INFO_102, sv102_hidden )) &&
             (value && !(*valueLocation)) &&
             (ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) {
                *AnnouncementInformationChanged = TRUE;
        }

        *valueLocation = value;

        break;
    }

    case DWORD_FIELD: {

        DWORD value = *(PDWORD)Value;
        PDWORD valueLocation;

        //
        // Make sure that the specified value is in the range of
        // legal values for the Field.
        //

        if ( value > Field->MaximumValue || value < Field->MinimumValue ) {
            return ERROR_INVALID_PARAMETER;
        }

        valueLocation = (PDWORD)( structure + Field->FieldOffset );
        *valueLocation = value;

        break;
    }

    case LPSTR_FIELD: {

        LPWCH value = *(LPWCH *)Value;
        LPWSTR valueLocation;
        ULONG maxLength;

        //
        // We are setting the name, comment, or userpath for the server.
        // Use the field offset to determine which.
        //

        if ( Field->FieldOffset ==
                 FIELD_OFFSET( SERVER_INFO_102, sv102_name ) ) {
            valueLocation = SsData.ServerNameBuffer;
            maxLength = sizeof( SsData.SsServerTransportAddress );
        } else if ( Field->FieldOffset ==
                        FIELD_OFFSET( SERVER_INFO_102, sv102_comment ) ) {
            valueLocation = SsData.ServerCommentBuffer;
            maxLength = MAXCOMMENTSZ;
        } else if ( Field->FieldOffset ==
                        FIELD_OFFSET( SERVER_INFO_102, sv102_userpath ) ) {
            valueLocation = SsData.UserPathBuffer;
            maxLength = MAX_PATH;
        } else if ( Field->FieldOffset ==
                        FIELD_OFFSET( SERVER_INFO_599, sv599_domain ) ) {
            valueLocation = SsData.DomainNameBuffer;
            maxLength = DNLEN;
        } else {
            SS_ASSERT( FALSE );
            return ERROR_INVALID_PARAMETER;
        }

        //
        // If the string is too long, return an error.
        //

        if ( (value != NULL) && (STRLEN(value) > maxLength) ) {
            return ERROR_INVALID_PARAMETER;
        }

        //
        // If we're changing the server comment, indicate that an
        // announcment-related parameter has changed.  This will cause
        // an announcement to be sent immediately.
        //

        if ( (Field->FieldOffset ==
                        FIELD_OFFSET( SERVER_INFO_102, sv102_comment )) &&
             ( ((value == NULL) && (*valueLocation != '\0')) ||
               ((value != NULL) && (wcscmp(value,valueLocation) != 0)) ) &&
             (ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) {
                *AnnouncementInformationChanged = TRUE;
        }

        //
        // If the input is NULL, make the string zero length.
        //

        if ( value == NULL ) {

            *valueLocation = '\0';
            *(valueLocation+1) = '\0';

        } else {

            wcscpy( valueLocation, value );

        }

        break;
    }

    } // end switch

    //
    // The change worked.  If requested, add the parameter to the
    // registry, thus effecting a sticky change.  Don't write it
    // to the registry if this is xxx_comment or xxx_disc since
    // we already write out their more well known aliases
    // srvcomment and autodisconnect.  Changes here should also be
    // made to SetStickyParameters().
    //

    if ( WriteToRegistry &&
         (_wcsicmp( Field->FieldName, DISC_VALUE_NAME ) != 0) &&
         (_wcsicmp( Field->FieldName, COMMENT_VALUE_NAME ) != 0) ) {

        SsAddParameterToRegistry( Field, Value );
    }

    return NO_ERROR;

} // SsSetField

UINT
SsGetDriveType (
    IN LPWSTR path
)
/*++

Routine Description:

    This routine calls GetDriveType, attempting to eliminate
    the DRIVE_NO_ROOT_DIR type

Arguments:

    A path

Return Value:

    The drive type

--*/
{
    UINT driveType = GetDriveType( path );

    if( driveType == DRIVE_NO_ROOT_DIR ) {

        if( path[0] != UNICODE_NULL && path[1] == L':' ) {

            WCHAR shortPath[ 4 ];

            shortPath[0] = path[0];
            shortPath[1] = L':';
            shortPath[2] = L'\\';
            shortPath[3] = L'\0';

            driveType = GetDriveType( shortPath );

        } else {

            ULONG len = wcslen( path );
            LPWSTR pathWithSlash = MIDL_user_allocate( (len + 2) * sizeof( *path ) );

            if( pathWithSlash != NULL ) {
                RtlCopyMemory( pathWithSlash, path, len * sizeof( *path ) );
                pathWithSlash[ len ] = L'\\';
                pathWithSlash[ len+1 ] = L'\0';
                driveType = GetDriveType( pathWithSlash );
                MIDL_user_free( pathWithSlash );
            }
        }
    }

    return driveType;
}

VOID
SsNotifyRdrOfGuid(
    LPGUID Guid
    )
{
    NTSTATUS status;
    HANDLE hMrxSmbHandle;
    UNICODE_STRING unicodeServerName;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK ioStatusBlock;

    //
    // Open the server device.
    //

    RtlInitUnicodeString( &unicodeServerName, MRXSMB_DEVICE_NAME );

    InitializeObjectAttributes(
        &objectAttributes,
        &unicodeServerName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
        );

    //
    // Opening the server with desired access = SYNCHRONIZE and open
    // options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have
    // to worry about waiting for the NtFsControlFile to complete--this
    // makes all IO system calls that use this handle synchronous.
    //

    status = NtOpenFile(
                 &hMrxSmbHandle,
                 FILE_ALL_ACCESS & ~SYNCHRONIZE,
                 &objectAttributes,
                 &ioStatusBlock,
                 0,
                 0
                 );

    if ( NT_SUCCESS(status) ) {
        status = NtFsControlFile( hMrxSmbHandle,
                                  NULL,
                                  NULL,
                                  NULL,
                                  &ioStatusBlock,
                                  FSCTL_LMR_SET_SERVER_GUID,
                                  Guid,
                                  sizeof(GUID),
                                  NULL,
                                  0 );

        NtClose( hMrxSmbHandle );
    }
}