/*++ Copyright (c) 2001-2002 Microsoft Corporation Module Name: mountmu.c Abstract: This module implements the XTL memory unit services. --*/ #include "basedll.h" #include #include #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