|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
fileren.c
Abstract:
This program is used to help make GUI Setup restartable, if setup was started in restartable mode.
Author:
Souren Aghajanyan (sourenag) July 2001
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include "msg.h"
#include "psp.h"
#define MAX_DOS_PATH_IN_NT_PATH 270
#define TXT_FILE_UNICODE_SIGN 0xfeff
#define SIZE_ULONG64(x, y) (((ULONGLONG)x)-((ULONGLONG)y))
const PCWSTR UndoFilePath = L"\\SystemRoot\\System32\\UNDO_GUIMODE.TXT";
typedef struct _SP_FILE_OPERATION { LIST_ENTRY Entry; UNICODE_STRING Name; UNICODE_STRING Value; } SP_FILE_OPERATION, *PSP_FILE_OPERATION;
BOOLEAN SpRemoveFileObject_U( IN PUNICODE_STRING FileObjectPath );
NTSTATUS SpSaveFileOperation( IN OUT PLIST_ENTRY ListHead, IN PCWSTR Name, IN PCWSTR Value OPTIONAL ) { PSP_FILE_OPERATION p = NULL; UNICODE_STRING UnicodeName; UNICODE_STRING UnicodeValue;
RtlInitUnicodeString( &UnicodeName, Name ); RtlInitUnicodeString( &UnicodeValue, Value );
p = (PSP_FILE_OPERATION)MALLOC(sizeof(*p) + UnicodeName.MaximumLength); if(!p){ return STATUS_NO_MEMORY; }
InitializeListHead(&p->Entry); p->Name.Buffer = (PWSTR)(p+1); p->Name.Length = UnicodeName.Length; p->Name.MaximumLength = UnicodeName.MaximumLength; RtlMoveMemory(p->Name.Buffer, UnicodeName.Buffer, UnicodeName.MaximumLength); p->Value.Buffer = NULL; InsertHeadList( ListHead, &p->Entry );
if (p->Value.Buffer != NULL) { FREE(p->Value.Buffer); }
if(ARGUMENT_PRESENT(Value)){ p->Value.Buffer = (PWSTR)MALLOC(UnicodeValue.MaximumLength); if(!p->Value.Buffer){ RemoveEntryList(&p->Entry); FREE(p); return STATUS_NO_MEMORY; }
p->Value.Length = UnicodeValue.Length; p->Value.MaximumLength = UnicodeValue.MaximumLength; RtlMoveMemory(p->Value.Buffer, UnicodeValue.Buffer, UnicodeValue.MaximumLength); } else { RtlInitUnicodeString(&p->Value, NULL); }
return STATUS_SUCCESS; }
VOID SpProcessFileRenames( IN PLIST_ENTRY pFileRenameList ) { NTSTATUS Status; NTSTATUS OpenStatus; PLIST_ENTRY Next; PLIST_ENTRY thisEntry; PSP_FILE_OPERATION p; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE OldFileHandle,SetAttributesHandle; PFILE_RENAME_INFORMATION RenameInformation; FILE_INFORMATION_CLASS SetInfoClass; FILE_BASIC_INFORMATION BasicInfo; ULONG SetInfoLength; PVOID SetInfoBuffer; PWSTR s; BOOLEAN WasEnabled; UNICODE_STRING NewName; int pass;
Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled); if(!NT_SUCCESS(Status)){ WasEnabled = TRUE; }
//
// Process the list of file rename operations.
//
for (pass = 0 ; pass < 2 ; pass++) {
thisEntry = pFileRenameList->Flink;
while (thisEntry != pFileRenameList) {
p = CONTAINING_RECORD(thisEntry, SP_FILE_OPERATION, Entry); thisEntry = thisEntry->Flink;
DbgPrint("SPRESTRT: FileRename( [%wZ] => [%wZ] )\n", &p->Name, &p->Value);
//
// We left all syntax and fuctionality SMSS FileRename supports.
//
Status = 0;
if(p->Value.Length){ if (pass == 0) { //
// We have target path and it means rename operation
//
if(p->Name.Buffer[0] == '@'){ p->Name.Buffer += 1; p->Name.Length -= sizeof(WCHAR); }
InitializeObjectAttributes(&ObjectAttributes, &p->Name, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = NtOpenFile(&OldFileHandle, (ACCESS_MASK)DELETE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(Status)){ SetInfoClass = FileRenameInformation; SetInfoLength = p->Value.Length + sizeof(*RenameInformation); s = p->Value.Buffer; if (*s == L'!' || *s == L'@') { s++; SetInfoLength -= sizeof(UNICODE_NULL); }
SetInfoBuffer = MALLOC(SetInfoLength);
if (SetInfoBuffer) { RenameInformation = (FILE_RENAME_INFORMATION *)SetInfoBuffer; RenameInformation->ReplaceIfExists = (BOOLEAN)(s != p->Value.Buffer); RenameInformation->RootDirectory = NULL; RenameInformation->FileNameLength = SetInfoLength - sizeof(*RenameInformation); RtlMoveMemory(RenameInformation->FileName, s, RenameInformation->FileNameLength); } else { Status = STATUS_NO_MEMORY; }
if(NT_SUCCESS(Status)){ Status = NtSetInformationFile(OldFileHandle, &IoStatusBlock, SetInfoBuffer, SetInfoLength, SetInfoClass); if(!NT_SUCCESS( Status ) && Status == STATUS_OBJECT_NAME_COLLISION && RenameInformation->ReplaceIfExists){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "\nSPRESTRT: %wZ => %wZ failed - Status == %x, Possible readonly target\n", &p->Name, &p->Value, Status));
//
// A rename was attempted, but the source existing file is readonly.
// this is a problem because folks that use movefileex to do delayed
// renames expect this to work and can leave a machine unbootable if
// the rename fails
//
//
// Open the file for Write Attributes access
//
NewName.Length = p->Value.Length - sizeof(L'!'); NewName.MaximumLength = p->Value.MaximumLength - sizeof(L'!'); NewName.Buffer = s;
InitializeObjectAttributes(&ObjectAttributes, &NewName, OBJ_CASE_INSENSITIVE, NULL, NULL);
OpenStatus = NtOpenFile(&SetAttributesHandle, (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT);
if(NT_SUCCESS(OpenStatus)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, " SPRESTRT: Open Existing Success\n"));
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo)); BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
OpenStatus = NtSetInformationFile(SetAttributesHandle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo), FileBasicInformation); NtClose(SetAttributesHandle); if(NT_SUCCESS(OpenStatus)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, " SPRESTRT: Set To NORMAL OK\n"));
Status = NtSetInformationFile(OldFileHandle, &IoStatusBlock, SetInfoBuffer, SetInfoLength, SetInfoClass);
if(NT_SUCCESS(Status)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, " SPRESTRT: Re-Rename Worked OK\n")); } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, " SPRESTRT: Re-Rename Failed - Status == %x\n", Status)); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, " SPRESTRT: Set To NORMAL Failed - Status == %x\n", OpenStatus)); } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, " SPRESTRT: Open Existing file Failed - Status == %x\n", OpenStatus)); } } }
NtClose(OldFileHandle); } } } else if (pass == 1) { //
// p->Value.Length == NULL means delete operation.
//
Status = SpRemoveFileObject_U(&p->Name)? STATUS_SUCCESS: STATUS_ACCESS_DENIED; }
if (!NT_SUCCESS( Status )) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "SPRESTRT: %wZ => %wZ failed - Status == %x\n", &p->Name, &p->Value, Status));
} else if (pass == 1 && p->Value.Length == 0) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SPRESTRT: %wZ (deleted)\n", &p->Name));
} else if (pass == 0) {
KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_INFO_LEVEL, "SPRESTRT: %wZ (renamed to) %wZ\n", &p->Name, &p->Value)); }
if (pass == 1) { FREE(p); } } }
if (!WasEnabled) { Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, FALSE, FALSE, &WasEnabled); }
return; }
BOOLEAN SpRemoveFile( PCWSTR pFilePath ) { NTSTATUS Status; HANDLE FileHandle; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; FILE_DISPOSITION_INFORMATION Disposition; FILE_BASIC_INFORMATION BasicInfo; BOOLEAN bResult = FALSE;
INIT_OBJA(&ObjectAttributes, &UnicodeString, pFilePath);
Status = NtOpenFile(&FileHandle, FILE_WRITE_ATTRIBUTES | DELETE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
if(NT_SUCCESS(Status)) { //
// Change attribute to FILE_ATTRIBUTE_NORMAL.
//
RtlZeroMemory(&BasicInfo,sizeof(BasicInfo)); BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
NtSetInformationFile(FileHandle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo), FileBasicInformation);
//
// Perform delete operation.
//
Disposition.DeleteFile = TRUE; Status = NtSetInformationFile(FileHandle, &IoStatusBlock, &Disposition, sizeof(Disposition), FileDispositionInformation);
if(!NT_SUCCESS(Status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "RestartSetup: Unable to delete %ws (%lx)\n", pFilePath, Status)); } else { bResult = TRUE; }
NtClose(FileHandle); }
return bResult; }
BOOLEAN SpRemoveDir( PWSTR pFilePath, ULONG StringMaxSize ) { NTSTATUS Status; HANDLE DirectoryHandle; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; LONGLONG Buffer[2048/8]; BOOLEAN FirstQuery; PFILE_DIRECTORY_INFORMATION FileInfo; ULONG LengthChars; BOOLEAN AnyErrors; ULONG indexEndOfRootPath;
if(!pFilePath){ ASSERT(FALSE); return FALSE; }
indexEndOfRootPath = wcslen(pFilePath); ASSERT(indexEndOfRootPath);
INIT_OBJA(&ObjectAttributes, &UnicodeString, pFilePath);
Status = NtOpenFile(&DirectoryHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT );
if(!NT_SUCCESS(Status)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "RestartSetup: unable to open system32\\config for list access (%lx)\n", Status));
return(FALSE); }
FirstQuery = TRUE; FileInfo = (PFILE_DIRECTORY_INFORMATION)Buffer; AnyErrors = FALSE;
do {
Status = NtQueryDirectoryFile(DirectoryHandle, NULL, // no event to signal
NULL, // no apc routine
NULL, // no apc context
&IoStatusBlock, Buffer, sizeof(Buffer)-sizeof(WCHAR), // leave room for terminating nul
FileDirectoryInformation, TRUE, // want single entry
NULL, // get 'em all
FirstQuery);
if(NT_SUCCESS(Status)){ LengthChars = FileInfo->FileNameLength / sizeof(WCHAR); FileInfo->FileName[LengthChars] = 0;
if(wcscmp(FileInfo->FileName, L".") && wcscmp(FileInfo->FileName, L"..")){
if((wcslen(pFilePath) + 1/*'\\'*/ + wcslen(FileInfo->FileName)) < StringMaxSize){ wcscat(pFilePath, L"\\"); wcscat(pFilePath, FileInfo->FileName);
if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { SpRemoveDir(pFilePath, StringMaxSize); } else { SpRemoveFile(pFilePath); }
pFilePath[indexEndOfRootPath] = '\0'; } else{ ASSERT(FALSE); } } FirstQuery = FALSE; } } while(NT_SUCCESS(Status));
//
// Check for normal loop termination.
//
if(Status == STATUS_NO_MORE_FILES) { Status = STATUS_SUCCESS; }
//
// Even if we got errors, try to keep going.
//
if(!NT_SUCCESS(Status)) { AnyErrors = TRUE; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "RestartSetup: Status %lx enumerating files\n", Status)); }
NtClose(DirectoryHandle);
SpRemoveFile(pFilePath);
return ((BOOLEAN)!AnyErrors); }
BOOLEAN SpRemoveFileObject( IN PCWSTR pFileObjectPath ) { NTSTATUS Status; HANDLE FileHandle; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; FILE_BASIC_INFORMATION BasicInfo; ULONG logestNtPath; PWSTR pFilePathToDelete; BOOLEAN bResult = FALSE;
INIT_OBJA(&ObjectAttributes, &UnicodeString, pFileObjectPath);
Status = NtOpenFile(&FileHandle, SYNCHRONIZE | GENERIC_READ, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ, 0);
if(NT_SUCCESS(Status)){ RtlZeroMemory(&BasicInfo, sizeof(BasicInfo));
Status = NtQueryInformationFile(FileHandle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo), FileBasicInformation);
NtClose(FileHandle);
if(!NT_SUCCESS(Status)){ KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_WARNING_LEVEL, "RestartSetup: Unable to delete %ws (%lx)\n", pFileObjectPath, Status)); return FALSE; }
if(BasicInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY){ //
// We can possible have 2 * MAX_PATH as length of path
//
logestNtPath = 2 * MAX_DOS_PATH_IN_NT_PATH; ASSERT(wcslen(pFileObjectPath) < logestNtPath);
pFilePathToDelete = (PWSTR)MALLOC(logestNtPath * sizeof(WCHAR)); if(pFilePathToDelete && wcslen(pFileObjectPath) < logestNtPath){ wcscpy(pFilePathToDelete, pFileObjectPath);
bResult = SpRemoveDir(pFilePathToDelete, logestNtPath);
FREE(pFilePathToDelete); } } else { bResult = SpRemoveFile(pFileObjectPath); } }
return bResult; }
BOOLEAN SpRemoveFileObject_U( IN PUNICODE_STRING FileObjectPath ) { return SpRemoveFileObject(FileObjectPath->Buffer); }
BOOLEAN SpReadFileRenameOperations( IN PLIST_ENTRY pFileRenameList ) { OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING UnicodeString; NTSTATUS Status; HANDLE hUndoFile; WCHAR wUnicodeSign; WCHAR RenameOperationBuffer[2 * (MAX_DOS_PATH_IN_NT_PATH + 2/*"\n\r"*/)]; ULONG readBytes; ULONG readActualBytes; PCWSTR pDestinationFilePath; FILE_POSITION_INFORMATION currentPosition; PWSTR pEnd;
INIT_OBJA(&ObjectAttributes, &UnicodeString, UndoFilePath);
Status = NtOpenFile(&hUndoFile, FILE_READ_DATA | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if(!NT_SUCCESS(Status)) { //
// We do not have any operation to perform
//
return FALSE; }
Status = NtReadFile(hUndoFile, NULL, NULL, NULL, &IoStatusBlock, &wUnicodeSign, sizeof(wUnicodeSign), NULL, NULL); if(NT_SUCCESS(Status) && TXT_FILE_UNICODE_SIGN == wUnicodeSign){ currentPosition.CurrentByteOffset.QuadPart = sizeof(wUnicodeSign); do{ readBytes = sizeof(RenameOperationBuffer) - sizeof(RenameOperationBuffer[0]);
Status = NtReadFile(hUndoFile, NULL, NULL, NULL, &IoStatusBlock, RenameOperationBuffer, readBytes, NULL, NULL);
if(!NT_SUCCESS(Status)){ ASSERT(STATUS_END_OF_FILE == Status); break; }
readActualBytes = (ULONG)IoStatusBlock.Information; RenameOperationBuffer[readActualBytes / sizeof(RenameOperationBuffer[0])] = '\0';
pEnd = wcsstr(RenameOperationBuffer, L"\r\n"); if(!pEnd){ break; } *pEnd = '\0';
pDestinationFilePath = pEnd + 2;//wcslen(L"\r\n");
pEnd = wcsstr(pDestinationFilePath, L"\r\n"); if(!pEnd){ if(readActualBytes < readBytes){ pEnd = &RenameOperationBuffer[readActualBytes / sizeof(WCHAR)]; } else { //
// Ether we have path which len exceed MAX_PATH,
// or probably some crap.
//
ASSERT(FALSE); break; } } *pEnd = '\0';
pEnd += 2;//wcslen(L"\r\n");
SpSaveFileOperation(pFileRenameList, RenameOperationBuffer, *pDestinationFilePath? pDestinationFilePath: NULL);
currentPosition.CurrentByteOffset.QuadPart += (LONGLONG)SIZE_ULONG64(pEnd, RenameOperationBuffer); Status = NtSetInformationFile(hUndoFile, &IoStatusBlock, ¤tPosition, sizeof(currentPosition), FilePositionInformation); }while(NT_SUCCESS(Status)); }
NtClose(hUndoFile);
//
// Add this file to file operations list to be deleted.
//
SpSaveFileOperation(pFileRenameList, UndoFilePath, NULL);
return TRUE; }
BOOLEAN SetupDelayedFileRename( VOID ) /*++
Routine Description:
Arguments:
None.
Return Value:
Boolean value indicating whether we were successful.
--*/ { LIST_ENTRY listFileRename;
KdPrint(("SetupDelayedFileRename: Start"));
InitializeListHead(&listFileRename);
//
// Fill list of file operations
//
if(!SpReadFileRenameOperations(&listFileRename)){ return FALSE; }
//
// Perform file operations
//
SpProcessFileRenames(&listFileRename);
KdPrint(("SetupDelayedFileRename: End"));
return TRUE; }
|