795 lines
19 KiB
C
795 lines
19 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
lfn.c
|
|
|
|
Abstract:
|
|
|
|
Author:
|
|
|
|
Ted Miller (tedm) Apr 1996
|
|
|
|
--*/
|
|
|
|
#include "lfn.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
//
|
|
// Define result codes.
|
|
//
|
|
#define SUCCESS 0
|
|
#define FAILURE 1
|
|
|
|
//
|
|
// Define routine type for callbacks from EnumerateDrives()
|
|
//
|
|
typedef
|
|
BOOLEAN
|
|
(*PDRIVEENUM_ROUTINE) (
|
|
IN PCWSTR DriveRootPath
|
|
);
|
|
|
|
BOOLEAN
|
|
EnumerateDrives(
|
|
IN PDRIVEENUM_ROUTINE Callback
|
|
);
|
|
|
|
BOOLEAN
|
|
RestoreLfns(
|
|
IN PCWSTR DriveRootPath
|
|
);
|
|
|
|
BOOLEAN
|
|
Message(
|
|
IN ULONG MessageId,
|
|
IN ULONG DotCount,
|
|
...
|
|
);
|
|
|
|
BOOLEAN
|
|
RenameToLfn(
|
|
IN PCWSTR Directory,
|
|
IN PCWSTR ExistingFilename,
|
|
IN PCWSTR NewFilename
|
|
);
|
|
|
|
VOID
|
|
DeleteRenameFile(
|
|
IN PCWSTR DriveRootPath
|
|
);
|
|
|
|
VOID
|
|
RemoveFromBootExecute(
|
|
IN PCWSTR Cmd
|
|
);
|
|
|
|
int
|
|
__cdecl
|
|
main(
|
|
IN int argc,
|
|
IN char *argv[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Entry point for this program.
|
|
|
|
The $$RENAME.TXT at the root of each drive is read and
|
|
rename operations contained therein are performed.
|
|
|
|
Arguments:
|
|
|
|
Ignored.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
int i;
|
|
|
|
i = EnumerateDrives(RestoreLfns) ? SUCCESS : FAILURE;
|
|
|
|
RemoveFromBootExecute(L"autolfn");
|
|
|
|
return(i);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
EnumerateDrives(
|
|
IN PDRIVEENUM_ROUTINE Callback
|
|
)
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE DosDevicesDir;
|
|
CHAR DirInfoBuffer[512];
|
|
CHAR LinkTargetBuffer[512];
|
|
UNICODE_STRING LinkTarget;
|
|
UNICODE_STRING DesiredPrefix;
|
|
UNICODE_STRING DesiredPrefix2;
|
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
|
ULONG Context,Length;
|
|
UNICODE_STRING LinkTypeName;
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
BOOLEAN b;
|
|
|
|
//
|
|
// Open \DosDevices
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,L"\\DosDevices");
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenDirectoryObject(&DosDevicesDir,DIRECTORY_QUERY,&ObjectAttributes);
|
|
if(!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
LinkTarget.Buffer = (PVOID)LinkTargetBuffer;
|
|
RtlInitUnicodeString(&LinkTypeName,L"SymbolicLink");
|
|
RtlInitUnicodeString(&DesiredPrefix,L"\\Device\\Harddisk");
|
|
RtlInitUnicodeString(&DesiredPrefix2,L"\\Device\\Volume");
|
|
|
|
DirInfo = (POBJECT_DIRECTORY_INFORMATION)DirInfoBuffer;
|
|
|
|
b = TRUE;
|
|
|
|
//
|
|
// Query first object in \DosDevices directory
|
|
//
|
|
Status = NtQueryDirectoryObject(
|
|
DosDevicesDir,
|
|
DirInfo,
|
|
sizeof(DirInfoBuffer),
|
|
TRUE,
|
|
TRUE,
|
|
&Context,
|
|
&Length
|
|
);
|
|
|
|
while(NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Make sure item is a symbolic link
|
|
//
|
|
if(RtlEqualUnicodeString(&LinkTypeName,&DirInfo->TypeName,TRUE)) {
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DirInfo->Name,
|
|
OBJ_CASE_INSENSITIVE,
|
|
DosDevicesDir,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenSymbolicLinkObject(&Handle,SYMBOLIC_LINK_ALL_ACCESS,&ObjectAttributes);
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
LinkTarget.Length = 0;
|
|
LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
|
|
|
|
Status = NtQuerySymbolicLinkObject(Handle,&LinkTarget,NULL);
|
|
NtClose(Handle);
|
|
if(NT_SUCCESS(Status)
|
|
&& (RtlPrefixUnicodeString(&DesiredPrefix,&LinkTarget,TRUE) ||
|
|
RtlPrefixUnicodeString(&DesiredPrefix2,&LinkTarget,TRUE))) {
|
|
|
|
//
|
|
// OK, this is a symbolic link to a hard drive.
|
|
// Make sure it's 0-terminated and call the callback
|
|
// the the name.
|
|
//
|
|
LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0;
|
|
|
|
if(!Callback(LinkTarget.Buffer)) {
|
|
b = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query next object in \DosDevices directory
|
|
//
|
|
Status = NtQueryDirectoryObject(
|
|
DosDevicesDir,
|
|
DirInfo,
|
|
sizeof(DirInfoBuffer),
|
|
TRUE,
|
|
FALSE,
|
|
&Context,
|
|
&Length
|
|
);
|
|
}
|
|
|
|
NtClose(DosDevicesDir);
|
|
return(b);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RestoreLfns(
|
|
IN PCWSTR DriveRootPath
|
|
)
|
|
{
|
|
PMYTEXTFILE TextFile;
|
|
BOOLEAN b;
|
|
WCHAR Directory[NTMAXPATH];
|
|
WCHAR Line[3*NTMAXPATH];
|
|
ULONG d;
|
|
PWCHAR p,n;
|
|
PWSTR Sfn,Lfn;
|
|
|
|
b = FALSE;
|
|
|
|
//
|
|
// Load the rename file list.
|
|
//
|
|
if(TextFile = LoadRenameFile(DriveRootPath)) {
|
|
|
|
//
|
|
// Process each directory that is in the rename list file.
|
|
//
|
|
for(d=0; d<TextFile->SectionCount; d++) {
|
|
|
|
//
|
|
// Form the directory path.
|
|
//
|
|
wcscpy(Directory,DriveRootPath);
|
|
ConcatenatePaths(Directory,TextFile->Sections[d].Name,NTMAXPATH);
|
|
|
|
//
|
|
// Process each line in the section.
|
|
//
|
|
p = TextFile->Sections[d].Data;
|
|
while(GetLineInSection(p,Line,sizeof(Line)/sizeof(WCHAR),&n)) {
|
|
|
|
if(ParseLine(Line,&Sfn,&Lfn)) {
|
|
|
|
RenameToLfn(Directory,Sfn,Lfn);
|
|
}
|
|
|
|
p = n;
|
|
}
|
|
}
|
|
|
|
UnloadRenameFile(&TextFile);
|
|
|
|
//
|
|
// Upon success, delete the rename file.
|
|
//
|
|
DeleteRenameFile(DriveRootPath);
|
|
|
|
b = TRUE;
|
|
}
|
|
|
|
return(b);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Message(
|
|
IN ULONG MessageId,
|
|
IN ULONG DotCount,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Format and display a message, which is retreived from
|
|
the image's message resources.
|
|
|
|
Arguments:
|
|
|
|
MessageId - Supplies the message id of the message resource.
|
|
|
|
DotCount - Supplies number of trailing dots to be appended to
|
|
the message text prior to display. If this value is non-0,
|
|
then the message shouldn't have a trailing cr/lf!
|
|
|
|
Additional arguments specify message-specific inserts.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether the message was displayed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID ImageBase;
|
|
NTSTATUS Status;
|
|
PMESSAGE_RESOURCE_ENTRY MessageEntry;
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING UnicodeString;
|
|
va_list arglist;
|
|
WCHAR Buffer[1024];
|
|
ULONG u;
|
|
|
|
//
|
|
// Get our image base address
|
|
//
|
|
ImageBase = NtCurrentPeb()->ImageBaseAddress;
|
|
if(!ImageBase) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Find the message.
|
|
// For DBCS codepages we will use English resources instead of
|
|
// default resource because we can only display ASCII characters onto
|
|
// blue Screen via HalDisplayString()
|
|
//
|
|
Status = RtlFindMessage(
|
|
ImageBase,
|
|
11,
|
|
NLS_MB_CODE_PAGE_TAG ? MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US) : 0,
|
|
MessageId,
|
|
&MessageEntry
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// If the message is not unicode, convert to unicode.
|
|
// Let the conversion routine allocate the buffer.
|
|
//
|
|
if(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) {
|
|
|
|
RtlInitAnsiString(&AnsiString,MessageEntry->Text);
|
|
Status = RtlAnsiStringToUnicodeString(&UnicodeString,&AnsiString,TRUE);
|
|
if(!NT_SUCCESS(Status)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Message is already unicode. Make a copy.
|
|
//
|
|
if(!RtlCreateUnicodeString(&UnicodeString,(PWSTR)MessageEntry->Text)) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Format the message.
|
|
//
|
|
va_start(arglist,DotCount);
|
|
|
|
Status = RtlFormatMessage(
|
|
UnicodeString.Buffer,
|
|
0, // max width
|
|
FALSE, // don't ignore inserts
|
|
FALSE, // args are not ansi
|
|
FALSE, // args are not an array
|
|
&arglist,
|
|
Buffer,
|
|
sizeof(Buffer)/sizeof(Buffer[0]),
|
|
NULL
|
|
);
|
|
|
|
va_end(arglist);
|
|
|
|
//
|
|
// We don't need the message source any more. Free it.
|
|
//
|
|
RtlFreeUnicodeString(&UnicodeString);
|
|
|
|
//
|
|
// Add dots and cr.
|
|
//
|
|
for(u=0; u<DotCount; u++) {
|
|
wcscat(Buffer,L".");
|
|
}
|
|
wcscat(Buffer,L"\r");
|
|
|
|
//
|
|
// Print out the message
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,Buffer);
|
|
Status = NtDisplayString(&UnicodeString);
|
|
|
|
return(NT_SUCCESS(Status));
|
|
}
|
|
|
|
|
|
VOID
|
|
ConcatenatePaths(
|
|
IN OUT PWSTR Target,
|
|
IN PCWSTR Path,
|
|
IN ULONG TargetBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Concatenate 2 paths, ensuring that one, and only one,
|
|
path separator character is introduced at the junction point.
|
|
|
|
Arguments:
|
|
|
|
Target - supplies first part of path. Path is appended to this.
|
|
|
|
Path - supplies path to be concatenated to Target.
|
|
|
|
TargetBufferSize - supplies the size of the Target buffer,
|
|
in characters.
|
|
|
|
// ISSUE-2002/03/06-robertko This function returns VOID. Should probably be changed to reflect the
|
|
// comment below since the funtion truncates to prevent buffer overflow.
|
|
Return Value:
|
|
|
|
TRUE if the full path fit in Target buffer. Otherwise the path
|
|
will have been truncated.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG TargetLength,PathLength;
|
|
BOOL TrailingBackslash,LeadingBackslash;
|
|
ULONG EndingLength;
|
|
ULONG n;
|
|
|
|
TargetLength = wcslen(Target);
|
|
PathLength = wcslen(Path);
|
|
|
|
//
|
|
// See whether the target has a trailing backslash.
|
|
//
|
|
if(TargetLength && (Target[TargetLength-1] == L'\\')) {
|
|
TrailingBackslash = TRUE;
|
|
TargetLength--;
|
|
} else {
|
|
TrailingBackslash = FALSE;
|
|
}
|
|
|
|
//
|
|
// See whether the path has a leading backshash.
|
|
//
|
|
if(Path[0] == L'\\') {
|
|
LeadingBackslash = TRUE;
|
|
PathLength--;
|
|
} else {
|
|
LeadingBackslash = FALSE;
|
|
}
|
|
|
|
//
|
|
// Calculate the ending length, which is equal to the sum of
|
|
// the length of the two strings modulo leading/trailing
|
|
// backslashes, plus one path separator, plus a nul.
|
|
//
|
|
EndingLength = TargetLength + PathLength + 2;
|
|
|
|
if(!LeadingBackslash && (TargetLength < TargetBufferSize)) {
|
|
Target[TargetLength++] = L'\\';
|
|
}
|
|
|
|
if(TargetBufferSize > TargetLength) {
|
|
n = wcslen(Path);
|
|
if(n > TargetBufferSize-TargetLength) {
|
|
n = TargetBufferSize-TargetLength;
|
|
}
|
|
RtlCopyMemory(Target+TargetLength,Path,n*sizeof(WCHAR));
|
|
Target[TargetLength+n] = 0;
|
|
}
|
|
|
|
//
|
|
// Make sure the buffer is nul terminated in all cases.
|
|
//
|
|
if(TargetBufferSize) {
|
|
Target[TargetBufferSize-1] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RenameToLfn(
|
|
IN PCWSTR Directory,
|
|
IN PCWSTR ExistingFilename,
|
|
IN PCWSTR NewFilename
|
|
)
|
|
{
|
|
WCHAR Buffer[2*NTMAXPATH] = {0};
|
|
UNICODE_STRING UnicodeString;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
PFILE_RENAME_INFORMATION RenameInfo;
|
|
|
|
//
|
|
// Open the existing file for delete access.
|
|
//
|
|
wcsncpy(Buffer,Directory,sizeof(Buffer)/sizeof(Buffer[0]) - 1);
|
|
ConcatenatePaths(Buffer,ExistingFilename,sizeof(Buffer)/sizeof(WCHAR));
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,Buffer);
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
DELETE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrint(("LFN: Unable to open %ws for renaming (%x)\n",Buffer,Status));
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Rename to temporary intermediate file. This allows the filesystem to
|
|
// generate a short filename later, that doesn't collide with the name
|
|
// currently in use.
|
|
//
|
|
RenameInfo = (PFILE_RENAME_INFORMATION)Buffer;
|
|
|
|
wcscpy(RenameInfo->FileName,Directory);
|
|
ConcatenatePaths(RenameInfo->FileName,L"$$!!$$!!.~~~",NTMAXPATH);
|
|
|
|
RenameInfo->ReplaceIfExists = TRUE;
|
|
RenameInfo->RootDirectory = NULL;
|
|
RenameInfo->FileNameLength = wcslen(RenameInfo->FileName)*sizeof(WCHAR);
|
|
|
|
Status = NtSetInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
RenameInfo,
|
|
sizeof(FILE_RENAME_INFORMATION) + RenameInfo->FileNameLength,
|
|
FileRenameInformation
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrint(("LFN: Rename of %ws to intermediate temp filename failed (%x)\n",ExistingFilename,Status));
|
|
NtClose(Handle);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Rename to actual target file.
|
|
//
|
|
wcscpy(RenameInfo->FileName,Directory);
|
|
ConcatenatePaths(RenameInfo->FileName,NewFilename,NTMAXPATH);
|
|
|
|
RenameInfo->ReplaceIfExists = FALSE;
|
|
RenameInfo->RootDirectory = NULL;
|
|
RenameInfo->FileNameLength = wcslen(RenameInfo->FileName)*sizeof(WCHAR);
|
|
|
|
Status = NtSetInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
RenameInfo,
|
|
sizeof(FILE_RENAME_INFORMATION) + RenameInfo->FileNameLength,
|
|
FileRenameInformation
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrint(("LFN: Rename of file to %ws failed (%x)\n",NewFilename,Status));
|
|
NtClose(Handle);
|
|
return(FALSE);
|
|
}
|
|
|
|
NtClose(Handle);
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
VOID
|
|
DeleteRenameFile(
|
|
IN PCWSTR DriveRootPath
|
|
)
|
|
{
|
|
WCHAR Filename[NTMAXPATH] = {0};
|
|
UNICODE_STRING UnicodeString;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
FILE_DISPOSITION_INFORMATION DispositionInfo;
|
|
|
|
wcsncpy(Filename,DriveRootPath,sizeof(Filename)/sizeof(Filename[0]) - 1);
|
|
ConcatenatePaths(Filename,WINNT_OEM_LFNLIST_W,NTMAXPATH);
|
|
|
|
INIT_OBJA(&ObjectAttributes,&UnicodeString,Filename);
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
DELETE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
|
|
);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
|
|
DispositionInfo.DeleteFile = TRUE;
|
|
|
|
Status = NtSetInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&DispositionInfo,
|
|
sizeof(DispositionInfo),
|
|
FileDispositionInformation
|
|
);
|
|
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrint(("LFN: Unable to delete %ws (%x)\n",Filename,Status));
|
|
}
|
|
|
|
NtClose(Handle);
|
|
|
|
} else {
|
|
KdPrint(("LFN: Unable to open %ws for delete (%x)\n",Filename,Status));
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
RemoveFromBootExecute(
|
|
IN PCWSTR Cmd
|
|
)
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING UnicodeString;
|
|
NTSTATUS Status;
|
|
HANDLE hKey;
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
|
ULONG Length;
|
|
PWCHAR NewValue;
|
|
PWSTR SourceString,TargetString;
|
|
|
|
//
|
|
// Open the registry key we want.
|
|
// [\registry\machine\system\CurrentControlSet\control\Session Manager]
|
|
//
|
|
INIT_OBJA(
|
|
&ObjectAttributes,
|
|
&UnicodeString,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager"
|
|
);
|
|
|
|
Status = NtOpenKey(
|
|
&hKey,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrint(("LFN: Unable to open session manager key (%x)\n",Status));
|
|
goto c0;
|
|
}
|
|
|
|
//
|
|
// Query the current value of the BootExecute value.
|
|
//
|
|
RtlInitUnicodeString(&UnicodeString,L"BootExecute");
|
|
|
|
Status = NtQueryValueKey(
|
|
hKey,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
NULL,
|
|
0,
|
|
&Length
|
|
);
|
|
|
|
if(Status != STATUS_BUFFER_TOO_SMALL) {
|
|
KdPrint(("LFN: Unable to query BootExecute value size (%x)\n",Status));
|
|
goto c1;
|
|
}
|
|
|
|
ValueInfo = RtlAllocateHeap(RtlProcessHeap(),0,Length);
|
|
if(!ValueInfo) {
|
|
KdPrint(("LFN: Unable to allocate %u bytes of memory\n",Length));
|
|
goto c1;
|
|
}
|
|
|
|
Status = NtQueryValueKey(
|
|
hKey,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
ValueInfo,
|
|
Length,
|
|
&Length
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrint(("LFN: Unable to allocate %u bytes of memory\n",Length));
|
|
goto c2;
|
|
}
|
|
|
|
//
|
|
// Check data type.
|
|
//
|
|
if(ValueInfo->Type != REG_MULTI_SZ) {
|
|
KdPrint(("LFN: BootExecute is wrong data type (%u)\n",ValueInfo->Type));
|
|
}
|
|
|
|
//
|
|
// Allocate space for a new multi_sz we will build up.
|
|
//
|
|
NewValue = RtlAllocateHeap(RtlProcessHeap(),0,ValueInfo->DataLength);
|
|
if(!NewValue) {
|
|
KdPrint(("LFN: Unable to allocate %u bytes of memory\n",ValueInfo->DataLength));
|
|
goto c2;
|
|
}
|
|
|
|
//
|
|
// Process each string in the multi_sz. Copy to the new value
|
|
// we're building up; filter out any strings containing the given Cmd.
|
|
//
|
|
for(SourceString=(PWSTR)ValueInfo->Data,TargetString=NewValue;
|
|
*SourceString;
|
|
SourceString+=Length) {
|
|
|
|
Length = wcslen(SourceString)+1;
|
|
|
|
wcscpy(TargetString,SourceString);
|
|
_wcslwr(TargetString);
|
|
|
|
if(!wcsstr(TargetString,Cmd)) {
|
|
//
|
|
// Don't want to filter this string out of the multi_sz
|
|
// we're building up. Recopy from source to preserve case
|
|
// and advance the target string pointer.
|
|
//
|
|
wcscpy(TargetString,SourceString);
|
|
TargetString += Length;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Extra nul-terminator for multi_sz termination.
|
|
//
|
|
*TargetString++ = 0;
|
|
|
|
//
|
|
// Write the new value out into the registry.
|
|
//
|
|
Status = NtSetValueKey(
|
|
hKey,
|
|
&UnicodeString,
|
|
0,
|
|
REG_MULTI_SZ,
|
|
NewValue,
|
|
(ULONG)((TargetString-NewValue)*sizeof(WCHAR))
|
|
);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
KdPrint(("LFN: Unable to set BootExecute value (%x)\n",Status));
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(),0,NewValue);
|
|
c2:
|
|
RtlFreeHeap(RtlProcessHeap(),0,ValueInfo);
|
|
c1:
|
|
NtClose(hKey);
|
|
c0:
|
|
return;
|
|
}
|