windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/srvsvc/server/sssubs.c

1170 lines
27 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 );
}
}