xbox-kernel/private/ntos/xapi/k32/mountmu.c
2020-09-30 17:17:25 +02:00

675 lines
18 KiB
C

/*++
Copyright (c) 2001-2002 Microsoft Corporation
Module Name:
mountmu.c
Abstract:
This module implements the XTL memory unit services.
--*/
#include "basedll.h"
#include <usbxapi.h>
#include <xboxp.h>
#include "..\..\fatx\fat.h"
#ifdef XAPILIBP
extern CRITICAL_SECTION XapiMountMUCriticalSection;
#ifdef DBG
extern ULONG g_XapiCurrentUserDevices;
#endif
#else // XAPILIBP
INITIALIZED_CRITICAL_SECTION(XapiMountMUCriticalSection);
#ifdef DBG
ULONG g_XapiCurrentUserDevices = 0;
#endif
#endif // XAPILIBP
#ifndef XAPILIBP
DWORD
WINAPI
XMountMU(
IN DWORD dwPort,
IN DWORD dwSlot,
OUT POCHAR pchDrive
)
{
NTSTATUS Status;
OCHAR szTitleId[CCHMAX_HEX_DWORD];
OCHAR szDeviceName[64];
OCHAR szDosDevice[64];
OCHAR chDrive;
OBJECT_STRING DeviceName, DosDevice;
RIP_ON_NOT_TRUE_WITH_MESSAGE(XPP_XInitDevicesHasBeenCalled, "XMountMU: XInitDevices must be called first!");
if (NULL != pchDrive)
{
*pchDrive = OBJECT_NULL;
}
EnterCriticalSection(&XapiMountMUCriticalSection);
#if DBG
if ((dwPort < MU_PORT_MIN) || (dwPort > MU_PORT_MAX))
{
RIP("XMountMU() invalid parameter (dwPort)");
}
if ((dwSlot < MU_SLOT_MIN) || (dwSlot > MU_SLOT_MAX))
{
RIP("XMountMU() invalid parameter (dwSlot)");
}
if(MU_MaxUserDevices <= g_XapiCurrentUserDevices)
{
RIP("XMountMU() more devices than specified in XInitDevices");
}
#endif // DBG
//
// Make sure that the device is not already mounted.
// (if it is, we sort of fail)
chDrive = MU_DRIVE_LETTER_FROM_PORT_SLOT(dwPort, dwSlot);
if(MU_IS_MOUNTED(chDrive))
{
RIP("XMountMU() MU already mounted.");
*pchDrive = chDrive;
LeaveCriticalSection(&XapiMountMUCriticalSection);
return ERROR_ALREADY_ASSIGNED;
}
//
// Setup the string buffer
//
DeviceName.Length = 0;
DeviceName.MaximumLength = sizeof(szDeviceName)/sizeof(OCHAR)-1;
DeviceName.Buffer = szDeviceName;
//
// create the device object
//
Status = MU_CreateDeviceObject(
dwPort,
dwSlot,
&DeviceName
);
if(NT_SUCCESS(Status))
{
soprintf(szDosDevice,
OTEXT("\\??\\%c:"),
chDrive);
RtlInitObjectString(&DosDevice, szDosDevice);
DwordToStringO(XeImageHeader()->Certificate->TitleID, szTitleId);
//
// Tack on a '\\' to the end. This way we are passing the MU's root directory,
// rather than the raw volume. This is important to prevent the raw file system
// from mounting by mistake on a corrupt or unformatted MU. The main evil of
// mounting the raw file system is that the error codes coming will be really
// strange.
//
ASSERT(DeviceName.Length+sizeof(OCHAR)<=DeviceName.MaximumLength);
DeviceName.Buffer[DeviceName.Length++ / sizeof(OCHAR)] = OTEXT('\\');
Status = XapiMapLetterToDirectory((PCOBJECT_STRING)&DosDevice,
(PCOBJECT_STRING)&DeviceName,
szTitleId,
TRUE,
XeImageHeader()->Certificate->TitleName,
FALSE);
if (NT_SUCCESS(Status))
{
if (NULL != pchDrive)
{
*pchDrive = chDrive;
}
MU_SET_MOUNTED(chDrive);
#if DBG
g_XapiCurrentUserDevices++;
#endif
}
else
{
// Dismount the volume if necessary.
IoDismountVolumeByName(&DeviceName);
// We must assume that either a handle was successfully opened and
// the filesystem was dismounted, or that it never managed to mount.
// If not, then the following step breaks all further attempts to
// mount MU's, but things were probably pretty well broken before
// this anyway.
MU_CloseDeviceObject(dwPort, dwSlot);
}
}
LeaveCriticalSection(&XapiMountMUCriticalSection);
return RtlNtStatusToDosError(Status);
}
DWORD
WINAPI
XMountMURoot(
IN DWORD dwPort,
IN DWORD dwSlot,
OUT POCHAR pchDrive
)
{
NTSTATUS Status;
OCHAR szDeviceName[64];
OCHAR szDosDevice[64];
OCHAR chDrive;
OBJECT_STRING DeviceName, DosDevice;
OBJECT_ATTRIBUTES Obja;
HANDLE DirHandle;
IO_STATUS_BLOCK IoStatusBlock;
//
// Fill out the MU type so that XAPI can
// find it without explicitly referencing it.
// This way the MU driver is not sucked in
// by XAPI unless the MU is used.
//
if (NULL != pchDrive)
{
*pchDrive = OBJECT_NULL;
}
EnterCriticalSection(&XapiMountMUCriticalSection);
#if DBG
if ((dwPort < MU_PORT_MIN) || (dwPort > MU_PORT_MAX))
{
RIP("XMountMURoot() invalid parameter (dwPort)");
}
if ((dwSlot < MU_SLOT_MIN) || (dwSlot > MU_SLOT_MAX))
{
RIP("XMountMURoot() invalid parameter (dwSlot)");
}
if(MU_MaxUserDevices <= g_XapiCurrentUserDevices)
{
RIP("XMountMU() more devices than specified in XInitDevices");
}
#endif // DBG
//
// Make sure that the device is not already mounted.
// (if it is, we sort of fail)
chDrive = MU_DRIVE_LETTER_FROM_PORT_SLOT(dwPort, dwSlot);
if(MU_IS_MOUNTED(chDrive))
{
RIP("XMountMU() MU already mounted.");
*pchDrive = chDrive;
LeaveCriticalSection(&XapiMountMUCriticalSection);
return ERROR_ALREADY_ASSIGNED;
}
//
// Setup the string buffer
//
DeviceName.Length = 0;
//lie - so we have guaranteed space for a back slash (lie leave room for NULL, and a '\\')
DeviceName.MaximumLength = sizeof(szDeviceName)/sizeof(OCHAR)-2;
DeviceName.Buffer = szDeviceName;
//
// create the device object
//
Status = MU_CreateDeviceObject(
dwPort,
dwSlot,
&DeviceName
);
if(NT_SUCCESS(Status))
{
//We previously lied about the maximum length to guarantee room for a backslash
//undo that lie.
DeviceName.MaximumLength++;
//Add a back slash to the name returned from MU_CreateDeviceObject
szDeviceName[DeviceName.Length++] = OTEXT('\\');
szDeviceName[DeviceName.Length] = OTEXT('\0');
//Attempt to open the root directory (this effectively mounts the drive).
InitializeObjectAttributes(
&Obja,
(POBJECT_STRING)&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtCreateFile(
&DirHandle,
FILE_LIST_DIRECTORY | SYNCHRONIZE,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
);
//Whack the backslash off the device name
szDeviceName[--DeviceName.Length] = OTEXT('\0');
//If we succeeded in opening the root directory we can
//create the symbolic link, etc.
if (NT_SUCCESS(Status))
{
NtClose(DirHandle); //Close the handle, we no longer need it
soprintf(szDosDevice,
OTEXT("\\??\\%c:"),
chDrive);
RtlInitObjectString(&DosDevice, szDosDevice);
Status = IoCreateSymbolicLink(&DosDevice, &DeviceName);
if(NT_ERROR(Status))
{
XDBGWRN("XAPI", "XMountMURoot() - IoCreateSymbolicLink failed with 0x%0.8x", Status);
}
}
if (NT_SUCCESS(Status))
{
if (NULL != pchDrive)
{
*pchDrive = chDrive;
}
MU_SET_MOUNTED(chDrive);
#if DBG
g_XapiCurrentUserDevices++;
#endif
} else
//
// Failed to open the root or to create a symbolic link. This does not mean a filesystem
// didn't mount, just in case we must force a dismount.
{
IoDismountVolumeByName(&DeviceName);
//We must assume that either a handle was successfully opened and the filesystem was dismounted, or
//that it never managed to mount. If not, then the following step breaks all further attempts to
//mount MU's, but things were probably pretty well broken before this anyway.
MU_CloseDeviceObject(dwPort, dwSlot);
}
}
LeaveCriticalSection(&XapiMountMUCriticalSection);
return RtlNtStatusToDosError(Status);
}
DWORD
WINAPI
XUnmountMU(
IN DWORD dwPort,
IN DWORD dwSlot
)
{
NTSTATUS Status;
OCHAR szDosDevice[64];
OBJECT_STRING DosDevice;
OBJECT_ATTRIBUTES Obja;
HANDLE VolHandle;
IO_STATUS_BLOCK IoStatusBlock;
OCHAR chDrive = MU_DRIVE_LETTER_FROM_PORT_SLOT(dwPort, dwSlot);
#if DBG
if ((dwPort < MU_PORT_MIN) || (dwPort > MU_PORT_MAX))
{
RIP("XUnmountMU() invalid parameter (dwPort)");
}
if ((dwSlot < MU_SLOT_MIN) || (dwSlot > MU_SLOT_MAX))
{
RIP("XUnmountMU() invalid parameter (dwSlot)");
}
#endif // DBG
EnterCriticalSection(&XapiMountMUCriticalSection);
//
// Fail if the device is not already mounted.
//
if(!MU_IS_MOUNTED(chDrive))
{
RIP("XUnmountMU() MU not mounted.");
LeaveCriticalSection(&XapiMountMUCriticalSection);
return ERROR_INVALID_DRIVE;
}
//
// Unmount an alternate drive if it is mapped.
//
if (XapiMuInfo.DriveWithAltDriveMapped == chDrive)
{
//
// An alternate drive is mapped to this MU drive, so let's auto dismount it here
//
XDBGWRN("XAPI", "XUnmountMU() called on MU drive (%c:) with alternate drive mapped (%c:)",
chDrive, HD_ALT_UDATA_DRIVE);
XUnmountAlternateTitle(HD_ALT_UDATA_DRIVE);
}
//
// open the symbolic link
//
soprintf(szDosDevice, OTEXT("\\??\\%c:"), chDrive);
RtlInitObjectString(&DosDevice, szDosDevice);
InitializeObjectAttributes(
&Obja,
(POBJECT_STRING)&DosDevice,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtCreateFile(
&VolHandle,
SYNCHRONIZE,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT
);
//
// send FSCTL_DISMOUNT_VOLUME
//
if(NT_SUCCESS(Status))
{
Status = NtFsControlFile(VolHandle, NULL, NULL, NULL, &IoStatusBlock, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0);
NtClose(VolHandle); //Close handle whether the dismount succeeded or not.
}
if(NT_SUCCESS(Status))
{
//
// Delete the symbolic link.
//
Status = IoDeleteSymbolicLink(&DosDevice);
//
// Tell the MU driver to delete the DeviceObject
//
MU_CloseDeviceObject(dwPort, dwSlot);
//
// Mark the drive as not mounted
//
MU_CLEAR_MOUNTED(chDrive);
#if DBG
g_XapiCurrentUserDevices--;
#endif
}
LeaveCriticalSection(&XapiMountMUCriticalSection);
return RtlNtStatusToDosError(Status);
}
DWORD
WINAPI
XReadMUMetaData(
IN DWORD dwPort,
IN DWORD dwSlot,
IN LPVOID lpBuffer,
IN DWORD dwByteOffset,
IN DWORD dwNumberOfBytesToRead
)
{
NTSTATUS Status;
OCHAR szDeviceName[64];
OBJECT_STRING DeviceName;
OCHAR chDrive = MU_DRIVE_LETTER_FROM_PORT_SLOT(dwPort, dwSlot);
PDEVICE_OBJECT DeviceObject;
PFAT_VOLUME_METADATA VolumeMetadata;
LARGE_INTEGER StartingOffset;
DISK_GEOMETRY DiskGeometry;
PARTITION_INFORMATION PartitionInformation;
#if DBG
RIP_ON_NOT_TRUE_WITH_MESSAGE(XPP_XInitDevicesHasBeenCalled, "XReadMUMetaData: XInitDevices must be called first!");
if ((dwPort < MU_PORT_MIN) || (dwPort > MU_PORT_MAX))
{
RIP("XReadMUMetaData() invalid parameter (dwPort)");
}
if ((dwSlot < MU_SLOT_MIN) || (dwSlot > MU_SLOT_MAX))
{
RIP("XReadMUMetaData() invalid parameter (dwSlot)");
}
if (dwByteOffset >= PAGE_SIZE)
{
RIP("XReadMUMetaData() invalid parameter (dwByteOffset)");
}
if ((PAGE_SIZE - dwByteOffset) < dwNumberOfBytesToRead)
{
RIP("XReadMUMetaData() invalid parameter (dwNumberOfBytesToRead)");
}
#endif // DBG
EnterCriticalSection(&XapiMountMUCriticalSection);
if(!MU_IS_MOUNTED(chDrive))
{
//
// Setup the string buffer
//
DeviceName.Length = 0;
//lie - so we have guaranteed space for a back slash (lie leave room for NULL, and a '\\')
DeviceName.MaximumLength = sizeof(szDeviceName)/sizeof(OCHAR)-2;
DeviceName.Buffer = szDeviceName;
//
// create the device object
//
Status = MU_CreateDeviceObject(
dwPort,
dwSlot,
&DeviceName
);
}
else
{
Status = STATUS_SUCCESS;
}
if (NT_SUCCESS(Status))
{
DeviceObject = MU_GetExistingDeviceObject(dwPort, dwSlot),
//
// Before the memory unit can accept read requests, it must first
// initialize its drive geometry information. Send the same IOCTLs
// that the file system would send during the mount process. Neither
// of these need to synchronized with a file system that has already
// mounted, so send them directly to the device.
//
Status = IoSynchronousDeviceIoControlRequest(IOCTL_DISK_GET_DRIVE_GEOMETRY,
DeviceObject, NULL, 0, &DiskGeometry, sizeof(DISK_GEOMETRY),
NULL, FALSE);
if (NT_SUCCESS(Status))
{
Status = IoSynchronousDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
DeviceObject, NULL, 0, &PartitionInformation,
sizeof(PARTITION_INFORMATION), NULL, FALSE);
if (PartitionInformation.PartitionLength.QuadPart < PAGE_SIZE)
{
Status = STATUS_UNRECOGNIZED_VOLUME;
}
}
if (NT_SUCCESS(Status))
{
//
// Allocate a block of memory to hold the volume metadata block. All
// device objects must support at least PAGE_SIZE transfers in order to
// support the file system cache. Memory units typically have at least
// PAGE_SIZE sector sizes, so there's no point in trying to reduce this
// memory requirement.
//
VolumeMetadata = (PFAT_VOLUME_METADATA)LocalAlloc(LMEM_FIXED, PAGE_SIZE);
if (VolumeMetadata != NULL)
{
//
// Submit a read request for PAGE_SIZE bytes from the start of
// the device. Note that we don't need to synchronize this
// with the file system because all of the file systems we care
// about immediately flush any metadata writes to media.
//
StartingOffset.QuadPart = 0;
Status = IoSynchronousFsdRequest(IRP_MJ_READ, DeviceObject,
VolumeMetadata, PAGE_SIZE, &StartingOffset);
if (NT_SUCCESS(Status))
{
//
// Verify that the volume is formatted as FATX. Memory units
// never use any other file system.
//
if (VolumeMetadata->Signature == FAT_VOLUME_SIGNATURE)
{
CopyMemory(lpBuffer, (LPBYTE)VolumeMetadata + dwByteOffset,
dwNumberOfBytesToRead);
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_UNRECOGNIZED_VOLUME;
}
}
LocalFree(VolumeMetadata);
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
if(!MU_IS_MOUNTED(chDrive))
{
//
// Tell the MU driver to delete the DeviceObject
//
MU_CloseDeviceObject(dwPort, dwSlot);
}
}
LeaveCriticalSection(&XapiMountMUCriticalSection);
return RtlNtStatusToDosError(Status);
}
#endif // ! XAPILIBP
#ifdef XAPILIBP
DWORD
WINAPI
XMUNameFromPortSlot(
IN DWORD dwPort,
IN DWORD dwSlot,
OUT LPWSTR lpName,
IN UINT cchName
)
{
DWORD dwError;
WCHAR VolumeName[FAT_VOLUME_NAME_LENGTH];
dwError = XReadMUMetaData(dwPort, dwSlot, VolumeName,
FIELD_OFFSET(FAT_VOLUME_METADATA, VolumeName), sizeof(VolumeName));
if (dwError == ERROR_SUCCESS)
{
lstrcpynW(lpName, VolumeName, min(cchName, FAT_VOLUME_NAME_LENGTH));
}
return dwError;
}
#endif // XAPILIBP
#ifndef XAPILIBP
DWORD
WINAPI
XMUPortFromDriveLetter(
OCHAR chDrive
)
{
if ((chDrive >= MU_FIRST_DRIVE) && (chDrive <= MU_LAST_DRIVE))
{
return MU_PORT_FROM_DRIVE_LETTER(chDrive);
}
else
{
return -1;
}
}
DWORD
WINAPI
XMUSlotFromDriveLetter(
OCHAR chDrive
)
{
if ((chDrive >= MU_FIRST_DRIVE) && (chDrive <= MU_LAST_DRIVE))
{
return MU_SLOT_FROM_DRIVE_LETTER(chDrive);
}
else
{
return -1;
}
}
#endif // ! XAPILIBP