mirror of
https://github.com/reactos/reactos.git
synced 2025-05-06 18:31:26 +00:00
[BASESRV] Implement LUID mapped drive arrival/removal notification
CORE-16114
This commit is contained in:
parent
522821bb8b
commit
222ace7c6c
3 changed files with 367 additions and 7 deletions
|
@ -16,6 +16,7 @@
|
|||
#define COM_NO_WINDOWS_H
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#include <dbt.h>
|
||||
#define NTOS_MODE_USER
|
||||
#include <ndk/rtlfuncs.h>
|
||||
#include <ndk/obfuncs.h>
|
||||
|
@ -67,6 +68,7 @@ extern HANDLE BaseSrvSharedHeap;
|
|||
extern PBASE_STATIC_SERVER_DATA BaseStaticServerData;
|
||||
extern ULONG SessionId;
|
||||
extern ULONG ProtectionMode;
|
||||
extern RTL_CRITICAL_SECTION BaseSrvDDDBSMCritSec;
|
||||
|
||||
#define SM_REG_KEY \
|
||||
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager"
|
||||
|
|
|
@ -13,9 +13,21 @@
|
|||
#define NDEBUG
|
||||
#include <debug.h>
|
||||
|
||||
typedef struct _BSM_REQUEST
|
||||
{
|
||||
struct _BSM_REQUEST * Next;
|
||||
LUID BroadcastLuid;
|
||||
LONG DriveLetter;
|
||||
LONG RemoveDefinition;
|
||||
} BSM_REQUEST, *PBSM_REQUEST;
|
||||
|
||||
/* GLOBALS ********************************************************************/
|
||||
|
||||
static RTL_CRITICAL_SECTION BaseDefineDosDeviceCritSec;
|
||||
RTL_CRITICAL_SECTION BaseSrvDDDBSMCritSec;
|
||||
PBSM_REQUEST BSM_Request_Queue = NULL, BSM_Request_Queue_End = NULL;
|
||||
ULONG BaseSrvpBSMThreadCount = 0;
|
||||
LONG (WINAPI *PBROADCASTSYSTEMMESSAGEEXW)(DWORD, LPDWORD, UINT, WPARAM, LPARAM, PBSMINFO) = NULL;
|
||||
|
||||
/* PRIVATE FUNCTIONS **********************************************************/
|
||||
|
||||
|
@ -146,6 +158,317 @@ IsGlobalSymbolicLink(HANDLE LinkHandle,
|
|||
return Status;
|
||||
}
|
||||
|
||||
BOOLEAN
|
||||
CheckForGlobalDriveLetter(SHORT DriveLetter)
|
||||
{
|
||||
WCHAR Path[8];
|
||||
NTSTATUS Status;
|
||||
BOOLEAN IsGlobal;
|
||||
UNICODE_STRING PathU;
|
||||
HANDLE SymbolicLinkHandle;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
|
||||
/* Setup our drive path */
|
||||
wcsncpy(Path, L"\\??\\X:", (sizeof(L"\\??\\X:") / sizeof(WCHAR)));
|
||||
Path[4] = DriveLetter + L'A';
|
||||
Path[6] = UNICODE_NULL;
|
||||
|
||||
/* Prepare everything to open the link */
|
||||
RtlInitUnicodeString(&PathU, Path);
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
&PathU,
|
||||
OBJ_CASE_INSENSITIVE,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
/* Impersonate the caller */
|
||||
if (!CsrImpersonateClient(NULL))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Open our drive letter */
|
||||
Status = NtOpenSymbolicLinkObject(&SymbolicLinkHandle,
|
||||
SYMBOLIC_LINK_QUERY,
|
||||
&ObjectAttributes);
|
||||
|
||||
CsrRevertToSelf();
|
||||
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check whether it's global */
|
||||
Status = IsGlobalSymbolicLink(SymbolicLinkHandle, &IsGlobal);
|
||||
NtClose(SymbolicLinkHandle);
|
||||
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return IsGlobal;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
SendWinStationBSM(DWORD Flags,
|
||||
LPDWORD Recipients,
|
||||
UINT Message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
BroadcastDriveLetterChange(LONG DriveLetter,
|
||||
BOOLEAN RemoveDefinition,
|
||||
PLUID BroadcastLuid)
|
||||
{
|
||||
HANDLE hUser32;
|
||||
NTSTATUS Status;
|
||||
UNICODE_STRING User32U;
|
||||
ANSI_STRING ProcedureName;
|
||||
DWORD Recipients, Flags, wParam;
|
||||
LUID SystemLuid = SYSTEM_LUID;
|
||||
BSMINFO Info;
|
||||
DEV_BROADCAST_VOLUME Volume;
|
||||
|
||||
/* We need a broadcast LUID */
|
||||
if (BroadcastLuid == NULL)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Get the Csr procedure, and keep it forever */
|
||||
if (PBROADCASTSYSTEMMESSAGEEXW == NULL)
|
||||
{
|
||||
hUser32 = NULL;
|
||||
RtlInitUnicodeString(&User32U, L"user32");
|
||||
Status = LdrGetDllHandle(NULL, NULL, &User32U, &hUser32);
|
||||
if (hUser32 != NULL && NT_SUCCESS(Status))
|
||||
{
|
||||
RtlInitString(&ProcedureName, "CsrBroadcastSystemMessageExW");
|
||||
Status = LdrGetProcedureAddress(hUser32,
|
||||
&ProcedureName,
|
||||
0,
|
||||
(PVOID *)&PBROADCASTSYSTEMMESSAGEEXW);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
PBROADCASTSYSTEMMESSAGEEXW = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we failed to get broadcast procedure, no more actions left */
|
||||
if (PBROADCASTSYSTEMMESSAGEEXW == NULL)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize broadcast info */
|
||||
Info.cbSize = sizeof(BSMINFO);
|
||||
Info.hdesk = 0;
|
||||
Info.hwnd = 0;
|
||||
RtlCopyLuid(&Info.luid, BroadcastLuid);
|
||||
|
||||
/* Initialize volume information */
|
||||
Volume.dbcv_size = sizeof(DEV_BROADCAST_VOLUME);
|
||||
Volume.dbcv_devicetype = DBT_DEVTYP_VOLUME;
|
||||
Volume.dbcv_reserved = 0;
|
||||
Volume.dbcv_unitmask = 1 << DriveLetter;
|
||||
Volume.dbcv_flags = DBTF_NET;
|
||||
|
||||
/* Wide broadcast */
|
||||
Recipients = BSM_APPLICATIONS | BSM_ALLDESKTOPS;
|
||||
Flags = BSF_NOHANG | BSF_NOTIMEOUTIFNOTHUNG | BSF_FORCEIFHUNG;
|
||||
|
||||
/*
|
||||
* If we don't broadcast as system, it's not a global drive
|
||||
* notification, then mark it as LUID mapped drive
|
||||
*/
|
||||
if (!RtlEqualLuid(&Info.luid, &SystemLuid))
|
||||
{
|
||||
Flags |= BSF_LUID;
|
||||
}
|
||||
|
||||
/* Set event type */
|
||||
wParam = RemoveDefinition ? DBT_DEVICEREMOVECOMPLETE : DBT_DEVICEARRIVAL;
|
||||
|
||||
/* And broadcast! */
|
||||
Status = PBROADCASTSYSTEMMESSAGEEXW(Flags, &Recipients, WM_DEVICECHANGE, wParam, (LPARAM)&Volume, &Info);
|
||||
|
||||
/* If the drive is global, notify Winsta */
|
||||
if (!(Flags & BSF_LUID))
|
||||
{
|
||||
Status = SendWinStationBSM(Flags, &Recipients, WM_DEVICECHANGE, wParam, (LPARAM)&Volume);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
ULONG
|
||||
NTAPI
|
||||
BaseSrvBSMThread(PVOID StartupContext)
|
||||
{
|
||||
ULONG ExitStatus;
|
||||
NTSTATUS Status;
|
||||
PBSM_REQUEST CurrentRequest;
|
||||
|
||||
/* We have a thread */
|
||||
ExitStatus = 0;
|
||||
RtlEnterCriticalSection(&BaseSrvDDDBSMCritSec);
|
||||
++BaseSrvpBSMThreadCount;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
/* If we flushed the queue, job done */
|
||||
if (BSM_Request_Queue == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* Queue current request, and remove it from the queue */
|
||||
CurrentRequest = BSM_Request_Queue;
|
||||
BSM_Request_Queue = BSM_Request_Queue->Next;
|
||||
|
||||
/* If that was the last request, NULLify queue end */
|
||||
if (BSM_Request_Queue == NULL)
|
||||
{
|
||||
BSM_Request_Queue_End = NULL;
|
||||
}
|
||||
|
||||
RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
|
||||
|
||||
/* Broadcast the message */
|
||||
Status = BroadcastDriveLetterChange(CurrentRequest->DriveLetter,
|
||||
CurrentRequest->RemoveDefinition,
|
||||
&CurrentRequest->BroadcastLuid);
|
||||
|
||||
/* Reflect the last entry status on stop */
|
||||
CurrentRequest->Next = NULL;
|
||||
ExitStatus = Status;
|
||||
|
||||
RtlFreeHeap(BaseSrvHeap, 0, CurrentRequest);
|
||||
RtlEnterCriticalSection(&BaseSrvDDDBSMCritSec);
|
||||
}
|
||||
|
||||
/* Here, we've flushed the queue, quit the user thread */
|
||||
--BaseSrvpBSMThreadCount;
|
||||
RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
|
||||
|
||||
NtCurrentTeb()->FreeStackOnTermination = TRUE;
|
||||
NtTerminateThread(NtCurrentThread(), ExitStatus);
|
||||
|
||||
return ExitStatus;
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
CreateBSMThread(VOID)
|
||||
{
|
||||
/* This can only be true for LUID mappings */
|
||||
if (BaseStaticServerData->LUIDDeviceMapsEnabled == 0)
|
||||
{
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* Create our user thread */
|
||||
return RtlCreateUserThread(NtCurrentProcess(),
|
||||
NULL,
|
||||
FALSE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
BaseSrvBSMThread,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
NTSTATUS
|
||||
AddBSMRequest(LONG DriveLetter,
|
||||
BOOLEAN RemoveDefinition,
|
||||
PLUID BroadcastLuid)
|
||||
{
|
||||
LUID CallerLuid;
|
||||
NTSTATUS Status;
|
||||
LUID SystemLuid = SYSTEM_LUID;
|
||||
PBSM_REQUEST Request;
|
||||
|
||||
/* We need a broadcast LUID */
|
||||
if (BroadcastLuid == NULL)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/*
|
||||
* If LUID mappings are not enabled, this call makes no sense
|
||||
* It should not happen though
|
||||
*/
|
||||
if (BaseStaticServerData->LUIDDeviceMapsEnabled == 0)
|
||||
{
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* Get our caller LUID (not the broadcaster!) */
|
||||
Status = GetCallerLuid(&CallerLuid);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* System cannot create LUID mapped drives - thus broadcast makes no sense */
|
||||
if (!RtlEqualLuid(&CallerLuid, &SystemLuid))
|
||||
{
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* Allocate our request */
|
||||
Request = RtlAllocateHeap(BaseSrvHeap, 0, sizeof(BSM_REQUEST));
|
||||
if (Request == NULL)
|
||||
{
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* Initialize it */
|
||||
Request->DriveLetter = DriveLetter;
|
||||
Request->RemoveDefinition = RemoveDefinition;
|
||||
RtlCopyLuid(&Request->BroadcastLuid, BroadcastLuid);
|
||||
Request->Next = NULL;
|
||||
|
||||
/* And queue it */
|
||||
RtlEnterCriticalSection(&BaseSrvDDDBSMCritSec);
|
||||
|
||||
/* At the end of the queue if not empty */
|
||||
if (BSM_Request_Queue_End != NULL)
|
||||
{
|
||||
BSM_Request_Queue_End->Next = Request;
|
||||
}
|
||||
/* Otherwise, initialize the queue */
|
||||
else
|
||||
{
|
||||
BSM_Request_Queue = Request;
|
||||
}
|
||||
|
||||
/* We're in FIFO mode */
|
||||
BSM_Request_Queue_End = Request;
|
||||
|
||||
/* If we don't have a messaging thread running, then start one */
|
||||
if (BaseSrvpBSMThreadCount >= 1)
|
||||
{
|
||||
RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
|
||||
}
|
||||
else
|
||||
{
|
||||
RtlLeaveCriticalSection(&BaseSrvDDDBSMCritSec);
|
||||
Status = CreateBSMThread();
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* PUBLIC SERVER APIS *********************************************************/
|
||||
|
||||
CSR_API(BaseSrvDefineDosDevice)
|
||||
|
@ -167,7 +490,7 @@ CSR_API(BaseSrvDefineDosDevice)
|
|||
BOOLEAN DriveLetter = FALSE;
|
||||
BOOLEAN RemoveDefinition;
|
||||
BOOLEAN HandleTarget;
|
||||
BOOLEAN HandleSMB = FALSE;
|
||||
BOOLEAN Broadcast = FALSE;
|
||||
BOOLEAN IsGlobal = FALSE;
|
||||
ULONG CchLengthLeft;
|
||||
ULONG CchLength;
|
||||
|
@ -297,13 +620,19 @@ CSR_API(BaseSrvDefineDosDevice)
|
|||
_SEH2_LEAVE;
|
||||
}
|
||||
|
||||
/* While impersonating the caller, also get its LUID */
|
||||
/*
|
||||
* While impersonating the caller, also get its LUID.
|
||||
* This is mandatory in case we have a driver letter,
|
||||
* Because we're in the case we've got LUID mapping
|
||||
* enabled and broadcasting enabled. LUID will be required
|
||||
* for the latter
|
||||
*/
|
||||
if (DriveLetter)
|
||||
{
|
||||
Status = GetCallerLuid(&CallerLuid);
|
||||
if (NT_SUCCESS(Status))
|
||||
{
|
||||
HandleSMB = TRUE;
|
||||
Broadcast = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -744,10 +1073,31 @@ CSR_API(BaseSrvDefineDosDevice)
|
|||
/* Free our internal buffer */
|
||||
RtlFreeHeap(BaseSrvHeap, 0, lpBuffer);
|
||||
|
||||
/* Handle SMB */
|
||||
if (DriveLetter && Status == STATUS_SUCCESS && HandleSMB)
|
||||
/* Broadcast drive letter creation */
|
||||
if (DriveLetter && Status == STATUS_SUCCESS && Broadcast)
|
||||
{
|
||||
UNIMPLEMENTED;
|
||||
LUID SystemLuid = SYSTEM_LUID;
|
||||
|
||||
/* If that's a global drive, broadcast as system */
|
||||
if (IsGlobal)
|
||||
{
|
||||
RtlCopyLuid(&CallerLuid, &SystemLuid);
|
||||
}
|
||||
|
||||
/* Broadcast the event */
|
||||
AddBSMRequest(AbsLetter, RemoveDefinition, &CallerLuid);
|
||||
|
||||
/*
|
||||
* If we removed drive, and the drive was shadowing a global one
|
||||
* broadcast the arrival of the global drive (as system - global)
|
||||
*/
|
||||
if (RemoveDefinition && !RtlEqualLuid(&CallerLuid, &SystemLuid))
|
||||
{
|
||||
if (CheckForGlobalDriveLetter(AbsLetter))
|
||||
{
|
||||
AddBSMRequest(AbsLetter, FALSE, &CallerLuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Done! */
|
||||
|
|
|
@ -580,7 +580,10 @@ BaseInitializeStaticServerData(IN PCSR_SERVER_DLL LoadedServerDll)
|
|||
NULL);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
BaseStaticServerData->LUIDDeviceMapsEnabled = (BOOLEAN)LuidEnabled;
|
||||
if (!BaseStaticServerData->LUIDDeviceMapsEnabled)
|
||||
|
||||
/* Initialize Global */
|
||||
if (!BaseStaticServerData->LUIDDeviceMapsEnabled ||
|
||||
NT_SUCCESS(RtlInitializeCriticalSectionAndSpinCount(&BaseSrvDDDBSMCritSec, 0x80000000)))
|
||||
{
|
||||
/* Make Global point back to BNO */
|
||||
RtlInitUnicodeString(&DirectoryName, L"Global");
|
||||
|
@ -638,6 +641,11 @@ BaseInitializeStaticServerData(IN PCSR_SERVER_DLL LoadedServerDll)
|
|||
&ObjectAttributes);
|
||||
ASSERT(NT_SUCCESS(Status));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* That should never happen */
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
/* Initialize NLS */
|
||||
BaseSrvNLSInit(BaseStaticServerData);
|
||||
|
|
Loading…
Reference in a new issue