/*++ 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 #include #include #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