You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1282 lines
33 KiB
1282 lines
33 KiB
|
|
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smcrash.c
|
|
|
|
Abstract:
|
|
|
|
Routines related to crashdump creation.
|
|
|
|
Author:
|
|
|
|
Matthew D Hendel (math) 28-Aug-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "smsrvp.h"
|
|
#include <ntiodump.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
|
|
#define REVIEW KdBreakPoint
|
|
#define CRASHDUMP_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\CrashControl"
|
|
|
|
|
|
typedef struct _CRASH_PARAMETERS {
|
|
UNICODE_STRING DumpFileName;
|
|
UNICODE_STRING MiniDumpDir;
|
|
ULONG Overwrite;
|
|
ULONG TempDestination;
|
|
} CRASH_PARAMETERS, *PCRASH_PARAMETERS;
|
|
|
|
|
|
//
|
|
// These are the first two fields of a crashdump file.
|
|
//
|
|
|
|
typedef struct _SMP_DUMP_HEADER_SIGNATURE {
|
|
ULONG Signature;
|
|
ULONG ValidDump;
|
|
} SMP_DUMP_HEADER_SIGNATURE, *PSMP_DUMP_HEADER_SIGNATURE;
|
|
|
|
//
|
|
// Verify that these fields haven't changed location.
|
|
//
|
|
|
|
C_ASSERT (FIELD_OFFSET (SMP_DUMP_HEADER_SIGNATURE, Signature) ==
|
|
FIELD_OFFSET (DUMP_HEADER, Signature));
|
|
C_ASSERT (FIELD_OFFSET (SMP_DUMP_HEADER_SIGNATURE, ValidDump) ==
|
|
FIELD_OFFSET (DUMP_HEADER, ValidDump));
|
|
|
|
//
|
|
// Forward declarations
|
|
//
|
|
|
|
BOOLEAN
|
|
SmpQueryFileExists(
|
|
IN PUNICODE_STRING FileName
|
|
);
|
|
|
|
NTSTATUS
|
|
SmpCanCopyCrashDump(
|
|
IN PDUMP_HEADER DumpHeader,
|
|
IN PCRASH_PARAMETERS Parameters,
|
|
IN PUNICODE_STRING PageFileName,
|
|
IN ULONGLONG PageFileSize,
|
|
OUT PUNICODE_STRING DumpFile
|
|
);
|
|
|
|
NTSTATUS
|
|
SmpGetCrashParameters(
|
|
IN PDUMP_HEADER DumpHeader,
|
|
OUT PCRASH_PARAMETERS CrashParameters
|
|
);
|
|
|
|
NTSTATUS
|
|
SmpCopyDumpFile(
|
|
IN PDUMP_HEADER MemoryDump,
|
|
IN HANDLE PageFile,
|
|
IN PUNICODE_STRING DumpFileName
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Functions
|
|
//
|
|
|
|
|
|
PVOID
|
|
SmpAllocateString(
|
|
IN SIZE_T Length
|
|
)
|
|
{
|
|
return RtlAllocateHeap (RtlProcessHeap(),
|
|
MAKE_TAG( INIT_TAG ),
|
|
Length);
|
|
}
|
|
|
|
VOID
|
|
SmpFreeString(
|
|
IN PVOID Pointer
|
|
)
|
|
{
|
|
RtlFreeHeap (RtlProcessHeap(),
|
|
0,
|
|
Pointer);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpSetDumpSecurity(
|
|
IN HANDLE File
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the correct security descriptors for the dump file. The security
|
|
descriptors are:
|
|
|
|
Everybody - None.
|
|
|
|
LocalSystem - Generic-All, Delete, Write-Dac, Write-Owner
|
|
|
|
Admin - Generic-All, Delete, Write-Dac, Write-Owner. Admin is owner.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies a handle to the dump file whose security descriptors
|
|
will be set.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY ;
|
|
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY ;
|
|
PSID EveryoneSid = NULL;
|
|
PSID LocalSystemSid = NULL;
|
|
PSID AdminSid = NULL;
|
|
UCHAR DescriptorBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
|
|
UCHAR AclBuffer[1024];
|
|
PACL Acl;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
|
|
|
|
|
|
Acl = (PACL)AclBuffer;
|
|
SecurityDescriptor = (PSECURITY_DESCRIPTOR)DescriptorBuffer;
|
|
|
|
|
|
RtlAllocateAndInitializeSid( &WorldAuthority, 1, SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &EveryoneSid );
|
|
|
|
RtlAllocateAndInitializeSid( &NtAuthority, 1, SECURITY_LOCAL_SYSTEM_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &LocalSystemSid );
|
|
|
|
RtlAllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0, 0, 0, 0, 0, 0, &AdminSid );
|
|
|
|
//
|
|
// You can be fancy and compute the exact size, but since the
|
|
// security descriptor capture code has to do that anyway, why
|
|
// do it twice?
|
|
//
|
|
|
|
RtlCreateSecurityDescriptor (SecurityDescriptor,
|
|
SECURITY_DESCRIPTOR_REVISION);
|
|
|
|
RtlCreateAcl (Acl, 1024, ACL_REVISION);
|
|
|
|
#if 0
|
|
//
|
|
// anybody can delete it
|
|
//
|
|
|
|
RtlAddAccessAllowedAce (Acl,
|
|
ACL_REVISION,
|
|
DELETE,
|
|
EveryoneSid);
|
|
#endif
|
|
|
|
//
|
|
// Administrator and system have full control
|
|
//
|
|
|
|
RtlAddAccessAllowedAce (Acl,
|
|
ACL_REVISION,
|
|
GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER,
|
|
AdminSid);
|
|
|
|
RtlAddAccessAllowedAce (Acl,
|
|
ACL_REVISION,
|
|
GENERIC_ALL | DELETE | WRITE_DAC | WRITE_OWNER,
|
|
LocalSystemSid);
|
|
|
|
RtlSetDaclSecurityDescriptor (SecurityDescriptor, TRUE, Acl, FALSE);
|
|
RtlSetOwnerSecurityDescriptor (SecurityDescriptor, AdminSid, FALSE);
|
|
|
|
Status = NtSetSecurityObject (File,
|
|
DACL_SECURITY_INFORMATION,
|
|
SecurityDescriptor);
|
|
|
|
RtlFreeHeap (RtlProcessHeap(), 0, EveryoneSid);
|
|
RtlFreeHeap (RtlProcessHeap(), 0, LocalSystemSid);
|
|
RtlFreeHeap (RtlProcessHeap(), 0, AdminSid);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
SmpInitializeVolumePath(
|
|
IN PUNICODE_STRING FileOnVolume,
|
|
OUT PUNICODE_STRING VolumePath
|
|
)
|
|
{
|
|
ULONG n;
|
|
PWSTR s;
|
|
|
|
*VolumePath = *FileOnVolume;
|
|
n = VolumePath->Length;
|
|
VolumePath->Length = 0;
|
|
s = VolumePath->Buffer;
|
|
|
|
while (n) {
|
|
|
|
if (*s++ == L':' && *s == OBJ_NAME_PATH_SEPARATOR) {
|
|
s++;
|
|
break;
|
|
}
|
|
else {
|
|
n -= sizeof( WCHAR );
|
|
}
|
|
}
|
|
|
|
VolumePath->Length = (USHORT)((PCHAR)s - (PCHAR)VolumePath->Buffer);
|
|
}
|
|
|
|
NTSTATUS
|
|
SmpQueryPathFromRegistry(
|
|
IN HANDLE Key,
|
|
IN PWSTR Value,
|
|
IN PWSTR DefaultValue,
|
|
OUT PUNICODE_STRING Path
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING ValueName;
|
|
ULONG KeyValueLength;
|
|
UCHAR KeyValueBuffer [VALUE_BUFFER_SIZE];
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
|
|
WCHAR Buffer[258];
|
|
UNICODE_STRING TempString;
|
|
UNICODE_STRING ExpandedString;
|
|
PWSTR DosPathName;
|
|
BOOLEAN Succ;
|
|
|
|
|
|
DosPathName = NULL;
|
|
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
|
|
|
|
RtlInitUnicodeString (&ValueName, Value);
|
|
KeyValueLength = sizeof (KeyValueBuffer);
|
|
Status = NtQueryValueKey (Key,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInfo,
|
|
KeyValueLength,
|
|
&KeyValueLength);
|
|
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
if (KeyValueInfo->Type == REG_EXPAND_SZ) {
|
|
|
|
TempString.Length = (USHORT)KeyValueLength;
|
|
TempString.MaximumLength = (USHORT)KeyValueLength;
|
|
TempString.Buffer = (PWSTR)KeyValueInfo->Data;
|
|
|
|
ExpandedString.Length = 0;
|
|
ExpandedString.MaximumLength = sizeof (Buffer);
|
|
ExpandedString.Buffer = Buffer;
|
|
|
|
Status = RtlExpandEnvironmentStrings_U (NULL,
|
|
&TempString,
|
|
&ExpandedString,
|
|
NULL);
|
|
if (NT_SUCCESS (Status)) {
|
|
DosPathName = ExpandedString.Buffer;
|
|
}
|
|
|
|
} else if (KeyValueInfo->Type == REG_SZ) {
|
|
DosPathName = (PWSTR)KeyValueInfo->Data;
|
|
}
|
|
}
|
|
|
|
if (!DosPathName) {
|
|
DosPathName = DefaultValue;
|
|
}
|
|
|
|
Succ = RtlDosPathNameToNtPathName_U (DosPathName, Path, NULL, NULL);
|
|
|
|
return (Succ ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpQueryDwordFromRegistry(
|
|
IN HANDLE Key,
|
|
IN PWSTR Value,
|
|
IN ULONG DefaultValue,
|
|
OUT PULONG Dword
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING ValueName;
|
|
ULONG KeyValueLength;
|
|
UCHAR KeyValueBuffer [VALUE_BUFFER_SIZE];
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInfo;
|
|
|
|
KeyValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
|
|
|
|
RtlInitUnicodeString (&ValueName, L"Overwrite");
|
|
KeyValueLength = sizeof (KeyValueBuffer);
|
|
Status = NtQueryValueKey (Key,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInfo,
|
|
KeyValueLength,
|
|
&KeyValueLength);
|
|
|
|
if (NT_SUCCESS (Status) && KeyValueInfo->Type == REG_DWORD) {
|
|
*Dword = *(PULONG)KeyValueInfo->Data;
|
|
} else {
|
|
*Dword = DefaultValue;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpCreateUnicodeString(
|
|
IN PUNICODE_STRING String,
|
|
IN PWSTR InitString,
|
|
IN ULONG MaximumLength
|
|
)
|
|
{
|
|
if (MaximumLength == -1) {
|
|
MaximumLength = (wcslen (InitString) + 1) * 2;
|
|
}
|
|
|
|
if (MaximumLength >= UNICODE_STRING_MAX_CHARS) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
String->Buffer = RtlAllocateStringRoutine (MaximumLength + 1);
|
|
if (String->Buffer == NULL) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
String->MaximumLength = (USHORT)MaximumLength;
|
|
|
|
if (InitString) {
|
|
wcscpy (String->Buffer, InitString);
|
|
String->Length = (USHORT)wcslen (String->Buffer) * sizeof (WCHAR);
|
|
} else {
|
|
String->Length = 0;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpCreateTempFile(
|
|
IN PUNICODE_STRING Directory,
|
|
IN PWSTR Prefix,
|
|
OUT PUNICODE_STRING TempFileName
|
|
)
|
|
{
|
|
ULONG i;
|
|
ULONG Tick;
|
|
WCHAR Buffer [260];
|
|
UNICODE_STRING FileName;
|
|
NTSTATUS Status;
|
|
|
|
Tick = NtGetTickCount ();
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
|
|
swprintf (Buffer,
|
|
L"%s\\%s%4.4x.tmp",
|
|
Directory->Buffer,
|
|
Prefix,
|
|
(Tick + i) & 0xFFFF);
|
|
|
|
Status = RtlDosPathNameToNtPathName_U (Buffer,
|
|
&FileName,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (!SmpQueryFileExists (&FileName)) {
|
|
*TempFileName = FileName;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmpQueryVolumeFreeSpace(
|
|
IN PUNICODE_STRING FileOnVolume,
|
|
OUT PULONGLONG VolumeFreeSpace
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING VolumePath;
|
|
PWCHAR s;
|
|
ULONG n;
|
|
HANDLE Handle;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_FS_SIZE_INFORMATION SizeInfo;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONGLONG AvailableBytes;
|
|
|
|
//
|
|
// Create an unicode string (VolumePath) containing only the
|
|
// volume path from the pagefile name description (e.g. we get
|
|
// "C:\" from "C:\pagefile.sys".
|
|
//
|
|
|
|
VolumePath = *FileOnVolume;
|
|
n = VolumePath.Length;
|
|
VolumePath.Length = 0;
|
|
s = VolumePath.Buffer;
|
|
|
|
while (n) {
|
|
|
|
if (*s++ == L':' && *s == OBJ_NAME_PATH_SEPARATOR) {
|
|
s++;
|
|
break;
|
|
}
|
|
else {
|
|
n -= sizeof( WCHAR );
|
|
}
|
|
}
|
|
|
|
VolumePath.Length = (USHORT)((PCHAR)s - (PCHAR)VolumePath.Buffer);
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&VolumePath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenFile( &Handle,
|
|
(ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Determine the size parameters of the volume.
|
|
//
|
|
|
|
Status = NtQueryVolumeInformationFile( Handle,
|
|
&IoStatusBlock,
|
|
&SizeInfo,
|
|
sizeof( SizeInfo ),
|
|
FileFsSizeInformation
|
|
);
|
|
NtClose( Handle );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Compute the AvailableBytes on the volume.
|
|
// Deal with 64 bit sizes.
|
|
//
|
|
|
|
AvailableBytes = SizeInfo.AvailableAllocationUnits.QuadPart *
|
|
SizeInfo.SectorsPerAllocationUnit *
|
|
SizeInfo.BytesPerSector;
|
|
|
|
*VolumeFreeSpace = AvailableBytes;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SmpQuerySameVolume(
|
|
IN PUNICODE_STRING FileName1,
|
|
IN PUNICODE_STRING FileName2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if FileName1 and FileName2 are on the same volume.
|
|
|
|
Arguments:
|
|
|
|
FileName1 - Supplies the name of the first file to open.
|
|
|
|
FileName2 - Supplies the name of the second file to check against.
|
|
|
|
Return Value:
|
|
|
|
TRUE - If the files are on the same volume.
|
|
|
|
FALSE - Otherwise.
|
|
|
|
--*/
|
|
{
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
ULONG SerialNumber;
|
|
struct {
|
|
FILE_FS_VOLUME_INFORMATION Volume;
|
|
WCHAR Buffer [100];
|
|
} VolumeInfo;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
UNICODE_STRING VolumePath;
|
|
|
|
SmpInitializeVolumePath (FileName1, &VolumePath);
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&VolumePath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile (&Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = NtQueryVolumeInformationFile (Handle,
|
|
&IoStatusBlock,
|
|
&VolumeInfo,
|
|
sizeof (VolumeInfo),
|
|
FileFsVolumeInformation);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
NtClose (Handle);
|
|
return FALSE;
|
|
}
|
|
|
|
SerialNumber = VolumeInfo.Volume.VolumeSerialNumber;
|
|
NtClose (Handle);
|
|
|
|
SmpInitializeVolumePath (FileName2, &VolumePath);
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&VolumePath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile (&Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = NtQueryVolumeInformationFile (Handle,
|
|
&IoStatusBlock,
|
|
&VolumeInfo,
|
|
sizeof (VolumeInfo),
|
|
FileFsVolumeInformation);
|
|
NtClose (Handle);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return ((SerialNumber == VolumeInfo.Volume.VolumeSerialNumber) ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpSetEndOfFile(
|
|
IN HANDLE File,
|
|
IN ULONGLONG EndOfFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Expand or truncate a file to a specific size.
|
|
|
|
Arguments:
|
|
|
|
File - Supplies the file handle of the file to be expanded
|
|
or truncated.
|
|
|
|
EndOfFile - Supplies the final size of the file.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
|
|
FILE_ALLOCATION_INFORMATION AllocationInfo;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
EndOfFileInfo.EndOfFile.QuadPart = EndOfFile;
|
|
Status = NtSetInformationFile (File,
|
|
&IoStatusBlock,
|
|
&EndOfFileInfo,
|
|
sizeof (EndOfFileInfo),
|
|
FileEndOfFileInformation);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
return Status;
|
|
}
|
|
|
|
AllocationInfo.AllocationSize.QuadPart = EndOfFile;
|
|
Status = NtSetInformationFile (File,
|
|
&IoStatusBlock,
|
|
&AllocationInfo,
|
|
sizeof (AllocationInfo),
|
|
FileAllocationInformation);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpQueryFileSize(
|
|
IN HANDLE FileHandle,
|
|
OUT PULONGLONG FileSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Query the size of the specified file.
|
|
|
|
Arguments:
|
|
|
|
FileHandle - Supplies a handle to the file whose size is
|
|
to be querried.
|
|
|
|
FileSize - Supplies a pointer to a buffer where the size is
|
|
copied.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_STANDARD_INFORMATION StandardInfo;
|
|
|
|
Status = NtQueryInformationFile (FileHandle,
|
|
&IoStatusBlock,
|
|
&StandardInfo,
|
|
sizeof (StandardInfo),
|
|
FileStandardInformation);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
*FileSize = StandardInfo.AllocationSize.QuadPart;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SmpQueryFileExists(
|
|
IN PUNICODE_STRING FileName
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Handle;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
FileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile (&Handle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
NtClose (Handle);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SmpCheckForCrashDump(
|
|
IN PUNICODE_STRING PageFileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check the paging file to see if there is a valid crashdump
|
|
in it. This can only be done before we call NtCreatePagingFile.
|
|
|
|
Arguments:
|
|
|
|
PageFileName - Name of the paging file we are about to create.
|
|
|
|
Return Value:
|
|
|
|
TRUE - If the paging file contains a valid crashdump.
|
|
|
|
FALSE - If the paging file does not contain a valid crashdump.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE PageFile;
|
|
HANDLE Key;
|
|
BOOLEAN Copied;
|
|
DUMP_HEADER DumpHeader;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
ULONGLONG PageFileSize;
|
|
UNICODE_STRING String;
|
|
CRASH_PARAMETERS CrashParameters;
|
|
UNICODE_STRING DumpFileName;
|
|
BOOLEAN ClosePageFile;
|
|
BOOLEAN CloseKey;
|
|
|
|
RtlZeroMemory (&CrashParameters, sizeof (CRASH_PARAMETERS));
|
|
RtlZeroMemory (&DumpFileName, sizeof (UNICODE_STRING));
|
|
PageFile = (HANDLE)-1;
|
|
ClosePageFile = FALSE;
|
|
Key = (HANDLE)-1;
|
|
CloseKey = FALSE;
|
|
Copied = FALSE;
|
|
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
PageFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile (&PageFile,
|
|
GENERIC_READ | GENERIC_WRITE |
|
|
DELETE | WRITE_DAC | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT |
|
|
FILE_NO_INTERMEDIATE_BUFFERING);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
Copied = FALSE;
|
|
goto done;
|
|
} else {
|
|
ClosePageFile = TRUE;
|
|
}
|
|
|
|
Status = SmpQueryFileSize (PageFile, &PageFileSize);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
PageFileSize = 0;
|
|
}
|
|
|
|
Status = NtReadFile (PageFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
&DumpHeader,
|
|
sizeof (DUMP_HEADER),
|
|
NULL,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS (Status) &&
|
|
DumpHeader.Signature == DUMP_SIGNATURE &&
|
|
DumpHeader.ValidDump == DUMP_VALID_DUMP) {
|
|
|
|
Status = SmpGetCrashParameters (&DumpHeader, &CrashParameters);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
Status = SmpCanCopyCrashDump (&DumpHeader,
|
|
&CrashParameters,
|
|
PageFileName,
|
|
PageFileSize,
|
|
&DumpFileName);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
Status = SmpCopyDumpFile (&DumpHeader,
|
|
PageFile,
|
|
&DumpFileName);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
Copied = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NtClose (PageFile);
|
|
PageFile = (HANDLE) -1;
|
|
ClosePageFile = FALSE;
|
|
|
|
if (Copied) {
|
|
|
|
//
|
|
// It is not necessary to create a new pagefile of the same
|
|
// size as the old one. The function NtCreatePagingFile will
|
|
// completely desroy the old paging file.
|
|
//
|
|
|
|
//
|
|
// If we successfully copied, we want to create
|
|
// a volitile registry key that others can use
|
|
// to locate the dump file.
|
|
//
|
|
|
|
RtlInitUnicodeString (&String, CRASHDUMP_KEY L"\\MachineCrash");
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&String,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtCreateKey (&Key,
|
|
KEY_READ | KEY_WRITE,
|
|
&ObjectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_VOLATILE,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
CloseKey = TRUE;
|
|
|
|
//
|
|
// We are setting volatile key CrashControl\MachineCrash\DumpFile
|
|
// to the name of the dump file.
|
|
//
|
|
|
|
RtlInitUnicodeString (&String, L"DumpFile");
|
|
Status = NtSetValueKey (Key,
|
|
&String,
|
|
0,
|
|
REG_SZ,
|
|
&DumpFileName.Buffer[4],
|
|
DumpFileName.Length - (3 * sizeof (WCHAR)));
|
|
|
|
RtlInitUnicodeString (&String, L"TempDestination");
|
|
Status = NtSetValueKey (Key,
|
|
&String,
|
|
0,
|
|
REG_DWORD,
|
|
&CrashParameters.TempDestination,
|
|
sizeof (CrashParameters.TempDestination));
|
|
|
|
NtClose (Key);
|
|
Key = (HANDLE) -1;
|
|
CloseKey = FALSE;
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
//
|
|
// Cleanup and return
|
|
//
|
|
|
|
if (CrashParameters.DumpFileName.Length != 0) {
|
|
RtlFreeUnicodeString (&CrashParameters.DumpFileName);
|
|
}
|
|
|
|
if (CrashParameters.MiniDumpDir.Length != 0) {
|
|
RtlFreeUnicodeString (&CrashParameters.MiniDumpDir);
|
|
}
|
|
|
|
if (ClosePageFile) {
|
|
NtClose (PageFile);
|
|
}
|
|
|
|
if (CloseKey) {
|
|
NtClose (Key);
|
|
}
|
|
|
|
return Copied;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpGetCrashParameters(
|
|
IN PDUMP_HEADER DumpHeader,
|
|
OUT PCRASH_PARAMETERS CrashParameters
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the parameters for the crashdump from
|
|
the registry.
|
|
|
|
Arguments:
|
|
|
|
DumpHeader - Pointer to the mapped dump headers.
|
|
|
|
CrashParameters - Supplies a buffer where the crash parameters
|
|
should be copied.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE Key;
|
|
BOOLEAN CloseKey;
|
|
UNICODE_STRING KeyName;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
WCHAR DefaultPath[260];
|
|
|
|
Key = (HANDLE) -1;
|
|
CloseKey = FALSE;
|
|
|
|
RtlInitUnicodeString (&KeyName, CRASHDUMP_KEY);
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
Status = NtOpenKey (&Key, KEY_READ, &ObjectAttributes);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
CloseKey = TRUE;
|
|
} else {
|
|
goto done;
|
|
}
|
|
|
|
swprintf (DefaultPath, L"%s\\MEMORY.DMP", SmpSystemRoot.Buffer);
|
|
|
|
Status = SmpQueryPathFromRegistry (Key,
|
|
L"DumpFile",
|
|
DefaultPath,
|
|
&CrashParameters->DumpFileName);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
swprintf (DefaultPath, L"%s\\Minidump", SmpSystemRoot.Buffer);
|
|
|
|
Status = SmpQueryPathFromRegistry (Key,
|
|
L"MiniDumpDir",
|
|
DefaultPath,
|
|
&CrashParameters->MiniDumpDir);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
Status = SmpQueryDwordFromRegistry (Key,
|
|
L"Overwrite",
|
|
1,
|
|
&CrashParameters->Overwrite);
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// This value is initialized by SmpCanCopyCrashDump.
|
|
//
|
|
|
|
CrashParameters->TempDestination = FALSE;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
done:
|
|
if (CloseKey) {
|
|
NtClose (Key);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmpCopyDumpFile(
|
|
IN PDUMP_HEADER MemoryDump,
|
|
IN HANDLE PageFile,
|
|
IN PUNICODE_STRING DumpFileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copy the dump file from the pagefile to the crash dump file.
|
|
|
|
Arguments:
|
|
|
|
DumpHeader - Pointer to the beginning of the crashdump header.
|
|
|
|
PageFile - Pointer to an opened handle to the pagefile that contains
|
|
the dump.
|
|
|
|
DumpFileName -
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONGLONG DumpFileSize;
|
|
struct {
|
|
FILE_RENAME_INFORMATION Rename;
|
|
WCHAR Buffer[255];
|
|
} RenameInfoBuffer;
|
|
PFILE_RENAME_INFORMATION RenameInfo;
|
|
ULONG RenameInfoSize;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_BASIC_INFORMATION BasicInformation;
|
|
|
|
RenameInfo = &RenameInfoBuffer.Rename;
|
|
DumpFileSize = MemoryDump->RequiredDumpSpace.QuadPart;
|
|
|
|
Status = SmpSetEndOfFile (PageFile, DumpFileSize);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
RenameInfoSize = sizeof (FILE_RENAME_INFORMATION) + DumpFileName->Length;
|
|
|
|
RenameInfo->ReplaceIfExists = TRUE;
|
|
RenameInfo->RootDirectory = NULL;
|
|
RenameInfo->FileNameLength = DumpFileName->Length;
|
|
wcscpy (RenameInfo->FileName, DumpFileName->Buffer);
|
|
|
|
Status = NtSetInformationFile (PageFile,
|
|
&IoStatusBlock,
|
|
RenameInfo,
|
|
RenameInfoSize,
|
|
FileRenameInformation);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Reset the file SYSTEM and HIDDEN attributes.
|
|
//
|
|
|
|
Status = NtQueryInformationFile (PageFile,
|
|
&IoStatusBlock,
|
|
&BasicInformation,
|
|
sizeof (BasicInformation),
|
|
FileBasicInformation);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
|
BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_SYSTEM;
|
|
|
|
Status = NtSetInformationFile (PageFile,
|
|
&IoStatusBlock,
|
|
&BasicInformation,
|
|
sizeof (BasicInformation),
|
|
FileBasicInformation);
|
|
|
|
//
|
|
// Reset the file security.
|
|
//
|
|
|
|
Status = SmpSetDumpSecurity (PageFile);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SmpCanCopyCrashDump(
|
|
IN PDUMP_HEADER DumpHeader,
|
|
IN PCRASH_PARAMETERS CrashParameters,
|
|
IN PUNICODE_STRING PageFileName,
|
|
IN ULONGLONG PageFileSize,
|
|
OUT PUNICODE_STRING DumpFileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Figure out whether it's ok to copy the dump file or not.
|
|
|
|
Arguments:
|
|
|
|
DumpHeader - Supplies the header to the dump file.
|
|
|
|
CrashParameters - Supplies the parameters required to copy the file.
|
|
|
|
DumpFileName - Supplies a unicode string buffer that the crashdump
|
|
will be copied to.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN SameVolume;
|
|
BOOLEAN UseTempFile;
|
|
ULONGLONG CrashFileSize;
|
|
ULONGLONG VolumeFreeSpace;
|
|
|
|
UseTempFile = FALSE;
|
|
|
|
if (DumpHeader->DumpType == DUMP_TYPE_TRIAGE) {
|
|
UseTempFile = TRUE;
|
|
} else {
|
|
SameVolume = SmpQuerySameVolume (PageFileName,
|
|
&CrashParameters->DumpFileName);
|
|
|
|
|
|
if (SameVolume) {
|
|
|
|
//
|
|
// If we're on the same volume and there is an existing dump file
|
|
// then :
|
|
// if overwrite flag was not set, fail.
|
|
// otherwise, reclaim the space for this file.
|
|
//
|
|
|
|
if (SmpQueryFileExists (&CrashParameters->DumpFileName)) {
|
|
|
|
if (CrashParameters->Overwrite) {
|
|
|
|
SmpDeleteFile (&CrashParameters->DumpFileName);
|
|
|
|
} else {
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// We're not on the same volume, so we'll need to create a temp
|
|
// file.
|
|
//
|
|
|
|
UseTempFile = TRUE;
|
|
}
|
|
}
|
|
|
|
CrashFileSize = DumpHeader->RequiredDumpSpace.QuadPart;
|
|
|
|
Status = SmpQueryVolumeFreeSpace (&CrashParameters->DumpFileName,
|
|
&VolumeFreeSpace);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// The space reserved by the pagefile is already taken into account here.
|
|
// Do not add it in (a second time) here.
|
|
//
|
|
|
|
if (CrashFileSize < VolumeFreeSpace) {
|
|
if (!UseTempFile) {
|
|
Status = SmpCreateUnicodeString (DumpFileName,
|
|
CrashParameters->DumpFileName.Buffer,
|
|
-1);
|
|
} else {
|
|
Status = SmpCreateTempFile (&SmpSystemRoot, L"DUMP", DumpFileName);
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
CrashParameters->TempDestination = UseTempFile;
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
//
|
|
// NB: Log an error saying we were unable
|
|
// to copy the crashdump for some reason.
|
|
//
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
const PRTL_ALLOCATE_STRING_ROUTINE RtlAllocateStringRoutine = SmpAllocateString;
|
|
const PRTL_FREE_STRING_ROUTINE RtlFreeStringRoutine = SmpFreeString;
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
__cdecl
|
|
main(
|
|
)
|
|
{
|
|
BOOLEAN CopiedDump;
|
|
UNICODE_STRING PageFile;
|
|
|
|
RtlInitUnicodeString (&SmpSystemRoot,
|
|
L"C:\\WINNT");
|
|
|
|
RtlDosPathNameToNtPathName_U (L"C:\\Public\\crashdmp.teo\\memory.dmp",
|
|
&PageFile,
|
|
NULL,
|
|
NULL);
|
|
|
|
CopiedDump = SmpCheckForCrashDump (&PageFile);
|
|
}
|
|
|
|
|
|
#endif // TEST
|