vbsf.c revision 84f633d28fd1840520118fcc4ff7c726aeaa9184
/* $Id$ */
/** @file
* VirtualBox Windows Guest Shared Folders.
*
* File System Driver initialization and generic routines
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "vbsf.h"
/*
* The current state of the driver.
*/
typedef enum _MRX_VBOX_STATE_
{
/*
* The VBoxSF dispatch table.
*/
static struct _MINIRDR_DISPATCH VBoxMRxDispatch;
/*
* The VBoxSF device object.
*/
{
Log(("VBOXSF: MRxFsdDispatch: major %d, minor %d: %s\n",
{
Log(("VBOXSF: MRxFsdDispatch: Invalid device request detected %p %p\n",
return STATUS_INVALID_DEVICE_REQUEST;
}
Log(("VBOXSF: MRxFsdDispatch: Returned 0x%X\n",
Status));
return Status;
}
{
Log(("VBOXSF: MRxUnload\n"));
if (VBoxMRxDeviceObject)
{
pDeviceExtension = (PMRX_VBOX_DEVICE_EXTENSION)((PBYTE)VBoxMRxDeviceObject + sizeof(RDBSS_DEVICE_OBJECT));
}
vboxUninit();
if (VBoxMRxDeviceObject)
{
{
if (Status == STATUS_SUCCESS)
{
State = (MRX_VBOX_STATE)InterlockedCompareExchange((LONG *)&VBoxMRxState, MRX_VBOX_STARTABLE, MRX_VBOX_STARTED);
if (State != MRX_VBOX_STARTABLE)
}
}
else
}
if (Status != STATUS_SUCCESS)
}
static void vbsfInitMRxDispatch(void)
{
Log(("VBOXSF: vbsfInitMRxDispatch: Called.\n"));
VBoxMRxDispatch.MRxCompleteBufferingStateChangeRequest = VBoxMRxCompleteBufferingStateChangeRequest;
Log(("VBOXSF: vbsfInitMRxDispatch: Success.\n"));
return;
}
{
/* The FilePathName here looks like: \vboxsrv\... */
{
/* Both vboxsvr & vboxsrv are now accepted */
{
}
else
{
}
{
/* There is something after '\vboxsrv'. */
}
}
else
return PrefixOK;
}
{
/* Make a local copy, it will be needed after the Irp completion. */
PMRX_VBOX_DEVICE_EXTENSION pDeviceExtension = (PMRX_VBOX_DEVICE_EXTENSION)((PBYTE)pDevObj + sizeof(RDBSS_DEVICE_OBJECT));
Log(("VBOXSF: MRXDeviceControl: pDevObj %p, pDeviceExtension %p, code %x\n",
switch (IoControlCode)
{
case IOCTL_REDIR_QUERY_PATH_EX: /* Vista */
case IOCTL_REDIR_QUERY_PATH: /* XP and earlier */
{
/* This IOCTL is intercepted for 2 reasons:
* 1) Claim the vboxsvr and vboxsrv prefixes. All name-based operations for them
* will be routed to the VBox provider automatically without any prefix resolution
* since the prefix is already in the prefix cache.
* 2) Reject other prefixes immediately to speed up the UNC path resolution a bit,
* because RDBSS will not be involved then.
*/
ULONG PathNameLength = 0;
{
/* MSDN: Network redirectors should only honor kernel-mode senders of this IOCTL, by verifying
* that RequestorMode member of the IRP structure is KernelMode.
*/
Log(("VBOXSF: MRxDeviceControl: IOCTL_REDIR_QUERY_PATH(_EX): not kernel mode!!!\n",
/* Continue to RDBSS. */
break;
}
if (IoControlCode == IOCTL_REDIR_QUERY_PATH)
{
Log(("VBOXSF: MRxDeviceControl: IOCTL_REDIR_QUERY_PATH: Called (pid %x).\n", IoGetCurrentProcess()));
{
Log(("VBOXSF: MRxDeviceControl: IOCTL_REDIR_QUERY_PATH: short input buffer %d.\n",
/* Continue to RDBSS. */
break;
}
Log(("VBOXSF: MRxDeviceControl: FilePathName = %.*ls.\n", pReq->PathNameLength / sizeof (WCHAR), pReq->FilePathName));
}
else
{
Log(("VBOXSF: MRxDeviceControl: IOCTL_REDIR_QUERY_PATH_EX: Called.\n"));
{
Log(("VBOXSF: MRxDeviceControl: IOCTL_REDIR_QUERY_PATH_EX: short input buffer %d.\n",
/* Continue to RDBSS. */
break;
}
Log(("VBOXSF: MRxDeviceControl: FilePathName = %.*ls.\n", pReqEx->PathName.Length / sizeof (WCHAR), pReqEx->PathName.Buffer));
}
if (!PrefixOK)
{
/* Immediately fail the IOCTL with STATUS_BAD_NETWORK_NAME as recommended by MSDN.
* No need to involve RDBSS.
*/
Log(("VBOXSF: MRxDeviceControl: returned STATUS_BAD_NETWORK_NAME\n"));
return Status;
}
if (pResp)
{
/* Always claim entire \vboxsrv prefix. The LengthAccepted initially is equal to entire path.
* Here it is assigned to the length of \vboxsrv prefix.
*/
Log(("VBOXSF: MRxDeviceControl: claiming the path.\n"));
return Status;
}
/* No pResp pointer, should not happen. Just a precaution. */
Log(("VBOXSF: MRxDeviceControl: returned STATUS_INVALID_PARAMETER\n"));
return Status;
}
default:
break;
}
/* Pass the IOCTL to RDBSS. */
{
}
else
{
/* No RDBSS, should not happen. Just a precaution. */
Log(("VBOXSF: MRxDeviceControl: returned STATUS_NOT_IMPLEMENTED\n"));
}
return Status;
}
{
ULONG i;
int vboxRC;
if (!DriverObject)
{
Log(("VBOXSF: DriverEntry: driver object is NULL.\n"));
return STATUS_UNSUCCESSFUL;
}
/* Initialize VBox subsystem. */
if (RT_FAILURE(vboxRC))
{
return STATUS_UNSUCCESSFUL;
}
/* Connect the HGCM client */
if (RT_FAILURE(vboxRC))
{
Log(("VBOXSF: DriverEntry: ERROR while connecting to host (%Rrc)!\n",
vboxRC));
vboxUninit();
return STATUS_UNSUCCESSFUL;
}
/* Init the driver object. */
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
}
/* Forward to RDBSS. */
if (Status != STATUS_SUCCESS)
{
goto failure;
}
{
Log(("VBOXSF: DriverEntry: RxRegisterMinirdr: calling VBoxMRxDeviceObject %p\n",
/* Don use RX_REGISTERMINI_FLAG_DONT_PROVIDE_UNCS or else
* UNC mappings don't work (including Windows explorer browsing).
*/
sizeof(MRX_VBOX_DEVICE_EXTENSION),
Log(("VBOXSF: DriverEntry: RxRegisterMinirdr: returned 0x%08X VBoxMRxDeviceObject %p\n",
if (Status!=STATUS_SUCCESS)
{
}
/* Init the device extension.
* NOTE: the device extension actually points to fields
* in the RDBSS_DEVICE_OBJECT. Our space is past the end
* of this struct!!
*/
pDeviceExtension = (PMRX_VBOX_DEVICE_EXTENSION)((PBYTE)VBoxMRxDeviceObject + sizeof(RDBSS_DEVICE_OBJECT));
{
}
/* Mutex for synchronizining our connection list */
/* The device object has been created. Need to setup a symbolic
* link so that the device may be accessed from a Win32 user mode
* application.
*/
Log(("VBOXSF: DriverEntry: Calling IoCreateSymbolicLink\n"));
if (Status != STATUS_SUCCESS)
{
Log(("VBOXSF: DriverEntry: IoCreateSymbolicLink: 0x%08X\n",
Status));
}
Log(("VBOXSF: DriverEntry: Symbolic link created.\n"));
/*
* Build the dispatch tables for the minirdr
*/
;
}
{
;
}
if (Status != STATUS_SUCCESS)
{
Status));
goto failure;
}
/* The redirector driver must intercept the IOCTL to avoid VBOXSVR name resolution
* by other redirectors. These additional name resolutions cause long delays.
*/
Log(("VBOXSF: DriverEntry: VBoxMRxDeviceObject = %p, rdbss %p, devext %p\n",
/* @todo start the redirector here RxStartMiniRdr. */
Log(("VBOXSF: DriverEntry: Init successful!\n"));
return STATUS_SUCCESS;
vboxUninit();
if (VBoxMRxDeviceObject)
{
}
return Status;
}
{
Log(("VBOXSF: MRxStart\n"));
CurrentState = (MRX_VBOX_STATE)InterlockedCompareExchange((PLONG)&VBoxMRxState, MRX_VBOX_STARTED, MRX_VBOX_START_IN_PROGRESS);
{
Log(("VBOXSF: MRxStart: Start in progress -> started\n"));
}
else if (VBoxMRxState == MRX_VBOX_STARTED)
{
Log(("VBOXSF: MRxStart: Already started\n"));
}
else
{
}
return Status;
}
{
Log(("VBOXSF: MRxStop\n"));
return STATUS_SUCCESS;
}
{
Log(("VBOXSF: MRxIoCtl: IoControlCode = 0x%08X\n",
return STATUS_INVALID_DEVICE_REQUEST;
}
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: MajorFunction = 0x%02X\n",
switch (RxContext->MajorFunction)
{
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IRP_MN_USER_FS_REQUEST: 0x%08X\n",
break;
}
case IRP_MJ_DEVICE_CONTROL:
{
{
case IOCTL_MRX_VBOX_ADDCONN:
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_ADDCONN\n"));
break;
}
case IOCTL_MRX_VBOX_DELCONN:
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_DELCONN\n"));
break;
}
case IOCTL_MRX_VBOX_GETLIST:
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETLIST\n"));
if (!pDeviceExtension)
{
RxContext->InformationToReturn = 0;
break;
}
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETLIST: Copying local connections\n"));
if (fLocked)
{
}
}
else
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETLIST: cbOut is too small %d bytes\n",
cbOut));
RxContext->InformationToReturn = 0;
}
break;
}
/*
* Returns the root IDs of shared folder mappings.
*/
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETGLOBALLIST\n"));
RxContext->InformationToReturn = 0;
if (!pDeviceExtension)
break;
{
if (vboxRC == VINF_SUCCESS)
{
uint32_t i;
{
}
}
else
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETGLOBALLIST failed: 0x%08X\n",
Status));
}
}
break;
}
/*
* Translates a local connection name (e.g. drive "S:") to the
* corresponding remote name (e.g. \\vboxsrv\share).
*/
case IOCTL_MRX_VBOX_GETCONN:
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETCONN: ConnectName = %.*ls, Len = %d, RemoteName = 0x%p, Len = %d\n",
if (!pDeviceExtension)
{
break;
}
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETCONN: Looking up connection name and connections\n"));
{
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETCONN: Index is invalid!\n"));
break;
}
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETCONN: LocalConnectionName is NULL!\n"));
break;
}
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETCONN: LocalConnectionName = %.*ls\n",
{
RtlCopyMemory(pwcRemoteName, pDeviceExtension->wszLocalConnectionName[idx]->Buffer, cbLocalConnectionName);
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETCONN: Remote name = %.*ls, Len = %d\n",
}
else
{
}
}
else
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETCONN: bad connect name!n"));
}
break;
}
{
ULONG ReturnedSize = 0;
int vboxRC;
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETGLOBALCONN: Connection ID = %d, RemoteName = 0x%x, Len = %d\n",
/* Allocate empty string where the host can store cbRemoteName bytes. */
if (Status != STATUS_SUCCESS)
break;
if ( vboxRC == VINF_SUCCESS
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_GETGLOBALCONN: Returned name = %.*ls, Len = %d\n",
}
else
break;
}
case IOCTL_MRX_VBOX_START:
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: capFobx %p\n",
capFobx));
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: process: current 0x%X, RDBSS 0x%X\n",
IoGetCurrentProcess(), RxGetRDBSSProcess()));
switch (VBoxMRxState)
{
case MRX_VBOX_STARTABLE:
Log(("VBOXSF: MRxDevFcbXXXControlFile: MRX_VBOX_STARTABLE\n"));
if (capFobx)
{
break;;
}
Log(("VBOXSF: MRxDevFcbXXXControlFile: MRX_VBOX_START_IN_PROGRESS RxStartMiniRdr Status 0x%08X, post %d\n",
if (Status == STATUS_REDIRECTOR_STARTED)
{
break;
}
if ( Status == STATUS_PENDING
{
/* Will be restarted in RDBSS process. */
break;
}
/* Allow restricted users to use shared folders; works only in XP and Vista. (@@todo hack) */
if (Status == STATUS_SUCCESS)
{
/* Create empty security descriptor */
if (Status != STATUS_SUCCESS)
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: MRX_VBOX_START_IN_PROGRESS: RtlCreateSecurityDescriptor failed with 0x%08X!\n",
Status));
return Status;
}
/* Open our symbolic link device name */
if (Status != STATUS_SUCCESS)
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: MRX_VBOX_START_IN_PROGRESS: ZwOpenFile %ls failed with 0x%08X!\n",
return Status;
}
/* Override the discretionary access control list (DACL) settings */
if (Status != STATUS_SUCCESS)
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: MRX_VBOX_START_IN_PROGRESS: ZwSetSecurityObject failed with 0x%08X!\n",
Status));
return Status;
}
if (Status != STATUS_SUCCESS)
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: MRX_VBOX_START_IN_PROGRESS: ZwClose failed with 0x%08X\n",
Status));
return Status;
}
}
break;
case MRX_VBOX_STARTED:
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: MRX_VBOX_STARTED: Already started\n"));
break;
default:
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: Invalid state (%d)!\n",
VBoxMRxState));
break;
}
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_START: Returned 0x%08X\n",
Status));
break;
}
case IOCTL_MRX_VBOX_STOP:
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_STOP: capFobx %p\n",
capFobx));
if (capFobx)
{
break;
}
{
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_STOP: Open handles = %d\n",
break;
}
CurrentState = (MRX_VBOX_STATE)InterlockedCompareExchange((PLONG) & VBoxMRxState, MRX_VBOX_STARTABLE, MRX_VBOX_STARTED);
Log(("VBOXSF: MRxDevFcbXXXControlFile: IOCTL_MRX_VBOX_STOP: Returned 0x%08X\n",
Status));
break;
}
default:
break;
}
break;
}
{
break;
}
default:
Log(("VBOXSF: MRxDevFcbXXXControlFile: unimplemented major function 0x%02X\n",
break;
}
Log(("VBOXSF: MRxDevFcbXXXControlFile: Status = 0x%08X, Info = 0x%08X\n",
return Status;
}
{
/* Check that the connection name is valid:
* "\Device\VBoxMiniRdr\;X:\vboxsvr\sf"
*/
ULONG i;
/* Unicode chars in the string. */
/* Check that the name starts with correct prefix. */
pwc1 = &spwszPrefix[0];
{
break;
}
cRemainingName = cConnectionName - i;
Log(("VBOXSF: vbsfVerifyConnectionName: prefix %d remaining %d [%.*ls]\n",
if (*pwc1 == 0)
{
/* pwc should point to a drive letter followed by ':\' that is at least 3 chars more. */
if (cRemainingName >= 3)
{
{
pwc += 2;
cRemainingName -= 2;
/* @todo should also check that the drive letter corresponds to the name. */
}
}
}
return Status;
}
{
Log(("VBOXSF: vbsfOpenConnectionHandle: ConnectionName = %.*ls\n",
if (NT_SUCCESS(Status))
{
/* Have to create a OBJ_KERNEL_HANDLE. Otherwise the driver verifier on Windows 7 bugchecks. */
NULL,
NULL);
NULL,
NULL,
0);
}
if ( Status != STATUS_SUCCESS
|| Handle == INVALID_HANDLE_VALUE)
{
Log(("VBOXSF: vbsfOpenConnectionHandle: ZwCreateFile failed status 0x%08X or invalid handle!\n",
Status));
}
return Handle;
}
{
Log(("VBOXSF: vbsfCreateConnection\n"));
{
Log(("VBOXSF: vbsfCreateConnection: post to file system process\n"));
return STATUS_PENDING;
}
if (!pDeviceExtension)
return STATUS_INVALID_PARAMETER;
if (cbConnectName == 0 || !pwcConnectName)
{
Log(("VBOXSF: vbsfCreateConnection: Connection name / length is invalid!\n"));
return STATUS_INVALID_PARAMETER;
}
Log(("VBOXSF: vbsfCreateConnection: Name = %.*ls, Len = %d\n",
if (Handle != INVALID_HANDLE_VALUE)
{
ULONG i;
/* Skip the "\Device\VBoxMiniRdr\;X:" of the string "\Device\VBoxMiniRdr\;X:\vboxsrv\sf" */
for (i = 0; i < cbConnectName; i += sizeof(WCHAR))
{
if (*pwc == L':')
break;
pwc++;
}
if (i >= sizeof(WCHAR) && i < cbConnectName)
{
pwc--; /* Go back to the drive letter, "X" for example. */
{
{
Log(("VBOXSF: vbsfCreateConnection: Index 0x%x is invalid!\n",
idx));
}
else
{
{
Log(("VBOXSF: vbsfCreateConnection: LocalConnectionName at index %d is NOT empty!\n",
idx));
}
pDeviceExtension->wszLocalConnectionName[idx] = (PUNICODE_STRING)vbsfAllocNonPagedMem(sizeof(UNICODE_STRING) + cbConnectName);
{
Log(("VBOXSF: vbsfCreateConnection: LocalConnectionName at index %d NOT allocated!\n",
idx));
}
else
{
Log(("VBOXSF: vbsfCreateConnection: RemoteName %.*ls, Len = %d\n",
}
}
}
}
else
{
Log(("VBOXSF: vbsfCreateConnection: bad format\n"));
}
}
else
{
Log(("VBOXSF: vbsfCreateConnection: connection was not found\n"));
}
return Status;
}
{
{
Log(("VBOXSF: vbsfDeleteConnection: post to file system process\n"));
return STATUS_PENDING;
}
Log(("VBOXSF: vbsfDeleteConnection: pwcConnectName = %.*ls\n",
if (Handle != INVALID_HANDLE_VALUE)
{
Log(("VBOXSF: vbsfDeleteConnection: ObReferenceObjectByHandle Status 0x%08X\n",
Status));
if (NT_SUCCESS(Status))
{
{
}
else
{
Log(("VBOXSF: vbsfDeleteConnection: wrong FsContext2\n"));
}
}
}
if (NT_SUCCESS(Status))
{
ULONG i;
/* Skip the "\Device\VBoxMiniRdr\;X:" of the string "\Device\VBoxMiniRdr\;X:\vboxsrv\sf" */
for (i = 0; i < cbConnectName; i += sizeof(WCHAR))
{
if (*pwc == L':')
{
break;
}
pwc++;
}
if (i >= sizeof(WCHAR) && i < cbConnectName)
{
pwc--;
{
{
Log(("VBOXSF: vbsfDeleteConnection: Index 0x%x is invalid!\n",
idx));
}
else
{
/* Free saved name */
{
}
Log(("VBOXSF: vbsfDeleteConnection: deleted index 0x%x\n",
idx));
}
}
}
else
{
Log(("VBOXSF: vbsfCreateConnection: bad format\n"));
}
}
return Status;
}
{
Log(("VBOXSF: MRxQueryEaInfo: Ea buffer len remaining is %d\n",
return STATUS_SUCCESS;
}
{
Log(("VBOXSF: MRxSetEaInfo\n"));
return STATUS_NOT_IMPLEMENTED;
}
{
Log(("VBOXSF: MRxFsCtl\n"));
return STATUS_INVALID_DEVICE_REQUEST;
}
{
Log(("VBOXSF: MRxNotifyChangeDirectory\n"));
return STATUS_NOT_IMPLEMENTED;
}
{
Log(("VBOXSF: MRxQuerySdInfo\n"));
return STATUS_NOT_IMPLEMENTED;
}
{
Log(("VBOXSF: MRxSetSdInfo\n"));
return STATUS_NOT_IMPLEMENTED;
}
/*
* WML stubs which are referenced by rdbsslib.
*/
{
return STATUS_WMI_GUID_NOT_FOUND;
}
{
return STATUS_SUCCESS;
}