xbox-kernel/private/ntos/dm/xbdm/sysfileupd.c
2020-09-30 17:17:25 +02:00

279 lines
9.4 KiB
C

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
sysfileupd.c
Abstract:
This module implements routines to update system files on XDK.
--*/
#include "dmp.h"
#undef DeleteFile
NTSTATUS
ReplaceFile(
IN OBJECT_STRING* NewFileName,
IN HANDLE ExistingHandle
)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjA;
HANDLE Handle;
IO_STATUS_BLOCK IoStatusBlock;
FILE_BASIC_INFORMATION BasicInfo;
FILE_RENAME_INFORMATION RenameInfo;
// Clear read-only file attribute as needed
InitializeObjectAttributes(&ObjA, NewFileName, OBJ_CASE_INSENSITIVE, 0, 0);
Status = NtOpenFile(&Handle, (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&ObjA, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
if (NT_SUCCESS(Status)) {
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));
BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
NtSetInformationFile(Handle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo),
FileBasicInformation);
NtClose(Handle);
}
// Rename the file
RenameInfo.ReplaceIfExists = TRUE;
RenameInfo.RootDirectory = 0;
RenameInfo.FileName.Length = NewFileName->Length;
RenameInfo.FileName.MaximumLength = NewFileName->MaximumLength;
RenameInfo.FileName.Buffer = NewFileName->Buffer;
Status = NtSetInformationFile(ExistingHandle, &IoStatusBlock, &RenameInfo,
sizeof(RenameInfo), FileRenameInformation);
return Status;
}
HRESULT HrReceiveSystemFile(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
CCS *pccs = (CCS *)pdmcc->CustomData;
FILE_DISPOSITION_INFORMATION fdi;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
if (pdmcc->DataSize) {
pccs->sysupd.BytesReceived += pdmcc->DataSize;
ASSERT(pccs->sysupd.BytesReceived <= DISK_BUF_SIZE);
pdmcc->BytesRemaining -= pdmcc->DataSize;
} else if (pdmcc->BytesRemaining) {
DmFreePool(pccs->sysupd.FileBuffer);
DmFreePool(pccs->sysupd.FileName.Buffer);
fdi.DeleteFile = TRUE;
NtSetInformationFile(pccs->sysupd.FileHandle, &IoStatusBlock, &fdi,
sizeof(fdi), FileDispositionInformation);
_snprintf(szResponse, cchResponse, "Unexpected data size");
szResponse[cchResponse - 1] = 0;
return E_UNEXPECTED;
}
// See if we're ready for a disk write
if (!pdmcc->BytesRemaining || pccs->sysupd.BytesReceived == DISK_BUF_SIZE) {
pccs->sysupd.Crc = Crc32(pccs->sysupd.Crc, pccs->sysupd.FileBuffer,
pccs->sysupd.BytesReceived);
Status = NtWriteFile(pccs->sysupd.FileHandle, NULL, NULL,
NULL, &IoStatusBlock, pccs->sysupd.FileBuffer,
pccs->sysupd.BytesReceived, NULL);
if (!NT_SUCCESS(Status)) {
cleanup:
fdi.DeleteFile = TRUE;
NtSetInformationFile(pccs->sysupd.FileHandle, &IoStatusBlock,
&fdi, sizeof(fdi), FileDispositionInformation);
NtClose(pccs->sysupd.FileHandle);
pccs->sysupd.FileHandle = NULL;
_snprintf(szResponse, cchResponse, "File write error");
szResponse[cchResponse - 1] = 0;
return HrFromStatus(Status, XBDM_CANNOTCREATE);
} else if (!pdmcc->BytesRemaining) {
if (pccs->sysupd.Crc != pccs->sysupd.ExpectedCrc) {
_snprintf(szResponse, cchResponse, "Invalid CRC");
szResponse[cchResponse - 1] = 0;
goto cleanup;
}
Status = ReplaceFile(&pccs->sysupd.FileName, pccs->sysupd.FileHandle);
if (!NT_SUCCESS(Status)) {
_snprintf(szResponse, cchResponse, "Replace file failed (0x%X)", Status);
szResponse[cchResponse - 1] = 0;
goto cleanup;
}
NtClose(pccs->sysupd.FileHandle);
}
pccs->sysupd.BytesReceived = 0;
if (!pdmcc->BytesRemaining) {
DmFreePool(pccs->sysupd.FileBuffer);
pccs->sysupd.FileBuffer = NULL;
}
}
// Make sure the buffer pointer is set up
pdmcc->Buffer = pccs->sysupd.FileBuffer + pccs->sysupd.BytesReceived;
pdmcc->BufferSize = DISK_BUF_SIZE - pccs->sysupd.BytesReceived;
return XBDM_NOERR;
}
VOID
CreateDirectoryFromPath(
IN CONST OBJECT_STRING* Path
)
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjA;
HANDLE Handle;
OBJECT_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
WORD Length;
FileName.Buffer = Path->Buffer;
Length = FileName.Length = Path->Length;
FileName.MaximumLength = Path->MaximumLength;
InitializeObjectAttributes(&ObjA, &FileName, OBJ_CASE_INSENSITIVE, 0, 0);
// REVIEW: there should be a better way to do this
for (FileName.Length=1; FileName.Length<Length; FileName.Length++) {
if (FileName.Buffer[FileName.Length] != '\\') {
continue;
}
Status = NtCreateFile(&Handle, FILE_LIST_DIRECTORY | SYNCHRONIZE,
&ObjA, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | \
FILE_OPEN_FOR_BACKUP_INTENT);
if (NT_SUCCESS(Status)) {
NtClose(Handle);
}
}
}
HRESULT HrUpdateSystemFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
CCS* pccs;
SIZE_T ImageSize;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjA;
OBJECT_STRING ObjS;
OCHAR buffer[512];
LARGE_INTEGER FileSize;
OCHAR* psz;
IO_STATUS_BLOCK IoStatusBlock;
FILE_END_OF_FILE_INFORMATION feof;
FILE_DISPOSITION_INFORMATION fdi;
HRESULT hr;
if (!pdmcc) {
return E_FAIL;
}
pccs = (CCS*)pdmcc->CustomData;
// Get file size and CRC32 of entire file
if (!FGetNamedDwParam(sz, "size", &pdmcc->BytesRemaining, szResp) || \
!FGetNamedDwParam(sz, "crc", &pccs->sysupd.ExpectedCrc, szResp)) {
_snprintf(szResp, cchResp, "Invalid parameter(s)");
szResp[cchResp - 1] = 0;
return E_FAIL;
}
// Allocate buffer for file name and file buffer
pccs->sysupd.FileBuffer = DmAllocatePoolWithTag(
DISK_BUF_SIZE + sizeof(buffer), 'ufsx');
if (!pccs->sysupd.FileBuffer) {
_snprintf(szResp, cchResp, "Not enough memory");
szResp[cchResp - 1] = 0;
return E_OUTOFMEMORY;
}
pccs->sysupd.FileName.Length = 0;
pccs->sysupd.FileName.MaximumLength = sizeof(buffer);
pccs->sysupd.FileName.Buffer = (PCHAR)\
((ULONG_PTR)pccs->sysupd.FileBuffer + DISK_BUF_SIZE);
if (!FGetSzParam(sz, "name", pccs->sysupd.FileName.Buffer,
pccs->sysupd.FileName.MaximumLength/sizeof(OCHAR))) {
_snprintf(szResp, cchResp, "missing or invalid file name");
szResp[cchResp - 1] = 0;
hr = E_FAIL;
goto cleanup;
}
pccs->sysupd.FileName.Length = strlen(pccs->sysupd.FileName.Buffer) * \
sizeof(OCHAR);
ASSERT(pccs->sysupd.FileName.Length < sizeof(buffer));
// Create temporary file which later will be renamed to the real target
strcpy(buffer, pccs->sysupd.FileName.Buffer);
psz = strrchr(buffer, '\\');
if (!psz || sizeof(buffer) - ((ULONG_PTR)psz - (ULONG_PTR)buffer + 1) < \
sizeof("\\temp1234.tmp")) {
_snprintf(szResp, cchResp, "Invalid file name");
szResp[cchResp - 1] = 0;
hr = E_INVALIDARG;
goto cleanup;
}
sprintf(psz, "\\temp%04x.tmp", NtGetTickCount() & 0xFFFF);
RtlInitObjectString(&ObjS, buffer);
InitializeObjectAttributes(&ObjA, &ObjS, OBJ_CASE_INSENSITIVE, 0, 0);
CreateDirectoryFromPath(&ObjS);
FileSize.HighPart = 0;
FileSize.LowPart = pdmcc->BytesRemaining;
Status = NtCreateFile(&pccs->sysupd.FileHandle, GENERIC_WRITE | DELETE | SYNCHRONIZE,
&ObjA, &IoStatusBlock, &FileSize, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_CREATE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
if (NT_SUCCESS(Status)) {
// Commit the size of the file
feof.EndOfFile = FileSize;
Status = NtSetInformationFile(pccs->sysupd.FileHandle, &IoStatusBlock, &feof,
sizeof(feof), FileEndOfFileInformation);
if (NT_SUCCESS(Status)) {
pccs->sysupd.BytesReceived = 0;
pccs->sysupd.Crc = ~0UL;
pdmcc->Buffer = pccs->sysupd.FileBuffer;
pdmcc->BufferSize = DISK_BUF_SIZE;
pdmcc->HandlingFunction = HrReceiveSystemFile;
return XBDM_READYFORBIN;
} else {
// Mark the file for deletion and close the file
fdi.DeleteFile = TRUE;
NtSetInformationFile(&pccs->sysupd.FileHandle, &IoStatusBlock, &fdi, sizeof(fdi),
FileDispositionInformation);
NtClose(pccs->sysupd.FileHandle);
pccs->sysupd.FileHandle = NULL;
_snprintf(szResp, cchResp, "Disk full");
szResp[cchResp - 1] = 0;
}
}
hr = HrFromStatus(Status, XBDM_CANNOTCREATE);
cleanup:
DmFreePool(pccs->sysupd.FileBuffer);
return hr;
}