597 lines
19 KiB
C
597 lines
19 KiB
C
|
/*++
|
||
|
|
||
|
File Description:
|
||
|
|
||
|
This file contains the utility function used
|
||
|
to go scan drives and examine the related
|
||
|
ACLs.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Matt Holle (matth) Feb 1998
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
//
|
||
|
// System header files
|
||
|
//
|
||
|
#include <nt.h>
|
||
|
//
|
||
|
// Disable the DbgPrint for non-debug builds
|
||
|
//
|
||
|
#ifndef DBG
|
||
|
#define _DBGNT_
|
||
|
#endif
|
||
|
#include <ntrtl.h>
|
||
|
#include <nturtl.h>
|
||
|
#include <ntverp.h>
|
||
|
#include <wtypes.h>
|
||
|
|
||
|
//
|
||
|
// CRT header files
|
||
|
//
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
//
|
||
|
// Private header files
|
||
|
//
|
||
|
#include "setupcl.h"
|
||
|
|
||
|
NTSTATUS
|
||
|
DeleteUsnJournal(
|
||
|
PWSTR DrivePath
|
||
|
);
|
||
|
|
||
|
NTSTATUS
|
||
|
ResetACLs(
|
||
|
IN WCHAR *DirName,
|
||
|
ULONG indent
|
||
|
);
|
||
|
|
||
|
NTSTATUS
|
||
|
EnumerateDrives(
|
||
|
VOID
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
===============================================================================
|
||
|
Routine Description:
|
||
|
|
||
|
This function will enumerate all drives on the machine. We're looking
|
||
|
for NTFS volumes. For each that we find, we'll go scan the drive and
|
||
|
poke each directory and file for its ACLs.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status is returned.
|
||
|
===============================================================================
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
HANDLE DosDevicesDir;
|
||
|
CHAR DirInfoBuffer[2048],
|
||
|
LinkTargetBuffer[2048];
|
||
|
UNICODE_STRING UnicodeString,
|
||
|
LinkTarget,
|
||
|
DesiredPrefix1,
|
||
|
DesiredPrefix2,
|
||
|
LinkTypeName;
|
||
|
POBJECT_DIRECTORY_INFORMATION DirInfo;
|
||
|
ULONG Context,
|
||
|
Length;
|
||
|
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);
|
||
|
TEST_STATUS_RETURN( "SETUPCL: EnumerateDrives - Failed to open DosDevices." );
|
||
|
|
||
|
LinkTarget.Buffer = (PVOID)LinkTargetBuffer;
|
||
|
RtlInitUnicodeString(&LinkTypeName,L"SymbolicLink");
|
||
|
RtlInitUnicodeString(&DesiredPrefix1,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)) {
|
||
|
|
||
|
//
|
||
|
// Terminate these guys just in case...
|
||
|
//
|
||
|
DirInfo->Name.Buffer[DirInfo->Name.Length/sizeof(WCHAR)] = 0;
|
||
|
DirInfo->TypeName.Buffer[DirInfo->TypeName.Length/sizeof(WCHAR)] = 0;
|
||
|
|
||
|
|
||
|
DbgPrint( "SETUPCL: EnumerateDrives - About to examine an object: %ws\n", DirInfo->Name.Buffer );
|
||
|
|
||
|
//
|
||
|
// Make sure he's a symbolic link.
|
||
|
//
|
||
|
// It's possible for two objects to link to the same device. To
|
||
|
// preclude following duplicate links, disallow any objects except
|
||
|
// those that are a drive letter. Crude but effective...
|
||
|
//
|
||
|
|
||
|
if( (DirInfo->Name.Buffer[1] == L':') &&
|
||
|
(RtlEqualUnicodeString(&LinkTypeName,&DirInfo->TypeName,TRUE)) ) {
|
||
|
|
||
|
DbgPrint( "\tSETUPCL: EnumerateDrives - Object: %ws is a symbolic link\n", DirInfo->Name.Buffer );
|
||
|
|
||
|
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 );
|
||
|
|
||
|
LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0;
|
||
|
DbgPrint( "\tSETUPCL: EnumerateDrives - We queried him and his name is %ws.\n", LinkTarget.Buffer );
|
||
|
NtClose(Handle);
|
||
|
|
||
|
if( NT_SUCCESS(Status) &&
|
||
|
( RtlPrefixUnicodeString(&DesiredPrefix1,&LinkTarget,TRUE) ||
|
||
|
RtlPrefixUnicodeString(&DesiredPrefix2,&LinkTarget,TRUE) ) ) {
|
||
|
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
UCHAR buffer[4096];
|
||
|
PFILE_FS_ATTRIBUTE_INFORMATION Info = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer;
|
||
|
OBJECT_ATTRIBUTES Obja;
|
||
|
|
||
|
//
|
||
|
// OK, this is a symbolic link to a hard drive.
|
||
|
// Make sure it's 0-terminated.
|
||
|
//
|
||
|
LinkTarget.Buffer[LinkTarget.Length/sizeof(WCHAR)] = 0;
|
||
|
|
||
|
DbgPrint( "\tSETUPCL: EnumerateDrives - He's a drive.\n" );
|
||
|
|
||
|
//
|
||
|
// Is he an NTFS drive? Open him and see.
|
||
|
//
|
||
|
InitializeObjectAttributes( &Obja,
|
||
|
&LinkTarget,
|
||
|
OBJ_CASE_INSENSITIVE,
|
||
|
NULL,
|
||
|
NULL );
|
||
|
Status = NtOpenFile( &Handle,
|
||
|
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
||
|
&Obja,
|
||
|
&IoStatusBlock,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
FILE_SYNCHRONOUS_IO_ALERT);
|
||
|
|
||
|
if( NT_SUCCESS(Status) ) {
|
||
|
|
||
|
Status = NtQueryVolumeInformationFile( Handle,
|
||
|
&IoStatusBlock,
|
||
|
buffer,
|
||
|
sizeof(buffer),
|
||
|
FileFsAttributeInformation );
|
||
|
|
||
|
if( NT_SUCCESS(Status) ) {
|
||
|
Info->FileSystemName[Info->FileSystemNameLength/sizeof(WCHAR)] = 0;
|
||
|
DbgPrint( "\tSETUPCL: EnumerateDrives - His file system is: %ws\n", Info->FileSystemName );
|
||
|
if( !_wcsicmp(Info->FileSystemName,L"NTFS") ) {
|
||
|
//
|
||
|
// He's NTFS. Go whack the change journal, then
|
||
|
// scan this drive and fix up the ACLs.
|
||
|
//
|
||
|
DeleteUsnJournal( LinkTarget.Buffer );
|
||
|
|
||
|
//
|
||
|
// ISSUE-2002/02/26-brucegr,jcohen - potential buffer overrun?
|
||
|
//
|
||
|
wcscat( LinkTarget.Buffer, L"\\" );
|
||
|
|
||
|
ResetACLs( LinkTarget.Buffer, 0 );
|
||
|
}
|
||
|
} else {
|
||
|
TEST_STATUS( "SETUPCL: EnumerateDrives - failed call to NtQueryVolumeInformationFile" );
|
||
|
}
|
||
|
} else {
|
||
|
TEST_STATUS( "SETUPCL: EnumerateDrives - Failed NtOpenFile on this drive" );
|
||
|
}
|
||
|
|
||
|
NtClose(Handle);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Query next object in \DosDevices directory
|
||
|
//
|
||
|
Status = NtQueryDirectoryObject( DosDevicesDir,
|
||
|
DirInfo,
|
||
|
sizeof(DirInfoBuffer),
|
||
|
TRUE,
|
||
|
FALSE,
|
||
|
&Context,
|
||
|
&Length );
|
||
|
}
|
||
|
|
||
|
NtClose(DosDevicesDir);
|
||
|
|
||
|
return( STATUS_SUCCESS );
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
ResetACLs(
|
||
|
IN WCHAR *ObjectName,
|
||
|
ULONG indent
|
||
|
)
|
||
|
/*++
|
||
|
===============================================================================
|
||
|
Routine Description:
|
||
|
|
||
|
This function will go search a drive and inspect each file and directory
|
||
|
for an ACL. If found, it will look for, and replace, any ACL that
|
||
|
contains the old SID with the new SID.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status is returned.
|
||
|
===============================================================================
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
UNICODE_STRING UnicodeString;
|
||
|
OBJECT_ATTRIBUTES Obja;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
HANDLE Handle;
|
||
|
PFILE_BOTH_DIR_INFORMATION DirectoryInfo;
|
||
|
PWSTR NewObjectName;
|
||
|
DWORD dwDirectoryInfoSize;
|
||
|
BOOLEAN bStartScan = TRUE,
|
||
|
bContinue = TRUE;
|
||
|
ULONG i;
|
||
|
|
||
|
#if 0
|
||
|
for( i = 0; i < indent; i++ )
|
||
|
DbgPrint( " " );
|
||
|
DbgPrint( "About to operate on a new object: %ws\n", ObjectName );
|
||
|
#endif
|
||
|
|
||
|
DisplayUI();
|
||
|
|
||
|
//
|
||
|
// Open the file/directory and whack his ACL.
|
||
|
//
|
||
|
INIT_OBJA(&Obja, &UnicodeString, ObjectName);
|
||
|
|
||
|
Status = NtOpenFile( &Handle,
|
||
|
READ_CONTROL | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY,
|
||
|
&Obja,
|
||
|
&IoStatusBlock,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
0 );
|
||
|
TEST_STATUS( "SETUPCL: ResetACLs - Failed to open file/directory." );
|
||
|
|
||
|
Status = TestSetSecurityObject( Handle );
|
||
|
|
||
|
TEST_STATUS( "SETUPCL: ResetACLs - Failed to reset ACL on file/directory." );
|
||
|
|
||
|
NtClose( Handle );
|
||
|
|
||
|
//
|
||
|
// Now list the directory.
|
||
|
//
|
||
|
Status = NtOpenFile( &Handle,
|
||
|
FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
||
|
&Obja,
|
||
|
&IoStatusBlock,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT );
|
||
|
|
||
|
//
|
||
|
// Don't report this error because we'll fail if the handle points
|
||
|
// to a file, which is quite possible and valid. Just quietly return.
|
||
|
//
|
||
|
|
||
|
// TEST_STATUS_RETURN( "SETUPCL: ResetACLs - Failed to open file/directory for list access." );
|
||
|
if( !NT_SUCCESS(Status) ) {
|
||
|
return( STATUS_SUCCESS );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// It is gruesome to have the allocation/deallocation of this inside
|
||
|
// the while loop, but it saves a *lot* of stack space. We aren't after
|
||
|
// speed here.
|
||
|
//
|
||
|
dwDirectoryInfoSize = (MAX_PATH * 2) + sizeof(FILE_BOTH_DIR_INFORMATION);
|
||
|
DirectoryInfo = (PFILE_BOTH_DIR_INFORMATION)RtlAllocateHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
dwDirectoryInfoSize );
|
||
|
|
||
|
if ( NULL == DirectoryInfo )
|
||
|
{
|
||
|
bContinue = FALSE;
|
||
|
}
|
||
|
|
||
|
while( bContinue )
|
||
|
{
|
||
|
Status = NtQueryDirectoryFile( Handle,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&IoStatusBlock,
|
||
|
DirectoryInfo,
|
||
|
dwDirectoryInfoSize,
|
||
|
FileBothDirectoryInformation,
|
||
|
TRUE,
|
||
|
NULL,
|
||
|
bStartScan );
|
||
|
|
||
|
if ( NT_SUCCESS( Status ) )
|
||
|
{
|
||
|
//
|
||
|
// Make sure the scan doesn't get restarted...
|
||
|
//
|
||
|
bStartScan = FALSE;
|
||
|
|
||
|
//
|
||
|
// Terminate the name, just in case.
|
||
|
//
|
||
|
DirectoryInfo->FileName[DirectoryInfo->FileNameLength/sizeof(WCHAR)] = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( STATUS_NO_MORE_FILES == Status )
|
||
|
{
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PRINT_STATUS( "SETUPCL: ResetACLs - Failed to query directory." );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We want to exit the loop...
|
||
|
//
|
||
|
bContinue = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We can't really do anything with encrypted files...
|
||
|
//
|
||
|
if ( bContinue &&
|
||
|
( ( !(DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||
|
!(DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ) ||
|
||
|
//
|
||
|
// Don't recurse into the "." and ".." directories...
|
||
|
//
|
||
|
( (DirectoryInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||
|
( (wcscmp( DirectoryInfo->FileName, L"." )) &&
|
||
|
(wcscmp( DirectoryInfo->FileName, L".." )) ) ) ) )
|
||
|
{
|
||
|
//
|
||
|
// Calculate the maximum buffer size we need to allocate...
|
||
|
// We need to factor in 4 things: 1) the current object
|
||
|
// 2) the directory name
|
||
|
// 3) a possible backslash
|
||
|
// 4) the null terminator
|
||
|
//
|
||
|
DWORD dwObjectLength = wcslen(ObjectName),
|
||
|
dwNewObjectLength = dwObjectLength + (DirectoryInfo->FileNameLength / sizeof(WCHAR)) + 2;
|
||
|
|
||
|
//
|
||
|
// Build the name of the new object.
|
||
|
//
|
||
|
NewObjectName = (PWSTR)RtlAllocateHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
dwNewObjectLength * sizeof(WCHAR) );
|
||
|
//
|
||
|
// Make sure the allocation succeeded...
|
||
|
//
|
||
|
if ( NewObjectName )
|
||
|
{
|
||
|
memset( NewObjectName, 0, dwNewObjectLength * sizeof(WCHAR) );
|
||
|
wcsncpy( NewObjectName, ObjectName, dwNewObjectLength - 1 );
|
||
|
|
||
|
//
|
||
|
// If there's not an ending backslash, append one...
|
||
|
// Note: we've already accounted for this possible backslash in the buffer allocation...
|
||
|
//
|
||
|
if ( ObjectName[dwObjectLength - 1] != L'\\' )
|
||
|
{
|
||
|
wcscat( NewObjectName, L"\\" );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Append the FileName buffer onto our NewObjectName buffer...
|
||
|
// Note: we've already accounted for the filename length in the buffer allocation...
|
||
|
//
|
||
|
wcscat( NewObjectName, DirectoryInfo->FileName );
|
||
|
|
||
|
//
|
||
|
// Call ourselves on the new object.
|
||
|
//
|
||
|
ResetACLs( NewObjectName, indent + 1 );
|
||
|
|
||
|
RtlFreeHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
NewObjectName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PRINT_STATUS( "SETUPCL: ResetACLs - Failed to allocate NewObjectName buffer." );
|
||
|
|
||
|
bContinue = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Free the DirectoryInfo pointer...
|
||
|
//
|
||
|
if ( DirectoryInfo )
|
||
|
{
|
||
|
RtlFreeHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
DirectoryInfo );
|
||
|
}
|
||
|
|
||
|
NtClose( Handle );
|
||
|
|
||
|
return( Status );
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
DeleteUsnJournal(
|
||
|
PWSTR DrivePath
|
||
|
)
|
||
|
/*++
|
||
|
===============================================================================
|
||
|
Routine Description:
|
||
|
|
||
|
This function will remove the Change Journal on NTFS partitions.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
DriveLetter Supplies the drive letter of the partition we'll be
|
||
|
operating on.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Status is returned.
|
||
|
===============================================================================
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
UNICODE_STRING UnicodeString;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
HANDLE FileHandle;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
PUSN_JOURNAL_DATA OutputBuffer = NULL;
|
||
|
PDELETE_USN_JOURNAL_DATA InputBuffer = NULL;
|
||
|
ULONG OutputBufferSize, InputBufferSize;
|
||
|
|
||
|
//
|
||
|
// Build the volume name, then open it.
|
||
|
//
|
||
|
INIT_OBJA( &ObjectAttributes,
|
||
|
&UnicodeString,
|
||
|
DrivePath );
|
||
|
Status = NtOpenFile( &FileHandle,
|
||
|
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
|
||
|
&ObjectAttributes,
|
||
|
&IoStatusBlock,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
FILE_SYNCHRONOUS_IO_ALERT);
|
||
|
|
||
|
TEST_STATUS_RETURN( "SETUPCL: DeleteUsnJournal - Failed to open volume." );
|
||
|
|
||
|
//
|
||
|
// Allocate buffers for the query and delete operation.
|
||
|
//
|
||
|
OutputBufferSize = sizeof(USN_JOURNAL_DATA);
|
||
|
OutputBuffer = (PUSN_JOURNAL_DATA)RtlAllocateHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
sizeof(USN_JOURNAL_DATA) );
|
||
|
|
||
|
InputBufferSize = sizeof(DELETE_USN_JOURNAL_DATA);
|
||
|
InputBuffer = (PDELETE_USN_JOURNAL_DATA)RtlAllocateHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
sizeof(DELETE_USN_JOURNAL_DATA) );
|
||
|
|
||
|
if( !(OutputBuffer && InputBuffer) ) {
|
||
|
DbgPrint( "SETUPCL: DeleteUsnJournal - Failed to allocate buffers.\n" );
|
||
|
//
|
||
|
// ISSUE-2002/02/26-brucegr,jcohen - Leaks Input or Output buffers and FileHandle!
|
||
|
//
|
||
|
return( STATUS_UNSUCCESSFUL );
|
||
|
}
|
||
|
|
||
|
Status = NtFsControlFile( FileHandle,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&IoStatusBlock,
|
||
|
FSCTL_QUERY_USN_JOURNAL,
|
||
|
NULL,
|
||
|
0,
|
||
|
OutputBuffer,
|
||
|
OutputBufferSize );
|
||
|
TEST_STATUS( "SETUPCL: DeleteUsnJournal - Failed to query journal." );
|
||
|
|
||
|
if( NT_SUCCESS( Status ) ) {
|
||
|
//
|
||
|
// Now delete him.
|
||
|
//
|
||
|
|
||
|
InputBuffer->DeleteFlags = USN_DELETE_FLAG_DELETE;
|
||
|
InputBuffer->UsnJournalID = OutputBuffer->UsnJournalID;
|
||
|
|
||
|
Status = NtFsControlFile( FileHandle,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&IoStatusBlock,
|
||
|
FSCTL_DELETE_USN_JOURNAL,
|
||
|
InputBuffer,
|
||
|
InputBufferSize ,
|
||
|
NULL,
|
||
|
0 );
|
||
|
|
||
|
TEST_STATUS( "SETUPCL: DeleteUsnJournal - Failed to delete journal." );
|
||
|
}
|
||
|
|
||
|
NtClose( FileHandle );
|
||
|
|
||
|
RtlFreeHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
OutputBuffer );
|
||
|
|
||
|
RtlFreeHeap( RtlProcessHeap(),
|
||
|
0,
|
||
|
InputBuffer );
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|