5761 lines
163 KiB
C
5761 lines
163 KiB
C
/*++
|
||
|
||
Copyright (c) 2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
asrbkup.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the following ASR routines:
|
||
AsrCreateStateFile{A|W}
|
||
AsrAddSifEntry{A|W}
|
||
AsrFreeContext
|
||
|
||
|
||
Author:
|
||
|
||
Guhan Suriyanarayanan (guhans) 27-May-2000
|
||
|
||
Environment:
|
||
|
||
User-mode only.
|
||
|
||
Notes:
|
||
|
||
Naming conventions:
|
||
_AsrpXXX private ASR Macros
|
||
AsrpXXX private ASR routines
|
||
AsrXXX Publically defined and documented routines
|
||
|
||
Revision History:
|
||
|
||
27-May-2000 guhans
|
||
Moved ASR-backup related routines from asr.c to
|
||
asrbkup.c
|
||
|
||
01-Jan-2000 guhans
|
||
Initial implementation for Asr routines in asr.c
|
||
|
||
--*/
|
||
#include "setupp.h"
|
||
#pragma hdrstop
|
||
|
||
#include <initguid.h> // DiskClassGuid
|
||
#include <diskguid.h> // GPT partition type guids
|
||
#include <ntddvol.h> // ioctl_volume_query_failover_set
|
||
#include <setupapi.h> // SetupDi routines
|
||
#include <mountmgr.h> // mountmgr ioctls
|
||
#include <rpcdce.h> // UuidToStringW, RpcStringFreeW
|
||
#include <winasr.h> // ASR public routines
|
||
|
||
#define THIS_MODULE 'B'
|
||
#include "asrpriv.h" // Private ASR definitions and routines
|
||
|
||
|
||
//
|
||
// --------
|
||
// constants local to this module. These are not accessed outside this file.
|
||
// --------
|
||
//
|
||
|
||
//
|
||
// The Setup Key to find the system partition from
|
||
//
|
||
const WCHAR ASR_REGKEY_SETUP[] = L"SYSTEM\\SETUP";
|
||
const WCHAR ASR_REGVALUE_SYSTEM_PARTITION[] = L"SystemPartition";
|
||
|
||
//
|
||
// The ASR registry entries. Currently, this is used to look
|
||
// up the commands to run during an Asr backup, but we could
|
||
// have other settings here later.
|
||
//
|
||
const WCHAR ASR_REGKEY_ASR_COMMANDS[]
|
||
= L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Asr\\Commands";
|
||
|
||
const WCHAR ASR_REGKEY_ASR[]
|
||
= L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Asr\\";
|
||
|
||
const WCHAR ASR_REGVALUE_TIMEOUT[] = L"ProcessTimeOut";
|
||
|
||
//
|
||
// File to save the PnP information in.
|
||
//
|
||
const WCHAR ASR_DEFAULT_SIF_PATH[] = L"\\\\?\\%systemroot%\\repair\\asr.sif";
|
||
const WCHAR ASRPNP_DEFAULT_SIF_NAME[] = L"asrpnp.sif";
|
||
|
||
//
|
||
// We only support x86, AMD64, and IA64 architectures.
|
||
//
|
||
const WCHAR ASR_PLATFORM_X86[] = L"x86";
|
||
const WCHAR ASR_PLATFORM_AMD64[] = L"AMD64";
|
||
const WCHAR ASR_PLATFORM_IA64[] = L"IA64";
|
||
|
||
//
|
||
// This is the suffix that we add when launching the apps registered for ASR.
|
||
// Remember to change the length if you're changing this. The length should
|
||
// include space for 20 digits (max 64-bit int) + null + space at the beginning.
|
||
//
|
||
#define ASR_COMMANDLINE_SUFFIX_LEN 35
|
||
const WCHAR ASR_COMMANDLINE_SUFFIX[] = L" /context=%I64u";
|
||
|
||
//
|
||
// Miscellaneous constants
|
||
//
|
||
const WCHAR ASR_DOS_DEVICES_PREFIX[] = L"\\DosDevices\\";
|
||
const WCHAR ASR_DEVICE_PATH_PREFIX[] = L"\\Device\\Harddisk";
|
||
|
||
//
|
||
// Sections in asr.sif
|
||
//
|
||
const WCHAR ASR_SIF_VERSION_SECTION_NAME[] = L"[VERSION]";
|
||
const WCHAR ASR_SIF_SYSTEM_SECTION_NAME[] = L"[SYSTEMS]";
|
||
const WCHAR ASR_SIF_BUSES_SECTION_NAME[] = L"[BUSES]";
|
||
const WCHAR ASR_SIF_MBR_DISKS_SECTION_NAME[] = L"[DISKS.MBR]";
|
||
const WCHAR ASR_SIF_GPT_DISKS_SECTION_NAME[] = L"[DISKS.GPT]";
|
||
const WCHAR ASR_SIF_MBR_PARTITIONS_SECTION_NAME[] = L"[PARTITIONS.MBR]";
|
||
const WCHAR ASR_SIF_GPT_PARTITIONS_SECTION_NAME[] = L"[PARTITIONS.GPT]";
|
||
|
||
|
||
const WCHAR ASR_SIF_PROVIDER_PREFIX[] = L"Provider=";
|
||
|
||
// wcslen("Provider=""\r\n\0")
|
||
#define ASR_SIF_CCH_PROVIDER_STRING 14
|
||
|
||
|
||
//
|
||
// While launching registered applications during an ASR backup, we
|
||
// add two environment variables to the environment block for the
|
||
// process being launched: the AsrContext and the critical volume
|
||
// list.
|
||
//
|
||
#define ASR_CCH_ENVBLOCK_ASR_ENTRIES (32 + 1 + 28 + 2)
|
||
const WCHAR ASR_ENVBLOCK_CONTEXT_ENTRY[] = L"_AsrContext=%I64u";
|
||
|
||
const WCHAR ASR_ENVBLOCK_CRITICAL_VOLUME_ENTRY[]
|
||
= L"_AsrCriticalVolumeList=";
|
||
|
||
//
|
||
// Pre-defined flags designating the boot and system partitions
|
||
// in the partitions section of asr.sif. Remember to change the
|
||
// counter-parts in setupdd.sys if you change these!
|
||
//
|
||
const BYTE ASR_FLAGS_BOOT_PTN = 1;
|
||
const BYTE ASR_FLAGS_SYSTEM_PTN = 2;
|
||
|
||
//
|
||
// For now, we only allow one system per sif file. If a sif
|
||
// already exists at the location AsrCreateStateFile is called,
|
||
// the existing sif is deleted. The asr.sif architecture does
|
||
// allow for multiple systems per sif file, but
|
||
// - I don't see any compelling reason to support this, and
|
||
// - It would be a test nightmare
|
||
//
|
||
const BYTE ASR_SYSTEM_KEY = 1;
|
||
|
||
//
|
||
// _AsrpCheckTrue: primarily used with WriteFile calls.
|
||
//
|
||
#define _AsrpCheckTrue( Expression ) \
|
||
if (!Expression) { \
|
||
return FALSE; \
|
||
}
|
||
|
||
//
|
||
// --------
|
||
// constants used across asr modules.
|
||
// --------
|
||
//
|
||
const WCHAR ASR_SIF_SYSTEM_SECTION[] = L"SYSTEMS";
|
||
const WCHAR ASR_SIF_BUSES_SECTION[] = L"BUSES";
|
||
const WCHAR ASR_SIF_MBR_DISKS_SECTION[] = L"DISKS.MBR";
|
||
const WCHAR ASR_SIF_GPT_DISKS_SECTION[] = L"DISKS.GPT";
|
||
const WCHAR ASR_SIF_MBR_PARTITIONS_SECTION[] = L"PARTITIONS.MBR";
|
||
const WCHAR ASR_SIF_GPT_PARTITIONS_SECTION[] = L"PARTITIONS.GPT";
|
||
|
||
const WCHAR ASR_WSZ_VOLUME_PREFIX[]
|
||
= L"\\??\\Volume";
|
||
|
||
const WCHAR ASR_WSZ_DEVICE_PATH_FORMAT[]
|
||
= L"\\Device\\Harddisk%d\\Partition%d";
|
||
|
||
//
|
||
// --------
|
||
// function prototypes
|
||
// --------
|
||
//
|
||
|
||
//
|
||
// Function prototype for AsrCreatePnpStateFileW.
|
||
// (linked into syssetup.dll from pnpsif.lib)
|
||
//
|
||
BOOL
|
||
AsrCreatePnpStateFileW(
|
||
IN PCWSTR lpFilePath
|
||
);
|
||
|
||
|
||
//
|
||
// --------
|
||
// private functions
|
||
// --------
|
||
//
|
||
|
||
BOOL
|
||
AsrpGetMountPoints(
|
||
IN PCWSTR DeviceName,
|
||
IN CONST DWORD SizeDeviceName,
|
||
OUT PMOUNTMGR_MOUNT_POINTS *pMountPointsOut
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the current list of mount-points for DeviceName, by querying the
|
||
mount manager.
|
||
|
||
Arguments:
|
||
|
||
DeviceName - The device name that the mount-point list is requested for.
|
||
Typically, this is something of the form
|
||
\Device\HarddiskX\PartitionY or \DosDevices\X:
|
||
|
||
SizeDeviceName - The size, in bytes, of DeviceName. This includes the
|
||
terminating null character.
|
||
|
||
pMountPointsOut - Receives the output list of mount-points. The caller
|
||
must free this memory by calling HeapFree for the current process
|
||
heap.
|
||
|
||
Return Values:
|
||
|
||
TRUE, if everything went well. MountPointsOut contains the promised data.
|
||
|
||
FALSE, if the mount manager returned an error. MountPoints is NULL. Call
|
||
GetLastError() for more information.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMOUNTMGR_MOUNT_POINT mountPointIn = NULL;
|
||
PMOUNTMGR_MOUNT_POINTS mountPointsOut = NULL;
|
||
MOUNTMGR_MOUNT_POINTS mountPointsTemp;
|
||
DWORD mountPointsSize = 0;
|
||
|
||
HANDLE mpHandle = NULL;
|
||
HANDLE heapHandle = NULL;
|
||
|
||
ULONG index = 0;
|
||
LONG status = ERROR_SUCCESS;
|
||
BOOL result = FALSE;
|
||
|
||
memset(&mountPointsTemp, 0L, sizeof(MOUNTMGR_MOUNT_POINTS));
|
||
|
||
MYASSERT(pMountPointsOut);
|
||
*pMountPointsOut = NULL;
|
||
|
||
heapHandle = GetProcessHeap();
|
||
MYASSERT(heapHandle);
|
||
|
||
mountPointIn = (PMOUNTMGR_MOUNT_POINT) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof (MOUNTMGR_MOUNT_POINT) + (SizeDeviceName - sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode((!mountPointIn), status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Try with a decently sized mountPoints out: if it isn't big
|
||
// enough, we'll realloc as needed
|
||
//
|
||
mountPointsOut = (PMOUNTMGR_MOUNT_POINTS) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
(MAX_PATH + 1) * (sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode(!mountPointsOut, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Get a handle to the mount manager
|
||
//
|
||
mpHandle = CreateFileW(
|
||
MOUNTMGR_DOS_DEVICE_NAME, // lpFileName
|
||
0, // dwDesiredAccess
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
|
||
NULL, // lpSecurityAttributes
|
||
OPEN_EXISTING, // dwCreationFlags
|
||
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
|
||
NULL // hTemplateFile
|
||
);
|
||
_AsrpErrExitCode((!mpHandle || INVALID_HANDLE_VALUE == mpHandle),
|
||
status,
|
||
GetLastError()
|
||
);
|
||
|
||
//
|
||
// put the DeviceName right after struct mountPointIn
|
||
//
|
||
wcsncpy((PWSTR) (mountPointIn + 1),
|
||
DeviceName,
|
||
(SizeDeviceName / sizeof(WCHAR)) - 1
|
||
);
|
||
mountPointIn->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
|
||
mountPointIn->DeviceNameLength = (USHORT)(SizeDeviceName - sizeof(WCHAR));
|
||
|
||
result = DeviceIoControl(
|
||
mpHandle,
|
||
IOCTL_MOUNTMGR_QUERY_POINTS,
|
||
mountPointIn,
|
||
sizeof(MOUNTMGR_MOUNT_POINT) + mountPointIn->DeviceNameLength,
|
||
&mountPointsTemp,
|
||
sizeof(MOUNTMGR_MOUNT_POINTS),
|
||
&mountPointsSize,
|
||
NULL
|
||
);
|
||
|
||
while (!result) {
|
||
|
||
status = GetLastError();
|
||
|
||
if (ERROR_MORE_DATA == status) {
|
||
//
|
||
// The buffer is not big enough, re-size and try again
|
||
//
|
||
status = ERROR_SUCCESS;
|
||
_AsrpHeapFree(mountPointsOut);
|
||
|
||
mountPointsOut = (PMOUNTMGR_MOUNT_POINTS) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
mountPointsTemp.Size
|
||
);
|
||
_AsrpErrExitCode((!mountPointsOut),
|
||
status,
|
||
ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
result = DeviceIoControl(
|
||
mpHandle,
|
||
IOCTL_MOUNTMGR_QUERY_POINTS,
|
||
mountPointIn,
|
||
sizeof(MOUNTMGR_MOUNT_POINT) + mountPointIn->DeviceNameLength,
|
||
mountPointsOut,
|
||
mountPointsTemp.Size,
|
||
&mountPointsSize,
|
||
NULL
|
||
);
|
||
_AsrpErrExitCode((!mountPointsSize), status, GetLastError());
|
||
|
||
}
|
||
else {
|
||
//
|
||
// If some other error occurred, EXIT.
|
||
//
|
||
result = TRUE;
|
||
status = GetLastError();
|
||
// _AsrpErrExitCode(status, status, GetLastError());
|
||
}
|
||
}
|
||
|
||
|
||
EXIT:
|
||
//
|
||
// Free up locally allocated memory
|
||
//
|
||
_AsrpHeapFree(mountPointIn);
|
||
|
||
if (ERROR_SUCCESS != status) {
|
||
//
|
||
// On failure, free up mountPointsOut as well
|
||
//
|
||
_AsrpHeapFree(mountPointsOut);
|
||
}
|
||
|
||
_AsrpCloseHandle(mpHandle);
|
||
|
||
*pMountPointsOut = mountPointsOut;
|
||
|
||
return (BOOL) (ERROR_SUCCESS == status);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpGetMorePartitionInfo(
|
||
IN PCWSTR DeviceName,
|
||
IN CONST DWORD SizeDeviceName,
|
||
IN CONST PASR_SYSTEM_INFO pSystemInfo OPTIONAL,
|
||
OUT PWSTR pVolumeGuid,
|
||
OUT USHORT* pPartitionFlags OPTIONAL,
|
||
OUT UCHAR* pFileSystemType OPTIONAL,
|
||
OUT LPDWORD pClusterSize OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets additional information about the partition specified by DeviceName,
|
||
including the volume guid (if any) for the volume that maps to the
|
||
partition specified by DeviceName.
|
||
|
||
If the partition is the current system or boot drive, pPartitionFlags and
|
||
pFileSystemType are set appropriately.
|
||
|
||
Arguments:
|
||
|
||
DeviceName - A null terminated string containing the device path to the
|
||
partition, typically of the form \Device\HarddiskX\PartitionY
|
||
|
||
SizeDeviceName - Size, in bytes, of DeviceName, including \0 at the end
|
||
|
||
pSystemInfo - The SYSTEM_INFO structure for the current system; used for
|
||
finding out the current system partition.
|
||
|
||
This is an optional parameter. If absent, pPartitionFlags will
|
||
not have the SYSTEM_FLAG set, even if DeviceName is in fact the
|
||
system partition.
|
||
|
||
pVolumeGuid - Receives a null-terminated string containing the GUID for
|
||
the volume on this partition. This is only relevant for basic
|
||
disks, where volumes and partitions have a one-on-one
|
||
relationship.
|
||
|
||
This will be set to a blank null-terminated string if there is no
|
||
volume (or multiple volumes) on this partition.
|
||
|
||
|
||
*** Note that if ANY of three of the OPTIONAL parameters are not present,
|
||
NONE of them will be filled with valid data.
|
||
|
||
pPartitionFlags - If the current partition is a partition of interest,
|
||
this receives the appropriate flags, IN ADDITION TO THE FLAGS
|
||
ALREADY SET when the routine is called (i.e., caller should
|
||
usually zero this out). Currently, the two flags of interest are:
|
||
ASR_FLAGS_BOOT_PTN for the boot partition
|
||
ASR_FLAGS_SYSTEM_PTN for (you guessed it) the system partition
|
||
|
||
pFileSystemType - If (and ONLY if) the current partition is a partition of
|
||
interest, this will contain a UCHAR to the file-system type of the
|
||
partition. Currently, the three file-systems this recognises are:
|
||
PARTITION_HUGE (FAT)
|
||
PARTITION_FAT32 (FAT32)
|
||
PARTITION_IFS (NTFS)
|
||
|
||
pClusterSize - The file-system cluster size. Set to 0 if the information
|
||
could not be obtained.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
PMOUNTMGR_MOUNT_POINTS mountPointsOut = NULL;
|
||
HANDLE heapHandle = NULL;
|
||
|
||
ULONG index = 0;
|
||
LONG status = ERROR_SUCCESS;
|
||
BOOL result = FALSE;
|
||
BOOL volumeGuidSet = FALSE;
|
||
|
||
//
|
||
// set OUT variables to known values.
|
||
//
|
||
MYASSERT(pVolumeGuid);
|
||
wcscpy(pVolumeGuid, L"");
|
||
|
||
/* if (ARGUMENT_PRESENT(pPartitionFlags)) {
|
||
*pPartitionFlags = 0;
|
||
}
|
||
*/
|
||
|
||
if (ARGUMENT_PRESENT(pClusterSize)) {
|
||
*pClusterSize = 0;
|
||
}
|
||
|
||
heapHandle = GetProcessHeap();
|
||
MYASSERT(heapHandle);
|
||
|
||
//
|
||
// Open the mount manager, and get a list of all the symbolic links
|
||
// this partition
|
||
//
|
||
result = AsrpGetMountPoints(DeviceName, SizeDeviceName, &mountPointsOut);
|
||
_AsrpErrExitCode((!result), status, GetLastError());
|
||
_AsrpErrExitCode((!mountPointsOut), status, ERROR_SUCCESS);
|
||
|
||
//
|
||
// Check if this is the system partition, by comparing the
|
||
// device path with the one stored in the Setup key.
|
||
//
|
||
if (ARGUMENT_PRESENT(pSystemInfo) && ARGUMENT_PRESENT(pPartitionFlags)) {
|
||
|
||
PWSTR deviceName = (PWSTR) (
|
||
((LPBYTE) mountPointsOut) +
|
||
mountPointsOut->MountPoints[index].DeviceNameOffset
|
||
);
|
||
|
||
UINT sizeDeviceName =
|
||
(UINT)(mountPointsOut->MountPoints[index].DeviceNameLength);
|
||
|
||
if ((pSystemInfo->SystemPath) &&
|
||
(wcslen(pSystemInfo->SystemPath)==sizeDeviceName/sizeof(WCHAR)) &&
|
||
(!wcsncmp(pSystemInfo->SystemPath, deviceName,
|
||
sizeDeviceName/sizeof(WCHAR)))
|
||
) {
|
||
*pPartitionFlags |= ASR_FLAGS_SYSTEM_PTN;
|
||
}
|
||
}
|
||
|
||
for (index = 0; index < mountPointsOut->NumberOfMountPoints; index++) {
|
||
|
||
//
|
||
// Go through the list of mount points returned, and find the one
|
||
// that looks like an nt volume guid
|
||
//
|
||
PWSTR linkName = (PWSTR) (((LPBYTE) mountPointsOut) +
|
||
mountPointsOut->MountPoints[index].SymbolicLinkNameOffset
|
||
);
|
||
|
||
UINT sizeLinkName =
|
||
(UINT)(mountPointsOut->MountPoints[index].SymbolicLinkNameLength);
|
||
|
||
if ((!volumeGuidSet) &&
|
||
|
||
!wcsncmp(ASR_WSZ_VOLUME_PREFIX,
|
||
linkName,
|
||
wcslen(ASR_WSZ_VOLUME_PREFIX))
|
||
|
||
) {
|
||
|
||
wcsncpy(pVolumeGuid, linkName, sizeLinkName / sizeof(WCHAR));
|
||
volumeGuidSet = TRUE; // we got a GUID, no need to check again
|
||
|
||
}
|
||
else if (
|
||
ARGUMENT_PRESENT(pSystemInfo) &&
|
||
ARGUMENT_PRESENT(pPartitionFlags)
|
||
) {
|
||
|
||
//
|
||
// Also, if this link isn't a GUID, it might be a drive letter.
|
||
// use the boot directory's drive letter to check if this
|
||
// is the boot volume, and mark it if so.
|
||
//
|
||
|
||
if (!wcsncmp(ASR_DOS_DEVICES_PREFIX,
|
||
linkName,
|
||
wcslen(ASR_DOS_DEVICES_PREFIX))
|
||
) {
|
||
|
||
if ((pSystemInfo->BootDirectory) &&
|
||
(pSystemInfo->BootDirectory[0]
|
||
== linkName[wcslen(ASR_DOS_DEVICES_PREFIX)])
|
||
) {
|
||
|
||
*pPartitionFlags |= ASR_FLAGS_BOOT_PTN;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
EXIT:
|
||
//
|
||
// If this is a partition of interest, we need to get the file-system
|
||
// type as well
|
||
//
|
||
if (ARGUMENT_PRESENT(pFileSystemType) &&
|
||
ARGUMENT_PRESENT(pPartitionFlags) &&
|
||
ARGUMENT_PRESENT(pClusterSize)
|
||
) {
|
||
|
||
if (*pPartitionFlags) {
|
||
WCHAR fsName[20];
|
||
DWORD dwSectorsPerCluster = 0,
|
||
dwBytesPerSector = 0,
|
||
dwNumFreeClusters = 0,
|
||
dwTotalNumClusters = 0;
|
||
|
||
//
|
||
// Convert the NT Volume-GUID (starts with \??\) to a DOS
|
||
// Volume (begins with a \\?\, and ends with a back-slash),
|
||
// since GetVolumeInformation needs it in this format.
|
||
//
|
||
pVolumeGuid[1] = L'\\';
|
||
wcscat(pVolumeGuid, L"\\");
|
||
|
||
memset(fsName, 0L, 20*sizeof(WCHAR));
|
||
result = GetVolumeInformationW(pVolumeGuid, NULL, 0L,
|
||
NULL, NULL, NULL, fsName, 20);
|
||
|
||
if (result) {
|
||
if (!wcscmp(fsName, L"NTFS")) {
|
||
*pFileSystemType = PARTITION_IFS;
|
||
}
|
||
else if (!wcscmp(fsName, L"FAT32")) {
|
||
*pFileSystemType = PARTITION_FAT32;
|
||
}
|
||
else if (!wcscmp(fsName, L"FAT")) {
|
||
*pFileSystemType = PARTITION_HUGE;
|
||
}
|
||
else {
|
||
*pFileSystemType = 0;
|
||
}
|
||
}
|
||
else {
|
||
GetLastError(); // debug
|
||
}
|
||
|
||
result = GetDiskFreeSpace(pVolumeGuid,
|
||
&dwSectorsPerCluster,
|
||
&dwBytesPerSector,
|
||
&dwNumFreeClusters,
|
||
&dwTotalNumClusters
|
||
);
|
||
if (result) {
|
||
*pClusterSize = dwSectorsPerCluster * dwBytesPerSector;
|
||
}
|
||
else {
|
||
GetLastError(); // debug
|
||
}
|
||
|
||
//
|
||
// Convert the guid back to NT namespace, by changing \\?\
|
||
// to \??\ and removing the trailing slash.
|
||
//
|
||
pVolumeGuid[1] = L'?';
|
||
pVolumeGuid[wcslen(pVolumeGuid)-1] = L'\0';
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Free up locally allocated data
|
||
//
|
||
_AsrpHeapFree(mountPointsOut);
|
||
|
||
//
|
||
// If we hit errors, make sure the VolumeGuid is a blank string.
|
||
//
|
||
if (status != ERROR_SUCCESS) {
|
||
wcscpy(pVolumeGuid, L"");
|
||
}
|
||
|
||
return (BOOL) (status == ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpDetermineBuses(
|
||
IN PASR_DISK_INFO pDiskList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This attempts to group the disks based on which bus they are on. For
|
||
SCSI disks, this is relatively easy, since it can be based on the
|
||
location info (port).
|
||
|
||
For other disks, we attempt to get the PnP parent node of the disks, and
|
||
group all disks having the same parent.
|
||
|
||
The groups are identified by the SifBusKey field of each disk structure--
|
||
i.e., all disks that have SifBusKey == 1 are on one bus, SifBusKey == 2
|
||
are on another bus, and so on. The SifBusKey values are guaranteed to be
|
||
sequential, and not have any holes (i.e., For a system with "n" buses,
|
||
the SifBusKey values will be 1,2,3,...,n).
|
||
|
||
At the end SifBusKey is zero for disks which couldn't be grouped.
|
||
|
||
Arguments:
|
||
|
||
pDiskList - The ASR_DISK_INFO list of disks present on the current system.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL done = FALSE,
|
||
newPass = TRUE;
|
||
|
||
ULONG port = 0,
|
||
sifBusKey = 0;
|
||
|
||
DEVINST parent;
|
||
|
||
STORAGE_BUS_TYPE busType = BusTypeUnknown;
|
||
PASR_DISK_INFO pCurrentDisk = pDiskList;
|
||
|
||
//
|
||
// The first pass goes through and groups all the scsi disks together.
|
||
// Note that this works for IDE too, since IDE disks respond to the
|
||
// IOCTL_SCSI_GET_ADDRESS and appear to us to have valid location info.
|
||
//
|
||
do {
|
||
|
||
sifBusKey++;
|
||
pCurrentDisk = pDiskList;
|
||
done = TRUE;
|
||
newPass = TRUE;
|
||
|
||
while (pCurrentDisk) {
|
||
|
||
if ((BusTypeUnknown == pCurrentDisk->BusType) ||
|
||
(!pCurrentDisk->pScsiAddress)) {
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
if (0 == pCurrentDisk->SifBusKey) {
|
||
|
||
done = FALSE;
|
||
|
||
if (newPass) {
|
||
pCurrentDisk->SifBusKey = sifBusKey;
|
||
port = pCurrentDisk->pScsiAddress->PortNumber;
|
||
busType = pCurrentDisk->BusType;
|
||
newPass = FALSE;
|
||
}
|
||
else {
|
||
if ((pCurrentDisk->pScsiAddress->PortNumber == port) &&
|
||
(pCurrentDisk->BusType == busType)) {
|
||
pCurrentDisk->SifBusKey = sifBusKey;
|
||
}
|
||
}
|
||
}
|
||
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
} while (!done);
|
||
|
||
//
|
||
// By now, the only disks with SifBusKey is 0 are disks for which
|
||
// pScsiAddress is NULL, ie (most-likely) non SCSI/IDE disks. Attempt
|
||
// to group them on the basis of their parent dev node (which is usually
|
||
// the bus). We may have to loop through multiple times again.
|
||
//
|
||
--sifBusKey; // compensate for the last pass above
|
||
do {
|
||
sifBusKey++;
|
||
pCurrentDisk = pDiskList;
|
||
done = TRUE;
|
||
newPass = TRUE;
|
||
|
||
while (pCurrentDisk) {
|
||
|
||
if ((BusTypeUnknown == pCurrentDisk->BusType) ||
|
||
(!pCurrentDisk->pScsiAddress)) {
|
||
|
||
if ((0 == pCurrentDisk->SifBusKey)
|
||
&& (pCurrentDisk->ParentDevInst)) {
|
||
|
||
done = FALSE;
|
||
|
||
if (newPass) {
|
||
pCurrentDisk->SifBusKey = sifBusKey;
|
||
parent = pCurrentDisk->ParentDevInst;
|
||
newPass = FALSE;
|
||
}
|
||
else {
|
||
if (pCurrentDisk->ParentDevInst == parent) {
|
||
pCurrentDisk->SifBusKey = sifBusKey;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
|
||
} while (!done);
|
||
|
||
//
|
||
// Disks that still have SifBusKey = 0 couldn't be grouped. Either the
|
||
// BusType is unknown, or the parent node couldn't be found.
|
||
//
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpGetDiskLayout(
|
||
IN CONST HANDLE hDisk,
|
||
IN CONST PASR_SYSTEM_INFO pSystemInfo,
|
||
OUT PASR_DISK_INFO pCurrentDisk,
|
||
IN BOOL AllDetails
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Fills in the fields of the pCurrentDisk structure with the relevant
|
||
information about the disk represented by hDisk, by querying the system
|
||
with the appropriate IOCTL's.
|
||
|
||
Arguments:
|
||
|
||
hDisk - handle to the disk of interest.
|
||
|
||
pSystemInfo - The SYSTEM_INFO structure for the current system.
|
||
|
||
pCurrentDisk - Receives the information about the disk represented by
|
||
hDisk
|
||
|
||
AllDetails - If FALSE, only the pDriveLayout information of pCurrentDisk
|
||
is filled in. This is an optimisation that comes in handy when
|
||
we're dealing with disks on a shared cluster bus.
|
||
|
||
If TRUE, all the fields of pCurrentDisk are filled in.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
{
|
||
DWORD index = 0,
|
||
status = ERROR_SUCCESS;
|
||
|
||
DWORD dwBytesReturned = 0L,
|
||
bufferLength = 0L;
|
||
|
||
BOOL result = FALSE;
|
||
|
||
PDISK_GEOMETRY diskGeometry = NULL;
|
||
DWORD sizeDiskGeometry = 0L;
|
||
|
||
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutEx = NULL;
|
||
DWORD sizeDriveLayoutEx = 0L;
|
||
|
||
STORAGE_DEVICE_NUMBER deviceNumber;
|
||
DWORD sizeDeviceNumber = 0L;
|
||
|
||
PPARTITION_INFORMATION_EX partition0Ex = NULL;
|
||
DWORD sizePartition0Ex = 0L;
|
||
|
||
PASR_PTN_INFO pPartitionTable = NULL;
|
||
DWORD sizePartitionTable = 0L;
|
||
|
||
STORAGE_PROPERTY_QUERY propertyQuery;
|
||
STORAGE_DEVICE_DESCRIPTOR *deviceDesc = NULL;
|
||
STORAGE_BUS_TYPE busType = BusTypeUnknown;
|
||
|
||
PSCSI_ADDRESS scsiAddress = NULL;
|
||
|
||
HANDLE heapHandle = GetProcessHeap(); // For memory allocations
|
||
MYASSERT(heapHandle); // It better not be NULL
|
||
|
||
MYASSERT(pCurrentDisk);
|
||
MYASSERT((hDisk) && (INVALID_HANDLE_VALUE != hDisk));
|
||
|
||
//
|
||
// Initialize OUT variables to known values
|
||
//
|
||
pCurrentDisk->Style = PARTITION_STYLE_RAW;
|
||
|
||
pCurrentDisk->pDriveLayoutEx = NULL;
|
||
pCurrentDisk->sizeDriveLayoutEx = 0L;
|
||
|
||
pCurrentDisk->pDiskGeometry = NULL;
|
||
pCurrentDisk->sizeDiskGeometry = 0L;
|
||
|
||
pCurrentDisk->pPartition0Ex = NULL;
|
||
pCurrentDisk->sizePartition0Ex = 0L;
|
||
|
||
pCurrentDisk->pScsiAddress = NULL;
|
||
pCurrentDisk->BusType = BusTypeUnknown;
|
||
|
||
pCurrentDisk->SifBusKey = 0L;
|
||
|
||
SetLastError(ERROR_SUCCESS);
|
||
|
||
//
|
||
// Get the device number for this device. This should succeed even if
|
||
// this is a clustered disk that this node doesn't own.
|
||
//
|
||
result = DeviceIoControl(
|
||
hDisk,
|
||
IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
||
NULL,
|
||
0,
|
||
&deviceNumber,
|
||
sizeof(STORAGE_DEVICE_NUMBER),
|
||
&sizeDeviceNumber,
|
||
NULL
|
||
);
|
||
_AsrpErrExitCode(!result, status, GetLastError());
|
||
|
||
pCurrentDisk->DeviceNumber = deviceNumber.DeviceNumber;
|
||
|
||
//
|
||
// The output buffer for IOCTL_DISK_GET_DRIVE_LAYOUT_EX consists of a
|
||
// DRIVE_LAYOUT_INFORMATION_EX structure as a header, followed by an
|
||
// array of PARTITION_INFORMATION_EX structures.
|
||
//
|
||
// We initially allocate enough space for the DRIVE_LAYOUT_INFORMATION_EX
|
||
// struct, which contains a single PARTITION_INFORMATION_EX struct, and
|
||
// 3 more PARTITION_INFORMATION_EX structs, since each (MBR) disk will
|
||
// have a minimum of four partitions, even if they are not all in use.
|
||
// If the disk contains more than four partitions, we'll increase the
|
||
// buffer size as needed
|
||
//
|
||
bufferLength = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
|
||
(sizeof(PARTITION_INFORMATION_EX) * 3);
|
||
|
||
driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
bufferLength
|
||
);
|
||
_AsrpErrExitCode(!driveLayoutEx, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
result = FALSE;
|
||
while (!result) {
|
||
|
||
result = DeviceIoControl(
|
||
hDisk,
|
||
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
||
NULL,
|
||
0L,
|
||
driveLayoutEx,
|
||
bufferLength,
|
||
&sizeDriveLayoutEx,
|
||
NULL
|
||
);
|
||
|
||
if (!result) {
|
||
status = GetLastError();
|
||
_AsrpHeapFree(driveLayoutEx);
|
||
|
||
//
|
||
// If the buffer is of insufficient size, resize the buffer.
|
||
// Note that get-drive-layout-ex could return error-insufficient-
|
||
// buffer (instead of? in addition to? error-more-data)
|
||
//
|
||
if ((ERROR_MORE_DATA == status) ||
|
||
(ERROR_INSUFFICIENT_BUFFER == status)
|
||
) {
|
||
status = ERROR_SUCCESS;
|
||
bufferLength += sizeof(PARTITION_INFORMATION_EX) * 4;
|
||
|
||
driveLayoutEx = (PDRIVE_LAYOUT_INFORMATION_EX) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
bufferLength
|
||
);
|
||
_AsrpErrExitCode(!driveLayoutEx,
|
||
status,
|
||
ERROR_NOT_ENOUGH_MEMORY
|
||
);
|
||
}
|
||
else {
|
||
//
|
||
// some other error occurred, EXIT, and go to the next drive.
|
||
//
|
||
result = TRUE;
|
||
status = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
else {
|
||
|
||
if (!AllDetails) {
|
||
//
|
||
// If we don't want all the details for this disk, just exit
|
||
// now. This is used in the case of clusters, where we don't
|
||
// want to get all the details twice even if the current node
|
||
// owns the disk
|
||
//
|
||
pCurrentDisk->pDriveLayoutEx = driveLayoutEx;
|
||
pCurrentDisk->sizeDriveLayoutEx = sizeDriveLayoutEx;
|
||
|
||
//
|
||
// Jump to EXIT
|
||
//
|
||
_AsrpErrExitCode(TRUE, status, ERROR_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// The disk geometry: so that we can match the bytes-per-sector
|
||
// value during restore.
|
||
//
|
||
diskGeometry = (PDISK_GEOMETRY) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof(DISK_GEOMETRY)
|
||
);
|
||
_AsrpErrExitCode(!diskGeometry, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
result = DeviceIoControl(
|
||
hDisk,
|
||
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
||
NULL,
|
||
0,
|
||
diskGeometry,
|
||
sizeof(DISK_GEOMETRY),
|
||
&sizeDiskGeometry,
|
||
NULL
|
||
);
|
||
_AsrpErrExitCode(!result, status, ERROR_READ_FAULT);
|
||
|
||
|
||
partition0Ex = (PPARTITION_INFORMATION_EX) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof(PARTITION_INFORMATION_EX)
|
||
);
|
||
_AsrpErrExitCode(!partition0Ex, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Information about partition 0 (the whole disk), to get the true
|
||
// sector count of the disk
|
||
//
|
||
result = DeviceIoControl(
|
||
hDisk,
|
||
IOCTL_DISK_GET_PARTITION_INFO_EX,
|
||
NULL,
|
||
0,
|
||
partition0Ex,
|
||
sizeof(PARTITION_INFORMATION_EX),
|
||
&sizePartition0Ex,
|
||
NULL
|
||
);
|
||
_AsrpErrExitCode(!result, status, ERROR_READ_FAULT);
|
||
|
||
//
|
||
// Figure out the bus that this disk is on. This will only be
|
||
// used to group the disks--all the disks on a bus will be
|
||
// restored to the same bus if possible
|
||
//
|
||
propertyQuery.QueryType = PropertyStandardQuery;
|
||
propertyQuery.PropertyId = StorageDeviceProperty;
|
||
|
||
deviceDesc = (STORAGE_DEVICE_DESCRIPTOR *) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
ASR_BUFFER_SIZE
|
||
);
|
||
_AsrpErrExitCode(!deviceDesc, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
result = DeviceIoControl(
|
||
hDisk,
|
||
IOCTL_STORAGE_QUERY_PROPERTY,
|
||
&propertyQuery,
|
||
sizeof(STORAGE_PROPERTY_QUERY),
|
||
deviceDesc,
|
||
ASR_BUFFER_SIZE,
|
||
&dwBytesReturned,
|
||
NULL
|
||
);
|
||
if (result) {
|
||
busType = deviceDesc->BusType;
|
||
}
|
||
_AsrpHeapFree(deviceDesc);
|
||
|
||
scsiAddress = (PSCSI_ADDRESS) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof(SCSI_ADDRESS)
|
||
);
|
||
_AsrpErrExitCode(!scsiAddress, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
result = DeviceIoControl(
|
||
hDisk,
|
||
IOCTL_SCSI_GET_ADDRESS,
|
||
NULL,
|
||
0,
|
||
scsiAddress,
|
||
sizeof(SCSI_ADDRESS),
|
||
&dwBytesReturned,
|
||
NULL
|
||
);
|
||
if (!result) { // Not fatal--expected for non SCSI/IDE disks
|
||
_AsrpHeapFree(scsiAddress);
|
||
result = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (driveLayoutEx) {
|
||
PPARTITION_INFORMATION_EX currentPartitionEx = NULL;
|
||
WCHAR devicePath[MAX_PATH + 1];
|
||
|
||
pCurrentDisk->Style = driveLayoutEx->PartitionStyle;
|
||
|
||
sizePartitionTable = sizeof(ASR_PTN_INFO) *
|
||
(driveLayoutEx->PartitionCount);
|
||
|
||
pPartitionTable = (PASR_PTN_INFO) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizePartitionTable
|
||
);
|
||
_AsrpErrExitCode(!pPartitionTable, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
for (index = 0; index < driveLayoutEx->PartitionCount; index++) {
|
||
|
||
currentPartitionEx = &driveLayoutEx->PartitionEntry[index];
|
||
pPartitionTable[index].SlotIndex = index;
|
||
|
||
if (currentPartitionEx->PartitionNumber) {
|
||
swprintf(devicePath,
|
||
ASR_WSZ_DEVICE_PATH_FORMAT,
|
||
deviceNumber.DeviceNumber,
|
||
currentPartitionEx->PartitionNumber
|
||
);
|
||
|
||
pPartitionTable[index].PartitionFlags = 0;
|
||
|
||
//
|
||
// Check specially for the EFI system partition
|
||
//
|
||
if ((PARTITION_STYLE_GPT == driveLayoutEx->PartitionStyle) &&
|
||
IsEqualGUID(&(currentPartitionEx->Gpt.PartitionType), &(PARTITION_SYSTEM_GUID))
|
||
) {
|
||
|
||
pPartitionTable[index].PartitionFlags |= ASR_FLAGS_SYSTEM_PTN;
|
||
}
|
||
|
||
AsrpGetMorePartitionInfo(
|
||
devicePath,
|
||
(wcslen(devicePath)+1) * sizeof(WCHAR), // cb including \0
|
||
pSystemInfo,
|
||
pPartitionTable[index].szVolumeGuid,
|
||
&(pPartitionTable[index].PartitionFlags),
|
||
&(pPartitionTable[index].FileSystemType),
|
||
&(pPartitionTable[index].ClusterSize)
|
||
);
|
||
|
||
//
|
||
// Make sure that the file-system type for the EFI system
|
||
// partition is set to FAT
|
||
//
|
||
if ((PARTITION_STYLE_GPT == driveLayoutEx->PartitionStyle) &&
|
||
IsEqualGUID(&(currentPartitionEx->Gpt.PartitionType), &(PARTITION_SYSTEM_GUID))
|
||
) {
|
||
|
||
pPartitionTable[index].FileSystemType = PARTITION_HUGE;
|
||
}
|
||
|
||
if (pPartitionTable[index].PartitionFlags) {
|
||
pCurrentDisk->IsCritical = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
pCurrentDisk->pDriveLayoutEx = driveLayoutEx;
|
||
pCurrentDisk->sizeDriveLayoutEx = sizeDriveLayoutEx;
|
||
|
||
pCurrentDisk->pDiskGeometry = diskGeometry;
|
||
pCurrentDisk->sizeDiskGeometry = sizeDiskGeometry;
|
||
|
||
pCurrentDisk->DeviceNumber = deviceNumber.DeviceNumber;
|
||
|
||
pCurrentDisk->pPartition0Ex = partition0Ex;
|
||
pCurrentDisk->sizePartition0Ex = sizePartition0Ex;
|
||
|
||
pCurrentDisk->pScsiAddress = scsiAddress;
|
||
pCurrentDisk->BusType = busType;
|
||
|
||
pCurrentDisk->PartitionInfoTable = pPartitionTable;
|
||
pCurrentDisk->sizePartitionInfoTable = sizePartitionTable;
|
||
|
||
EXIT:
|
||
//
|
||
// Free up locally allocated memory on failure
|
||
//
|
||
if (status != ERROR_SUCCESS) {
|
||
_AsrpHeapFree(driveLayoutEx);
|
||
_AsrpHeapFree(diskGeometry);
|
||
_AsrpHeapFree(partition0Ex);
|
||
_AsrpHeapFree(scsiAddress);
|
||
_AsrpHeapFree(pPartitionTable);
|
||
}
|
||
|
||
//
|
||
// Make sure the last error is set if we are going to return FALSE
|
||
//
|
||
if ((ERROR_SUCCESS != status) && (ERROR_SUCCESS == GetLastError())) {
|
||
SetLastError(status);
|
||
}
|
||
|
||
return (BOOL) (status == ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpGetSystemPath(
|
||
IN PASR_SYSTEM_INFO pSystemInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Gets the system partition DevicePath, and fills in the SystemPath field
|
||
of the ASR_SYSTEM_INFO struct, by looking up the HKLM\Setup registry key.
|
||
This key is updated at every boot with the path to the current system
|
||
device. The path is of the form
|
||
\Device\Harddisk0\Partition1 (basic disks)
|
||
\Device\HarddiskDmVolumes\DgName\Volume1 (dynamic disks)
|
||
|
||
Arguments:
|
||
|
||
pSystemInfo - The SystemPath field of this struct will be filled with
|
||
a pointer to the path to the current system device
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
pSystemInfo->SystemPath is a pointer to a null-terminated string
|
||
containing the path to the current system device. The caller is
|
||
reponsible for freeing this memory with a call to
|
||
HeapFree(GetProcessHeap(),...).
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError(). pSystemInfo->SystemPath is set
|
||
to NULL.
|
||
|
||
--*/
|
||
|
||
{
|
||
HKEY regKey = NULL;
|
||
DWORD type = 0L;
|
||
|
||
HANDLE heapHandle = NULL;
|
||
DWORD status = ERROR_SUCCESS;
|
||
|
||
PWSTR systemPartition = NULL;
|
||
DWORD cbSystemPartition = 0L;
|
||
|
||
heapHandle = GetProcessHeap();
|
||
MYASSERT(heapHandle);
|
||
|
||
MYASSERT(pSystemInfo);
|
||
if (!pSystemInfo) {
|
||
SetLastError(ERROR_BAD_ENVIRONMENT);
|
||
return FALSE;
|
||
}
|
||
|
||
pSystemInfo->SystemPath = NULL;
|
||
|
||
//
|
||
// Open the reg key to find the system partition
|
||
//
|
||
status = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE, // hKey
|
||
ASR_REGKEY_SETUP, // lpSubKey
|
||
0, // ulOptions--Reserved, must be 0
|
||
MAXIMUM_ALLOWED, // samDesired
|
||
®Key // phkResult
|
||
);
|
||
_AsrpErrExitCode(status, status, ERROR_REGISTRY_IO_FAILED);
|
||
|
||
//
|
||
// Allocate a reasonably sized buffer for the system partition, to
|
||
// start off with. If this isn't big enough, we'll re-allocate as
|
||
// needed.
|
||
//
|
||
cbSystemPartition = (MAX_PATH + 1) * sizeof(WCHAR);
|
||
systemPartition = HeapAlloc(heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
cbSystemPartition
|
||
);
|
||
|
||
_AsrpErrExitCode((!systemPartition), status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Get the system partition device Name. This is of the form
|
||
// \Device\Harddisk0\Partition1 (basic disks)
|
||
// \Device\HarddiskDmVolumes\DgName\Volume1 (dynamic disks)
|
||
//
|
||
status = RegQueryValueExW(
|
||
regKey,
|
||
ASR_REGVALUE_SYSTEM_PARTITION,
|
||
NULL,
|
||
&type,
|
||
(LPBYTE)systemPartition,
|
||
&cbSystemPartition // \0 is included
|
||
);
|
||
_AsrpErrExitCode((type != REG_SZ), status, ERROR_REGISTRY_IO_FAILED);
|
||
|
||
while (ERROR_MORE_DATA == status) {
|
||
//
|
||
// Our buffer wasn't big enough, cbSystemPartition contains
|
||
// the required size.
|
||
//
|
||
_AsrpHeapFree(systemPartition);
|
||
systemPartition = HeapAlloc(heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
cbSystemPartition
|
||
);
|
||
_AsrpErrExitCode((!systemPartition), status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
status = RegQueryValueExW(
|
||
regKey,
|
||
ASR_REGVALUE_SYSTEM_PARTITION,
|
||
NULL,
|
||
&type,
|
||
(LPBYTE)systemPartition,
|
||
&cbSystemPartition // \0 is included
|
||
);
|
||
}
|
||
|
||
EXIT:
|
||
if (regKey) {
|
||
RegCloseKey(regKey);
|
||
regKey = NULL;
|
||
}
|
||
|
||
if (ERROR_SUCCESS != status) {
|
||
_AsrpHeapFree(systemPartition);
|
||
return FALSE;
|
||
}
|
||
else {
|
||
pSystemInfo->SystemPath = systemPartition;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpInitSystemInformation(
|
||
IN OUT PASR_SYSTEM_INFO pSystemInfo,
|
||
IN CONST BOOL bEnableAutoExtend
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialisation routine to allocate memory for various fields in the
|
||
ASR_SYSTEM_INFO structure, and fill them in with the relevant information.
|
||
|
||
Arguments:
|
||
pSystemInfo - The struct to be filled in with info about the current
|
||
system.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value. The caller
|
||
is responsible for freeing memory pointed to by the various
|
||
pointers in the struct, using HeapFree(GetProcessHeap(),...).
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError(). The caller is still responsible
|
||
for checking the fields and releasing any non-NULL pointers using
|
||
HeapFree(GetProcessHeap(),...).
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD cchBootDirectory = 0L,
|
||
reqdSize = 0L;
|
||
|
||
BOOL result = FALSE;
|
||
|
||
HANDLE heapHandle = GetProcessHeap();
|
||
|
||
//
|
||
// Initialise the structure to zeroes
|
||
//
|
||
memset(pSystemInfo, 0L, sizeof (ASR_SYSTEM_INFO));
|
||
|
||
//
|
||
// The auto-extension feature
|
||
//
|
||
pSystemInfo->AutoExtendEnabled = bEnableAutoExtend;
|
||
|
||
//
|
||
// Get the machine name
|
||
//
|
||
pSystemInfo->sizeComputerName = MAX_COMPUTERNAME_LENGTH + 1;
|
||
if (!GetComputerNameW(pSystemInfo->ComputerName,
|
||
&(pSystemInfo->sizeComputerName)
|
||
)) {
|
||
//
|
||
// GetComputerName sets LastError
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Get the Processor Architecture. We expect the process architecture
|
||
// to be a either x86, amd64, or ia64, so if it doesn't fit in our buffer of
|
||
// six characters, we don't support it anyway.
|
||
//
|
||
pSystemInfo->Platform = HeapAlloc(heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
6*sizeof(WCHAR)
|
||
);
|
||
|
||
if (!pSystemInfo->Platform) {
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
reqdSize = GetEnvironmentVariableW(L"PROCESSOR_ARCHITECTURE",
|
||
pSystemInfo->Platform,
|
||
6
|
||
);
|
||
|
||
if (0 == reqdSize) {
|
||
//
|
||
// We couldn't find the PROCESSOR_ARCHITECTURE variable
|
||
//
|
||
SetLastError(ERROR_BAD_ENVIRONMENT);
|
||
return FALSE;
|
||
}
|
||
|
||
if (reqdSize > 6) {
|
||
//
|
||
// The architecture didn't fit in our buffer
|
||
//
|
||
SetLastError(ERROR_NOT_SUPPORTED);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Get the OS version
|
||
//
|
||
pSystemInfo->OsVersionEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||
result = GetVersionEx((LPOSVERSIONINFO) (&(pSystemInfo->OsVersionEx)));
|
||
if (!result) {
|
||
//
|
||
// GetVersionEx sets the LastError
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Get the boot directory
|
||
//
|
||
pSystemInfo->BootDirectory = HeapAlloc(heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
(MAX_PATH+1)*sizeof(WCHAR)
|
||
);
|
||
|
||
if (!(pSystemInfo->BootDirectory)) {
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
cchBootDirectory = GetWindowsDirectoryW(pSystemInfo->BootDirectory,
|
||
MAX_PATH + 1
|
||
);
|
||
if (0 == cchBootDirectory) {
|
||
//
|
||
// GetWindowsDirectory sets LastError
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
if (cchBootDirectory >
|
||
ASR_SIF_ENTRY_MAX_CHARS - MAX_COMPUTERNAME_LENGTH - 26) {
|
||
//
|
||
// We can't write out sif lines that are more than
|
||
// ASR_SIF_ENTRY_MAX_CHARS chars long
|
||
//
|
||
SetLastError(ERROR_BAD_ENVIRONMENT);
|
||
return FALSE;
|
||
}
|
||
|
||
if (cchBootDirectory > MAX_PATH) {
|
||
UINT cchNewSize = cchBootDirectory + 1;
|
||
//
|
||
// Our buffer wasn't big enough, free and re-alloc as needed
|
||
//
|
||
_AsrpHeapFree(pSystemInfo->BootDirectory);
|
||
pSystemInfo->BootDirectory = HeapAlloc(heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
(cchNewSize + 1) * sizeof(WCHAR)
|
||
);
|
||
if (!(pSystemInfo->BootDirectory)) {
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
cchBootDirectory = GetWindowsDirectoryW(pSystemInfo->BootDirectory,
|
||
MAX_PATH + 1
|
||
);
|
||
if (!cchBootDirectory) {
|
||
//
|
||
// GetWindowsDirectory sets LastError
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
if (cchBootDirectory > cchNewSize) {
|
||
SetLastError(ERROR_BAD_ENVIRONMENT);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the system directory
|
||
//
|
||
if (!AsrpGetSystemPath(pSystemInfo)) {
|
||
//
|
||
// AsrpGetSystemPath sets LastError
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Get the time-zone information. We need to save and restore this since
|
||
// GUI-mode Setup (ASR) will otherwise default to GMT, and the file-time
|
||
// stamps on all the restored files will be off, since most back-up apps
|
||
// assume that they're restoring in the same time-zone that they backed
|
||
// up in and do nothing special to restore the time-zone first.
|
||
//
|
||
GetTimeZoneInformation(&(pSystemInfo->TimeZoneInformation));
|
||
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpInitLayoutInformation(
|
||
IN CONST PASR_SYSTEM_INFO pSystemInfo,
|
||
IN OUT PASR_DISK_INFO pDiskList,
|
||
OUT PULONG MaxDeviceNumber OPTIONAL,
|
||
IN BOOL AllDetails
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialisation routine to fill in layout and other interesting information
|
||
about the disks on the system.
|
||
|
||
Arguments:
|
||
|
||
pSystemInfo - the ASR_SYSTEM_INFO for the current system
|
||
|
||
pDiskList - ASR_DISK_INFO list of disks on the current system, with
|
||
the DevicePath field for each disk pointing to a null terminated
|
||
path to the disk, that can be used to open a handle to the disk.
|
||
|
||
The other fields of the structure are filled in by this routine,
|
||
if the disk could be accessed and the appropriate info could be
|
||
obtained.
|
||
|
||
MaxDeviceNumber - Receives the max device number of all the disks on the
|
||
system. This can be used as an optimisation to allocate memory
|
||
for a table of disks based on the device number.
|
||
|
||
This is an optional argument.
|
||
|
||
AllDetails - If FALSE, only the pDriveLayout information is filled in for
|
||
each disk. This is an optimisation that comes in handy when
|
||
we're dealing with disks on a shared cluster bus.
|
||
|
||
If TRUE, all the fields are filled in for each disk.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL result = FALSE;
|
||
HANDLE hDisk = NULL;
|
||
PASR_DISK_INFO currentDisk = pDiskList;
|
||
|
||
if (ARGUMENT_PRESENT(MaxDeviceNumber)) {
|
||
*MaxDeviceNumber = 0;
|
||
}
|
||
|
||
while (currentDisk) {
|
||
//
|
||
// Open the disk. If an error occurs, get the next
|
||
// disk from the disk list and continue.
|
||
//
|
||
hDisk = CreateFileW(
|
||
currentDisk->DevicePath, // lpFileName
|
||
0, // dwDesiredAccess
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
|
||
NULL, // lpSecurityAttributes
|
||
OPEN_EXISTING, // dwCreationFlags
|
||
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
|
||
NULL // hTemplateFile
|
||
);
|
||
|
||
if ((!hDisk) || (INVALID_HANDLE_VALUE == hDisk)) {
|
||
//
|
||
// We couldn't open the disk. If this is a critical disk, we'll
|
||
// fail later in AsrpMarkCriticalDisks, so it's okay to ignore
|
||
// this error for now.
|
||
//
|
||
currentDisk = currentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Get the layout and other interesting info for this disk.
|
||
// If this fails, we must abort.
|
||
//
|
||
result = AsrpGetDiskLayout(hDisk,
|
||
pSystemInfo,
|
||
currentDisk,
|
||
AllDetails
|
||
);
|
||
if (!result) {
|
||
DWORD status = GetLastError();
|
||
_AsrpCloseHandle(hDisk); // this may change LastError.
|
||
SetLastError(status);
|
||
return FALSE;
|
||
}
|
||
|
||
_AsrpCloseHandle(hDisk);
|
||
|
||
//
|
||
// Set the max device number if needed
|
||
//
|
||
if (ARGUMENT_PRESENT(MaxDeviceNumber) &&
|
||
(currentDisk->DeviceNumber > *MaxDeviceNumber)
|
||
) {
|
||
*MaxDeviceNumber = currentDisk->DeviceNumber;
|
||
}
|
||
|
||
//
|
||
// Get the next drive from the drive list.
|
||
//
|
||
currentDisk = currentDisk->pNext;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpInitDiskInformation(
|
||
OUT PASR_DISK_INFO *ppDiskList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialisation routine to get a list of disks present on the system. This
|
||
routine allocates a ASR_DISK_INFO struct for each disk on the machine, and
|
||
fills in the DevicePath and ParentDevInst fields of each with a path to
|
||
the disk. It is expected that the other fields will be filled in with a
|
||
subsequent call to AsrpInitLayoutInformation().
|
||
|
||
Arguments:
|
||
ppDiskList - Receives the location of the first ASR_DISK_INFO struct.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError(). ppDiskList may point to an
|
||
incomplete list of disks on the system, and it is the caller's
|
||
responsibility to free the memory allocated, if any, using
|
||
HeapFree(GetProcessHeap(),...).
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD count = 0,
|
||
status = ERROR_SUCCESS;
|
||
|
||
HDEVINFO hdevInfo = NULL;
|
||
|
||
BOOL result = FALSE;
|
||
|
||
PASR_DISK_INFO pNewDisk = NULL;
|
||
|
||
HANDLE heapHandle = NULL;
|
||
|
||
PSP_DEVICE_INTERFACE_DETAIL_DATA_W pDiDetail = NULL;
|
||
|
||
SP_DEVICE_INTERFACE_DATA devInterfaceData;
|
||
|
||
DWORD sizeDiDetail = 0;
|
||
|
||
SP_DEVINFO_DATA devInfoData;
|
||
|
||
//
|
||
// Initialise stuff to zeros
|
||
//
|
||
memset(&devInterfaceData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
|
||
*ppDiskList = NULL;
|
||
|
||
heapHandle = GetProcessHeap(); // used for HeapAlloc functions
|
||
MYASSERT(heapHandle);
|
||
|
||
//
|
||
// Get a device interface set which includes all Disk devices
|
||
// present on the machine. DiskClassGuid is a predefined GUID that
|
||
// will return all disk-type device interfaces
|
||
//
|
||
hdevInfo = SetupDiGetClassDevsW(
|
||
&DiskClassGuid,
|
||
NULL,
|
||
NULL,
|
||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
|
||
);
|
||
_AsrpErrExitCode(
|
||
((NULL == hdevInfo) || (INVALID_HANDLE_VALUE == hdevInfo)),
|
||
status,
|
||
ERROR_IO_DEVICE
|
||
);
|
||
|
||
//
|
||
// Iterate over all devices interfaces in the set
|
||
//
|
||
for (count = 0; ; count++) {
|
||
|
||
// must set size first
|
||
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||
|
||
//
|
||
// Retrieve the device interface data for each device interface
|
||
//
|
||
result = SetupDiEnumDeviceInterfaces(
|
||
hdevInfo,
|
||
NULL,
|
||
&DiskClassGuid,
|
||
count,
|
||
&devInterfaceData
|
||
);
|
||
|
||
if (!result) {
|
||
//
|
||
// If we retrieved the last item, break
|
||
//
|
||
status = GetLastError();
|
||
|
||
if (ERROR_NO_MORE_ITEMS == status) {
|
||
status = ERROR_SUCCESS;
|
||
break;
|
||
}
|
||
else {
|
||
//
|
||
// Some other error occured, goto EXIT. We overwrite the
|
||
// last error.
|
||
//
|
||
_AsrpErrExitCode(status, status, ERROR_IO_DEVICE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Get the required buffer-size for the device path
|
||
//
|
||
result = SetupDiGetDeviceInterfaceDetailW(
|
||
hdevInfo,
|
||
&devInterfaceData,
|
||
NULL,
|
||
0,
|
||
&sizeDiDetail,
|
||
NULL
|
||
);
|
||
|
||
if (!result) {
|
||
|
||
status = GetLastError();
|
||
//
|
||
// If a value other than "insufficient buffer" is returned,
|
||
// an error occured
|
||
//
|
||
_AsrpErrExitCode((ERROR_INSUFFICIENT_BUFFER != status),
|
||
status,
|
||
ERROR_IO_DEVICE
|
||
);
|
||
}
|
||
else {
|
||
//
|
||
// The call should have failed since we're getting the
|
||
// required buffer size. If it doesn't, and error occurred.
|
||
//
|
||
_AsrpErrExitCode(status, status, ERROR_IO_DEVICE);
|
||
}
|
||
|
||
//
|
||
// Allocate memory for the buffer
|
||
//
|
||
pDiDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeDiDetail
|
||
);
|
||
_AsrpErrExitCode(!pDiDetail, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
// must set the struct's size member
|
||
pDiDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
||
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||
|
||
//
|
||
// Finally, retrieve the device interface detail info
|
||
//
|
||
result = SetupDiGetDeviceInterfaceDetailW(
|
||
hdevInfo,
|
||
&devInterfaceData,
|
||
pDiDetail,
|
||
sizeDiDetail,
|
||
NULL,
|
||
&devInfoData
|
||
);
|
||
_AsrpErrExitCode(!result, status, GetLastError());
|
||
|
||
//
|
||
// Okay, now alloc a struct for this disk, and fill in the DevicePath
|
||
// field with the Path from the interface detail.
|
||
//
|
||
pNewDisk = (PASR_DISK_INFO) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof(ASR_DISK_INFO)
|
||
);
|
||
_AsrpErrExitCode(!pNewDisk, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Insert at the head so this is O(1) and not O(n!)
|
||
//
|
||
pNewDisk->pNext = *ppDiskList;
|
||
*ppDiskList = pNewDisk;
|
||
|
||
pNewDisk->DevicePath = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof(WCHAR) * (wcslen(pDiDetail->DevicePath) + 1)
|
||
);
|
||
_AsrpErrExitCode(!(pNewDisk->DevicePath),
|
||
status,
|
||
ERROR_NOT_ENOUGH_MEMORY
|
||
);
|
||
wcscpy(pNewDisk->DevicePath, pDiDetail->DevicePath);
|
||
|
||
//
|
||
// Get the PnP parent of this disk, so we can use it for grouping
|
||
// disks later based on the bus they are on.
|
||
//
|
||
CM_Get_Parent(&(pNewDisk->ParentDevInst),
|
||
devInfoData.DevInst,
|
||
0
|
||
);
|
||
|
||
_AsrpHeapFree(pDiDetail);
|
||
}
|
||
|
||
EXIT:
|
||
//
|
||
// Free local mem allocs
|
||
//
|
||
_AsrpHeapFree(pDiDetail);
|
||
|
||
if ((hdevInfo) && (INVALID_HANDLE_VALUE != hdevInfo)) {
|
||
SetupDiDestroyDeviceInfoList(hdevInfo);
|
||
hdevInfo = NULL;
|
||
}
|
||
|
||
return (BOOL) (status == ERROR_SUCCESS);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpMarkCriticalDisks(
|
||
IN PASR_DISK_INFO pDiskList,
|
||
IN PCWSTR CriticalVolumeList,
|
||
IN ULONG MaxDeviceNumber
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the IsCritical flag of each of the critical disks on the system. A
|
||
disk is deemed "critical" if it is part of part of the failover set for
|
||
any of the critical volumes present on the system.
|
||
|
||
Arguments:
|
||
|
||
pDiskList - The list of disks on the current system.
|
||
|
||
CriticalVolumeList - A multi-string containing a list of the volume GUID's
|
||
of each of the critical volumes present on the system. The GUID's
|
||
must be in the NT name-space, i.e., must be of the form:
|
||
\??\Volume{GUID}
|
||
|
||
MaxDeviceNumber - The highest storage device number of the disks present
|
||
in the disk list, as determined by calling
|
||
IOCTL_STORAGE_GET_DEVICE_NUMBER for each of them.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
PCWSTR volGuid = NULL;
|
||
|
||
PASR_DISK_INFO currentDisk = NULL;
|
||
|
||
PVOLUME_FAILOVER_SET failoverSet = NULL;
|
||
|
||
DWORD index = 0,
|
||
reqdSize=0,
|
||
sizeFailoverSet = 0,
|
||
status = ERROR_SUCCESS;
|
||
|
||
BOOL result = TRUE,
|
||
*criticalDiskTable = NULL;
|
||
|
||
WCHAR devicePath[ASR_CCH_DEVICE_PATH_FORMAT + 1];
|
||
|
||
HANDLE heapHandle = NULL,
|
||
hDevice = NULL;
|
||
|
||
memset(devicePath, 0L, (ASR_CCH_DEVICE_PATH_FORMAT+1)*sizeof(WCHAR));
|
||
|
||
if (!CriticalVolumeList) {
|
||
//
|
||
// No critical volumes:
|
||
//
|
||
#ifdef PRERELEASE
|
||
return TRUE;
|
||
#else
|
||
return FALSE;
|
||
#endif
|
||
}
|
||
|
||
if (!pDiskList) {
|
||
//
|
||
// No disks on machine?!
|
||
//
|
||
MYASSERT(0 && L"DiskList is NULL");
|
||
return FALSE;
|
||
}
|
||
|
||
heapHandle = GetProcessHeap();
|
||
MYASSERT(heapHandle);
|
||
|
||
//
|
||
// criticalDiskTable is our table of BOOL values.
|
||
//
|
||
criticalDiskTable = (BOOL *) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeof (BOOL) * (MaxDeviceNumber + 1)
|
||
);
|
||
_AsrpErrExitCode(!criticalDiskTable, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Try with a reasonable sized buffer first--say 10 disks. We'll
|
||
// realloc as needed if this isn't enough.
|
||
//
|
||
sizeFailoverSet = sizeof(VOLUME_FAILOVER_SET) + (10 * sizeof(ULONG));
|
||
failoverSet = (PVOLUME_FAILOVER_SET) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeFailoverSet
|
||
);
|
||
_AsrpErrExitCode(!failoverSet, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
volGuid = CriticalVolumeList;
|
||
while (*volGuid) {
|
||
//
|
||
// Convert the \??\ to \\?\ so that CreateFile can use it
|
||
//
|
||
wcsncpy(devicePath, volGuid, ASR_CCH_DEVICE_PATH_FORMAT);
|
||
devicePath[1] = L'\\';
|
||
|
||
//
|
||
// Get a handle so we can send the ioctl
|
||
//
|
||
hDevice = CreateFileW(
|
||
devicePath, // lpFileName
|
||
0, // dwDesiredAccess
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
|
||
NULL, // lpSecurityAttributes
|
||
OPEN_EXISTING, // dwCreationFlags
|
||
0, // dwFlagsAndAttributes
|
||
NULL // hTemplateFile
|
||
);
|
||
_AsrpErrExitCode(((!hDevice) || (INVALID_HANDLE_VALUE == hDevice)),
|
||
status,
|
||
GetLastError());
|
||
|
||
result = DeviceIoControl(
|
||
hDevice,
|
||
IOCTL_VOLUME_QUERY_FAILOVER_SET,
|
||
NULL,
|
||
0,
|
||
failoverSet,
|
||
sizeFailoverSet,
|
||
&reqdSize,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// We're doing this in a while loop because if the disk configuration
|
||
// changes in the small interval between when we get the reqd buffer
|
||
// size and when we send the ioctl again with a buffer of the "reqd"
|
||
// size, we may still end up with a buffer that isn't big enough.
|
||
//
|
||
while (!result) {
|
||
status = GetLastError();
|
||
|
||
if (ERROR_MORE_DATA == status) {
|
||
//
|
||
// The buffer was too small, reallocate the reqd size.
|
||
//
|
||
status = ERROR_SUCCESS;
|
||
|
||
sizeFailoverSet = (sizeof(VOLUME_FAILOVER_SET) +
|
||
((failoverSet->NumberOfDisks) * sizeof(ULONG)));
|
||
|
||
_AsrpHeapFree(failoverSet);
|
||
|
||
failoverSet = (PVOLUME_FAILOVER_SET) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
sizeFailoverSet
|
||
);
|
||
_AsrpErrExitCode(!failoverSet,
|
||
status,
|
||
ERROR_NOT_ENOUGH_MEMORY
|
||
);
|
||
|
||
result = DeviceIoControl(
|
||
hDevice,
|
||
IOCTL_VOLUME_QUERY_FAILOVER_SET,
|
||
NULL,
|
||
0,
|
||
failoverSet,
|
||
sizeFailoverSet,
|
||
&reqdSize,
|
||
NULL
|
||
);
|
||
}
|
||
else {
|
||
//
|
||
// The IOCTL failed because of something else, this is
|
||
// fatal since we can't find the critical disk list now.
|
||
//
|
||
_AsrpErrExitCode((TRUE), status, status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Mark the appropriate entries in our table
|
||
//
|
||
for (index = 0; index < failoverSet->NumberOfDisks; index++) {
|
||
criticalDiskTable[failoverSet->DiskNumbers[index]] = 1;
|
||
}
|
||
_AsrpCloseHandle(hDevice);
|
||
|
||
//
|
||
// Repeat for next volumeguid in list
|
||
//
|
||
volGuid += (wcslen(CriticalVolumeList) + 1);
|
||
}
|
||
|
||
//
|
||
// Now go through the list of disks, and mark the critical flags.
|
||
//
|
||
currentDisk = pDiskList;
|
||
while (currentDisk) {
|
||
|
||
if (currentDisk->IsClusterShared) {
|
||
//
|
||
// By definition, cluster shared disks cannot be critical.
|
||
//
|
||
currentDisk = currentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
currentDisk->IsCritical =
|
||
(criticalDiskTable[currentDisk->DeviceNumber] ? TRUE : FALSE);
|
||
|
||
//
|
||
// Increment the entry, so that we can track how many critical volumes
|
||
// reside on this disk, and--more importantly--ensure that all the
|
||
// critical disks exist on the system (next loop below)
|
||
//
|
||
if (currentDisk->IsCritical) {
|
||
++(criticalDiskTable[currentDisk->DeviceNumber]);
|
||
}
|
||
|
||
currentDisk = currentDisk->pNext;
|
||
|
||
}
|
||
|
||
//
|
||
// Finally, we want to make sure that we don't have any critical disks
|
||
// in our table that we didn't find physical disks for. (I.e., make
|
||
// sure that the system has no "missing" critical disks)
|
||
//
|
||
for (index = 0; index < MaxDeviceNumber; index++) {
|
||
if (1 == criticalDiskTable[index]) {
|
||
//
|
||
// If the table still has "1" for the value, it was never
|
||
// incremented in the while loop above, ie our diskList doesn't
|
||
// have a disk corresponding to this.
|
||
//
|
||
_AsrpErrExitCode(TRUE, status, ERROR_DEV_NOT_EXIST);
|
||
}
|
||
}
|
||
|
||
EXIT:
|
||
_AsrpHeapFree(failoverSet);
|
||
_AsrpHeapFree(criticalDiskTable);
|
||
_AsrpCloseHandle(hDevice);
|
||
|
||
return (BOOL)(ERROR_SUCCESS == status);
|
||
}
|
||
|
||
|
||
PASR_DISK_INFO
|
||
AsrpFreeDiskInfo(
|
||
PASR_DISK_INFO pCurrentDisk
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Helper function to free memory pointed to by various pointers in the
|
||
ASR_DISK_INFO struct, and then free the struct itself.
|
||
|
||
Arguments:
|
||
|
||
pCurrentDisk - the struct to be freed
|
||
|
||
Return Value:
|
||
|
||
pCurrentDisk->Next, which is a pointer to the next disk in the list
|
||
|
||
--*/
|
||
|
||
{
|
||
HANDLE heapHandle = NULL;
|
||
PASR_DISK_INFO pNext = NULL;
|
||
|
||
heapHandle = GetProcessHeap();
|
||
MYASSERT(heapHandle);
|
||
|
||
if (pCurrentDisk) {
|
||
|
||
pNext = pCurrentDisk->pNext;
|
||
//
|
||
// If it's a packed struct, then we only need to free the struct
|
||
// itself. If not, we need to free the memory the pointers point
|
||
// to as well.
|
||
//
|
||
if (!pCurrentDisk->IsPacked) {
|
||
_AsrpHeapFree(pCurrentDisk->DevicePath);
|
||
_AsrpHeapFree(pCurrentDisk->pDriveLayoutEx);
|
||
_AsrpHeapFree(pCurrentDisk->pDiskGeometry);
|
||
_AsrpHeapFree(pCurrentDisk->pPartition0Ex);
|
||
_AsrpHeapFree(pCurrentDisk->pScsiAddress);
|
||
_AsrpHeapFree(pCurrentDisk->PartitionInfoTable);
|
||
}
|
||
|
||
_AsrpHeapFree(pCurrentDisk);
|
||
}
|
||
|
||
return pNext;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpIsRemovableOrInaccesibleMedia(
|
||
IN PASR_DISK_INFO pDisk
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks if the disk represented by pDisk should be removed from our list
|
||
of disks that we'll store information on in the state file.
|
||
|
||
Disks that should be removed include disks that are removable, or disks
|
||
that we couldn't access.
|
||
|
||
Arguments:
|
||
|
||
pDisk - the disk structure to be checked
|
||
|
||
Return Value:
|
||
|
||
TRUE if the device is removable, or some key information about the disk is
|
||
missing. Since code depends on the driveLayout being non-NULL,
|
||
for instance, we shall just remove the disk from the list if we
|
||
couldn't get it's drive layout. We shall therefore not backup
|
||
information about any disk whose drive geo or layout we couldn't
|
||
get, and not restore to any such disk.
|
||
|
||
FALSE if the structure contains all the required information, and is not
|
||
a removable device.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
if ((NULL == pDisk->pDiskGeometry) ||
|
||
(NULL == pDisk->pDriveLayoutEx) ||
|
||
(NULL == pDisk->pPartition0Ex) ||
|
||
(FixedMedia != pDisk->pDiskGeometry->MediaType)
|
||
) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpFreeNonFixedMedia(
|
||
IN OUT PASR_DISK_INFO *ppDiskList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Removes removable media, and disks that are inaccessible, from the list of
|
||
disks passed in.
|
||
|
||
Arguments:
|
||
|
||
ppDiskList - a pointer to the address of the first disk in the list of all
|
||
the disks present on the current system.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
Currently, this function always succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
PASR_DISK_INFO prevDisk = NULL,
|
||
currentDisk = *ppDiskList;
|
||
|
||
while (currentDisk) {
|
||
|
||
if (AsrpIsRemovableOrInaccesibleMedia(currentDisk)) {
|
||
//
|
||
// Disk is not Fixed, we should remove it from out list
|
||
//
|
||
if (NULL == prevDisk) { // this is the first disk in the list
|
||
*ppDiskList = currentDisk->pNext;
|
||
}
|
||
else {
|
||
prevDisk->pNext = currentDisk->pNext;
|
||
}
|
||
|
||
//
|
||
// Free it and get a pointer to the next disk
|
||
//
|
||
currentDisk = AsrpFreeDiskInfo(currentDisk);
|
||
}
|
||
else {
|
||
//
|
||
// Disk is okay, move on to the next disk
|
||
//
|
||
prevDisk = currentDisk;
|
||
currentDisk = currentDisk->pNext;
|
||
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
AsrpFreeStateInformation(
|
||
IN OUT PASR_DISK_INFO *ppDiskList OPTIONAL,
|
||
IN OUT PASR_SYSTEM_INFO pSystemInfo OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Frees the memory addressed by pointers in the ASR_DISK_INFO and
|
||
ASR_SYSTEM_INFO structs.
|
||
|
||
Frees the list of disks pointed to by the ASR_DISK_INFO struct.
|
||
|
||
Arguments:
|
||
|
||
ppDiskList - Pointer to the address of the first Disk in the DiskList
|
||
being freed. The address is set to NULL after the list is freed,
|
||
to prevent further unintended accesses to the freed object.
|
||
|
||
pSystemInfo - A pointer to the ASR_SYSTEM_INFO struct, containing the
|
||
pointers to be freed.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
*ppDiskList is set to NULL.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
Currently, this function always succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
PASR_DISK_INFO pTempDisk = NULL;
|
||
|
||
HANDLE heapHandle = GetProcessHeap();
|
||
|
||
MYASSERT(heapHandle);
|
||
|
||
if (ARGUMENT_PRESENT(ppDiskList)) {
|
||
|
||
pTempDisk = *ppDiskList;
|
||
|
||
while (pTempDisk) {
|
||
pTempDisk = AsrpFreeDiskInfo(pTempDisk);
|
||
}
|
||
|
||
*ppDiskList = NULL;
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT(pSystemInfo)) {
|
||
_AsrpHeapFree(pSystemInfo->SystemPath);
|
||
_AsrpHeapFree(pSystemInfo->BootDirectory);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
AsrpFreePartitionList(
|
||
IN OUT PASR_PTN_INFO_LIST *ppPtnList OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Frees the list of partitions, along with memory addressed by all the
|
||
pointers in the list.
|
||
|
||
Arguments:
|
||
|
||
ppPtnList - Pointer to the address of the first partition in the list
|
||
being freed. The address is set to NULL after the list is freed,
|
||
to prevent further unintended accesses to the freed object.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
*ppPtnList is set to NULL.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
Currently, this function always succeeds.
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD index = 0,
|
||
numberOfPartitions = 0;
|
||
|
||
PASR_PTN_INFO_LIST pList = NULL;
|
||
|
||
PASR_PTN_INFO pCurrent = NULL,
|
||
pNext = NULL;
|
||
|
||
HANDLE heapHandle = GetProcessHeap();
|
||
|
||
if (!ARGUMENT_PRESENT(ppPtnList) || !(*ppPtnList)) {
|
||
return;
|
||
}
|
||
|
||
pList = *ppPtnList;
|
||
|
||
numberOfPartitions = pList[0].numTotalPtns;
|
||
|
||
for (index = 0; index < numberOfPartitions; index++) {
|
||
|
||
pCurrent = pList[index].pOffsetHead;
|
||
|
||
while (pCurrent) {
|
||
//
|
||
// Save a pointer to the next
|
||
//
|
||
pNext = pCurrent->pOffsetNext;
|
||
|
||
//
|
||
// No pointers in PASR_PTN_INFO, okay to free as-is.
|
||
//
|
||
_AsrpHeapFree(pCurrent);
|
||
|
||
pCurrent = pNext;
|
||
}
|
||
}
|
||
|
||
_AsrpHeapFree(pList);
|
||
*ppPtnList = NULL;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpWriteVersionSection(
|
||
IN CONST HANDLE SifHandle,
|
||
IN PCWSTR Provider OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the VERSION section of the ASR state file, and writes out the
|
||
entries in that section to file.
|
||
|
||
Arguments:
|
||
|
||
SifHandle - Handle to asr.sif, the ASR state file.
|
||
|
||
Provider - Pointer to a null-terminated string containing the name of the
|
||
application creating the asr.sif. The length of this string must
|
||
not exceed (ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING)
|
||
characters.
|
||
|
||
This is an optional argument.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
DWORD size;
|
||
|
||
//
|
||
// Write out the section name
|
||
//
|
||
swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_VERSION_SECTION_NAME);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
//
|
||
// Section Entries
|
||
//
|
||
wcscpy(infstring, L"Signature=\"$Windows NT$\"\r\n");
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
wcscpy(infstring, L"ASR-Version=\"1.0\"\r\n");
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
if (ARGUMENT_PRESENT(Provider)) {
|
||
if (wcslen(Provider) >
|
||
(ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING)
|
||
) {
|
||
//
|
||
// This string is too long to fit into one line in asr.sif
|
||
//
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
swprintf(infstring, L"%ws\"%.*ws\"\r\n",
|
||
ASR_SIF_PROVIDER_PREFIX,
|
||
(ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING),
|
||
Provider
|
||
);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpWriteSystemsSection(
|
||
IN CONST HANDLE SifHandle,
|
||
IN CONST PASR_SYSTEM_INFO pSystemInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the SYSTEMS section of the ASR state file, and writes out the
|
||
entries in that section to file.
|
||
|
||
Arguments:
|
||
|
||
SifHandle - Handle to asr.sif, the ASR state file.
|
||
|
||
pSystemInfo - Pointer to information about the current system.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
DWORD size = 0, SKU = 0;
|
||
|
||
if ((!pSystemInfo) || (!pSystemInfo->BootDirectory)) {
|
||
//
|
||
// We need a boot directory
|
||
//
|
||
SetLastError(ERROR_BAD_ENVIRONMENT);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Write out the section name
|
||
//
|
||
swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_SYSTEM_SECTION_NAME);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
SKU = (DWORD) (pSystemInfo->OsVersionEx.wProductType);
|
||
SKU = SKU << 16; // shift the ProductType left 2 bytes
|
||
SKU = SKU | (DWORD) (pSystemInfo->OsVersionEx.wSuiteMask);
|
||
//
|
||
// Create the section entry, and write it out to file.
|
||
//
|
||
swprintf(infstring,
|
||
L"1=\"%ws\",\"%ws\",\"%d.%d\",\"%ws\",%d,0x%08x,\"%ld %ld %ld %hd-%hd-%hd-%hd %hd:%02hd:%02hd.%hd %hd-%hd-%hd-%hd %hd:%02hd:%02hd.%hd\",\"%ws\",\"%ws\"\r\n",
|
||
pSystemInfo->ComputerName,
|
||
pSystemInfo->Platform,
|
||
pSystemInfo->OsVersionEx.dwMajorVersion,
|
||
pSystemInfo->OsVersionEx.dwMinorVersion,
|
||
pSystemInfo->BootDirectory,
|
||
((pSystemInfo->AutoExtendEnabled) ? 1 : 0),
|
||
|
||
// Product SKU
|
||
SKU,
|
||
|
||
// Time-zone stuff
|
||
pSystemInfo->TimeZoneInformation.Bias,
|
||
pSystemInfo->TimeZoneInformation.StandardBias,
|
||
pSystemInfo->TimeZoneInformation.DaylightBias,
|
||
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wYear,
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wMonth,
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wDayOfWeek,
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wDay,
|
||
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wHour,
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wMinute,
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wSecond,
|
||
pSystemInfo->TimeZoneInformation.StandardDate.wMilliseconds,
|
||
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wYear,
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wMonth,
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wDayOfWeek,
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wDay,
|
||
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wHour,
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wMinute,
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wSecond,
|
||
pSystemInfo->TimeZoneInformation.DaylightDate.wMilliseconds,
|
||
|
||
pSystemInfo->TimeZoneInformation.StandardName,
|
||
pSystemInfo->TimeZoneInformation.DaylightName
|
||
);
|
||
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpWriteBusesSection(
|
||
IN CONST HANDLE SifHandle,
|
||
IN CONST PASR_DISK_INFO pDiskList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the BUSES section of the ASR state file, and writes out the
|
||
entries in that section to file.
|
||
|
||
Arguments:
|
||
|
||
SifHandle - Handle to asr.sif, the ASR state file.
|
||
|
||
pDiskList - List of disks present on the current system.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD size = 0,
|
||
busKey = 1;
|
||
|
||
BOOL done = FALSE;
|
||
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
|
||
PASR_DISK_INFO pCurrentDisk = NULL;
|
||
|
||
//
|
||
// Write out the section name
|
||
//
|
||
swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_BUSES_SECTION_NAME);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
//
|
||
// Create the list of buses. This routine fills in the SifBusKey field
|
||
// for each disk.
|
||
//
|
||
AsrpDetermineBuses(pDiskList);
|
||
|
||
//
|
||
// Go through the list of disks now, and add one entry in asr.sif for each
|
||
// bus present on the system (i.e., each unique SifBusKey value). Note
|
||
// that we won't care about disks for which we couldn't get any bus info--
|
||
// SifBusKey is 0 for such disks, and we start here from SifBusKey == 1.
|
||
//
|
||
// Also, we assume that SifBusKey values have no holes.
|
||
//
|
||
while (!done) {
|
||
|
||
done = TRUE; // assume that we've been through all the buses.
|
||
//
|
||
// Start from the beginning of the list
|
||
//
|
||
pCurrentDisk = pDiskList;
|
||
|
||
while (pCurrentDisk) {
|
||
|
||
if (pCurrentDisk->SifBusKey > busKey) {
|
||
//
|
||
// There are SifBusKeys we haven't covered yet.
|
||
//
|
||
done = FALSE;
|
||
}
|
||
|
||
if (pCurrentDisk->SifBusKey == busKey) {
|
||
//
|
||
// This is the SifBusKey we're looking for, so lets write
|
||
// out the bus type to file.
|
||
//
|
||
swprintf(infstring, L"%lu=%d,%lu\r\n",
|
||
busKey,
|
||
ASR_SYSTEM_KEY,
|
||
pCurrentDisk->BusType
|
||
);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
//
|
||
// We've already covered this SifBusKey, lets move on to the
|
||
// next.
|
||
//
|
||
++busKey;
|
||
}
|
||
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpWriteMbrDisksSection(
|
||
IN CONST HANDLE SifHandle, // handle to the state file
|
||
IN CONST PASR_DISK_INFO pDiskList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the DISKS.MBR section of the ASR state file, and writes out the
|
||
entries in that section to file.
|
||
|
||
Arguments:
|
||
|
||
SifHandle - Handle to asr.sif, the ASR state file.
|
||
|
||
pDiskList - List of disks present on the current system.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD size = 0,
|
||
diskKey = 1;
|
||
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
|
||
PASR_DISK_INFO pCurrentDisk = pDiskList;
|
||
|
||
//
|
||
// Write out the section name: [DISKS.MBR]
|
||
//
|
||
swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_MBR_DISKS_SECTION_NAME);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
//
|
||
// Go through the list of disks, and write one entry for each MBR disk
|
||
// on the list.
|
||
//
|
||
while (pCurrentDisk) {
|
||
|
||
if (PARTITION_STYLE_MBR !=
|
||
pCurrentDisk->pDriveLayoutEx->PartitionStyle
|
||
) {
|
||
//
|
||
// Skip non-MBR (i.e., GPT) disks.
|
||
//
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
pCurrentDisk->SifDiskKey = diskKey;
|
||
swprintf(infstring, L"%lu=%d,%lu,%lu,0x%08x,%lu,%lu,%lu,%I64u\r\n",
|
||
diskKey,
|
||
ASR_SYSTEM_KEY,
|
||
pCurrentDisk->SifBusKey,
|
||
pCurrentDisk->IsCritical,
|
||
pCurrentDisk->pDriveLayoutEx->Mbr.Signature,
|
||
pCurrentDisk->pDiskGeometry->BytesPerSector,
|
||
pCurrentDisk->pDiskGeometry->SectorsPerTrack,
|
||
pCurrentDisk->pDiskGeometry->TracksPerCylinder,
|
||
(ULONG64)(pCurrentDisk->pPartition0Ex->PartitionLength.QuadPart /
|
||
pCurrentDisk->pDiskGeometry->BytesPerSector)
|
||
);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
++diskKey;
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpWriteGptDisksSection(
|
||
IN CONST HANDLE SifHandle, // handle to the state file
|
||
IN CONST PASR_DISK_INFO pDiskList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the DISKS.GPT section of the ASR state file, and writes out the
|
||
entries in that section to file.
|
||
|
||
Arguments:
|
||
|
||
SifHandle - Handle to asr.sif, the ASR state file.
|
||
|
||
pDiskList - List of disks present on the current system.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD size = 0,
|
||
diskKey = 1;
|
||
|
||
PWSTR lpGuidString = NULL;
|
||
|
||
RPC_STATUS rpcStatus = RPC_S_OK;
|
||
|
||
PASR_DISK_INFO pCurrentDisk = pDiskList;
|
||
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
|
||
//
|
||
// Write out the section name: [DISKS.GPT]
|
||
//
|
||
swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_GPT_DISKS_SECTION_NAME);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
//
|
||
// Go through the list of disks, and write one entry for each GPT disk
|
||
// on the list.
|
||
//
|
||
while (pCurrentDisk) {
|
||
|
||
if (PARTITION_STYLE_GPT !=
|
||
pCurrentDisk->pDriveLayoutEx->PartitionStyle
|
||
) {
|
||
//
|
||
// Skip non-GPT (i.e., MBR) disks.
|
||
//
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Convert the DiskId to a printable string
|
||
//
|
||
rpcStatus = UuidToStringW(
|
||
&pCurrentDisk->pDriveLayoutEx->Gpt.DiskId,
|
||
&lpGuidString
|
||
);
|
||
if (rpcStatus != RPC_S_OK) {
|
||
if (lpGuidString) {
|
||
RpcStringFreeW(&lpGuidString);
|
||
}
|
||
//
|
||
// The only error from UuidToStringW is RPC_S_OUT_OF_MEMORY
|
||
//
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
pCurrentDisk->SifDiskKey = diskKey;
|
||
swprintf(infstring, L"%lu=%d,%lu,%lu,%ws%ws%ws,%lu,%lu,%lu,%lu,%I64u\r\n",
|
||
diskKey,
|
||
ASR_SYSTEM_KEY,
|
||
pCurrentDisk->SifBusKey,
|
||
pCurrentDisk->IsCritical,
|
||
(lpGuidString ? L"\"" : L""),
|
||
(lpGuidString ? lpGuidString : L""),
|
||
(lpGuidString ? L"\"" : L""),
|
||
pCurrentDisk->pDriveLayoutEx->Gpt.MaxPartitionCount,
|
||
pCurrentDisk->pDiskGeometry->BytesPerSector,
|
||
pCurrentDisk->pDiskGeometry->SectorsPerTrack,
|
||
pCurrentDisk->pDiskGeometry->TracksPerCylinder,
|
||
(ULONG64) (pCurrentDisk->pPartition0Ex->PartitionLength.QuadPart /
|
||
pCurrentDisk->pDiskGeometry->BytesPerSector)
|
||
);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring, wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
if (lpGuidString) {
|
||
RpcStringFreeW(&lpGuidString);
|
||
lpGuidString = NULL;
|
||
}
|
||
|
||
++diskKey;
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpWriteMbrPartitionsSection(
|
||
IN CONST HANDLE SifHandle, // handle to the state file
|
||
IN CONST PASR_DISK_INFO pDiskList,
|
||
IN CONST PASR_SYSTEM_INFO pSystemInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the PARTITIONS.MBR section of the ASR state file, and writes
|
||
out the entries in that section to file.
|
||
|
||
Arguments:
|
||
|
||
SifHandle - Handle to asr.sif, the ASR state file.
|
||
|
||
pDiskList - List of disks present on the current system.
|
||
|
||
pSystemInfo - Info about the current system, used to determine the current
|
||
boot and system partitions (and mark them appropriately in
|
||
asr.sif)
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
DWORD size = 0,
|
||
index = 0,
|
||
partitionKey = 1;
|
||
|
||
UCHAR fsType = 0;
|
||
|
||
PWSTR volumeGuid = NULL;
|
||
|
||
BOOL writeVolumeGuid = FALSE;
|
||
|
||
PASR_DISK_INFO pCurrentDisk = pDiskList;
|
||
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
|
||
PPARTITION_INFORMATION_EX currentPartitionEx = NULL;
|
||
|
||
//
|
||
// Write out the section name: [PARTITIONS.MBR]
|
||
//
|
||
swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_MBR_PARTITIONS_SECTION_NAME);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
//
|
||
// Go through the list of disks, and write one entry for each partition on
|
||
// each of the MBR disks on the list.
|
||
//
|
||
while (pCurrentDisk) {
|
||
|
||
if (pCurrentDisk->pDriveLayoutEx) {
|
||
|
||
if (PARTITION_STYLE_MBR !=
|
||
pCurrentDisk->pDriveLayoutEx->PartitionStyle
|
||
) {
|
||
//
|
||
// Skip non-MBR (i.e., GPT) disks
|
||
//
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Enumerate partitions on the disk. We expect to find only
|
||
// MBR partitions.
|
||
//
|
||
for (index =0;
|
||
index < pCurrentDisk->pDriveLayoutEx->PartitionCount;
|
||
index++
|
||
) {
|
||
|
||
currentPartitionEx =
|
||
&pCurrentDisk->pDriveLayoutEx->PartitionEntry[index];
|
||
|
||
MYASSERT(currentPartitionEx->PartitionStyle ==
|
||
PARTITION_STYLE_MBR);
|
||
|
||
if (currentPartitionEx->Mbr.PartitionType == 0) {
|
||
//
|
||
// Empty partition table entry.
|
||
//
|
||
continue;
|
||
}
|
||
|
||
fsType =
|
||
pCurrentDisk->PartitionInfoTable[index].FileSystemType;
|
||
|
||
volumeGuid =
|
||
pCurrentDisk->PartitionInfoTable[index].szVolumeGuid;
|
||
|
||
//
|
||
// We only want to write out the Volume GUID for basic
|
||
// (recognized) partitions/volumes, since it does not make
|
||
// sense in the context of LDM or other unknown partition
|
||
// types which would need special handling from their
|
||
// respective recovery agents such as asr_ldm in GUI-mode
|
||
// Setup.
|
||
//
|
||
writeVolumeGuid = (wcslen(volumeGuid) > 0) &&
|
||
IsRecognizedPartition(currentPartitionEx->Mbr.PartitionType);
|
||
|
||
//
|
||
// Create the entry and write it to file.
|
||
//
|
||
swprintf(
|
||
infstring,
|
||
L"%d=%d,%d,%lu,%ws%ws%ws,0x%02x,0x%02x,0x%02x,%I64u,%I64u,0x%x\r\n",
|
||
partitionKey,
|
||
pCurrentDisk->SifDiskKey,
|
||
index,
|
||
pCurrentDisk->PartitionInfoTable[index].PartitionFlags,
|
||
(writeVolumeGuid ? L"\"" : L""),
|
||
(writeVolumeGuid ? volumeGuid : L""),
|
||
(writeVolumeGuid ? L"\"" : L""),
|
||
(currentPartitionEx->Mbr.BootIndicator)?0x80:0,
|
||
currentPartitionEx->Mbr.PartitionType,
|
||
|
||
((fsType) ? fsType :
|
||
currentPartitionEx->Mbr.PartitionType),
|
||
|
||
(ULONG64) ((currentPartitionEx->StartingOffset.QuadPart)/
|
||
(pCurrentDisk->pDiskGeometry->BytesPerSector)),
|
||
|
||
(ULONG64) ((currentPartitionEx->PartitionLength.QuadPart)/
|
||
(pCurrentDisk->pDiskGeometry->BytesPerSector)),
|
||
|
||
pCurrentDisk->PartitionInfoTable[index].ClusterSize
|
||
);
|
||
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
++partitionKey;
|
||
}
|
||
}
|
||
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpWriteGptPartitionsSection(
|
||
IN CONST HANDLE SifHandle,
|
||
IN CONST PASR_DISK_INFO pDiskList,
|
||
IN CONST PASR_SYSTEM_INFO pSystemInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates the PARTITIONS.GPT section of the ASR state file, and writes
|
||
out the entries in that section to file.
|
||
|
||
Arguments:
|
||
|
||
SifHandle - Handle to asr.sif, the ASR state file.
|
||
|
||
pDiskList - List of disks present on the current system.
|
||
|
||
pSystemInfo - Info about the current system, used to determine the current
|
||
boot and system partitions (and mark them appropriately in
|
||
asr.sif)
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD size = 0,
|
||
index = 0,
|
||
partitionKey = 1;
|
||
|
||
UCHAR fsType = 0;
|
||
|
||
PWSTR volumeGuid = NULL,
|
||
partitionId = NULL,
|
||
partitionType = NULL;
|
||
|
||
BOOL writeVolumeGuid = FALSE;
|
||
|
||
RPC_STATUS rpcStatus = RPC_S_OK;
|
||
|
||
PASR_DISK_INFO pCurrentDisk = pDiskList;
|
||
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
|
||
PPARTITION_INFORMATION_EX currentPartitionEx = NULL;
|
||
|
||
//
|
||
// Write out the section name: [PARTITIONS.GPT]
|
||
//
|
||
swprintf(infstring, L"\r\n%ws\r\n", ASR_SIF_GPT_PARTITIONS_SECTION_NAME);
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
//
|
||
// Go through the list of disks, and write one entry for each partition on
|
||
// each of the GPT disks on the list.
|
||
//
|
||
while (pCurrentDisk) {
|
||
|
||
if (pCurrentDisk->pDriveLayoutEx) {
|
||
|
||
if (PARTITION_STYLE_GPT !=
|
||
pCurrentDisk->pDriveLayoutEx->PartitionStyle
|
||
) {
|
||
//
|
||
// Skip non-GPT (i.e., MBR) disks.
|
||
//
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Enumerate partitions on the disk. We expect to find only
|
||
// GPT partitions.
|
||
//
|
||
for (index =0;
|
||
index < pCurrentDisk->pDriveLayoutEx->PartitionCount;
|
||
index++) {
|
||
|
||
currentPartitionEx =
|
||
&pCurrentDisk->pDriveLayoutEx->PartitionEntry[index];
|
||
|
||
MYASSERT(currentPartitionEx->PartitionStyle ==
|
||
PARTITION_STYLE_GPT);
|
||
|
||
//
|
||
// Convert the Guids to printable strings
|
||
//
|
||
rpcStatus = UuidToStringW(
|
||
¤tPartitionEx->Gpt.PartitionType,
|
||
&partitionType
|
||
);
|
||
if (rpcStatus != RPC_S_OK) {
|
||
|
||
if (partitionType) {
|
||
RpcStringFreeW(&partitionType);
|
||
partitionType = NULL;
|
||
}
|
||
|
||
//
|
||
// The only error from UuidToString is RPC_S_OUT_OF_MEMORY
|
||
//
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
rpcStatus = UuidToStringW(
|
||
¤tPartitionEx->Gpt.PartitionId,
|
||
&partitionId
|
||
);
|
||
if (rpcStatus != RPC_S_OK) {
|
||
|
||
if (partitionType) {
|
||
RpcStringFreeW(&partitionType);
|
||
partitionType = NULL;
|
||
}
|
||
|
||
if (partitionId) {
|
||
RpcStringFreeW(&partitionId);
|
||
partitionId = NULL;
|
||
}
|
||
|
||
//
|
||
// The only error from UuidToString is RPC_S_OUT_OF_MEMORY
|
||
//
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
fsType =
|
||
pCurrentDisk->PartitionInfoTable[index].FileSystemType;
|
||
|
||
volumeGuid =
|
||
pCurrentDisk->PartitionInfoTable[index].szVolumeGuid;
|
||
|
||
//
|
||
// We only want to write out the Volume GUID for basic
|
||
// (recognized) partitions/volumes, since it does not make
|
||
// sense in the context of LDM or other unknown partition
|
||
// types which would need special handling from their
|
||
// respective recovery agents such as asr_ldm in GUI-mode
|
||
// Setup.
|
||
//
|
||
writeVolumeGuid = (wcslen(volumeGuid) > 0) &&
|
||
IsEqualGUID(&(partitionType), &(PARTITION_BASIC_DATA_GUID));
|
||
|
||
//
|
||
// Create the entry and write it to file.
|
||
//
|
||
swprintf(
|
||
infstring,
|
||
L"%d=%d,%d,%d,%ws%ws%ws,%ws%ws%ws,%ws%ws%ws,0x%I64x,%ws%ws%ws,0x%02x,%I64u,%I64u,0x%x\r\n",
|
||
|
||
partitionKey,
|
||
pCurrentDisk->SifDiskKey,
|
||
index, //slot-index
|
||
pCurrentDisk->PartitionInfoTable[index].PartitionFlags,
|
||
|
||
(writeVolumeGuid ? L"\"" : L""),
|
||
(writeVolumeGuid ? volumeGuid : L""),
|
||
(writeVolumeGuid ? L"\"" : L""),
|
||
|
||
(partitionType ? L"\"" : L""),
|
||
(partitionType ? partitionType : L""),
|
||
(partitionType ? L"\"" : L""),
|
||
|
||
(partitionId ? L"\"" : L""),
|
||
(partitionId ? partitionId : L""),
|
||
(partitionId ? L"\"" : L""),
|
||
|
||
currentPartitionEx->Gpt.Attributes,
|
||
|
||
(currentPartitionEx->Gpt.Name ? L"\"" : L""),
|
||
(currentPartitionEx->Gpt.Name ?
|
||
currentPartitionEx->Gpt.Name : L""),
|
||
(currentPartitionEx->Gpt.Name ? L"\"" : L""),
|
||
|
||
//
|
||
// ISSUE-2000/04/12-guhans: GetVolumeInformation does not
|
||
// work on GPT and fstype is always zero
|
||
//
|
||
fsType,
|
||
|
||
(ULONG64) ((currentPartitionEx->StartingOffset.QuadPart)/
|
||
(pCurrentDisk->pDiskGeometry->BytesPerSector)),
|
||
|
||
(ULONG64) ((currentPartitionEx->PartitionLength.QuadPart)/
|
||
(pCurrentDisk->pDiskGeometry->BytesPerSector)),
|
||
|
||
pCurrentDisk->PartitionInfoTable[index].ClusterSize
|
||
);
|
||
|
||
_AsrpCheckTrue(WriteFile(SifHandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL));
|
||
|
||
if (partitionType) {
|
||
RpcStringFreeW(&partitionType);
|
||
partitionType = NULL;
|
||
}
|
||
if (partitionId) {
|
||
RpcStringFreeW(&partitionId);
|
||
partitionId = NULL;
|
||
}
|
||
|
||
++partitionKey;
|
||
}
|
||
}
|
||
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpCreateEnvironmentBlock(
|
||
IN PCWSTR CriticalVolumeList,
|
||
IN HANDLE SifHandle,
|
||
OUT PWSTR *NewBlock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Creates a new environment block that is passed in to apps launched as part
|
||
of an ASR backup. This routine retrieves the current process's
|
||
environment block, adds the ASR environment variables to it, and creates a
|
||
multi-sz suitable for being passed in as the lpEnvironment parameter of
|
||
CreateProcess.
|
||
|
||
|
||
Arguments:
|
||
|
||
CriticalVolumeList - A multi-string containing a list of the volume GUID's
|
||
of each of the critical volumes present on the system. The GUID's
|
||
must be in the NT name-space, i.e., must be of the form:
|
||
\??\Volume{GUID}
|
||
|
||
This multi-sz is used to create the semi-colon separated list of
|
||
volumes in the "_AsrCriticalVolumeList" variable in NewBlock.
|
||
|
||
SifHandle - A (duplicate) handle to asr.sif, the ASR state file. This is
|
||
used in creating the "_AsrContext" variable in NewBlock.
|
||
|
||
NewBlock - Receives the new environment block. In addition to all the
|
||
environment variables in the current process environment block,
|
||
this block contains two additional "ASR" variables:
|
||
_AsrContext=<DWORD_PTR value>
|
||
_AsrCriticalVolumeList=<volumeguid>;<volumeguid>;...;<volumeguid>
|
||
|
||
The caller is responsible for freeing this block when it is no
|
||
longer needed, using HeapFree(GetProcessHeap(),...).
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError(). (*NewBlock) is set to NULL.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCWSTR lpTemp = CriticalVolumeList;
|
||
|
||
PWSTR lpCurrentEnvStrings = NULL;
|
||
|
||
DWORD cchContextEntry = 0,
|
||
cchEnvBlock = 0,
|
||
cbEnvBlock = 0,
|
||
cbCurrentProcessEnvBlock = 0,
|
||
status = ERROR_SUCCESS;
|
||
|
||
HANDLE heapHandle = GetProcessHeap();
|
||
|
||
MYASSERT(NewBlock);
|
||
|
||
//
|
||
// Find out how much space the environment block will need
|
||
//
|
||
|
||
//
|
||
// For _AsrContext=1234 and _AsrCriticalVolumes="..." entries
|
||
//
|
||
lpTemp = CriticalVolumeList;
|
||
if (CriticalVolumeList) {
|
||
while (*lpTemp) {
|
||
lpTemp += (wcslen(lpTemp) + 1);
|
||
}
|
||
}
|
||
cbEnvBlock = (DWORD) ((lpTemp - CriticalVolumeList + 1) * sizeof(WCHAR));
|
||
cbEnvBlock += ASR_CCH_ENVBLOCK_ASR_ENTRIES * sizeof(WCHAR);
|
||
|
||
//
|
||
// For all the current environment strings
|
||
//
|
||
lpCurrentEnvStrings = GetEnvironmentStringsW();
|
||
lpTemp = lpCurrentEnvStrings;
|
||
if (lpCurrentEnvStrings ) {
|
||
while (*lpTemp) {
|
||
lpTemp += (wcslen(lpTemp) + 1);
|
||
}
|
||
}
|
||
cbCurrentProcessEnvBlock = (DWORD) ((lpTemp - lpCurrentEnvStrings + 1) * sizeof(WCHAR));
|
||
cbEnvBlock += cbCurrentProcessEnvBlock;
|
||
|
||
//
|
||
// And allocate the space
|
||
//
|
||
*NewBlock = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
cbEnvBlock
|
||
);
|
||
_AsrpErrExitCode(!(*NewBlock), status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// First, add the AsrContext=1234 entry in the environment block
|
||
//
|
||
swprintf(
|
||
(*NewBlock),
|
||
ASR_ENVBLOCK_CONTEXT_ENTRY,
|
||
(ULONG64) (SifHandle)
|
||
);
|
||
|
||
//
|
||
// Keep track of where this entry ends, so we can add a NULL at this
|
||
// index later.
|
||
//
|
||
cchContextEntry = wcslen((*NewBlock));
|
||
wcscat((*NewBlock), L" "); // this character will be replaced by a NULL later
|
||
|
||
//
|
||
// Append each critical volume GUID, separated by a semi-colon.
|
||
//
|
||
wcscat((*NewBlock), ASR_ENVBLOCK_CRITICAL_VOLUME_ENTRY);
|
||
if (CriticalVolumeList) {
|
||
lpTemp = CriticalVolumeList;
|
||
while (*lpTemp) {
|
||
wcscat((*NewBlock), lpTemp);
|
||
wcscat((*NewBlock), L";");
|
||
lpTemp += (wcslen(lpTemp) + 1);
|
||
}
|
||
}
|
||
else {
|
||
wcscat((*NewBlock), L";");
|
||
}
|
||
|
||
//
|
||
// Mark the end with two NULL's
|
||
//
|
||
cchEnvBlock = wcslen(*NewBlock) - 1;
|
||
// (*NewBlock)[cchEnvBlock - 1] = L'"';
|
||
(*NewBlock)[cchEnvBlock] = L'\0';
|
||
|
||
//
|
||
// Separate the two entries with a NULL
|
||
//
|
||
(*NewBlock)[cchContextEntry] = L'\0';
|
||
|
||
//
|
||
// Copy over the current environment strings
|
||
//
|
||
RtlCopyMemory(&(*NewBlock)[cchEnvBlock + 1],
|
||
lpCurrentEnvStrings,
|
||
cbCurrentProcessEnvBlock
|
||
);
|
||
|
||
EXIT:
|
||
if (lpCurrentEnvStrings) {
|
||
FreeEnvironmentStringsW(lpCurrentEnvStrings);
|
||
lpCurrentEnvStrings = NULL;
|
||
}
|
||
|
||
if (ERROR_SUCCESS != status) {
|
||
_AsrpHeapFree((*NewBlock));
|
||
}
|
||
|
||
return (BOOL) (ERROR_SUCCESS == status);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpLaunchRegisteredCommands(
|
||
IN HANDLE SifHandle,
|
||
IN PCWSTR CriticalVolumeList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This launches apps that have registered to be part of an ASR-backup. The
|
||
commands are read from the following ASR-Commands key:
|
||
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Asr\\Commands"
|
||
|
||
This key contains a REG_EXPAND_SZ entry for each application to be
|
||
launched, with the data containing the full command-line to be invoked:
|
||
ApplicationName::REG_EXPAND_SZ::<Command-line with parameters>
|
||
|
||
Such as:
|
||
ASR utility::REG_EXPAND_SZ::"%systemroot%\\system32\\asr_fmt.exe /backup"
|
||
|
||
When invoking the app, we expand out all the environment variables in the
|
||
command-line. In addition, we append a "context" parameter to the command
|
||
line, which the app is expected to use in calls to AsrAddSifEntry.
|
||
The above entry would thus translate something like:
|
||
|
||
c:\windows\system32\asr_fmt.exe /backup /context=2000
|
||
|
||
The environment block of the process is a duplicate of the current process
|
||
environment block, with one exception--it contains two additional "Asr"
|
||
variables:
|
||
_AsrContext=<DWORD_PTR value>
|
||
_AsrCriticalVolumeList=<volumeguid>;<volumeguid>;...;<volumeguid>
|
||
|
||
Each application invoked must complete in the allowed time-out value.
|
||
The time-out is configurable in the registry, by changing the value of the
|
||
"ProcessTimeOut" value under the ASR key. We ship with a default of 3600
|
||
seconds, but the sys-admin can change it if needed. (0=infinite).
|
||
|
||
Arguments:
|
||
|
||
SifHandle - A handle to asr.sif, the ASR state file. A duplicate of this
|
||
handle is passed in to applications as the "context" parameter,
|
||
and as the "_AsrContext" variable in the environment block.
|
||
|
||
CriticalVolumeList - A multi-string containing a list of the volume GUID's
|
||
of each of the critical volumes present on the system. The GUID's
|
||
must be in the NT name-space, i.e., must be of the form:
|
||
\??\Volume{GUID}
|
||
|
||
This multi-sz is used to create the semi-colon separated list of
|
||
volumes in the "_AsrCriticalVolumeList" variable in the env
|
||
block of the new processes.
|
||
|
||
Applications (such as volume-managers) can use this list to
|
||
determine if they manage any critical volumes, and make a note of
|
||
it in the asr.sif. This way, they can intelligently decide to
|
||
abort the ASR restore process if needed.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value. This
|
||
implies that all the applications invoked were successful (i.e.,
|
||
returned an exit code of 0).
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
Note that if any of the applications returned an exit code other than 0,
|
||
we interpret that as a fatal error, and will return an error.
|
||
|
||
--*/
|
||
|
||
{
|
||
HKEY regKey = NULL;
|
||
|
||
DWORD status = ERROR_SUCCESS,
|
||
waitResult = WAIT_ABANDONED,
|
||
|
||
lpcValues = 0L,
|
||
index = 0L,
|
||
|
||
cbData = 0L,
|
||
cbMaxDataLen = 0L,
|
||
|
||
cchValueName = 0L,
|
||
cchMaxValueLen = 0L,
|
||
|
||
cbCommand = 0L,
|
||
cchReqd = 0L,
|
||
|
||
timeLeft = 0L,
|
||
maxTimeOutValue = 0L;
|
||
|
||
HANDLE heapHandle = NULL,
|
||
processHandle = NULL,
|
||
dupSifHandle = NULL;
|
||
|
||
PWSTR valueName = NULL,
|
||
data = NULL,
|
||
command = NULL,
|
||
lpEnvBlock = NULL;
|
||
|
||
WCHAR cmdLineSuffix[ASR_COMMANDLINE_SUFFIX_LEN + 1];
|
||
|
||
BOOL result = FALSE;
|
||
|
||
STARTUPINFOW startUpInfo;
|
||
|
||
PROCESS_INFORMATION processInfo;
|
||
|
||
heapHandle = GetProcessHeap();
|
||
processHandle = GetCurrentProcess();
|
||
MYASSERT(heapHandle && processHandle);
|
||
|
||
ZeroMemory(cmdLineSuffix, (ASR_COMMANDLINE_SUFFIX_LEN + 1) * sizeof(WCHAR));
|
||
ZeroMemory(&startUpInfo, sizeof(STARTUPINFOW));
|
||
ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));
|
||
|
||
//
|
||
// Get the time out value for processes, if set in the registry
|
||
// If the key is missing, or is set to "0", the timeout is set
|
||
// to INFINITE.
|
||
//
|
||
status = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE, // hKey
|
||
ASR_REGKEY_ASR, // lpSubKey
|
||
0, // ulOptions--Reserved, must be 0
|
||
MAXIMUM_ALLOWED, // samDesired
|
||
®Key // phkResult
|
||
);
|
||
|
||
if ((regKey) && (ERROR_SUCCESS == status)) {
|
||
DWORD type = 0L,
|
||
timeOut = 0L,
|
||
cbTimeOut = (sizeof(DWORD));
|
||
|
||
status = RegQueryValueExW(
|
||
regKey, // hKey
|
||
ASR_REGVALUE_TIMEOUT, // lpValueName
|
||
NULL, // lpReserved
|
||
&type, // lpType
|
||
(LPBYTE) &timeOut, // lpData
|
||
&cbTimeOut // lpcbData
|
||
);
|
||
|
||
if ((ERROR_SUCCESS == status) && (REG_DWORD == type)) {
|
||
maxTimeOutValue = timeOut;
|
||
}
|
||
}
|
||
|
||
if (regKey) {
|
||
RegCloseKey(regKey);
|
||
regKey = NULL;
|
||
}
|
||
|
||
//
|
||
// Open and enumerate the entries in the ASR command key. If
|
||
// the key doesn't exist, we don't have to execute anything
|
||
//
|
||
status = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE, // hKey
|
||
ASR_REGKEY_ASR_COMMANDS, // lpSubKey
|
||
0, // ulOptions--Reserved, must be 0
|
||
MAXIMUM_ALLOWED, // samDesired
|
||
®Key // phkResult
|
||
);
|
||
|
||
if ((!regKey) || (ERROR_SUCCESS != status)) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Get the max ValueName and Data entries, and
|
||
// allocate memory for them
|
||
//
|
||
status = RegQueryInfoKey(
|
||
regKey,
|
||
NULL, // class
|
||
NULL, // lpcClass
|
||
NULL, // lpReserved
|
||
NULL, // lpcSubKeys
|
||
NULL, // lpcMaxSubKeyLen
|
||
NULL, // lpcMaxClassLen,
|
||
&lpcValues, // number of values
|
||
&cchMaxValueLen, // max value length, in cch
|
||
&cbMaxDataLen, // max data length, in cb
|
||
NULL, // lpcbSecurityDescriptor
|
||
NULL // lpftLastWriteTime
|
||
);
|
||
_AsrpErrExitCode((ERROR_SUCCESS != status), status, status);
|
||
_AsrpErrExitCode((0 == lpcValues), status, ERROR_SUCCESS); // Key is empty, we're done
|
||
|
||
valueName = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
(cchMaxValueLen + 1) * sizeof (WCHAR) // cch not cb
|
||
);
|
||
_AsrpErrExitCode(!valueName, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
data = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
cbMaxDataLen + ((ASR_COMMANDLINE_SUFFIX_LEN + 2) * sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode(!data, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// "command" will contain the full command string, after any environment
|
||
// variables (eg %systemroot%) in "data" have been expanded. We'll start
|
||
// off with "command" being MAX_PATH characters longer than "data", and
|
||
// we'll re-allocate a bigger buffer if/when needed
|
||
//
|
||
cbCommand = cbMaxDataLen +
|
||
((ASR_COMMANDLINE_SUFFIX_LEN + MAX_PATH + 2) * sizeof(WCHAR));
|
||
|
||
command = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
cbCommand
|
||
);
|
||
_AsrpErrExitCode(!command, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
do {
|
||
cchValueName = cchMaxValueLen + 1;
|
||
cbData = cbMaxDataLen + sizeof(WCHAR);
|
||
|
||
//
|
||
// Enumerate the commands, and execute them one after the other
|
||
//
|
||
status = RegEnumValueW(
|
||
regKey, // hKey
|
||
index++, // dwIndex
|
||
valueName, // lpValueName
|
||
&cchValueName, // lpcValueName
|
||
NULL, // lpReserved
|
||
NULL, // lpType
|
||
(LPBYTE)data, // lpData
|
||
&cbData // lpcbData
|
||
);
|
||
_AsrpErrExitCode((ERROR_NO_MORE_ITEMS == status),
|
||
status,
|
||
ERROR_SUCCESS
|
||
); // done with enum
|
||
_AsrpErrExitCode((ERROR_SUCCESS != status), status, status);
|
||
|
||
//
|
||
// Create a copy of the sif handle to pass to the app launched.
|
||
// We clean-up close the handle after the app is done.
|
||
//
|
||
result = DuplicateHandle(
|
||
processHandle,
|
||
SifHandle,
|
||
processHandle,
|
||
&dupSifHandle,
|
||
0L,
|
||
TRUE,
|
||
DUPLICATE_SAME_ACCESS
|
||
);
|
||
_AsrpErrExitCode((!result), status, GetLastError());
|
||
|
||
//
|
||
// Append the "/context=<duplicate-sif-handle>" to
|
||
// the command line
|
||
//
|
||
swprintf(cmdLineSuffix,
|
||
ASR_COMMANDLINE_SUFFIX,
|
||
(ULONG64)(dupSifHandle)
|
||
);
|
||
wcscat(data, cmdLineSuffix);
|
||
|
||
//
|
||
// Expand any environment strings in the command line
|
||
//
|
||
cchReqd = ExpandEnvironmentStringsW(data,
|
||
command,
|
||
(cbCommand / sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode((!cchReqd), status, GetLastError());
|
||
|
||
if ((cchReqd * sizeof(WCHAR)) > cbCommand) {
|
||
//
|
||
// Our "command" buffer wasn't big enough, re-allocate as needed
|
||
//
|
||
_AsrpHeapFree(command);
|
||
cbCommand = ((cchReqd + 1) * sizeof(WCHAR));
|
||
|
||
command = HeapAlloc(heapHandle, HEAP_ZERO_MEMORY, cbCommand);
|
||
_AsrpErrExitCode(!command, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Try expanding the env strings again ...
|
||
//
|
||
cchReqd = ExpandEnvironmentStringsW(data,
|
||
command,
|
||
(cbCommand / sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode(
|
||
((!cchReqd) || (cchReqd * sizeof(WCHAR)) > cbCommand),
|
||
status,
|
||
GetLastError()
|
||
);
|
||
}
|
||
|
||
//
|
||
// Create the environment block to be passed to the
|
||
// process being launched. The environment block
|
||
// contains the entries:
|
||
// _AsrCriticalVolumes=\??\Volume{Guid1};\??\Volume{Guid2}
|
||
// _AsrContext=<duplicate-sif-handle>
|
||
//
|
||
// in addition to all the environment strings in the current process.
|
||
//
|
||
result = AsrpCreateEnvironmentBlock(CriticalVolumeList,
|
||
dupSifHandle,
|
||
&lpEnvBlock
|
||
);
|
||
_AsrpErrExitCode((!result), status, GetLastError());
|
||
|
||
//
|
||
// Execute the command as a separate process
|
||
//
|
||
memset(&startUpInfo, 0L, sizeof (startUpInfo));
|
||
result = CreateProcessW(
|
||
NULL, // lpApplicationName
|
||
command, // lpCommandLine
|
||
NULL, // lpProcessAttributes
|
||
NULL, // lpThreadAttributes
|
||
TRUE, // bInheritHandles
|
||
CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags
|
||
lpEnvBlock, // new environment block
|
||
NULL, // current directory name (null=current dir)
|
||
&startUpInfo, // statup information
|
||
&processInfo // process information
|
||
);
|
||
_AsrpErrExitCode((!result),
|
||
status,
|
||
GetLastError()
|
||
); // process couldn't be launched
|
||
|
||
//
|
||
// Process was launched: start the timer countdown if a maximum
|
||
// timeout was specified in the registry. Loop till either the
|
||
// process completes, or the timer expires
|
||
//
|
||
timeLeft = maxTimeOutValue;
|
||
if (timeLeft) {
|
||
do {
|
||
waitResult = WaitForSingleObject(processInfo.hProcess, 1000); // 1000 ms = 1 sec
|
||
--timeLeft;
|
||
} while ((WAIT_TIMEOUT == waitResult) && (timeLeft));
|
||
|
||
if (!timeLeft) {
|
||
//
|
||
// The process did not terminate in the allowed time. We treat
|
||
// this as a fatal error--terminate the process, and set its
|
||
// error code to ERROR_TIMEOUT
|
||
//
|
||
TerminateProcess(processInfo.hProcess, ERROR_TIMEOUT);
|
||
}
|
||
}
|
||
else {
|
||
//
|
||
// No timeout was specified in the registry, wait for process to
|
||
// complete.
|
||
//
|
||
waitResult = WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||
|
||
}
|
||
|
||
//
|
||
// Check if the wait failed above. If last error is something useful,
|
||
// we don't want to destroy it--if it's ERROR_SUCCESS, we'll set it to
|
||
// ERROR_TIMEOUT
|
||
//
|
||
status = GetLastError();
|
||
_AsrpErrExitCode((WAIT_OBJECT_0!=waitResult), status,
|
||
(ERROR_SUCCESS == status ? ERROR_TIMEOUT : status)); // wait failed above
|
||
|
||
//
|
||
// Get the process's exit code: if it doesn't return ERROR_SUCCESS,
|
||
// we exit the loop, set the last error to the error returned,
|
||
// and return FALSE
|
||
//
|
||
GetExitCodeProcess(processInfo.hProcess, &status);
|
||
_AsrpErrExitCode((ERROR_SUCCESS != status), status, status);
|
||
|
||
_AsrpCloseHandle(dupSifHandle);
|
||
_AsrpHeapFree(lpEnvBlock);
|
||
|
||
} while (ERROR_SUCCESS == status);
|
||
|
||
|
||
EXIT:
|
||
//
|
||
// Clean-up
|
||
//
|
||
if (regKey) {
|
||
RegCloseKey(regKey);
|
||
regKey = NULL;
|
||
}
|
||
|
||
_AsrpCloseHandle(dupSifHandle);
|
||
_AsrpHeapFree(valueName);
|
||
_AsrpHeapFree(data);
|
||
_AsrpHeapFree(command);
|
||
_AsrpHeapFree(lpEnvBlock);
|
||
|
||
if (ERROR_SUCCESS != status) {
|
||
SetLastError(status);
|
||
return FALSE;
|
||
}
|
||
else {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpIsSupportedConfiguration(
|
||
IN CONST PASR_DISK_INFO pDiskList,
|
||
IN CONST PASR_SYSTEM_INFO pSystemInfo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks if ASR backup can be performed on the system. We do not support
|
||
systems that have:
|
||
- PROCESSOR_ARCHITECTURE other than "x86", "amd64", or "ia64"
|
||
- any FT volumes present anywhere on the system
|
||
|
||
Arguments:
|
||
|
||
pDiskList - The list of disks on the system.
|
||
|
||
pSystemInfo - System information for this system.
|
||
|
||
Return Value:
|
||
|
||
If we support this ASR configuration, the return value is non-zero.
|
||
|
||
If this configuration is not supported, the return value is zero.
|
||
GetLastError() will return ERROR_NOT_SUPPORTED.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PASR_DISK_INFO pCurrentDisk = pDiskList;
|
||
ULONG index;
|
||
|
||
//
|
||
// 1. platform must be x86, amd64, or ia64
|
||
//
|
||
if (wcscmp(pSystemInfo->Platform, ASR_PLATFORM_X86) &&
|
||
wcscmp(pSystemInfo->Platform, ASR_PLATFORM_AMD64) &&
|
||
wcscmp(pSystemInfo->Platform, ASR_PLATFORM_IA64)) {
|
||
|
||
SetLastError(ERROR_NOT_SUPPORTED);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// 2. System cannot any FT volumes. All mirrors, stripes and so on are
|
||
// expected to be LDM volumes on dynamic disks.
|
||
//
|
||
while (pCurrentDisk) {
|
||
|
||
if (!(pCurrentDisk->pDriveLayoutEx) || !(pCurrentDisk->pDiskGeometry)) {
|
||
MYASSERT(0);
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
continue;
|
||
}
|
||
|
||
if (pCurrentDisk->pDriveLayoutEx->PartitionStyle == PARTITION_STYLE_MBR) {
|
||
|
||
for (index =0; index < pCurrentDisk->pDriveLayoutEx->PartitionCount; index++) {
|
||
|
||
MYASSERT(pCurrentDisk->pDriveLayoutEx->PartitionEntry[index].PartitionStyle == PARTITION_STYLE_MBR);
|
||
|
||
if (IsFTPartition(pCurrentDisk->pDriveLayoutEx->PartitionEntry[index].Mbr.PartitionType)) {
|
||
|
||
SetLastError(ERROR_NOT_SUPPORTED);
|
||
return FALSE;
|
||
}
|
||
|
||
}
|
||
}
|
||
else if (pCurrentDisk->pDriveLayoutEx->PartitionStyle == PARTITION_STYLE_GPT) {
|
||
//
|
||
// GPT disks can't have FT Mirrors.
|
||
//
|
||
}
|
||
|
||
pCurrentDisk = pCurrentDisk->pNext;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// -----
|
||
// The following routines are helpers for AsrAddSifEntry
|
||
// -----
|
||
//
|
||
|
||
BOOL
|
||
AsrpSifCheckSectionNameSyntax(
|
||
IN PCWSTR lpSectionName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs some basic validation of lpSectionName to make sure that
|
||
it conforms to the expected format for a section header
|
||
|
||
Arguments:
|
||
|
||
lpSectionName - The null-terminated string to be checked.
|
||
|
||
Return Value:
|
||
|
||
If lpSectionName appears to be a valid section name, the return value is a
|
||
nonzero value.
|
||
|
||
If lpSectionName does not pass our basic validation, the return value is
|
||
zero. Note that GetLastError will NOT return additional error
|
||
information in this case.
|
||
|
||
--*/
|
||
|
||
{
|
||
UINT i = 0;
|
||
WCHAR wch = 0;
|
||
|
||
//
|
||
// Must be non-null
|
||
//
|
||
if (!lpSectionName) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Must have atleast 3 chars, ([.]) and at most ASR_SIF_ENTRY_MAX_CHARS
|
||
// chars
|
||
//
|
||
if ((ASR_SIF_ENTRY_MAX_CHARS < wcslen(lpSectionName)) ||
|
||
3 > wcslen(lpSectionName)) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// First char must be [, and last char must be ].
|
||
//
|
||
if (L'[' != lpSectionName[0] ||
|
||
L']' != lpSectionName[wcslen(lpSectionName)-1]) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Check for illegal characters. Legal set of chars: A-Z a-z . _
|
||
//
|
||
for (i = 1; i < wcslen(lpSectionName)-1; i++) {
|
||
|
||
wch = lpSectionName[i];
|
||
if ((wch < L'A' || wch > 'Z') &&
|
||
(wch < L'a' || wch > 'z') &&
|
||
(wch < L'0' || wch > '9') &&
|
||
(wch != L'.') &&
|
||
(wch != '_')) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpSifCheckCommandsEntrySyntax(
|
||
PCWSTR pwszEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs some basic validation of pwszEntry to make sure that it conforms
|
||
to the expected entry format for the Commands section
|
||
|
||
Arguments:
|
||
|
||
pwszEntry - The null-terminated string to be checked.
|
||
|
||
Return Value:
|
||
|
||
If pwszEntry appears to be a valid section name, the return value is a
|
||
nonzero value.
|
||
|
||
If pwszEntry does not pass our basic validation, the return value is
|
||
zero. Note that GetLastError will NOT return additional error
|
||
information in this case.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL fValid = FALSE;
|
||
|
||
if (!pwszEntry) {
|
||
return TRUE; // NULL is okay
|
||
}
|
||
|
||
//
|
||
// COMMANDS section entry format:
|
||
// system-key,sequence-number,action-on-completion,"command","parameters"
|
||
// system-key must be 1
|
||
// 1000 <= sequence-number <= 4999
|
||
// 0 <= action-on-completion <= 1
|
||
// command: no syntax check
|
||
// parameters: no syntax check
|
||
//
|
||
fValid = (
|
||
// must be atleast 10 chars (1,0000,0,c)
|
||
10 <= wcslen(pwszEntry) &&
|
||
|
||
// system-key must be 1
|
||
L'1' == pwszEntry[0] &&
|
||
L',' == pwszEntry[1] &&
|
||
|
||
// 1000 <= sequence-number <= 4999
|
||
L'1' <= pwszEntry[2] &&
|
||
L'4' >= pwszEntry[2] &&
|
||
|
||
L'0' <= pwszEntry[3] &&
|
||
L'9' >= pwszEntry[3] &&
|
||
|
||
L'0' <= pwszEntry[4] &&
|
||
L'9' >= pwszEntry[4] &&
|
||
|
||
L'0' <= pwszEntry[5] &&
|
||
L'9' >= pwszEntry[5] &&
|
||
|
||
L',' == pwszEntry[6] &&
|
||
|
||
// action-on-completion = [0|1]
|
||
L'0' <= pwszEntry[7] &&
|
||
L'1' >= pwszEntry[7]
|
||
);
|
||
|
||
return fValid;
|
||
}
|
||
|
||
|
||
INT
|
||
AsrpSkipMatchingQuotes(
|
||
IN PCWSTR pwszEntry,
|
||
IN const INT StartingOffset
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Checks if this entry starts with a quote. If it does, it finds the ending
|
||
quote, and returns the index of the char after the ending quote (usually
|
||
is a comma).
|
||
|
||
Arguments:
|
||
|
||
pwszEntry - The null-terminated string to check.
|
||
|
||
StartingOffset - The index of the starting-quote in pwszEntry.
|
||
|
||
Return Value:
|
||
|
||
If the character at StartingOffset is a quote, this returns the index of
|
||
the character after the next quote (the matching end-quote) in the
|
||
string. If a matching end-quote is not found, it returns -1.
|
||
|
||
If the character at StartingOffset is not a quote, this returns
|
||
StartingOffset.
|
||
|
||
Essentially, this returns the position where we expect the next comma in
|
||
the sif entry to be.
|
||
|
||
--*/
|
||
|
||
{
|
||
INT offset = StartingOffset;
|
||
|
||
if (pwszEntry[offset] == L'"') {
|
||
//
|
||
// Find the ending quote and make sure we don't go out of bounds.
|
||
//
|
||
while ( (pwszEntry[++offset]) &&
|
||
(pwszEntry[offset] != L'\"')) {
|
||
;
|
||
}
|
||
|
||
if (!pwszEntry[offset]) {
|
||
//
|
||
// We didn't find the closing quotes--we went out of bounds
|
||
//
|
||
offset = -1;
|
||
}
|
||
else {
|
||
//
|
||
// Found closing quote
|
||
//
|
||
offset++;
|
||
}
|
||
}
|
||
|
||
return offset;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpSifCheckInstallFilesEntrySyntax(
|
||
IN PCWSTR pwszEntry,
|
||
OUT PINT DestinationFilePathIndex OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Performs some basic validation of pwszEntry to make sure that it conforms
|
||
to the expected entry format for the InstallFiles section
|
||
|
||
Arguments:
|
||
|
||
pwszEntry - The null-terminated string to be checked.
|
||
|
||
DestinationFilePathIndex - This receives the index at which the
|
||
destination-file-path field in the sif entry (pwszEntry) begins.
|
||
|
||
This is an optional parameter.
|
||
|
||
Return Value:
|
||
|
||
If pwszEntry appears to be a valid section name, the return value is a
|
||
nonzero value.
|
||
|
||
If pwszEntry does not pass our basic validation, the return value is
|
||
zero. Note that GetLastError will NOT return additional error
|
||
information in this case.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
INT offset = 0;
|
||
|
||
if (ARGUMENT_PRESENT(DestinationFilePathIndex)) {
|
||
*DestinationFilePathIndex = 0;
|
||
}
|
||
|
||
//
|
||
// NULL is okay
|
||
//
|
||
if (!pwszEntry) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// INSTALLFILES section entry format:
|
||
// system-key,source-media-label,source-device,
|
||
// source-file-path,destination-file-path,vendor-name,flags
|
||
//
|
||
// system-key must be 1
|
||
//
|
||
// must be atleast 10 chars (1,m,d,p,,v)
|
||
//
|
||
if (wcslen(pwszEntry) < 10) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// system-key must be 1
|
||
//
|
||
if (L'1' != pwszEntry[0] || L',' != pwszEntry[1] || L'"' != pwszEntry[2]) {
|
||
return FALSE;
|
||
}
|
||
|
||
offset = 2;
|
||
|
||
//
|
||
// source-media-label
|
||
//
|
||
offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
|
||
if ((offset < 0) || L',' != pwszEntry[offset]) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// source-device
|
||
//
|
||
if (L'"' != pwszEntry[++offset]) {
|
||
return FALSE;
|
||
}
|
||
offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
|
||
if ((offset < 0) || L',' != pwszEntry[offset]) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// source-file-path, must be enclosed in quotes.
|
||
//
|
||
if (L'"' != pwszEntry[++offset]) {
|
||
return FALSE;
|
||
}
|
||
offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
|
||
if ((offset < 0) || L',' != pwszEntry[offset]) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// destination-file-path, must be enclosed in quotes.
|
||
//
|
||
if (L'"' != pwszEntry[++offset]) {
|
||
return FALSE;
|
||
}
|
||
if (ARGUMENT_PRESENT(DestinationFilePathIndex)) {
|
||
*DestinationFilePathIndex = offset;
|
||
}
|
||
|
||
offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
|
||
if ((offset < 0) || L',' != pwszEntry[offset]) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// vendor-name, must be enclosed in quotes.
|
||
//
|
||
if (L'"' != pwszEntry[++offset]) {
|
||
return FALSE;
|
||
}
|
||
offset = AsrpSkipMatchingQuotes(pwszEntry, offset);
|
||
if (offset < 0) {
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpIsRunningOnPersonalSKU(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
Routine Description:
|
||
|
||
This function checks the system to see if we are running on the personal
|
||
version of the operating system.
|
||
|
||
The personal version is denoted by the product id equal to WINNT, which is
|
||
really workstation, and the product suite containing the personal suite
|
||
string.
|
||
|
||
This is lifted from "IsRunningOnPersonal" by WesW.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE if we are running on personal, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
OSVERSIONINFOEXW OsVer = {0};
|
||
ULONGLONG ConditionMask = 0;
|
||
|
||
OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||
OsVer.wSuiteMask = VER_SUITE_PERSONAL;
|
||
OsVer.wProductType = VER_NT_WORKSTATION;
|
||
|
||
VER_SET_CONDITION(ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
|
||
VER_SET_CONDITION(ConditionMask, VER_SUITENAME, VER_AND);
|
||
|
||
return VerifyVersionInfo(&OsVer,
|
||
VER_PRODUCT_TYPE | VER_SUITENAME,
|
||
ConditionMask
|
||
);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpIsInGroup(
|
||
IN CONST DWORD dwGroup
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This function checks to see if the specified SID is enabled
|
||
in the primary access token for the current thread.
|
||
|
||
This is based on a similar function in dmadmin.exe.
|
||
|
||
Arguments:
|
||
|
||
dwGroup - The SID to be checked for
|
||
|
||
Return Value:
|
||
|
||
TRUE if the specified SID is enabled, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
SID_IDENTIFIER_AUTHORITY sidAuth = SECURITY_NT_AUTHORITY;
|
||
|
||
PSID sidGroup = NULL;
|
||
|
||
BOOL bResult = FALSE,
|
||
bIsInGroup = TRUE;
|
||
|
||
//
|
||
// Build the SID for the Administrators group
|
||
//
|
||
bResult = AllocateAndInitializeSid(&sidAuth,
|
||
2,
|
||
SECURITY_BUILTIN_DOMAIN_RID,
|
||
dwGroup,
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
0,
|
||
&sidGroup
|
||
);
|
||
if (!bResult) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Check the current thread token membership
|
||
//
|
||
bResult = CheckTokenMembership(NULL, sidGroup, &bIsInGroup);
|
||
|
||
FreeSid(sidGroup);
|
||
|
||
return (bResult && bIsInGroup);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrpHasPrivilege(
|
||
CONST PCWSTR szPrivilege
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This function checks to see if the specified privilege is enabled
|
||
in the primary access token for the current thread.
|
||
|
||
This is based on a similar function in dmadmin.exe.
|
||
|
||
Arguments:
|
||
|
||
szPrivilege - The privilege to be checked for
|
||
|
||
Return Value:
|
||
|
||
TRUE if the specified privilege is enabled, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
LUID luidValue; // LUID (locally unique ID) for the privilege
|
||
|
||
BOOL bResult = FALSE,
|
||
bHasPrivilege = FALSE;
|
||
|
||
HANDLE hToken = NULL;
|
||
|
||
PRIVILEGE_SET privilegeSet;
|
||
|
||
//
|
||
// Get the LUID for the privilege from the privilege name
|
||
//
|
||
bResult = LookupPrivilegeValue(
|
||
NULL,
|
||
szPrivilege,
|
||
&luidValue
|
||
);
|
||
if (!bResult) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// We want to use the token for the current process
|
||
//
|
||
bResult = OpenProcessToken(GetCurrentProcess(),
|
||
MAXIMUM_ALLOWED,
|
||
&hToken
|
||
);
|
||
if (!bResult) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// And check for the privilege
|
||
//
|
||
privilegeSet.PrivilegeCount = 1;
|
||
privilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
|
||
privilegeSet.Privilege[0].Luid = luidValue;
|
||
privilegeSet.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||
|
||
bResult = PrivilegeCheck(hToken, &privilegeSet, &bHasPrivilege);
|
||
|
||
CloseHandle(hToken);
|
||
|
||
return (bResult && bHasPrivilege);
|
||
}
|
||
|
||
|
||
|
||
BOOL
|
||
AsrpCheckBackupPrivilege(
|
||
VOID
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This function checks to see if the current process has the
|
||
SE_BACKUP_NAME privilege enabled.
|
||
|
||
This is based on a similar function in dmadmin.exe.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the SE_BACKUP_NAME privilege is enabled, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
BOOL bHasPrivilege = FALSE;
|
||
|
||
bHasPrivilege = AsrpHasPrivilege(SE_BACKUP_NAME);
|
||
|
||
/*
|
||
//
|
||
// Don't give up yet--check for the local administrator rights
|
||
//
|
||
if (!bHasPrivilege) {
|
||
bHasPrivilege = AsrpIsInGroup(DOMAIN_ALIAS_RID_ADMINS);
|
||
}
|
||
*/
|
||
|
||
if (!bHasPrivilege) {
|
||
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
|
||
}
|
||
|
||
return bHasPrivilege;
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// -------------------
|
||
// Public functions
|
||
// -------------------
|
||
//
|
||
// The functions below are for use by external backup and
|
||
// restore applications supporting ASR.
|
||
//
|
||
|
||
|
||
//
|
||
// ---- AsrCreateStateFile
|
||
//
|
||
BOOL
|
||
AsrCreateStateFileW(
|
||
IN PCWSTR lpFilePath OPTIONAL,
|
||
IN PCWSTR lpProviderName OPTIONAL,
|
||
IN CONST BOOL bEnableAutoExtend,
|
||
IN PCWSTR mszCriticalVolumes,
|
||
OUT DWORD_PTR *lpAsrContext
|
||
)
|
||
|
||
/*--
|
||
|
||
Routine Description:
|
||
|
||
AsrCreateStateFile creates an ASR state file with basic information about
|
||
the system, and launches third-party applications that have been
|
||
registered to be run as part of an ASR backup.
|
||
|
||
Arguments:
|
||
|
||
lpFileName - Pointer to a null-terminated string that specifies the
|
||
full path where the ASR state-file is to be created. If a file
|
||
already exists at the location pointed to by this parameter, it is
|
||
over-written.
|
||
|
||
This parameter can be NULL. If it is NULL, the ASR state-file is
|
||
created at the default location (%systemroot%\repair\asr.sif).
|
||
|
||
lpProviderName - Pointer to a null-terminated string that specifies the
|
||
full name and version of the backup-and-restore application
|
||
calling AsrCreateStateFile. There is a string size limit of
|
||
(ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING) characters
|
||
for this parameter.
|
||
|
||
This parameter can be NULL. If it is NULL, a "Provider=" entry is
|
||
not created in the Version section of the ASR state file.
|
||
|
||
bEnableAutoExtend - Indicates whether partitions are to be auto-extended
|
||
during an ASR restore. If this parameter is TRUE, partitions will be
|
||
auto-extended during the ASR restore. If this is FALSE,
|
||
partitions will not be extended.
|
||
|
||
lpCriticalVolumes - Pointer to a multi-string containing volume-GUID<49>s for
|
||
the critical volumes. This list is used to obtain the list of
|
||
critical disks in the system that must be restored for a
|
||
successful ASR.
|
||
|
||
The volume-GUID's must be in the NT-namespace, of the form
|
||
\??\Volume{Guid}
|
||
|
||
This parameter cannot be NULL.
|
||
|
||
lpAsrContext - Pointer to a variable receiving an ASR context. The
|
||
context returned should be used in calls to the other ASR API,
|
||
including AsrAddSifEntry. The calling application must call
|
||
AsrFreeContext to free this context when it is no longer needed.
|
||
|
||
This parameter cannot be NULL.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL result = FALSE;
|
||
|
||
DWORD status = ERROR_SUCCESS,
|
||
size = 0;
|
||
|
||
ULONG maxDeviceNumber = 0;
|
||
|
||
HANDLE sifhandle = NULL,
|
||
heapHandle = NULL;
|
||
|
||
PWSTR asrSifPath = NULL,
|
||
pnpSifPath = NULL,
|
||
tempPointer = NULL;
|
||
|
||
UINT cchAsrSifPath = 0;
|
||
|
||
char UnicodeFlag[3];
|
||
|
||
WCHAR infstring[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
|
||
SECURITY_ATTRIBUTES securityAttributes;
|
||
ASR_SYSTEM_INFO SystemInfo;
|
||
PASR_DISK_INFO OriginalDiskList = NULL;
|
||
|
||
|
||
if (AsrpIsRunningOnPersonalSKU()) {
|
||
//
|
||
// ASR is not supported on the Personal SKU
|
||
//
|
||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!AsrpCheckBackupPrivilege()) {
|
||
//
|
||
// The caller needs to first acquire SE_BACKUP_NAME
|
||
//
|
||
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Check the IN parameters:
|
||
//
|
||
#ifdef PRERELEASE
|
||
//
|
||
// Don't enforce "CriticalVolumes must be non-NULL" for test
|
||
//
|
||
if (!(lpAsrContext))
|
||
#else
|
||
if (!(lpAsrContext && mszCriticalVolumes))
|
||
#endif
|
||
{
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Set the OUT paramaters to known error values
|
||
//
|
||
*lpAsrContext = 0;
|
||
|
||
//
|
||
// Guard ourselves against returning ERROR_SUCCESS if we encountered
|
||
// an unexpected error. We should never actually return this, since
|
||
// we always SetLastError whereever we return FALSE from.
|
||
//
|
||
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
||
|
||
//
|
||
// Zero out structs
|
||
//
|
||
memset(&SystemInfo, 0L, sizeof (SYSTEM_INFO));
|
||
|
||
heapHandle = GetProcessHeap();
|
||
|
||
//
|
||
// Determine the file-path. If lpFilePath is provided, copy it over to
|
||
// locally allocated memory and use it, else use the default path.
|
||
//
|
||
if (ARGUMENT_PRESENT(lpFilePath)) {
|
||
cchAsrSifPath = wcslen(lpFilePath);
|
||
//
|
||
// Do a sanity check: we don't want to allow a file path
|
||
// more than 4096 characters long.
|
||
//
|
||
if (cchAsrSifPath > ASR_SIF_ENTRY_MAX_CHARS) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
asrSifPath = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
((cchAsrSifPath + 1) * sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode(!asrSifPath, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
wcsncpy(asrSifPath, lpFilePath, cchAsrSifPath);
|
||
|
||
}
|
||
else {
|
||
//
|
||
// lpFilePath is NULL, form the default path (of the form
|
||
// \\?\c:\windows\repair\asr.sif)
|
||
//
|
||
|
||
//
|
||
// Try with a reasonably sized buffer to begin with.
|
||
//
|
||
asrSifPath = AsrpExpandEnvStrings(ASR_DEFAULT_SIF_PATH);
|
||
_AsrpErrExitCode(!asrSifPath, status, ERROR_BAD_ENVIRONMENT);
|
||
|
||
//
|
||
// Set cchAsrSifPath to the size of the asrSif buffer, since we
|
||
// use this in determining the size of the pnpSif buffer below.
|
||
//
|
||
cchAsrSifPath = wcslen(asrSifPath);
|
||
}
|
||
|
||
//
|
||
// Determine the file-path of the asrpnp.sif file, based on the location
|
||
// of the asr.sif file.
|
||
//
|
||
pnpSifPath = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
((cchAsrSifPath + 1 + wcslen(ASRPNP_DEFAULT_SIF_NAME))* sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode(!pnpSifPath, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
wcscpy(pnpSifPath, asrSifPath);
|
||
|
||
tempPointer = pnpSifPath;
|
||
while (*tempPointer) {
|
||
tempPointer++;
|
||
}
|
||
while ((*tempPointer != L'\\')
|
||
&& (*tempPointer != L':')
|
||
&& (tempPointer >= pnpSifPath)
|
||
) {
|
||
tempPointer--;
|
||
}
|
||
tempPointer++;
|
||
wcscpy(tempPointer, ASRPNP_DEFAULT_SIF_NAME);
|
||
|
||
//
|
||
// We need to make the handle to asr.sif inheritable, since it will
|
||
// be passed (in the guise of the "AsrContext") to apps that have
|
||
// registered to be run as part of ASR.
|
||
//
|
||
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||
securityAttributes.lpSecurityDescriptor = NULL;
|
||
securityAttributes.bInheritHandle = TRUE;
|
||
|
||
//
|
||
// Create the file. The handle will be closed by the calling backup-app.
|
||
//
|
||
sifhandle = CreateFileW(
|
||
asrSifPath, // lpFileName
|
||
GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess
|
||
FILE_SHARE_READ, // dwShareMode
|
||
&securityAttributes, // lpSecurityAttributes
|
||
CREATE_ALWAYS, // dwCreationFlags
|
||
FILE_FLAG_BACKUP_SEMANTICS, // dwFlagsAndAttributes
|
||
NULL // hTemplateFile
|
||
);
|
||
if (!sifhandle || INVALID_HANDLE_VALUE == sifhandle) {
|
||
//
|
||
// LastError is set by CreateFile
|
||
//
|
||
_AsrpErrExitCode(TRUE, status, GetLastError());
|
||
}
|
||
|
||
//
|
||
// File was successfully created. Add the unicode flag at the beginning
|
||
// of the file, followed by comments.
|
||
//
|
||
sprintf(UnicodeFlag, "%c%c", 0xFF, 0xFE);
|
||
result = WriteFile(sifhandle, UnicodeFlag,
|
||
strlen(UnicodeFlag)*sizeof(char), &size, NULL);
|
||
_AsrpErrExitCode(!result, status, GetLastError());
|
||
|
||
wcscpy(infstring,
|
||
L";\r\n; Microsoft Windows Automated System Recovery State Information File\r\n;\r\n");
|
||
result = WriteFile(sifhandle, infstring,
|
||
wcslen(infstring)*sizeof(WCHAR), &size, NULL);
|
||
_AsrpErrExitCode(!result, status, GetLastError());
|
||
|
||
//
|
||
// Beyond this point, we must zero out asr.sif on any failure. Also, if
|
||
// there's any failure, we must be careful not to make further system
|
||
// calls that could change the error returned by GetLastError().
|
||
//
|
||
|
||
//
|
||
// Since the function return values below are and-ed, if any of the calls
|
||
// fails, we won't execute the ones following it.
|
||
//
|
||
result = (
|
||
//
|
||
// Initialise the global structures
|
||
//
|
||
AsrpInitSystemInformation(&SystemInfo, bEnableAutoExtend)
|
||
|
||
&& AsrpInitDiskInformation(&OriginalDiskList)
|
||
|
||
&& AsrpInitLayoutInformation(&SystemInfo,
|
||
OriginalDiskList,
|
||
&maxDeviceNumber,
|
||
TRUE
|
||
)
|
||
|
||
&& AsrpInitClusterSharedDisks(OriginalDiskList)
|
||
|
||
&& AsrpFreeNonFixedMedia(&OriginalDiskList)
|
||
|
||
&& AsrpMarkCriticalDisks(OriginalDiskList,
|
||
mszCriticalVolumes,
|
||
maxDeviceNumber
|
||
)
|
||
|
||
//
|
||
// Check if the system configuration is supported
|
||
//
|
||
&& AsrpIsSupportedConfiguration(OriginalDiskList, &SystemInfo)
|
||
|
||
//
|
||
// Write the required sections to asr.sif
|
||
//
|
||
&& AsrpWriteVersionSection(sifhandle, lpProviderName)
|
||
&& AsrpWriteSystemsSection(sifhandle, &SystemInfo)
|
||
&& AsrpWriteBusesSection(sifhandle, OriginalDiskList)
|
||
&& AsrpWriteMbrDisksSection(sifhandle, OriginalDiskList)
|
||
&& AsrpWriteGptDisksSection(sifhandle, OriginalDiskList)
|
||
|
||
&& AsrpWriteMbrPartitionsSection(sifhandle,
|
||
OriginalDiskList,
|
||
&SystemInfo
|
||
)
|
||
|
||
&& AsrpWriteGptPartitionsSection(sifhandle,
|
||
OriginalDiskList,
|
||
&SystemInfo
|
||
)
|
||
|
||
&& FlushFileBuffers(sifhandle)
|
||
|
||
//
|
||
// Create asrpnp.sif, containing entries needed to recover the PnP
|
||
// entries in the registry
|
||
//
|
||
&& AsrCreatePnpStateFileW(pnpSifPath)
|
||
|
||
);
|
||
|
||
if (result) {
|
||
// everything above succeeded
|
||
|
||
//
|
||
// Launch the apps registered to be run as part of ASR-backup. If any
|
||
// of these apps don't complete successfully, we'll fail the ASR-
|
||
// backup.
|
||
//
|
||
result = (
|
||
AsrpLaunchRegisteredCommands(sifhandle, mszCriticalVolumes)
|
||
|
||
&& FlushFileBuffers(sifhandle)
|
||
);
|
||
|
||
}
|
||
|
||
if (!result) {
|
||
//
|
||
// One of the functions above failed--we'll make asr.sif zero-length
|
||
// and return the error. CreateFileW or CloseHandle might over-write
|
||
// the LastError, so we save our error now and set it at the end.
|
||
//
|
||
status = GetLastError();
|
||
|
||
#ifndef PRERELEASE
|
||
|
||
//
|
||
// On release versions, we wipe out the asr.sif if we hit an error,
|
||
// so that the user doesn't unknowingly end up with an incomplete
|
||
// asr.sif
|
||
//
|
||
// We don't want to delete the incomplete asr.sif during test cycles,
|
||
// though, since the sif may be useful for debugging.
|
||
//
|
||
_AsrpCloseHandle(sifhandle);
|
||
|
||
//
|
||
// Delete asr.sif and create it again, so that we have a zero-length
|
||
// asr.sif
|
||
//
|
||
DeleteFileW(asrSifPath);
|
||
/* sifhandle = CreateFileW(
|
||
asrSifPath, // lpFileName
|
||
GENERIC_WRITE, // dwDesiredAccess
|
||
0, // dwShareMode
|
||
&securityAttributes, // lpSecurityAttributes
|
||
CREATE_ALWAYS, // dwCreationFlags
|
||
FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes
|
||
NULL // hTemplateFile
|
||
);
|
||
|
||
_AsrpCloseHandle(sifhandle);
|
||
*/
|
||
#endif
|
||
SetLastError(status);
|
||
}
|
||
|
||
|
||
EXIT:
|
||
//
|
||
// Clean up
|
||
//
|
||
_AsrpHeapFree(asrSifPath);
|
||
_AsrpHeapFree(pnpSifPath);
|
||
|
||
AsrpFreeStateInformation(&OriginalDiskList, &SystemInfo);
|
||
|
||
|
||
//
|
||
// Set the OUT parameters
|
||
//
|
||
*lpAsrContext = (DWORD_PTR)sifhandle;
|
||
|
||
if (ERROR_SUCCESS != status) {
|
||
SetLastError(status);
|
||
}
|
||
|
||
if (!result) {
|
||
if (ERROR_SUCCESS == GetLastError()) {
|
||
//
|
||
// We're going to return failure, but we haven't set the LastError to
|
||
// a failure code. This is bad, since we have no clue what went wrong.
|
||
//
|
||
// We shouldn't ever get here, because the function returning FALSE above
|
||
// should set the LastError as it sees fit.
|
||
//
|
||
// But I've added this in just to be safe. Let's set it to a generic
|
||
// error.
|
||
//
|
||
MYASSERT(0 && L"Returning failure, but LastError is not set");
|
||
SetLastError(ERROR_CAN_NOT_COMPLETE);
|
||
}
|
||
}
|
||
|
||
return ((result) && (ERROR_SUCCESS == status));
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrCreateStateFileA(
|
||
IN LPCSTR lpFilePath,
|
||
IN LPCSTR lpProviderName,
|
||
IN CONST BOOL bEnableAutoExtend,
|
||
IN LPCSTR mszCriticalVolumes,
|
||
OUT DWORD_PTR *lpAsrContext
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
This is the ANSI wrapper for AsrCreateStateFile. Please see
|
||
AsrCreateStateFileW for a detailed description.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
{
|
||
PWSTR asrSifPath = NULL,
|
||
providerName = NULL,
|
||
lpwszCriticalVolumes = NULL;
|
||
|
||
DWORD cchString = 0,
|
||
status = ERROR_SUCCESS;
|
||
|
||
BOOL result = FALSE;
|
||
|
||
HANDLE heapHandle = GetProcessHeap();
|
||
|
||
if (AsrpIsRunningOnPersonalSKU()) {
|
||
//
|
||
// ASR is not supported on the Personal SKU
|
||
//
|
||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!AsrpCheckBackupPrivilege()) {
|
||
//
|
||
// The caller needs to first acquire SE_BACKUP_NAME
|
||
//
|
||
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Check the IN parameters
|
||
//
|
||
#ifdef PRERELEASE
|
||
//
|
||
// Don't enforce "CriticalVolumes must be non-NULL" for test
|
||
//
|
||
if (!(lpAsrContext)) {
|
||
#else
|
||
if (!(lpAsrContext && mszCriticalVolumes)) {
|
||
#endif
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// if lpFilePath is not NULL, allocate a big enough buffer to hold
|
||
// it, and convert it to wide char
|
||
//
|
||
if (lpFilePath) {
|
||
cchString = strlen(lpFilePath);
|
||
//
|
||
// Do a sanity check: we don't want to allow a file path
|
||
// more than 4096 characters long.
|
||
//
|
||
_AsrpErrExitCode(
|
||
(cchString > ASR_SIF_ENTRY_MAX_CHARS),
|
||
status,
|
||
ERROR_INVALID_PARAMETER
|
||
);
|
||
|
||
//
|
||
// Allocate a big enough buffer, and copy it over
|
||
//
|
||
asrSifPath = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
((cchString + 1) * sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode(!asrSifPath, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
result = MultiByteToWideChar(CP_ACP, // CodePage
|
||
0, // dwFlags
|
||
lpFilePath, // lpMultiByteStr
|
||
-1, // cbMultiByte: -1 since lpMultiByteStr is null terminated
|
||
asrSifPath, // lpWideCharStr
|
||
(cchString + 1) // cchWideChar
|
||
);
|
||
_AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// if lpProviderName is not NULL, make sure it isn't insanely long,
|
||
// and convert it to wide char
|
||
//
|
||
if (lpProviderName) {
|
||
cchString = strlen(lpProviderName);
|
||
//
|
||
// Do a sanity check: we don't want to allow an entry
|
||
// more than 4096 characters long.
|
||
//
|
||
_AsrpErrExitCode(
|
||
(cchString > (ASR_SIF_ENTRY_MAX_CHARS - ASR_SIF_CCH_PROVIDER_STRING)),
|
||
status,
|
||
ERROR_INVALID_PARAMETER
|
||
);
|
||
|
||
//
|
||
// Allocate a big enough buffer, and copy it over
|
||
//
|
||
providerName = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
((cchString + 1) * sizeof(WCHAR))
|
||
);
|
||
_AsrpErrExitCode(!providerName, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// Convert to wide string
|
||
//
|
||
result = MultiByteToWideChar(CP_ACP,
|
||
0,
|
||
lpProviderName,
|
||
-1,
|
||
providerName,
|
||
cchString + 1
|
||
);
|
||
_AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
|
||
|
||
}
|
||
|
||
if (mszCriticalVolumes) {
|
||
//
|
||
// Find the total length of mszCriticalVolumes
|
||
//
|
||
LPCSTR lpVolume = mszCriticalVolumes;
|
||
|
||
while (*lpVolume) {
|
||
lpVolume += (strlen(lpVolume) + 1);
|
||
}
|
||
|
||
//
|
||
// Convert the string to wide-chars
|
||
//
|
||
cchString = (DWORD) (lpVolume - mszCriticalVolumes + 1);
|
||
lpwszCriticalVolumes = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
cchString * sizeof(WCHAR)
|
||
);
|
||
_AsrpErrExitCode(!lpwszCriticalVolumes, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
result = MultiByteToWideChar(CP_ACP,
|
||
0,
|
||
mszCriticalVolumes,
|
||
cchString,
|
||
lpwszCriticalVolumes,
|
||
cchString * sizeof(WCHAR)
|
||
);
|
||
_AsrpErrExitCode(!result, status, ERROR_INVALID_PARAMETER);
|
||
}
|
||
|
||
result = AsrCreateStateFileW(
|
||
asrSifPath,
|
||
providerName,
|
||
bEnableAutoExtend,
|
||
lpwszCriticalVolumes,
|
||
lpAsrContext
|
||
);
|
||
|
||
EXIT:
|
||
_AsrpHeapFree(asrSifPath);
|
||
_AsrpHeapFree(providerName);
|
||
_AsrpHeapFree(lpwszCriticalVolumes);
|
||
|
||
return ((result) && (ERROR_SUCCESS == status));
|
||
}
|
||
|
||
|
||
//
|
||
// ---- AsrAddSifEntry
|
||
//
|
||
BOOL
|
||
AsrAddSifEntryW(
|
||
IN DWORD_PTR AsrContext,
|
||
IN PCWSTR lpSectionName,
|
||
IN PCWSTR lpSifEntry OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The AsrSifEntry function adds entries to the ASR state file. It can be
|
||
used by applications that need to save application-specific information
|
||
in the ASR state file.
|
||
|
||
Arguments:
|
||
|
||
AsrContext - A valid ASR context. See the notes for more information
|
||
about this parameter.
|
||
|
||
lpSectionName - Pointer to a null-terminated string that specifies the
|
||
section name. This parameter cannot be NULL.
|
||
|
||
The section name has a string size limit of ASR_MAX_SIF_LINE
|
||
characters. This limit is related to how the AsrAddSifEntry
|
||
function parses entries in the ASR state file.
|
||
|
||
The section name is case-insensitive. It is converted to all-caps
|
||
before being added to the state file. The section name must not
|
||
contain spaces or non-printable characters. The valid character
|
||
set for section name is limited to letters (A-Z, a-z), numbers
|
||
(0-9), and the following special characters: underscore ("_")
|
||
and period ("."). If the state file does not contain a section
|
||
with the section name pointed to by lpSectionName, a new
|
||
section is created with this section name.
|
||
|
||
lpSifEntry - Pointer to a null-terminated string that is to be added to
|
||
the state file in the specified section. If *lpSifEntry is a
|
||
valid entry, there is a string size limit of
|
||
ASR_SIF_ENTRY_MAX_CHARS characters. This limit is related
|
||
to how the AsrAddSifEntry function parses entries in the
|
||
ASR state file.
|
||
|
||
If lpSifEntry parameter is NULL, an empty section with the
|
||
section name pointed to by lpSectionName is created if it
|
||
doesn't already exist.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
Notes:
|
||
|
||
The application calling AsrAddSifEntry obtains the ASR context by one of
|
||
two methods:
|
||
- If the application is the backup-and-restore application that creates
|
||
the ASR state file, it receives the context as a parameter returned by
|
||
AsrCreateStateFile.
|
||
- If the application is launched by AsrCreateStateFile as part of an ASR
|
||
backup, it receives the context to the state file as the /context
|
||
command-line parameter. The application is responsible for reading
|
||
this parameter to get the value of the context.
|
||
|
||
AsrAddSifEntry will fail if the section name is that of a reserved section
|
||
that applications are not allowed to add entries to. The following sections
|
||
in the ASR state file are reserved:
|
||
- Version, System, Disks.Mbr, Disk.Gpt, Partitions.Mbr and Partitions.Gpt
|
||
|
||
If the section name is recognised (Commands or InstallFiles), AsrAddSifEntry
|
||
will check the syntax of *lpSifEntry to ensure that it is in the proper
|
||
format. In addition, AsrAddSifEntry will check to ensure that there are no
|
||
filename collisions for the InstallFiles section. If a collision is
|
||
detected, the API returns ERROR_ALREADY_EXISTS. Applications must
|
||
use the following pre-defined values to access the recognised sections:
|
||
- ASR_COMMANDS_SECTION_NAME_W for the Commands section, and
|
||
- ASR_INSTALLFILES_SECTION_NAME for the InstallFiles section.
|
||
|
||
--*/
|
||
{
|
||
DWORD status = ERROR_SUCCESS,
|
||
nextKey = 0,
|
||
fileOffset = 0,
|
||
size = 0,
|
||
fileSize = 0,
|
||
bufferSize = 0,
|
||
destFilePos = 0;
|
||
|
||
HANDLE sifhandle = NULL;
|
||
|
||
WCHAR sifstring[ASR_SIF_ENTRY_MAX_CHARS *2 + 1],
|
||
ucaseSectionName[ASR_SIF_ENTRY_MAX_CHARS + 1]; // lpSectionName converted to upper case
|
||
|
||
PWSTR buffer = NULL,
|
||
sectionStart = NULL,
|
||
lastEqual = NULL,
|
||
nextSection = NULL,
|
||
nextChar = NULL,
|
||
sectionName = NULL;
|
||
|
||
BOOL commandsSection = FALSE,
|
||
installFilesSection = FALSE,
|
||
result = FALSE;
|
||
|
||
HANDLE heapHandle = NULL;
|
||
|
||
if (AsrpIsRunningOnPersonalSKU()) {
|
||
//
|
||
// ASR is not supported on the Personal SKU
|
||
//
|
||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!AsrpCheckBackupPrivilege()) {
|
||
//
|
||
// The caller needs to first acquire SE_BACKUP_NAME
|
||
//
|
||
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
|
||
return FALSE;
|
||
}
|
||
|
||
heapHandle = GetProcessHeap();
|
||
MYASSERT(heapHandle);
|
||
|
||
//
|
||
// Zero out local structs
|
||
//
|
||
memset(sifstring, 0, (ASR_SIF_ENTRY_MAX_CHARS *2 + 1) * sizeof(WCHAR));
|
||
memset(ucaseSectionName, 0, (ASR_SIF_ENTRY_MAX_CHARS + 1) * (sizeof (WCHAR)));
|
||
|
||
//
|
||
// No OUT parameters
|
||
//
|
||
|
||
//
|
||
// Check the IN parameters: The SectionName should meet
|
||
// syntax requirements, SifEntry shouldn't be too long,
|
||
// and the sifhandle should be valid.
|
||
//
|
||
if ((!AsrpSifCheckSectionNameSyntax(lpSectionName)) ||
|
||
|
||
(ARGUMENT_PRESENT(lpSifEntry)
|
||
&& (wcslen(lpSifEntry) > ASR_SIF_ENTRY_MAX_CHARS)) ||
|
||
|
||
((!AsrContext) ||
|
||
(INVALID_HANDLE_VALUE == (HANDLE)AsrContext))
|
||
|
||
) {
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
while (lpSectionName[size]) {
|
||
if ((lpSectionName[size] >= L'a') && (lpSectionName[size] <= L'z')) {
|
||
ucaseSectionName[size] = lpSectionName[size] - L'a' + L'A';
|
||
}
|
||
else {
|
||
ucaseSectionName[size] = lpSectionName[size];
|
||
}
|
||
size++;
|
||
}
|
||
|
||
//
|
||
// If the section is a recognised section (COMMANDS or INSTALLFILES),
|
||
// we check the format of the sif entry.
|
||
//
|
||
if (!wcscmp(ucaseSectionName, ASR_SIF_SECTION_COMMANDS_W)) {
|
||
|
||
// COMMANDS section
|
||
if (!AsrpSifCheckCommandsEntrySyntax(lpSifEntry)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
commandsSection = TRUE;
|
||
}
|
||
else if(!wcscmp(ucaseSectionName, ASR_SIF_SECTION_INSTALLFILES_W)) {
|
||
|
||
// INSTALLFILES section
|
||
if (!AsrpSifCheckInstallFilesEntrySyntax(lpSifEntry, &destFilePos)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
installFilesSection = TRUE;
|
||
}
|
||
|
||
//
|
||
// We do not allow anyone to write to reserved sections:
|
||
// VERSION, SYSTEMS, DISKS.[MBR|GPT], PARTITIONS.[MBR|GPT]
|
||
//
|
||
else if (
|
||
!wcscmp(ucaseSectionName, ASR_SIF_VERSION_SECTION_NAME) ||
|
||
!wcscmp(ucaseSectionName, ASR_SIF_SYSTEM_SECTION_NAME) ||
|
||
!wcscmp(ucaseSectionName, ASR_SIF_MBR_DISKS_SECTION_NAME) ||
|
||
!wcscmp(ucaseSectionName, ASR_SIF_GPT_DISKS_SECTION_NAME) ||
|
||
!wcscmp(ucaseSectionName, ASR_SIF_MBR_PARTITIONS_SECTION_NAME) ||
|
||
!wcscmp(ucaseSectionName, ASR_SIF_GPT_PARTITIONS_SECTION_NAME)
|
||
) {
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
sectionName = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
(wcslen(ucaseSectionName) + 5) * sizeof (WCHAR)
|
||
);
|
||
_AsrpErrExitCode(!sectionName, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
swprintf(sectionName, L"\r\n%ws\r\n", ucaseSectionName);
|
||
|
||
sifhandle = (HANDLE) AsrContext;
|
||
|
||
//
|
||
// The algorithm to add to the middle of asr.sif is rather ugly
|
||
// at the moment: we read the entire file into memory, make our
|
||
// necessary changes, and write back the changed portion of the
|
||
// file to disk. This is inefficient, but it's okay for now since
|
||
// we expect asr.sif to be about 5 or 6 KB at the most.
|
||
//
|
||
// We should revisit this if the performance is unacceptably poor.
|
||
//
|
||
|
||
//
|
||
// Allocate memory for the file
|
||
//
|
||
fileSize = GetFileSize(sifhandle, NULL);
|
||
GetLastError();
|
||
_AsrpErrExitCode((fileSize == 0xFFFFFFFF), status, ERROR_INVALID_DATA);
|
||
|
||
SetFilePointer(sifhandle, 0, NULL, FILE_BEGIN);
|
||
|
||
buffer = (PWSTR) HeapAlloc(
|
||
heapHandle,
|
||
HEAP_ZERO_MEMORY,
|
||
fileSize + 2
|
||
);
|
||
_AsrpErrExitCode(!buffer, status, ERROR_NOT_ENOUGH_MEMORY);
|
||
|
||
//
|
||
// And read file into memory.
|
||
//
|
||
result = ReadFile(sifhandle, buffer, fileSize, &size, NULL);
|
||
_AsrpErrExitCode(!result, status, GetLastError());
|
||
|
||
//
|
||
// Try to locate ucaseSectionName in the file
|
||
//
|
||
sectionStart = wcsstr(buffer, sectionName);
|
||
|
||
if (!sectionStart) {
|
||
|
||
//
|
||
// sectionName was not found, ie the section does not exist
|
||
// Add it at the end, and add the SifEntry right after it.
|
||
//
|
||
swprintf(sifstring,
|
||
L"\r\n%ws\r\n%ws%ws\r\n",
|
||
ucaseSectionName,
|
||
((commandsSection || installFilesSection) ? L"1=" : L""),
|
||
(ARGUMENT_PRESENT(lpSifEntry) ? lpSifEntry : L"")
|
||
);
|
||
|
||
//
|
||
// File pointer already points to the end (because of ReadFile above)
|
||
//
|
||
if (!WriteFile(sifhandle, sifstring,
|
||
wcslen(sifstring)*sizeof (WCHAR), &size, NULL)) {
|
||
status = GetLastError();
|
||
}
|
||
|
||
// We're done
|
||
|
||
}
|
||
else {
|
||
|
||
//
|
||
// The section exists, if lpSifEntry is NULL, we're done
|
||
//
|
||
if (ARGUMENT_PRESENT(lpSifEntry)) {
|
||
|
||
//
|
||
// SifEntry is not NULL, we'll add it at the end of the section
|
||
//
|
||
nextChar = sectionStart + 4; // Move pointer from \r to . in \r\n[.
|
||
nextKey = 1;
|
||
|
||
//
|
||
// Find where this section ends--look either for the start
|
||
// of the next section, or for the end of the file
|
||
//
|
||
while(*nextChar && *nextChar != L'[') {
|
||
|
||
//
|
||
// If this is a recognised section, we need to generate
|
||
// the <key> to add the entry in a <key>=<entry> format.
|
||
// We go through each line, and find the last key that
|
||
// already exists. The new key will be last key + 1.
|
||
//
|
||
if (commandsSection || installFilesSection) {
|
||
|
||
UINT commaCount = 0;
|
||
BOOL tracking = FALSE;
|
||
UINT count = 0;
|
||
WCHAR c1, c2;
|
||
|
||
while (*nextChar && (*nextChar != L'[') && (*nextChar != L'\n')) {
|
||
|
||
if (installFilesSection) {
|
||
if (*nextChar == L',') {
|
||
commaCount++;
|
||
}
|
||
|
||
if ((commaCount > 2) && (L'"' == *nextChar)) {
|
||
if (tracking) {
|
||
// duplicate file name
|
||
_AsrpErrExitCode((L'"'== lpSifEntry[destFilePos + count]), status, ERROR_ALREADY_EXISTS);
|
||
}
|
||
else {
|
||
tracking = TRUE;
|
||
count = 0;
|
||
}
|
||
}
|
||
|
||
if (tracking) {
|
||
|
||
c1 = *nextChar;
|
||
if (c1 >= L'a' && c1 <= L'z') {
|
||
c1 = c1 - L'a' + L'A';
|
||
}
|
||
|
||
c2 = lpSifEntry[destFilePos + count];
|
||
if (c2 >= L'a' && c2 <= L'z') {
|
||
c2 = c2 - L'a' + L'A';
|
||
}
|
||
|
||
if (c1 == c2) {
|
||
count++;
|
||
}
|
||
else {
|
||
tracking = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
nextChar++;
|
||
}
|
||
|
||
if (*nextChar == L'\n') {
|
||
|
||
++nextChar;
|
||
|
||
if (*nextChar >= L'0' && *nextChar <= L'9') {
|
||
nextKey = 0;
|
||
|
||
while (*nextChar >= L'0' && *nextChar <= L'9') {
|
||
nextKey = nextKey*10 + (*nextChar - L'0');
|
||
nextChar++;
|
||
}
|
||
|
||
nextKey++;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
nextChar++;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We save a pointer to the next section in the sif, since we
|
||
// need to write it out to disk.
|
||
//
|
||
if (*nextChar) {
|
||
nextSection = nextChar;
|
||
}
|
||
else {
|
||
nextSection = NULL;
|
||
}
|
||
|
||
if (commandsSection || installFilesSection) {
|
||
|
||
//
|
||
// Form the <key>=<entry> string
|
||
//
|
||
swprintf(
|
||
sifstring,
|
||
L"%lu=%ws\r\n",
|
||
nextKey,
|
||
lpSifEntry
|
||
);
|
||
}
|
||
else {
|
||
|
||
//
|
||
// Not a recognised section: don't add the <key>=<entry>
|
||
// format, keep the string exactly as passed in
|
||
//
|
||
wcscpy(sifstring, lpSifEntry);
|
||
wcscat(sifstring, L"\r\n");
|
||
}
|
||
|
||
|
||
if (nextSection) {
|
||
//
|
||
// There are sections following the section we're adding to
|
||
// We need to mark the point where the new entry is added.
|
||
// While writing out to disk, we'll start from this point.
|
||
//
|
||
fileOffset = (DWORD) (((LPBYTE)nextSection) - ((LPBYTE)buffer) - sizeof(WCHAR)*2);
|
||
// section start - file start - "\r\n"
|
||
SetFilePointer(sifhandle, fileOffset, NULL, FILE_BEGIN);
|
||
}
|
||
|
||
//
|
||
// file pointer points to where the entry must be added
|
||
//
|
||
if (!WriteFile(sifhandle, sifstring, wcslen(sifstring)*sizeof(WCHAR), &size, NULL)) {
|
||
status = GetLastError();
|
||
}
|
||
else if (nextSection) {
|
||
//
|
||
// write out all sections following this entry
|
||
//
|
||
if (!WriteFile(
|
||
sifhandle,
|
||
((LPBYTE)nextSection) - (sizeof(WCHAR)*2),
|
||
fileSize - fileOffset,
|
||
&size,
|
||
NULL
|
||
)) {
|
||
status = GetLastError();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
EXIT:
|
||
_AsrpHeapFree(sectionName);
|
||
_AsrpHeapFree(buffer);
|
||
|
||
return (BOOL) (ERROR_SUCCESS == status);
|
||
}
|
||
|
||
|
||
BOOL
|
||
AsrAddSifEntryA(
|
||
IN DWORD_PTR AsrContext,
|
||
IN LPCSTR lpSectionName,
|
||
IN LPCSTR lpSifEntry OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
This is the ANSI wrapper for AsrAddSifEntry.
|
||
See AsrAddSifEntryW for a full description.
|
||
|
||
--*/
|
||
{
|
||
WCHAR wszSectionName[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
WCHAR wszSifEntry[ASR_SIF_ENTRY_MAX_CHARS + 1];
|
||
|
||
if (AsrpIsRunningOnPersonalSKU()) {
|
||
//
|
||
// ASR is not supported on the Personal SKU
|
||
//
|
||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!AsrpCheckBackupPrivilege()) {
|
||
//
|
||
// The caller needs to first acquire SE_BACKUP_NAME
|
||
//
|
||
SetLastError(ERROR_PRIVILEGE_NOT_HELD);
|
||
return FALSE;
|
||
}
|
||
|
||
memset(wszSectionName, 0L, ASR_SIF_ENTRY_MAX_CHARS + 1);
|
||
memset(wszSifEntry, 0L, ASR_SIF_ENTRY_MAX_CHARS + 1);
|
||
|
||
//
|
||
// lpSectionName must be non-NULL
|
||
//
|
||
if ((!lpSectionName) || !(MultiByteToWideChar(
|
||
CP_ACP,
|
||
0,
|
||
lpSectionName,
|
||
-1,
|
||
wszSectionName,
|
||
ASR_SIF_ENTRY_MAX_CHARS + 1
|
||
))) {
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// lpSifEntry is allowed to be NULL
|
||
//
|
||
if (ARGUMENT_PRESENT(lpSifEntry) && !(MultiByteToWideChar(
|
||
CP_ACP,
|
||
0,
|
||
lpSifEntry,
|
||
-1,
|
||
wszSifEntry,
|
||
ASR_SIF_ENTRY_MAX_CHARS + 1
|
||
))) {
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
return AsrAddSifEntryW(
|
||
AsrContext,
|
||
wszSectionName,
|
||
wszSifEntry
|
||
);
|
||
}
|
||
|
||
|
||
//
|
||
// ---- AsrFreeContext
|
||
//
|
||
BOOL
|
||
AsrFreeContext(
|
||
IN OUT DWORD_PTR *lpAsrContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
AsrFreeContext frees the Asr Context, and sets lpAsrContext
|
||
to NULL.
|
||
|
||
Arguments:
|
||
|
||
lpAsrContext This is the Asr context to be freed. This argument must
|
||
not be NULL.
|
||
|
||
AsrFreeContext will set this value to NULL after freeing
|
||
it, to prevent further unintended accesses to the freed
|
||
object.
|
||
|
||
Return Value:
|
||
|
||
If the function succeeds, the return value is a nonzero value.
|
||
|
||
If the function fails, the return value is zero. To get extended error
|
||
information, call GetLastError().
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOL result = FALSE;
|
||
|
||
if (AsrpIsRunningOnPersonalSKU()) {
|
||
//
|
||
// ASR is not supported on the Personal SKU
|
||
//
|
||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Essentially, the lpAsrContext is just a file handle, and all we need
|
||
// to do to free it is call CloseHandle.
|
||
//
|
||
if ((lpAsrContext) &&
|
||
(*lpAsrContext) &&
|
||
(INVALID_HANDLE_VALUE != (HANDLE)(*lpAsrContext))
|
||
) {
|
||
result = CloseHandle((HANDLE)*lpAsrContext);
|
||
*lpAsrContext = 0;
|
||
}
|
||
else {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|