You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4662 lines
121 KiB
4662 lines
121 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
utils.c
|
|
|
|
Abstract:
|
|
|
|
Utilitaries for winnt32.
|
|
|
|
Author:
|
|
|
|
|
|
Revision History:
|
|
|
|
Ovidiu Temereanca (ovidiut) 24-Jul-2000
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include <mbstring.h>
|
|
#pragma hdrstop
|
|
|
|
|
|
VOID
|
|
MyWinHelp(
|
|
IN HWND Window,
|
|
IN UINT Command,
|
|
IN ULONG_PTR Data
|
|
)
|
|
{
|
|
TCHAR Buffer[MAX_PATH];
|
|
LPTSTR p;
|
|
HANDLE FindHandle;
|
|
BOOL b;
|
|
WIN32_FIND_DATA FindData;
|
|
LPCTSTR HelpFileName = TEXT("winnt32.hlp");
|
|
|
|
//
|
|
// The likely scenario is that a user invokes winnt32 from
|
|
// a network share. We'll expect the help file to be there too.
|
|
//
|
|
b = FALSE;
|
|
if(MyGetModuleFileName(NULL,Buffer,ARRAYSIZE(Buffer))
|
|
&& (p = _tcsrchr(Buffer,TEXT('\\'))))
|
|
{
|
|
//
|
|
// skip the slash (one TCHAR)
|
|
//
|
|
p++;
|
|
if (SUCCEEDED (StringCchCopy (p, Buffer + ARRAYSIZE(Buffer) - p, HelpFileName))) {
|
|
|
|
//
|
|
// See whether the help file is there. If so, use it.
|
|
//
|
|
FindHandle = FindFirstFile(Buffer,&FindData);
|
|
if(FindHandle != INVALID_HANDLE_VALUE) {
|
|
|
|
FindClose(FindHandle);
|
|
b = WinHelp(Window,Buffer,Command,Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!b) {
|
|
//
|
|
// Try just the base help file name.
|
|
//
|
|
b = WinHelp(Window,HelpFileName,Command,Data);
|
|
}
|
|
|
|
if(!b) {
|
|
//
|
|
// Tell user.
|
|
//
|
|
MessageBoxFromMessage(
|
|
Window,
|
|
MSG_CANT_OPEN_HELP_FILE,
|
|
FALSE,
|
|
AppTitleStringId,
|
|
MB_OK | MB_ICONINFORMATION,
|
|
HelpFileName
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
ConcatenatePaths(
|
|
IN OUT PTSTR Path1,
|
|
IN LPCTSTR Path2,
|
|
IN DWORD BufferSizeChars
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Concatenate two path strings together, supplying a path separator
|
|
character (\) if necessary between the 2 parts.
|
|
|
|
Arguments:
|
|
|
|
Path1 - supplies prefix part of path. Path2 is concatenated to Path1.
|
|
|
|
Path2 - supplies the suffix part of path. If Path1 does not end with a
|
|
path separator and Path2 does not start with one, then a path sep
|
|
is appended to Path1 before appending Path2.
|
|
|
|
BufferSizeChars - supplies the size in chars (Unicode version) or
|
|
bytes (Ansi version) of the buffer pointed to by Path1. The string
|
|
will be truncated as necessary to not overflow that size.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the 2 paths were successfully concatenated, without any truncation
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL NeedBackslash = TRUE;
|
|
PTSTR p;
|
|
BOOL b = FALSE;
|
|
|
|
if (!Path1 || BufferSizeChars < 1) {
|
|
MYASSERT (FALSE);
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine whether we need to stick a backslash
|
|
// between the components.
|
|
//
|
|
p = _tcsrchr (Path1, TEXT('\\'));
|
|
if(p && *(++p) == 0) {
|
|
|
|
if (p >= Path1 + BufferSizeChars) {
|
|
//
|
|
// buffer already overflowed
|
|
//
|
|
MYASSERT (FALSE);
|
|
SetLastError (ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
NeedBackslash = FALSE;
|
|
|
|
} else {
|
|
p = _tcschr (Path1, 0);
|
|
}
|
|
|
|
if(Path2 && *Path2 == TEXT('\\')) {
|
|
|
|
if(NeedBackslash) {
|
|
NeedBackslash = FALSE;
|
|
} else {
|
|
//
|
|
// Not only do we not need a backslash, but we
|
|
// need to eliminate one before concatenating.
|
|
//
|
|
Path2++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Append backslash if necessary.
|
|
// We verified above that we have enough space for this
|
|
//
|
|
if(NeedBackslash) {
|
|
*p++ = TEXT('\\');
|
|
*p = 0;
|
|
}
|
|
|
|
//
|
|
// Append second part of string to first part if it fits.
|
|
//
|
|
if (Path2) {
|
|
MYASSERT (Path1 + BufferSizeChars >= p);
|
|
if (FAILED (StringCchCopy (p, Path1 + BufferSizeChars - p, Path2))) {
|
|
//
|
|
// why is the buffer so small?
|
|
//
|
|
MYASSERT (FALSE);
|
|
*p = 0;
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
*p = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LPTSTR
|
|
DupString(
|
|
IN LPCTSTR String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make a duplicate of a nul-terminated string.
|
|
|
|
Arguments:
|
|
|
|
String - supplies pointer to nul-terminated string to copy.
|
|
|
|
Return Value:
|
|
|
|
Copy of string or NULL if OOM. Caller can free with FREE().
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR p;
|
|
|
|
if(p = MALLOC((lstrlen(String)+1)*sizeof(TCHAR))) {
|
|
lstrcpy(p,String);
|
|
}
|
|
|
|
return(p);
|
|
}
|
|
|
|
PTSTR
|
|
DupMultiSz (
|
|
IN PCTSTR MultiSz
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make a duplicate of a MultiSz.
|
|
|
|
Arguments:
|
|
|
|
MultiSz - supplies pointer to the multi-string to duplicate.
|
|
|
|
Return Value:
|
|
|
|
Copy of string or NULL if OOM. Caller can free with FREE().
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR p;
|
|
PTSTR q;
|
|
DWORD size = sizeof (TCHAR);
|
|
|
|
for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) {
|
|
size += (lstrlen (p) + 1) * sizeof(TCHAR);
|
|
}
|
|
if (q = MALLOC (size)) {
|
|
CopyMemory (q, MultiSz, size);
|
|
}
|
|
|
|
return q;
|
|
}
|
|
|
|
|
|
PTSTR
|
|
CreatePrintableString (
|
|
IN PCTSTR MultiSz
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a string of the form (str1, str2, ..., strN) from a MultiSz
|
|
|
|
Arguments:
|
|
|
|
MultiSz - supplies pointer to the MultiSz string to represent.
|
|
|
|
Return Value:
|
|
|
|
Pointer to the new string string or NULL if OOM. Caller can free with FREE().
|
|
|
|
--*/
|
|
|
|
{
|
|
PCTSTR p;
|
|
PTSTR q, r;
|
|
DWORD size = 3 * sizeof (TCHAR);
|
|
|
|
for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) {
|
|
size += (lstrlen (p) + 1) * sizeof(TCHAR);
|
|
}
|
|
if (r = MALLOC (size)) {
|
|
q = r;
|
|
*q++ = TEXT('(');
|
|
for (p = MultiSz; *p; p = _tcschr (p, 0) + 1) {
|
|
if (q - r > 1) {
|
|
*q++ = TEXT(',');
|
|
}
|
|
q += wsprintf (q, TEXT("%s"), p);
|
|
}
|
|
*q++ = TEXT(')');
|
|
*q = 0;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
PSTR
|
|
UnicodeToAnsi (
|
|
IN PCWSTR Unicode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes an ANSI duplicate of a UNICODE string.
|
|
|
|
Arguments:
|
|
|
|
Unicode - supplies pointer to the UNICODE string to duplicate.
|
|
|
|
Return Value:
|
|
|
|
Copy of string or NULL if OOM. Caller can free with FREE().
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR p;
|
|
DWORD size;
|
|
|
|
if (!Unicode) {
|
|
return NULL;
|
|
}
|
|
|
|
size = (lstrlenW (Unicode) + 1) * sizeof(WCHAR);
|
|
if (p = MALLOC (size)) {
|
|
if (!WideCharToMultiByte (
|
|
CP_ACP,
|
|
0,
|
|
Unicode,
|
|
-1,
|
|
p,
|
|
size,
|
|
NULL,
|
|
NULL
|
|
)) {
|
|
FREE (p);
|
|
p = NULL;
|
|
}
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
PWSTR
|
|
AnsiToUnicode (
|
|
IN PCSTR SzAnsi
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes a UNICODE duplicate of an ANSI string.
|
|
|
|
Arguments:
|
|
|
|
SzAnsi - supplies pointer to the ANSI string to duplicate.
|
|
|
|
Return Value:
|
|
|
|
Copy of string or NULL if OOM. Caller can free with FREE().
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR q;
|
|
DWORD size;
|
|
|
|
if (!SzAnsi) {
|
|
return NULL;
|
|
}
|
|
|
|
size = strlen (SzAnsi) + 1;
|
|
|
|
if (q = MALLOC (size * sizeof(WCHAR))) {
|
|
if (!MultiByteToWideChar (
|
|
CP_ACP,
|
|
0,
|
|
SzAnsi,
|
|
size,
|
|
q,
|
|
size
|
|
)) {
|
|
FREE (q);
|
|
q = NULL;
|
|
}
|
|
}
|
|
|
|
return q;
|
|
}
|
|
|
|
PWSTR
|
|
MultiSzAnsiToUnicode (
|
|
IN PCSTR MultiSzAnsi
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Makes a UNICODE duplicate of a multi-sz ANSI string.
|
|
|
|
Arguments:
|
|
|
|
MultiSzAnsi - supplies pointer to the multisz ANSI string to duplicate.
|
|
|
|
Return Value:
|
|
|
|
Copy of string or NULL if OOM. Caller can free with FREE().
|
|
|
|
--*/
|
|
|
|
{
|
|
PCSTR p;
|
|
PWSTR q;
|
|
DWORD size = 1;
|
|
|
|
if (!MultiSzAnsi) {
|
|
return NULL;
|
|
}
|
|
|
|
for (p = MultiSzAnsi; *p; p = _mbschr (p, 0) + 1) {
|
|
size += strlen (p) + 1;
|
|
}
|
|
|
|
if (q = MALLOC (size * sizeof(WCHAR))) {
|
|
if (!MultiByteToWideChar (
|
|
CP_ACP,
|
|
0,
|
|
MultiSzAnsi,
|
|
size,
|
|
q,
|
|
size
|
|
)) {
|
|
FREE (q);
|
|
q = NULL;
|
|
}
|
|
}
|
|
|
|
return q;
|
|
}
|
|
|
|
|
|
UINT
|
|
MyGetDriveType(
|
|
IN TCHAR Drive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Same as GetDriveType() Win32 API except on NT returns
|
|
DRIVE_FIXED for removeable hard drives.
|
|
|
|
Arguments:
|
|
|
|
Drive - supplies drive letter whose type is desired.
|
|
|
|
Return Value:
|
|
|
|
Same as GetDriveType().
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR DriveNameNt[] = TEXT("\\\\.\\?:");
|
|
TCHAR DriveName[] = TEXT("?:\\");
|
|
HANDLE hDisk;
|
|
BOOL b;
|
|
UINT rc;
|
|
DWORD DataSize;
|
|
DISK_GEOMETRY MediaInfo;
|
|
|
|
//
|
|
// First, get the win32 drive type. If it tells us DRIVE_REMOVABLE,
|
|
// then we need to see whether it's a floppy or hard disk. Otherwise
|
|
// just believe the api.
|
|
//
|
|
//
|
|
MYASSERT (Drive);
|
|
|
|
DriveName[0] = Drive;
|
|
rc = GetDriveType(DriveName);
|
|
|
|
#ifdef _X86_ //NEC98
|
|
//
|
|
// NT5 for NEC98 can not access AT formated HD during setup.
|
|
// We need except these type.
|
|
if (IsNEC98() && ISNT() && (rc == DRIVE_FIXED) && BuildNumber <= NT40) {
|
|
//
|
|
// Check ATA Card?
|
|
//
|
|
{
|
|
HANDLE hDisk;
|
|
|
|
DriveNameNt[4] = Drive;
|
|
hDisk = CreateFile( DriveNameNt,
|
|
GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if(hDisk == INVALID_HANDLE_VALUE) {
|
|
return(DRIVE_UNKNOWN);
|
|
}
|
|
if (CheckATACardonNT4(hDisk)){
|
|
CloseHandle(hDisk);
|
|
return(DRIVE_REMOVABLE);
|
|
}
|
|
CloseHandle(hDisk);
|
|
}
|
|
if (!IsValidDrive(Drive)){
|
|
// HD format is not NEC98 format.
|
|
return(DRIVE_UNKNOWN);
|
|
}
|
|
}
|
|
if((rc != DRIVE_REMOVABLE) || !ISNT() || (!IsNEC98() && (Drive < L'C'))) {
|
|
return(rc);
|
|
}
|
|
#else //NEC98
|
|
if((rc != DRIVE_REMOVABLE) || !ISNT() || (Drive < L'C')) {
|
|
return(rc);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// DRIVE_REMOVABLE on NT.
|
|
//
|
|
|
|
//
|
|
// Disallow use of removable media (e.g. Jazz, Zip, ...).
|
|
//
|
|
|
|
|
|
DriveNameNt[4] = Drive;
|
|
|
|
hDisk = CreateFile(
|
|
DriveNameNt,
|
|
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(hDisk != INVALID_HANDLE_VALUE) {
|
|
|
|
b = DeviceIoControl(
|
|
hDisk,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
&MediaInfo,
|
|
sizeof(MediaInfo),
|
|
&DataSize,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// It's really a hard disk if the media type is removable.
|
|
//
|
|
if(b && (MediaInfo.MediaType == RemovableMedia)) {
|
|
rc = DRIVE_FIXED;
|
|
}
|
|
|
|
CloseHandle(hDisk);
|
|
}
|
|
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
UINT
|
|
MyGetDriveType2 (
|
|
IN PCWSTR NtVolumeName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Same as GetDriveType() Win32 API except on NT returns
|
|
DRIVE_FIXED for removeable hard drives.
|
|
|
|
Arguments:
|
|
|
|
NtVolumeName - supplies device name whose type is desired.
|
|
|
|
Return Value:
|
|
|
|
Same as GetDriveType().
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING DeviceName;
|
|
HANDLE hDisk;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
BOOL b;
|
|
UINT rc;
|
|
DWORD DataSize;
|
|
DISK_GEOMETRY MediaInfo;
|
|
FILE_FS_DEVICE_INFORMATION DeviceInfo;
|
|
|
|
//
|
|
// First, get the win32 drive type. If it tells us DRIVE_REMOVABLE,
|
|
// then we need to see whether it's a floppy or hard disk. Otherwise
|
|
// just believe the api.
|
|
//
|
|
INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
|
|
Status = NtOpenFile (
|
|
&hDisk,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
|
|
);
|
|
if (!NT_SUCCESS( Status )) {
|
|
return DRIVE_NO_ROOT_DIR;
|
|
}
|
|
|
|
//
|
|
// Determine if this is a network or disk file system. If it
|
|
// is a disk file system determine if this is removable or not
|
|
//
|
|
Status = NtQueryVolumeInformationFile(
|
|
hDisk,
|
|
&IoStatusBlock,
|
|
&DeviceInfo,
|
|
sizeof(DeviceInfo),
|
|
FileFsDeviceInformation
|
|
);
|
|
if (!NT_SUCCESS (Status)) {
|
|
rc = DRIVE_UNKNOWN;
|
|
} else if (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE) {
|
|
rc = DRIVE_REMOTE;
|
|
} else {
|
|
switch (DeviceInfo.DeviceType) {
|
|
|
|
case FILE_DEVICE_NETWORK:
|
|
case FILE_DEVICE_NETWORK_FILE_SYSTEM:
|
|
rc = DRIVE_REMOTE;
|
|
break;
|
|
|
|
case FILE_DEVICE_CD_ROM:
|
|
case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
|
|
rc = DRIVE_CDROM;
|
|
break;
|
|
|
|
case FILE_DEVICE_VIRTUAL_DISK:
|
|
rc = DRIVE_RAMDISK;
|
|
break;
|
|
|
|
case FILE_DEVICE_DISK:
|
|
case FILE_DEVICE_DISK_FILE_SYSTEM:
|
|
if ( DeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA ) {
|
|
rc = DRIVE_REMOVABLE;
|
|
} else {
|
|
rc = DRIVE_FIXED;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
rc = DRIVE_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(rc == DRIVE_REMOVABLE) {
|
|
|
|
//
|
|
// DRIVE_REMOVABLE on NT.
|
|
// Disallow use of removable media (e.g. Jazz, Zip, ...).
|
|
//
|
|
Status = NtDeviceIoControlFile(
|
|
hDisk,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
&MediaInfo,
|
|
sizeof(DISK_GEOMETRY)
|
|
);
|
|
//
|
|
// It's really a hard disk if the media type is removable.
|
|
//
|
|
if(NT_SUCCESS (Status) && (MediaInfo.MediaType == RemovableMedia)) {
|
|
rc = DRIVE_FIXED;
|
|
}
|
|
}
|
|
|
|
NtClose (hDisk);
|
|
|
|
return(rc);
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
GetPartitionInfo(
|
|
IN TCHAR Drive,
|
|
OUT PPARTITION_INFORMATION PartitionInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fill in a PARTITION_INFORMATION structure with information about
|
|
a particular drive.
|
|
|
|
This routine is meaningful only when run on NT -- it always fails
|
|
on Win95.
|
|
|
|
Arguments:
|
|
|
|
Drive - supplies drive letter whose partition info is desired.
|
|
|
|
PartitionInfo - upon success, receives partition info for Drive.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether PartitionInfo has been filled in.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR DriveName[] = TEXT("\\\\.\\?:");
|
|
HANDLE hDisk;
|
|
BOOL b;
|
|
DWORD DataSize;
|
|
|
|
if(!ISNT()) {
|
|
return(FALSE);
|
|
}
|
|
|
|
DriveName[4] = Drive;
|
|
|
|
hDisk = CreateFile(
|
|
DriveName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(hDisk == INVALID_HANDLE_VALUE) {
|
|
return(FALSE);
|
|
}
|
|
|
|
b = DeviceIoControl(
|
|
hDisk,
|
|
IOCTL_DISK_GET_PARTITION_INFO,
|
|
NULL,
|
|
0,
|
|
PartitionInfo,
|
|
sizeof(PARTITION_INFORMATION),
|
|
&DataSize,
|
|
NULL
|
|
);
|
|
|
|
CloseHandle(hDisk);
|
|
|
|
return(b);
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
|
|
BOOL
|
|
GetPartitionInfo2 (
|
|
IN PCWSTR NtVolumeName,
|
|
OUT PPARTITION_INFORMATION PartitionInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fill in a PARTITION_INFORMATION structure with information about
|
|
a particular drive.
|
|
|
|
This routine is meaningful only when run on NT -- it always fails
|
|
on Win95.
|
|
|
|
Arguments:
|
|
|
|
NtVolumeName - supplies NT volume name whose partition info is desired.
|
|
|
|
PartitionInfo - upon success, receives partition info for Drive.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether PartitionInfo has been filled in.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING DeviceName;
|
|
HANDLE hDisk;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
BOOL b = FALSE;
|
|
DWORD DataSize;
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
|
|
Status = NtOpenFile (
|
|
&hDisk,
|
|
(ACCESS_MASK)FILE_READ_DATA | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
if (NT_SUCCESS (Status)) {
|
|
|
|
Status = NtDeviceIoControlFile (
|
|
hDisk,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_DISK_GET_PARTITION_INFO,
|
|
NULL,
|
|
0,
|
|
PartitionInfo,
|
|
sizeof(PARTITION_INFORMATION)
|
|
);
|
|
|
|
NtClose (hDisk);
|
|
|
|
b = NT_SUCCESS (Status);
|
|
}
|
|
|
|
return(b);
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
IsDriveNTFT(
|
|
IN TCHAR Drive,
|
|
IN PCTSTR NtVolumeName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine whether a drive is any kind of NTFT set.
|
|
|
|
This routine is meaningful only when run on NT -- it always fails
|
|
on Win95.
|
|
|
|
Arguments:
|
|
|
|
Drive - supplies drive letter to check; optional
|
|
NtVolumeName - supplies volume name to check; required if Drive not specified
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether the drive is NTFT.
|
|
|
|
--*/
|
|
|
|
{
|
|
PARTITION_INFORMATION PartitionInfo;
|
|
|
|
if(!ISNT()) {
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// If we can't open the drive, assume not NTFT.
|
|
//
|
|
if (Drive) {
|
|
if(!GetPartitionInfo(Drive,&PartitionInfo)) {
|
|
return(FALSE);
|
|
}
|
|
} else {
|
|
#ifdef UNICODE
|
|
if(!GetPartitionInfo2 (NtVolumeName, &PartitionInfo)) {
|
|
return(FALSE);
|
|
}
|
|
#else
|
|
MYASSERT (FALSE);
|
|
return(FALSE);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// It's FT if the partition type is marked NTFT (ie, high bit set).
|
|
//
|
|
|
|
if((IsRecognizedPartition(PartitionInfo.PartitionType)) &&
|
|
((PartitionInfo.PartitionType & PARTITION_NTFT) != 0)) {
|
|
|
|
#if defined(_IA64_)
|
|
//
|
|
// This check is dependant on the EFI system partition type not being
|
|
// a recognized type. It's unlikely that we'd start recognizing it
|
|
// before we start requiring GPT partitions on the system disk, but
|
|
// just in case we'll assert before returning true for an ESP.
|
|
//
|
|
|
|
ASSERT(PartitionInfo.PartitionType != 0xef);
|
|
#endif
|
|
|
|
return TRUE;
|
|
} else {
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsDriveVeritas(
|
|
IN TCHAR Drive,
|
|
IN PCTSTR NtVolumeName
|
|
)
|
|
{
|
|
TCHAR name[3];
|
|
TCHAR Target[MAX_PATH];
|
|
|
|
if(ISNT()) {
|
|
//
|
|
// Check for Veritas volume, which links to \Device\HarddiskDmVolumes...
|
|
//
|
|
if (Drive) {
|
|
name[0] = Drive;
|
|
name[1] = TEXT(':');
|
|
name[2] = 0;
|
|
if(!QueryDosDevice(name,Target,MAX_PATH)) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
lstrcpy (Target, NtVolumeName);
|
|
}
|
|
if(!_tcsnicmp(Target,TEXT("\\Device\\HarddiskDm"),ARRAYSIZE("\\Device\\HarddiskDm") - 1)) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Get Harddisk BPS
|
|
// I970721
|
|
//
|
|
ULONG
|
|
GetHDBps(
|
|
HANDLE hDisk
|
|
)
|
|
{
|
|
BOOL b;
|
|
UINT rc;
|
|
DWORD DataSize;
|
|
DISK_GEOMETRY MediaInfo;
|
|
|
|
b = DeviceIoControl(
|
|
hDisk,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
&MediaInfo,
|
|
sizeof(MediaInfo),
|
|
&DataSize,
|
|
NULL
|
|
);
|
|
|
|
if(!b) {
|
|
return(0);
|
|
} else {
|
|
return(MediaInfo.BytesPerSector);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
#ifdef _WIN64
|
|
|
|
//
|
|
// define IOCTL_VOLUME_IS_PARTITION since we don't include ntddvol.h
|
|
//
|
|
#define IOCTL_VOLUME_IS_PARTITION CTL_CODE(IOCTL_VOLUME_BASE, 10, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
|
|
BOOL
|
|
IsSoftPartition(
|
|
IN TCHAR Drive,
|
|
IN PCTSTR NtVolumeName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds out whether the given volume is soft partition
|
|
or not (i.e. does it have an underlying partition).
|
|
|
|
NOTE : We just use the IOCTL_VOLUME_IS_PARTITION.
|
|
|
|
Arguments:
|
|
|
|
Drive - supplies drive letter for the volume
|
|
|
|
NtVolumeName - supplies NT volume name
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the volume is soft partition otherwise FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL SoftPartition;
|
|
HANDLE VolumeHandle = INVALID_HANDLE_VALUE;
|
|
ULONG DataSize;
|
|
|
|
//
|
|
// Assume that the partition is a soft one.
|
|
// If we cannot determine whether or not the partition is a soft partition, then assume it is a soft
|
|
// partition. This will prevent us from placing $win_nt$.~ls in such a drive.
|
|
//
|
|
SoftPartition = TRUE;
|
|
|
|
if (Drive) {
|
|
TCHAR Name[] = TEXT("\\\\.\\?:");
|
|
BOOL Result;
|
|
|
|
Name[4] = Drive;
|
|
|
|
VolumeHandle = CreateFile(Name,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
INVALID_HANDLE_VALUE);
|
|
|
|
if (VolumeHandle != INVALID_HANDLE_VALUE) {
|
|
Result = DeviceIoControl(VolumeHandle,
|
|
IOCTL_VOLUME_IS_PARTITION,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&DataSize,
|
|
NULL);
|
|
|
|
SoftPartition = !Result;
|
|
|
|
CloseHandle(VolumeHandle);
|
|
}
|
|
} else {
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING DeviceName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
|
|
|
|
Status = NtOpenFile (&VolumeHandle,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
Status = NtDeviceIoControlFile(VolumeHandle,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_VOLUME_IS_PARTITION,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
SoftPartition = FALSE;
|
|
}
|
|
|
|
NtClose(VolumeHandle);
|
|
}
|
|
}
|
|
|
|
return SoftPartition;
|
|
}
|
|
|
|
#else
|
|
|
|
BOOL
|
|
IsSoftPartition(
|
|
IN TCHAR Drive,
|
|
IN PCTSTR NtVolumeName
|
|
)
|
|
{
|
|
TCHAR name[] = TEXT("\\\\.\\?:");
|
|
PARTITION_INFORMATION partInfo;
|
|
DWORD bytes;
|
|
BOOL SoftPartition = TRUE;
|
|
BOOL b;
|
|
HANDLE h = INVALID_HANDLE_VALUE;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER SoftPartitionStartingOffset;
|
|
ULONG bps;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING DeviceName;
|
|
DWORD DataSize;
|
|
DISK_GEOMETRY MediaInfo;
|
|
|
|
if( !IsDriveVeritas( Drive, NtVolumeName ) ) {
|
|
return( FALSE );
|
|
}
|
|
//
|
|
// Assume that the partition is a soft one.
|
|
// If we cannot determine whether or not the partition is a soft partition, then assume it is a soft
|
|
// partition. This will prevent us from placing $win_nt$.~ls in such a drive.
|
|
//
|
|
SoftPartition = TRUE;
|
|
|
|
if (Drive) {
|
|
name[4] = Drive;
|
|
|
|
h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
|
INVALID_HANDLE_VALUE);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
#if DBG
|
|
GetLastError();
|
|
#endif
|
|
goto Exit;
|
|
}
|
|
|
|
b = DeviceIoControl(
|
|
h,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
&MediaInfo,
|
|
sizeof(MediaInfo),
|
|
&DataSize,
|
|
NULL
|
|
);
|
|
|
|
if(!b) {
|
|
#if DBG
|
|
GetLastError();
|
|
#endif
|
|
goto CleanUp;
|
|
}
|
|
|
|
b = DeviceIoControl(h, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0,
|
|
&partInfo, sizeof(partInfo), &bytes, NULL);
|
|
if (!b) {
|
|
#if DBG
|
|
GetLastError();
|
|
#endif
|
|
goto CleanUp;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// Open the file
|
|
//
|
|
INIT_OBJA (&Obja, &DeviceName, NtVolumeName);
|
|
Status = NtOpenFile (
|
|
&h,
|
|
(ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
|
|
);
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto Exit;
|
|
}
|
|
Status = NtDeviceIoControlFile(
|
|
h,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_DISK_GET_DRIVE_GEOMETRY,
|
|
NULL,
|
|
0,
|
|
&MediaInfo,
|
|
sizeof(DISK_GEOMETRY)
|
|
);
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto CleanUp;
|
|
}
|
|
Status = NtDeviceIoControlFile(
|
|
h,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_DISK_GET_PARTITION_INFO,
|
|
NULL,
|
|
0,
|
|
&partInfo,
|
|
sizeof(PARTITION_INFORMATION)
|
|
);
|
|
if (!NT_SUCCESS (Status)) {
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
bps = MediaInfo.BytesPerSector;
|
|
|
|
//
|
|
// Find out the number of bytes per sector of the drive
|
|
//
|
|
//
|
|
// A soft partition always starts at sector 29 (0x1d)
|
|
//
|
|
SoftPartitionStartingOffset.QuadPart = 29*bps;
|
|
SoftPartition = ( partInfo.StartingOffset.QuadPart == SoftPartitionStartingOffset.QuadPart );
|
|
|
|
CleanUp:
|
|
if (Drive) {
|
|
CloseHandle(h);
|
|
} else {
|
|
NtClose (h);
|
|
}
|
|
Exit:
|
|
return( SoftPartition );
|
|
}
|
|
|
|
#endif // WIN64
|
|
|
|
BOOL
|
|
MyGetDiskFreeSpace (
|
|
IN PCWSTR NtVolumeName,
|
|
IN PDWORD SectorsPerCluster,
|
|
IN PDWORD BytesPerSector,
|
|
IN PDWORD NumberOfFreeClusters,
|
|
IN PDWORD TotalNumberOfClusters
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Handle;
|
|
UNICODE_STRING VolumeName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_FS_SIZE_INFORMATION SizeInfo;
|
|
|
|
INIT_OBJA (&Obja, &VolumeName, NtVolumeName);
|
|
|
|
//
|
|
// Open the file
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&Handle,
|
|
(ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_FREE_SPACE_QUERY
|
|
);
|
|
if (!NT_SUCCESS (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine the size parameters of the volume.
|
|
//
|
|
Status = NtQueryVolumeInformationFile(
|
|
Handle,
|
|
&IoStatusBlock,
|
|
&SizeInfo,
|
|
sizeof(SizeInfo),
|
|
FileFsSizeInformation
|
|
);
|
|
NtClose(Handle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (SizeInfo.TotalAllocationUnits.HighPart) {
|
|
SizeInfo.TotalAllocationUnits.LowPart = (ULONG)-1;
|
|
}
|
|
if (SizeInfo.AvailableAllocationUnits.HighPart) {
|
|
SizeInfo.AvailableAllocationUnits.LowPart = (ULONG)-1;
|
|
}
|
|
|
|
*SectorsPerCluster = SizeInfo.SectorsPerAllocationUnit;
|
|
*BytesPerSector = SizeInfo.BytesPerSector;
|
|
*NumberOfFreeClusters = SizeInfo.AvailableAllocationUnits.LowPart;
|
|
*TotalNumberOfClusters = SizeInfo.TotalAllocationUnits.LowPart;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
IsDriveNTFS(
|
|
IN TCHAR Drive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine whether a drive is any kind of NTFT set.
|
|
|
|
This routine is meaningful only when run on NT -- it always fails
|
|
on Win95.
|
|
|
|
Arguments:
|
|
|
|
Drive - supplies drive letter to check.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether the drive is NTFT.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR DriveName[4];
|
|
TCHAR Filesystem[256];
|
|
TCHAR VolumeName[MAX_PATH];
|
|
DWORD SerialNumber;
|
|
DWORD MaxComponent;
|
|
DWORD Flags;
|
|
BOOL b;
|
|
|
|
if(!ISNT()) {
|
|
return(FALSE);
|
|
}
|
|
|
|
MYASSERT (Drive);
|
|
|
|
DriveName[0] = Drive;
|
|
DriveName[1] = TEXT(':');
|
|
DriveName[2] = TEXT('\\');
|
|
DriveName[3] = 0;
|
|
|
|
b = GetVolumeInformation(
|
|
DriveName,
|
|
VolumeName,
|
|
ARRAYSIZE(VolumeName),
|
|
&SerialNumber,
|
|
&MaxComponent,
|
|
&Flags,
|
|
Filesystem,
|
|
ARRAYSIZE(Filesystem)
|
|
);
|
|
|
|
if(!b || !lstrcmpi(Filesystem,TEXT("NTFS"))) {
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
DWORD
|
|
MapFileForRead(
|
|
IN LPCTSTR FileName,
|
|
OUT PDWORD FileSize,
|
|
OUT PHANDLE FileHandle,
|
|
OUT PHANDLE MappingHandle,
|
|
OUT PVOID *BaseAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open and map an entire file for read access. The file must
|
|
not be 0-length or the routine fails.
|
|
|
|
Arguments:
|
|
|
|
FileName - supplies pathname to file to be mapped.
|
|
|
|
FileSize - receives the size in bytes of the file.
|
|
|
|
FileHandle - receives the win32 file handle for the open file.
|
|
The file will be opened for generic read access.
|
|
|
|
MappingHandle - receives the win32 handle for the file mapping
|
|
object. This object will be for read access. This value is
|
|
undefined if the file being opened is 0 length.
|
|
|
|
BaseAddress - receives the address where the file is mapped. This
|
|
value is undefined if the file being opened is 0 length.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR if the file was opened and mapped successfully.
|
|
The caller must unmap the file with UnmapFile when
|
|
access to the file is no longer desired.
|
|
|
|
Win32 error code if the file was not successfully mapped.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
|
|
//
|
|
// Open the file -- fail if it does not exist.
|
|
//
|
|
*FileHandle = CreateFile(
|
|
FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(*FileHandle == INVALID_HANDLE_VALUE) {
|
|
|
|
rc = GetLastError();
|
|
|
|
} else {
|
|
//
|
|
// Get the size of the file.
|
|
//
|
|
*FileSize = GetFileSize(*FileHandle,NULL);
|
|
if(*FileSize == (DWORD)(-1)) {
|
|
rc = GetLastError();
|
|
} else {
|
|
//
|
|
// Create file mapping for the whole file.
|
|
//
|
|
*MappingHandle = CreateFileMapping(
|
|
*FileHandle,
|
|
NULL,
|
|
PAGE_READONLY,
|
|
0,
|
|
*FileSize,
|
|
NULL
|
|
);
|
|
|
|
if(*MappingHandle) {
|
|
|
|
//
|
|
// Map the whole file.
|
|
//
|
|
*BaseAddress = MapViewOfFile(
|
|
*MappingHandle,
|
|
FILE_MAP_READ,
|
|
0,
|
|
0,
|
|
*FileSize
|
|
);
|
|
|
|
if(*BaseAddress) {
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
rc = GetLastError();
|
|
CloseHandle(*MappingHandle);
|
|
} else {
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
|
|
CloseHandle(*FileHandle);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
UnmapFile(
|
|
IN HANDLE MappingHandle,
|
|
IN PVOID BaseAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unmap and close a file.
|
|
|
|
Arguments:
|
|
|
|
MappingHandle - supplies the win32 handle for the open file mapping
|
|
object.
|
|
|
|
BaseAddress - supplies the address where the file is mapped.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR if the file was unmapped successfully.
|
|
|
|
Win32 error code if the file was not successfully unmapped.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
|
|
rc = UnmapViewOfFile(BaseAddress) ? NO_ERROR : GetLastError();
|
|
|
|
if(!CloseHandle(MappingHandle)) {
|
|
if(rc == NO_ERROR) {
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
VOID
|
|
GenerateCompressedName(
|
|
IN LPCTSTR Filename,
|
|
OUT LPTSTR CompressedName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a filename, generate the compressed form of the name.
|
|
The compressed form is generated as follows:
|
|
|
|
Look backwards for a dot. If there is no dot, append "._" to the name.
|
|
If there is a dot followed by 0, 1, or 2 charcaters, append "_".
|
|
Otherwise assume there is a 3-character extension and replace the
|
|
third character after the dot with "_".
|
|
|
|
Arguments:
|
|
|
|
Filename - supplies filename whose compressed form is desired.
|
|
|
|
CompressedName - receives compressed form. This routine assumes
|
|
that this buffer is MAX_PATH TCHARs in size.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR p,q;
|
|
|
|
//
|
|
// Leave room for the worst case, namely where there's no extension
|
|
// (and we thus have to append ._).
|
|
//
|
|
lstrcpyn(CompressedName,Filename,MAX_PATH-2);
|
|
|
|
p = _tcsrchr(CompressedName,TEXT('.'));
|
|
q = _tcsrchr(CompressedName,TEXT('\\'));
|
|
if(q < p) {
|
|
//
|
|
// If there are 0, 1, or 2 characters after the dot, just append
|
|
// the underscore. p points to the dot so include that in the length.
|
|
//
|
|
if(lstrlen(p) < 4) {
|
|
lstrcat(CompressedName,TEXT("_"));
|
|
} else {
|
|
//
|
|
// Assume there are 3 characters in the extension and replace
|
|
// the final one with an underscore.
|
|
//
|
|
p[3] = TEXT('_');
|
|
MYASSERT (!p[4]);
|
|
}
|
|
} else {
|
|
//
|
|
// No dot, just add ._.
|
|
//
|
|
lstrcat(CompressedName,TEXT("._"));
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
CreateMultiLevelDirectory(
|
|
IN LPCTSTR Directory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine ensures that a multi-level path exists by creating individual
|
|
levels one at a time. It can handle either paths of form x:... or \\?\Volume{...
|
|
|
|
Arguments:
|
|
|
|
Directory - supplies fully-qualified Win32 pathspec of directory to create
|
|
|
|
Return Value:
|
|
|
|
Win32 error code indicating outcome.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR Buffer[MAX_PATH];
|
|
PTCHAR p,q;
|
|
TCHAR c;
|
|
BOOL Done;
|
|
DWORD d = ERROR_SUCCESS;
|
|
INT Skip=0;
|
|
|
|
if (FAILED(StringCchCopy(Buffer,ARRAYSIZE(Buffer),Directory))) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
//
|
|
// If it already exists do nothing. (We do this before syntax checking
|
|
// to allow for remote paths that already exist. This is needed for
|
|
// remote boot machines.)
|
|
//
|
|
d = GetFileAttributes(Buffer);
|
|
if(d != (DWORD)(-1)) {
|
|
return((d & FILE_ATTRIBUTE_DIRECTORY) ? NO_ERROR : ERROR_DIRECTORY);
|
|
}
|
|
|
|
//
|
|
// Check path format
|
|
//
|
|
c = (TCHAR)CharUpper((LPTSTR)Buffer[0]);
|
|
if(((c < TEXT('A')) || (c > TEXT('Z')) || (Buffer[1] != TEXT(':'))) && c != TEXT('\\')) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (c != TEXT('\\')) {
|
|
//
|
|
// Ignore drive roots, which we allow to be either x:\ or x:.
|
|
//
|
|
if(Buffer[2] != TEXT('\\')) {
|
|
return(Buffer[2] ? ERROR_INVALID_PARAMETER : ERROR_SUCCESS);
|
|
}
|
|
q = Buffer + 3;
|
|
if(*q == 0) {
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
} else {
|
|
//
|
|
// support \\server\share[\xxx] format
|
|
//
|
|
q = NULL;
|
|
if (Buffer[1] != TEXT('\\') || Buffer[1] != 0 && Buffer[2] == TEXT('\\')) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
q = _tcschr (&Buffer[2], TEXT('\\'));
|
|
if (!q) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
if (q[1] == TEXT('\\')) {
|
|
return(ERROR_INVALID_PARAMETER);
|
|
}
|
|
q = _tcschr (&q[1], TEXT('\\'));
|
|
if (!q) {
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
q++;
|
|
|
|
#ifdef UNICODE
|
|
//
|
|
// Hack to make sure the system partition case works on IA64 (arc)
|
|
// We believe this should be the only case where we use a
|
|
// GlobalRoot style name as the other cases deal with OEM partitions etc.
|
|
// which we should never touch. WE skip over by the length of
|
|
// SystemPartitionVolumeGuid. We take care of the \ present at the end.
|
|
//
|
|
|
|
if (SystemPartitionVolumeGuid != NULL && _wcsnicmp (Buffer, SystemPartitionVolumeGuid, (wcslen(SystemPartitionVolumeGuid)-1)) == 0 ){
|
|
|
|
Skip = wcslen(SystemPartitionVolumeGuid)-1;
|
|
|
|
|
|
} else if (_wcsnicmp (Buffer, L"\\\\?\\Volume{", LENGTHOF("\\\\?\\Volume{")) == 0 &&
|
|
lstrlenW (Buffer) > 47 &&
|
|
Buffer[47] == L'}') {
|
|
//
|
|
// skip over the VolumeGUID part
|
|
//
|
|
Skip = 48;
|
|
}
|
|
|
|
if (Skip > 0) {
|
|
if (Buffer[Skip] == 0) {
|
|
return ERROR_SUCCESS;
|
|
}
|
|
q = Buffer + Skip + 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
Done = FALSE;
|
|
do {
|
|
//
|
|
// Locate the next path sep char. If there is none then
|
|
// this is the deepest level of the path.
|
|
//
|
|
if(p = _tcschr(q,TEXT('\\'))) {
|
|
*p = 0;
|
|
} else {
|
|
Done = TRUE;
|
|
}
|
|
|
|
//
|
|
// Create this portion of the path.
|
|
//
|
|
if(CreateDirectory(Buffer,NULL)) {
|
|
d = ERROR_SUCCESS;
|
|
} else {
|
|
d = GetLastError();
|
|
if(d == ERROR_ALREADY_EXISTS) {
|
|
d = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if(d == ERROR_SUCCESS) {
|
|
//
|
|
// Put back the path sep and move to the next component.
|
|
//
|
|
if(!Done) {
|
|
*p = TEXT('\\');
|
|
q = p+1;
|
|
}
|
|
} else {
|
|
Done = TRUE;
|
|
}
|
|
|
|
} while(!Done);
|
|
|
|
return(d);
|
|
}
|
|
|
|
|
|
BOOL
|
|
ForceFileNoCompress(
|
|
IN LPCTSTR Filename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes sure that a file on a volume that supports per-file
|
|
compression is not compressed. The caller need not ensure that the volume
|
|
actually supports this, since this routine will query the attributes of
|
|
the file before deciding whether any operation is actually necessary,
|
|
and the compressed attribute will not be set on volumes that don't support
|
|
per-file compression.
|
|
|
|
It assumed that the file exists. If the file does not exist, this routine
|
|
will fail.
|
|
|
|
Arguments:
|
|
|
|
Filename - supplies the filename of the file to mke uncompressed.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating outcome. If FALSE, last error is set.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG d;
|
|
HANDLE h;
|
|
BOOL b;
|
|
USHORT u;
|
|
DWORD Attributes;
|
|
|
|
Attributes = GetFileAttributes(Filename);
|
|
if(Attributes == (DWORD)(-1)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if(!(Attributes & FILE_ATTRIBUTE_COMPRESSED)) {
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Temporarily nullify attributes that might prevent opening
|
|
// the file for read-write access.
|
|
//
|
|
// We preserve the 'standard' attributes that the file might have,
|
|
// to be restored later.
|
|
//
|
|
Attributes &= (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE);
|
|
SetFileAttributes(Filename,FILE_ATTRIBUTE_NORMAL);
|
|
|
|
h = CreateFile(
|
|
Filename,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL
|
|
);
|
|
|
|
if(h == INVALID_HANDLE_VALUE) {
|
|
SetFileAttributes(Filename,Attributes);
|
|
return(FALSE);
|
|
}
|
|
|
|
u = 0;
|
|
b = DeviceIoControl( h,
|
|
FSCTL_SET_COMPRESSION,
|
|
&u,
|
|
sizeof(u),
|
|
NULL,
|
|
0,
|
|
&d,
|
|
FALSE);
|
|
d = GetLastError();
|
|
CloseHandle(h);
|
|
SetFileAttributes(Filename,Attributes);
|
|
SetLastError(d);
|
|
|
|
return(b);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsCurrentOsServer(
|
|
void
|
|
)
|
|
{
|
|
LONG l;
|
|
HKEY hKey;
|
|
DWORD d;
|
|
DWORD Size;
|
|
TCHAR Value[100];
|
|
DWORD Type;
|
|
|
|
|
|
l = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
NULL,
|
|
&hKey,
|
|
&d
|
|
);
|
|
|
|
if (l != NO_ERROR) {
|
|
return FALSE;
|
|
}
|
|
|
|
Size = sizeof(Value);
|
|
|
|
l = RegQueryValueEx(hKey,TEXT("ProductType"),NULL,&Type,(LPBYTE)Value,&Size);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if (l != NO_ERROR) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (lstrcmpi(Value,TEXT("winnt")) == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsCurrentAdvancedServer(
|
|
void
|
|
)
|
|
{
|
|
LONG l;
|
|
HKEY hKey;
|
|
DWORD d;
|
|
DWORD Size;
|
|
TCHAR Value[100];
|
|
DWORD Type;
|
|
|
|
|
|
l = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
NULL,
|
|
&hKey,
|
|
&d
|
|
);
|
|
|
|
if (l != NO_ERROR) {
|
|
return FALSE;
|
|
}
|
|
|
|
Size = sizeof(Value);
|
|
|
|
l = RegQueryValueEx(hKey,TEXT("ProductType"),NULL,&Type,(LPBYTE)Value,&Size);
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
if (l != NO_ERROR) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (lstrcmpi(Value,TEXT("lanmannt")) == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ConcatenateFile(
|
|
IN HANDLE OpenFile,
|
|
IN LPTSTR FileName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will go load the named file, and concatenate its
|
|
contents into the open file.
|
|
|
|
Arguments:
|
|
|
|
OpenFile Handle to the open file
|
|
FileName The name of the file we're going to concatenate.
|
|
|
|
Return Value:
|
|
|
|
TRUE Everything went okay.
|
|
FALSE We failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD rc;
|
|
HANDLE hFile, hFileMapping;
|
|
DWORD FileSize, BytesWritten;
|
|
PVOID pFileBase;
|
|
BOOL ReturnValue = FALSE;
|
|
|
|
//
|
|
// Open the file...
|
|
//
|
|
rc = MapFileForRead (
|
|
FileName,
|
|
&FileSize,
|
|
&hFile,
|
|
&hFileMapping,
|
|
&pFileBase
|
|
);
|
|
if (rc == NO_ERROR) {
|
|
//
|
|
// Write the file...
|
|
//
|
|
if (!WriteFile( OpenFile, pFileBase, FileSize, &BytesWritten, NULL )) {
|
|
rc = GetLastError ();
|
|
ReturnValue = FALSE;
|
|
}
|
|
|
|
UnmapFile (hFileMapping, pFileBase);
|
|
CloseHandle (hFile);
|
|
}
|
|
|
|
if (!ReturnValue) {
|
|
SetLastError (rc);
|
|
}
|
|
|
|
return( ReturnValue );
|
|
}
|
|
|
|
|
|
BOOL
|
|
FileExists(
|
|
IN PCTSTR FileName,
|
|
OUT PWIN32_FIND_DATA FindData OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if a file exists and is accessible.
|
|
Errormode is set (and then restored) so the user will not see
|
|
any pop-ups.
|
|
|
|
Arguments:
|
|
|
|
FileName - supplies full path of file to check for existance.
|
|
|
|
FindData - if specified, receives find data for the file.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the file exists and is accessible.
|
|
FALSE if not. GetLastError() returns extended error info.
|
|
|
|
--*/
|
|
|
|
{
|
|
WIN32_FIND_DATA findData;
|
|
HANDLE FindHandle;
|
|
UINT OldMode;
|
|
DWORD Error;
|
|
|
|
FindHandle = FindFirstFile(FileName,&findData);
|
|
if(FindHandle == INVALID_HANDLE_VALUE) {
|
|
Error = GetLastError();
|
|
} else {
|
|
FindClose(FindHandle);
|
|
if(FindData) {
|
|
*FindData = findData;
|
|
}
|
|
Error = NO_ERROR;
|
|
}
|
|
|
|
|
|
SetLastError(Error);
|
|
return (Error == NO_ERROR);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DoesDirectoryExist (
|
|
IN PCTSTR DirSpec
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if a directory exists and is accessible.
|
|
This routine works even with the root of drives or with the root of
|
|
network shares (like \\server\share).
|
|
|
|
Arguments:
|
|
|
|
DirSpec - supplies full path of dir to check for existance;
|
|
|
|
Return Value:
|
|
|
|
TRUE if the dir exists and is accessible.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR pattern[MAX_PATH];
|
|
BOOL b = FALSE;
|
|
|
|
if (DirSpec) {
|
|
if (BuildPathEx (pattern, ARRAYSIZE(pattern), DirSpec, TEXT("*"))) {
|
|
|
|
WIN32_FIND_DATA fd;
|
|
UINT oldMode;
|
|
HANDLE h;
|
|
|
|
oldMode = SetErrorMode (SEM_FAILCRITICALERRORS);
|
|
|
|
h = FindFirstFile (pattern, &fd);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
FindClose (h);
|
|
b = TRUE;
|
|
}
|
|
|
|
SetErrorMode (oldMode);
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
#if defined(_AMD64_) || defined(_X86_)
|
|
|
|
BOOLEAN
|
|
IsValidDrive(
|
|
IN TCHAR Drive
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine check formatted disk type
|
|
NEC98 of NT4 has supported NEC98 format and PC-AT format.
|
|
But BIOS is handling only NEC98 format.
|
|
So We need setup Boot stuff to ONLY NEC98 formated HD.
|
|
|
|
Arguments:
|
|
Drive Drive letter.
|
|
|
|
Return Value:
|
|
|
|
TRUE Dive is NEC98 format.
|
|
FALSE Drive is not NEC98 format.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE hDisk;
|
|
TCHAR HardDiskName[] = TEXT("\\\\.\\?:");
|
|
PUCHAR pBuffer,pUBuffer;
|
|
WCHAR Buffer[128];
|
|
WCHAR DevicePath[128];
|
|
WCHAR DriveName[3];
|
|
WCHAR DiskNo;
|
|
STORAGE_DEVICE_NUMBER number;
|
|
PWCHAR p;
|
|
ULONG bps;
|
|
NTSTATUS Sts;
|
|
DWORD DataSize,ExtentSize;
|
|
BOOL b;
|
|
PVOLUME_DISK_EXTENTS Extent;
|
|
|
|
|
|
if (!ISNT())
|
|
return TRUE;
|
|
|
|
HardDiskName[4] = Drive;
|
|
DriveName[0] = Drive;
|
|
DriveName[1] = ':';
|
|
DriveName[2] = 0;
|
|
if(QueryDosDeviceW(DriveName, Buffer, ARRAYSIZE(Buffer))) {
|
|
if (BuildNumber <= NT40){ //check NT Version
|
|
//
|
|
// QueryDosDevice in NT3.51 is buggy.
|
|
// This API return "\\Harddisk\...." or
|
|
// "\\harddisk\...."
|
|
// We need work around.
|
|
//
|
|
p = wcsstr(Buffer, L"arddisk");
|
|
if (!p) {
|
|
return FALSE;
|
|
}
|
|
DiskNo = (*(p + LENGTHOF(L"arddisk")) - 0x30);
|
|
} else {
|
|
hDisk = CreateFile(
|
|
HardDiskName,
|
|
0,
|
|
FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, 0, NULL
|
|
);
|
|
if(hDisk == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
b = DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
|
|
&number, sizeof(number), &DataSize, NULL);
|
|
if (b) {
|
|
DiskNo = (TCHAR) number.DeviceNumber;
|
|
} else {
|
|
Extent = malloc(1024);
|
|
ExtentSize = 1024;
|
|
if(!Extent) {
|
|
CloseHandle( hDisk );
|
|
return FALSE;
|
|
}
|
|
b = DeviceIoControl(hDisk, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
|
|
NULL, 0,
|
|
(PVOID)Extent, ExtentSize, &DataSize, NULL);
|
|
if (!b) {
|
|
free(Extent);
|
|
CloseHandle( hDisk );
|
|
return FALSE;
|
|
}
|
|
if (Extent->NumberOfDiskExtents != 1){
|
|
free(Extent);
|
|
CloseHandle( hDisk );
|
|
return FALSE;
|
|
}
|
|
DiskNo = (TCHAR)Extent->Extents->DiskNumber;
|
|
free(Extent);
|
|
}
|
|
CloseHandle(hDisk);
|
|
}
|
|
if (FAILED (StringCchPrintfW (DevicePath, ARRAYSIZE(DevicePath), L"\\\\.\\PHYSICALDRIVE%u", DiskNo))) {
|
|
MYASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
hDisk = CreateFileW( DevicePath,
|
|
GENERIC_READ|GENERIC_WRITE,
|
|
FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, 0, NULL);
|
|
if(hDisk == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
if ((bps = GetHDBps(hDisk)) == 0){
|
|
CloseHandle(hDisk);
|
|
return FALSE;
|
|
}
|
|
pUBuffer = MALLOC(bps * 2);
|
|
if (!pUBuffer) {
|
|
CloseHandle(hDisk);
|
|
return FALSE;
|
|
}
|
|
pBuffer = ALIGN(pUBuffer, bps);
|
|
RtlZeroMemory(pBuffer, bps);
|
|
Sts = SpReadWriteDiskSectors(hDisk,0,1,bps,pBuffer, NEC_READSEC);
|
|
if(!NT_SUCCESS(Sts)) {
|
|
FREE(pUBuffer);
|
|
CloseHandle(hDisk);
|
|
return FALSE;
|
|
}
|
|
if (!(pBuffer[4] == 'I'
|
|
&& pBuffer[5] == 'P'
|
|
&& pBuffer[6] == 'L'
|
|
&& pBuffer[7] == '1')){
|
|
FREE(pUBuffer);
|
|
CloseHandle(hDisk);
|
|
return FALSE;
|
|
}
|
|
FREE(pUBuffer);
|
|
CloseHandle(hDisk);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef _X86_
|
|
|
|
BOOLEAN
|
|
CheckATACardonNT4(
|
|
IN HANDLE hDisk
|
|
)
|
|
{
|
|
//
|
|
// NT4, NT3.51 for NEC98.
|
|
// NEC98 does not handle to boot from PCMCIA ATA card disk.
|
|
// So we need to check ATA Disk.
|
|
//
|
|
// Return
|
|
// TRUE is ATA Card
|
|
// FALSE is Other
|
|
//
|
|
|
|
#define IOCTL_DISK_GET_FORMAT_MEDIA CTL_CODE(IOCTL_DISK_BASE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
|
#define FORMAT_MEDIA_98 0 // TYPE NEC98
|
|
#define FORMAT_MEDIA_AT 1 // TYPE PC-AT
|
|
#define FORMAT_MEDIA_OTHER 2 // Unknown
|
|
|
|
struct _OutBuffer {
|
|
ULONG CurrentFormatMedia;
|
|
ULONG InitializeFormatMedia;
|
|
} OutBuffer;
|
|
DWORD ReturnedByteCount;
|
|
|
|
if (!(DeviceIoControl(hDisk, IOCTL_DISK_GET_FORMAT_MEDIA, NULL,
|
|
0,
|
|
&OutBuffer,
|
|
sizeof(struct _OutBuffer),
|
|
&ReturnedByteCount,
|
|
NULL
|
|
) )){
|
|
return FALSE;
|
|
}
|
|
|
|
if (OutBuffer.InitializeFormatMedia == FORMAT_MEDIA_AT){
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
IsMachineSupported(
|
|
OUT PCOMPATIBILITY_ENTRY CompEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function determines whether or not the machine is supported by the version
|
|
of NT to be installed.
|
|
|
|
Arguments:
|
|
|
|
CompEntry - If the machine is not supported, the Compatability Entry
|
|
is updated to describe why the machine is not supported.
|
|
|
|
Return Value:
|
|
|
|
Boolean value indicating whether the machine is supported.
|
|
|
|
--*/
|
|
|
|
{
|
|
TCHAR SetupLogPath[MAX_PATH];
|
|
TCHAR KeyName[MAX_PATH];
|
|
TCHAR HalName[MAX_PATH];
|
|
LPTSTR p;
|
|
LPTSTR szHalDll = TEXT("hal.dll");
|
|
LPTSTR SectionName;
|
|
LPTSTR UnsupportedName;
|
|
BOOL b;
|
|
|
|
//
|
|
// Assume that the machine is supported
|
|
//
|
|
b = TRUE;
|
|
|
|
#ifdef _X86_
|
|
|
|
try {
|
|
ULONG Name0, Name1, Name2, Family, Flags;
|
|
_asm {
|
|
push ebx ;; save ebx
|
|
mov eax, 0 ;; get processor vendor
|
|
_emit 00fh ;; CPUID(0)
|
|
_emit 0a2h ;;
|
|
mov Name0, ebx ;; Name[0-3]
|
|
mov Name1, edx ;; Name[4-7]
|
|
mov Name2, ecx ;; Name[8-11]
|
|
mov eax, 1 ;; get family/model/stepping and features
|
|
_emit 00fh ;; CPUID(1)
|
|
_emit 0a2h
|
|
mov Family, eax ;; save family/model/stepping
|
|
mov Flags, edx ;; save flags returned by CPUID
|
|
pop ebx ;; restore ebx
|
|
}
|
|
|
|
//
|
|
// Check the cmpxchg8b flag in the flags returned by CPUID.
|
|
//
|
|
|
|
if ((Flags & 0x100) == 0) {
|
|
|
|
//
|
|
// This processor doesn't support the CMPXCHG instruction
|
|
// which is required for Whistler.
|
|
//
|
|
// Some processors actually do support it but claim they
|
|
// don't because of a bug in NT 4. See if this processor
|
|
// is one of these.
|
|
//
|
|
|
|
if (!(((Name0 == 'uneG') &&
|
|
(Name1 == 'Teni') &&
|
|
(Name2 == '68xM') &&
|
|
(Family >= 0x542)) ||
|
|
(Name0 == 'tneC') &&
|
|
(Name1 == 'Hrua') &&
|
|
(Name2 == 'slua') &&
|
|
(Family >= 0x500))) {
|
|
b = FALSE;
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// If this processor doesn't support CPUID, we don't
|
|
// run on it.
|
|
//
|
|
|
|
b = FALSE;
|
|
}
|
|
|
|
if (!b) {
|
|
CompEntry->HtmlName = TEXT("cpufeat.htm");
|
|
CompEntry->TextName = TEXT("cpufeat.txt");
|
|
SectionName = TEXT("UnsupportedArchitectures");
|
|
UnsupportedName = TEXT("missprocfeat");
|
|
}
|
|
|
|
#endif // _X86_
|
|
|
|
if( b && ISNT() ) {
|
|
//
|
|
// Build the path to setup.log
|
|
//
|
|
MyGetWindowsDirectory( SetupLogPath, ARRAYSIZE(SetupLogPath) );
|
|
ConcatenatePaths( SetupLogPath, TEXT("repair\\setup.log"), ARRAYSIZE(SetupLogPath));
|
|
//
|
|
// Find out the actual name of the hal installed
|
|
//
|
|
|
|
if (!IsArc()) {
|
|
#if defined(_AMD64_) || defined(_X86_)
|
|
//
|
|
// On BIOS, look for %windir%\system32\hal.dll in the section
|
|
// [Files.WinNt]
|
|
//
|
|
GetSystemDirectory( KeyName, MAX_PATH );
|
|
ConcatenatePaths(KeyName, szHalDll, MAX_PATH );
|
|
SectionName = TEXT("Files.WinNt");
|
|
|
|
//
|
|
// While we are at it, see if this is Windows 2000 or higher
|
|
// to see if the hal should be preserved or not
|
|
//
|
|
#ifdef UNICODE
|
|
if (BUILDNUM() >= 2195) {
|
|
|
|
PCHAR halName;
|
|
|
|
halName = FindRealHalName( KeyName );
|
|
if (halName) {
|
|
|
|
WriteAcpiHalValue = TRUE;
|
|
#if defined(_AMD64_)
|
|
if (!strcmp(halName,"hal")) {
|
|
#else
|
|
if (!strcmp(halName,"halacpi") ||
|
|
!strcmp(halName,"halmacpi") ||
|
|
!strcmp(halName,"halaacpi")) {
|
|
#endif
|
|
AcpiHalValue = TRUE;
|
|
}
|
|
}
|
|
}
|
|
#endif // UNICODE
|
|
|
|
#endif // defined(_AMD64_) || defined(_X86_)
|
|
} else {
|
|
#ifdef UNICODE // Always true for ARC, never true for Win9x upgrade
|
|
//
|
|
// On ARC, look for hal.dll in the section [Files.SystemPartition]
|
|
//
|
|
lstrcpy( KeyName, szHalDll );
|
|
SectionName = TEXT("Files.SystemPartition");
|
|
#endif // UNICODE
|
|
} // if (!IsArc())
|
|
GetPrivateProfileString( SectionName,
|
|
KeyName,
|
|
TEXT(""),
|
|
HalName,
|
|
sizeof(HalName)/sizeof(TCHAR),
|
|
SetupLogPath );
|
|
//
|
|
// GetPrivateProfileString() will strip the first and last '"' from the logged value,
|
|
// so find the next '"' character and replace it with NUL, and we will end up with
|
|
// the actual hal name.
|
|
//
|
|
if( lstrlen(HalName) &&
|
|
( p = _tcschr( HalName, TEXT('"') ) )
|
|
) {
|
|
*p = TEXT('\0');
|
|
//
|
|
// Find out if the hal is listed in [UnsupportedArchitectures] (dosnet.inf)
|
|
//
|
|
SectionName = TEXT("UnsupportedArchitectures");
|
|
b = !InfDoesLineExistInSection( MainInf,
|
|
SectionName,
|
|
HalName );
|
|
UnsupportedName = HalName;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If architecture is not supported, look up the description.
|
|
//
|
|
|
|
if( !b ) {
|
|
CompEntry->Description = (LPTSTR)InfGetFieldByKey( MainInf,
|
|
SectionName,
|
|
UnsupportedName,
|
|
0 );
|
|
}
|
|
return( b );
|
|
}
|
|
|
|
|
|
BOOL
|
|
UnsupportedArchitectureCheck(
|
|
PCOMPAIBILITYCALLBACK CompatibilityCallback,
|
|
LPVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if the machine is no longer supported by Windows NT.
|
|
This routine is meaningful only when run on NT -- it always succeeds
|
|
on Win95.
|
|
|
|
Arguments:
|
|
|
|
CompatibilityCallback - pointer to call back function
|
|
Context - context pointer
|
|
|
|
Return Value:
|
|
|
|
Returns always TRUE.
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
COMPATIBILITY_ENTRY CompEntry;
|
|
|
|
CompEntry.Description = TEXT("MCA");//BUGBUG: must be changed
|
|
#ifdef _X86_
|
|
CompEntry.HtmlName = TEXT("mca.htm");
|
|
CompEntry.TextName = TEXT("mca.txt");
|
|
#else
|
|
CompEntry.HtmlName = TEXT("");
|
|
CompEntry.TextName = TEXT("");
|
|
#endif
|
|
CompEntry.RegKeyName = NULL;
|
|
CompEntry.RegValName = NULL;
|
|
CompEntry.RegValDataSize = 0;
|
|
CompEntry.RegValData = NULL;
|
|
CompEntry.SaveValue = NULL;
|
|
CompEntry.Flags = 0;
|
|
CompEntry.InfName = NULL;
|
|
CompEntry.InfSection = NULL;
|
|
|
|
if( !IsMachineSupported( &CompEntry ) ) {
|
|
if(!CompatibilityCallback(&CompEntry, Context)){
|
|
DWORD Error;
|
|
Error = GetLastError();
|
|
}
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetUserPrintableFileSizeString(
|
|
IN DWORDLONG Size,
|
|
OUT LPTSTR Buffer,
|
|
IN DWORD BufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes a size and comes up with a printable version of this size,
|
|
using the appropriate size format (ie., KB, MB, GB, Bytes, etc.)
|
|
|
|
Arguments:
|
|
|
|
Size - size to be converted (in bytes)
|
|
Buffer - string buffer to receive the data
|
|
BufferSize - indicates the buffer size, *in characters*
|
|
|
|
Return Value:
|
|
|
|
TRUE indicates success, FALSE indicates failure. If we fail,
|
|
call GetLastError() to get extended failure status.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR NumberString;
|
|
UINT uResource;
|
|
TCHAR ResourceString[100];
|
|
DWORD cb;
|
|
DWORD d;
|
|
BOOL RetVal = FALSE;
|
|
DWORDLONG TopPart;
|
|
DWORDLONG BottomPart;
|
|
|
|
//
|
|
// Determine which resource string to use
|
|
//
|
|
if (Size < 1024) {
|
|
uResource = IDS_SIZE_BYTES;
|
|
TopPart = 0;
|
|
BottomPart = 1;
|
|
wsprintf(ResourceString, TEXT("%u"), Size);
|
|
} else if (Size < (1024 * 1024)) {
|
|
|
|
uResource = IDS_SIZE_KBYTES;
|
|
TopPart = (Size%1024)*100;
|
|
BottomPart = 1024;
|
|
|
|
wsprintf(ResourceString,
|
|
TEXT("%u.%02u"),
|
|
(DWORD) (Size / 1024),
|
|
(DWORD)(TopPart/BottomPart));
|
|
} else if (Size < (1024 * 1024 * 1024)) {
|
|
uResource = IDS_SIZE_MBYTES;
|
|
TopPart = (Size%(1024*1024))*100;
|
|
BottomPart = 1024*1024;
|
|
wsprintf(ResourceString,
|
|
TEXT("%u.%02u"),
|
|
(DWORD)(Size / (1024 * 1024)),
|
|
(DWORD)(TopPart/BottomPart) );
|
|
} else {
|
|
uResource = IDS_SIZE_GBYTES;
|
|
TopPart = (Size%(1024*1024*1024))*100;
|
|
BottomPart = 1024*1024*1024;
|
|
wsprintf(ResourceString,
|
|
TEXT("%u.%02u"),
|
|
(DWORD)(Size / (1024 * 1024 * 1024)),
|
|
(DWORD)(TopPart/BottomPart) );
|
|
}
|
|
|
|
// Format the number string
|
|
cb = GetNumberFormat(LOCALE_USER_DEFAULT, 0, ResourceString, NULL, NULL, 0);
|
|
NumberString = (LPTSTR) MALLOC((cb + 1) * sizeof(TCHAR));
|
|
if (!NumberString) {
|
|
d = ERROR_NOT_ENOUGH_MEMORY;
|
|
RetVal = FALSE;
|
|
goto e0;
|
|
}
|
|
|
|
if (!GetNumberFormat(LOCALE_USER_DEFAULT, 0, ResourceString, NULL, NumberString, cb)) {
|
|
*NumberString = 0;
|
|
}
|
|
|
|
if (!LoadString(hInst, uResource, ResourceString, ARRAYSIZE(ResourceString))) {
|
|
ResourceString[0] = 0;
|
|
}
|
|
|
|
RetVal = SUCCEEDED (StringCchPrintf (Buffer, BufferSize, ResourceString, NumberString));
|
|
d = RetVal ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
FREE(NumberString);
|
|
e0:
|
|
SetLastError(d);
|
|
return(RetVal);
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
BuildSystemPartitionPathToFile (
|
|
IN PCTSTR FileName,
|
|
OUT PTSTR Path,
|
|
IN INT BufferSizeChars
|
|
)
|
|
{
|
|
//
|
|
// must have a root
|
|
//
|
|
if(SystemPartitionDriveLetter) {
|
|
Path[0] = SystemPartitionDriveLetter;
|
|
Path[1] = TEXT(':');
|
|
Path[2] = 0;
|
|
} else {
|
|
#ifdef UNICODE
|
|
if (SystemPartitionVolumeGuid) {
|
|
if (FAILED (StringCchCopy (Path, BufferSizeChars, SystemPartitionVolumeGuid))) {
|
|
//
|
|
// why is the buffer so small?
|
|
//
|
|
MYASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
MYASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
return ConcatenatePaths (Path, FileName, BufferSizeChars);
|
|
}
|
|
|
|
|
|
PTSTR
|
|
BuildPathEx (
|
|
IN PTSTR DestPath,
|
|
IN DWORD Chars,
|
|
IN PCTSTR Path1,
|
|
IN PCTSTR Path2
|
|
)
|
|
{
|
|
PTSTR p;
|
|
INT i;
|
|
BOOL haveWack = FALSE;
|
|
|
|
p = _tcsrchr (Path1, TEXT('\\'));
|
|
if (p && !p[1]) {
|
|
haveWack = TRUE;
|
|
}
|
|
if (*Path2 == TEXT('\\')) {
|
|
if (haveWack) {
|
|
Path2++;
|
|
} else {
|
|
haveWack = TRUE;
|
|
}
|
|
}
|
|
if (FAILED (StringCchPrintfEx (DestPath, Chars, &p, NULL, STRSAFE_NULL_ON_FAILURE, haveWack ? TEXT("%s%s") : TEXT("%s\\%s"), Path1, Path2))) {
|
|
//
|
|
// why is the buffer so small?
|
|
//
|
|
MYASSERT (Chars > sizeof(PTSTR) / sizeof (TCHAR));
|
|
SetLastError (ERROR_INSUFFICIENT_BUFFER);
|
|
return NULL;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumFirstFilePattern (
|
|
OUT PFILEPATTERN_ENUM Enum,
|
|
IN PCTSTR Dir,
|
|
IN PCTSTR FilePattern
|
|
)
|
|
{
|
|
TCHAR pattern[MAX_PATH];
|
|
|
|
//
|
|
// fail if invalid args are passed in
|
|
// or if [Dir+Backslash] doesn't fit into [Enum->FullPath]
|
|
//
|
|
if (!Dir || !FilePattern || lstrlen (Dir) >= ARRAYSIZE (Enum->FullPath)) {
|
|
SetLastError (ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!BuildPath (pattern, Dir, FilePattern)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Enum->Handle = FindFirstFile (pattern, &Enum->FindData);
|
|
if (Enum->Handle == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
lstrcpy (Enum->FullPath, Dir);
|
|
//
|
|
// set up other members
|
|
//
|
|
Enum->FileName = _tcschr (Enum->FullPath, 0);
|
|
*Enum->FileName++ = TEXT('\\');
|
|
*Enum->FileName = 0;
|
|
if (FAILED (StringCchCopy (
|
|
Enum->FileName,
|
|
ARRAYSIZE (Enum->FullPath) - (Enum->FileName - Enum->FullPath),
|
|
Enum->FindData.cFileName
|
|
))) {
|
|
//
|
|
// file name too long, skip it
|
|
//
|
|
DebugLog (
|
|
Winnt32LogWarning,
|
|
TEXT("Ignoring object %1\\%2 (name too long)"),
|
|
0,
|
|
Enum->FullPath,
|
|
Enum->FindData.cFileName
|
|
);
|
|
return EnumNextFilePattern (Enum);
|
|
}
|
|
|
|
if (Enum->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
if (!lstrcmp (Enum->FindData.cFileName, TEXT (".")) ||
|
|
!lstrcmp (Enum->FindData.cFileName, TEXT (".."))) {
|
|
return EnumNextFilePattern (Enum);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
EnumNextFilePattern (
|
|
IN OUT PFILEPATTERN_ENUM Enum
|
|
)
|
|
{
|
|
while (FindNextFile (Enum->Handle, &Enum->FindData)) {
|
|
|
|
if (Enum->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
if (!lstrcmp (Enum->FindData.cFileName, TEXT (".")) ||
|
|
!lstrcmp (Enum->FindData.cFileName, TEXT (".."))) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (FAILED (StringCchCopy (
|
|
Enum->FileName,
|
|
ARRAYSIZE (Enum->FullPath) - (Enum->FileName - Enum->FullPath),
|
|
Enum->FindData.cFileName
|
|
))) {
|
|
//
|
|
// file name too long, skip it
|
|
//
|
|
continue;
|
|
}
|
|
//
|
|
// found a valid object, return it
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
AbortEnumFilePattern (Enum);
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
AbortEnumFilePattern (
|
|
IN OUT PFILEPATTERN_ENUM Enum
|
|
)
|
|
{
|
|
if (Enum->Handle != INVALID_HANDLE_VALUE) {
|
|
|
|
//
|
|
// preserve error code
|
|
//
|
|
DWORD rc = GetLastError ();
|
|
|
|
FindClose (Enum->Handle);
|
|
Enum->Handle = INVALID_HANDLE_VALUE;
|
|
|
|
SetLastError (rc);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumFirstFilePatternRecursive (
|
|
OUT PFILEPATTERNREC_ENUM Enum,
|
|
IN PCTSTR Dir,
|
|
IN PCTSTR FilePattern,
|
|
IN DWORD ControlFlags
|
|
)
|
|
{
|
|
PFILEENUMLIST dir;
|
|
|
|
dir = CreateFileEnumCell (Dir, FilePattern, 0, ENUM_FIRSTFILE);
|
|
if (!dir) {
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
Enum->FilePattern = DupString (FilePattern);
|
|
if (!Enum->FilePattern) {
|
|
DeleteFileEnumCell (dir);
|
|
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
Enum->DirCurrent = dir;
|
|
Enum->FindData = &dir->Enum.FindData;
|
|
Enum->RootLen = lstrlen (Dir) + 1;
|
|
Enum->ControlFlags = ControlFlags;
|
|
Enum->Handle = INVALID_HANDLE_VALUE;
|
|
return EnumNextFilePatternRecursive (Enum);
|
|
}
|
|
|
|
BOOL
|
|
EnumNextFilePatternRecursive (
|
|
IN OUT PFILEPATTERNREC_ENUM Enum
|
|
)
|
|
{
|
|
TCHAR pattern[MAX_PATH];
|
|
WIN32_FIND_DATA fd;
|
|
PFILEENUMLIST dir;
|
|
|
|
while (Enum->DirCurrent) {
|
|
if (Enum->ControlFlags & ECF_ABORT_ENUM_DIR) {
|
|
//
|
|
// caller wants to abort enum of this subdir
|
|
// remove the current node from list
|
|
//
|
|
Enum->ControlFlags &= ~ECF_ABORT_ENUM_DIR;
|
|
dir = Enum->DirCurrent->Next;
|
|
DeleteFileEnumCell (Enum->DirCurrent);
|
|
Enum->DirCurrent = dir;
|
|
if (dir) {
|
|
Enum->FindData = &dir->Enum.FindData;
|
|
}
|
|
continue;
|
|
}
|
|
switch (Enum->DirCurrent->EnumState) {
|
|
case ENUM_FIRSTFILE:
|
|
if (EnumFirstFilePattern (&Enum->DirCurrent->Enum, Enum->DirCurrent->Dir, Enum->FilePattern)) {
|
|
Enum->DirCurrent->EnumState = ENUM_NEXTFILE;
|
|
Enum->FullPath = Enum->DirCurrent->Enum.FullPath;
|
|
Enum->SubPath = Enum->FullPath + Enum->RootLen;
|
|
Enum->FileName = Enum->DirCurrent->Enum.FileName;
|
|
return TRUE;
|
|
}
|
|
Enum->DirCurrent->EnumState = ENUM_SUBDIRS;
|
|
break;
|
|
case ENUM_NEXTFILE:
|
|
if (EnumNextFilePattern (&Enum->DirCurrent->Enum)) {
|
|
Enum->FullPath = Enum->DirCurrent->Enum.FullPath;
|
|
Enum->SubPath = Enum->FullPath + Enum->RootLen;
|
|
Enum->FileName = Enum->DirCurrent->Enum.FileName;
|
|
return TRUE;
|
|
}
|
|
Enum->DirCurrent->EnumState = ENUM_SUBDIRS;
|
|
//
|
|
// fall through
|
|
//
|
|
case ENUM_SUBDIRS:
|
|
if (BuildPath (pattern, Enum->DirCurrent->Dir, TEXT("*"))) {
|
|
Enum->Handle = FindFirstFile (pattern, &fd);
|
|
if (Enum->Handle != INVALID_HANDLE_VALUE) {
|
|
do {
|
|
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
if (!lstrcmp (fd.cFileName, TEXT (".")) ||
|
|
!lstrcmp (fd.cFileName, TEXT (".."))) {
|
|
continue;
|
|
}
|
|
if (!BuildPath (pattern, Enum->DirCurrent->Dir, fd.cFileName)) {
|
|
//
|
|
// dir name too long
|
|
//
|
|
if (Enum->ControlFlags & ECF_STOP_ON_LONG_PATHS) {
|
|
AbortEnumFilePatternRecursive (Enum);
|
|
//
|
|
// error already set by BuildPath
|
|
//
|
|
return FALSE;
|
|
}
|
|
//
|
|
// just skip it
|
|
//
|
|
DebugLog (
|
|
Winnt32LogWarning,
|
|
TEXT("Ignoring dir %1 (path too long)"),
|
|
0,
|
|
Enum->DirCurrent->Dir
|
|
);
|
|
continue;
|
|
}
|
|
if (!InsertList (
|
|
(PGENERIC_LIST*)&Enum->DirCurrent,
|
|
(PGENERIC_LIST) CreateFileEnumCell (
|
|
pattern,
|
|
Enum->FilePattern,
|
|
fd.dwFileAttributes,
|
|
Enum->ControlFlags & ECF_ENUM_SUBDIRS ?
|
|
ENUM_SUBDIR : ENUM_FIRSTFILE
|
|
)
|
|
)) {
|
|
AbortEnumFilePatternRecursive (Enum);
|
|
return FALSE;
|
|
}
|
|
}
|
|
} while (FindNextFile (Enum->Handle, &fd));
|
|
FindClose (Enum->Handle);
|
|
Enum->Handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
} else {
|
|
//
|
|
// dir name too long
|
|
//
|
|
if (Enum->ControlFlags & ECF_STOP_ON_LONG_PATHS) {
|
|
AbortEnumFilePatternRecursive (Enum);
|
|
//
|
|
// error already set by BuildPath
|
|
//
|
|
return FALSE;
|
|
}
|
|
//
|
|
// just skip it
|
|
//
|
|
DebugLog (
|
|
Winnt32LogWarning,
|
|
TEXT("Ignoring dir %1 (path too long)"),
|
|
0,
|
|
Enum->DirCurrent->Dir
|
|
);
|
|
}
|
|
//
|
|
// remove the current node from list
|
|
//
|
|
dir = Enum->DirCurrent->Next;
|
|
DeleteFileEnumCell (Enum->DirCurrent);
|
|
Enum->DirCurrent = dir;
|
|
if (dir) {
|
|
Enum->FindData = &dir->Enum.FindData;
|
|
}
|
|
break;
|
|
case ENUM_SUBDIR:
|
|
Enum->FullPath = Enum->DirCurrent->Dir;
|
|
Enum->SubPath = Enum->FullPath + Enum->RootLen;
|
|
Enum->FileName = _tcsrchr (Enum->FullPath, TEXT('\\')) + 1;
|
|
Enum->DirCurrent->EnumState = ENUM_FIRSTFILE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
AbortEnumFilePatternRecursive (
|
|
IN OUT PFILEPATTERNREC_ENUM Enum
|
|
)
|
|
{
|
|
//
|
|
// preserve error code
|
|
//
|
|
DWORD rc = GetLastError ();
|
|
|
|
if (Enum->DirCurrent) {
|
|
DeleteFileEnumList (Enum->DirCurrent);
|
|
Enum->DirCurrent = NULL;
|
|
}
|
|
if (Enum->Handle != INVALID_HANDLE_VALUE) {
|
|
FindClose (Enum->Handle);
|
|
Enum->Handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
SetLastError (rc);
|
|
}
|
|
|
|
|
|
BOOL
|
|
CopyTree (
|
|
IN PCTSTR SourceRoot,
|
|
IN PCTSTR DestRoot
|
|
)
|
|
{
|
|
DWORD rc;
|
|
FILEPATTERNREC_ENUM e;
|
|
TCHAR destFile[MAX_PATH];
|
|
PTSTR p;
|
|
BOOL b = TRUE;
|
|
|
|
if (EnumFirstFilePatternRecursive (&e, SourceRoot, TEXT("*"), ECF_STOP_ON_LONG_PATHS)) {
|
|
do {
|
|
if (!BuildPath (destFile, DestRoot, e.SubPath)) {
|
|
AbortEnumFilePatternRecursive (&e);
|
|
b = FALSE;
|
|
break;
|
|
}
|
|
p = _tcsrchr (destFile, TEXT('\\'));
|
|
if (!p) {
|
|
continue;
|
|
}
|
|
*p = 0;
|
|
rc = CreateMultiLevelDirectory (destFile);
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetLastError (rc);
|
|
AbortEnumFilePatternRecursive (&e);
|
|
b = FALSE;
|
|
break;
|
|
}
|
|
*p = TEXT('\\');
|
|
SetFileAttributes (destFile, FILE_ATTRIBUTE_NORMAL);
|
|
if (!CopyFile (e.FullPath, destFile, FALSE)) {
|
|
AbortEnumFilePatternRecursive (&e);
|
|
b = FALSE;
|
|
break;
|
|
}
|
|
} while (EnumNextFilePatternRecursive (&e));
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
PSTRINGLIST
|
|
CreateStringCell (
|
|
IN PCTSTR String
|
|
)
|
|
{
|
|
PSTRINGLIST p = MALLOC (sizeof (STRINGLIST));
|
|
if (p) {
|
|
ZeroMemory (p, sizeof (STRINGLIST));
|
|
if (String) {
|
|
p->String = DupString (String);
|
|
if (!p->String) {
|
|
FREE (p);
|
|
p = NULL;
|
|
}
|
|
} else {
|
|
p->String = NULL;
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
VOID
|
|
DeleteStringCell (
|
|
IN PSTRINGLIST Cell
|
|
)
|
|
{
|
|
if (Cell) {
|
|
FREE (Cell->String);
|
|
FREE (Cell);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DeleteStringList (
|
|
IN PSTRINGLIST List
|
|
)
|
|
{
|
|
PSTRINGLIST p, q;
|
|
|
|
for (p = List; p; p = q) {
|
|
q = p->Next;
|
|
DeleteStringCell (p);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
FindStringCell (
|
|
IN PSTRINGLIST StringList,
|
|
IN PCTSTR String,
|
|
IN BOOL CaseSensitive
|
|
)
|
|
{
|
|
PSTRINGLIST p;
|
|
INT i;
|
|
|
|
if (!StringList || !String) {
|
|
return FALSE;
|
|
}
|
|
for (p = StringList; p; p = p->Next) {
|
|
i = CaseSensitive ? _tcscmp (String, p->String) : _tcsicmp (String, p->String);
|
|
if (i == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
PFILEENUMLIST
|
|
CreateFileEnumCell (
|
|
IN PCTSTR Dir,
|
|
IN PCTSTR FilePattern,
|
|
IN DWORD Attributes,
|
|
IN DWORD EnumState
|
|
)
|
|
{
|
|
PFILEENUMLIST p = MALLOC (sizeof (FILEENUMLIST));
|
|
if (p) {
|
|
ZeroMemory (p, sizeof (FILEENUMLIST));
|
|
p->Enum.FindData.dwFileAttributes = Attributes;
|
|
p->EnumState = EnumState;
|
|
p->Enum.Handle = INVALID_HANDLE_VALUE;
|
|
p->Dir = DupString (Dir);
|
|
if (!p->Dir) {
|
|
FREE (p);
|
|
p = NULL;
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
VOID
|
|
DeleteFileEnumCell (
|
|
IN PFILEENUMLIST Cell
|
|
)
|
|
{
|
|
if (Cell) {
|
|
FREE (Cell->Dir);
|
|
AbortEnumFilePattern (&Cell->Enum);
|
|
FREE (Cell);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
InsertList (
|
|
IN OUT PGENERIC_LIST* List,
|
|
IN PGENERIC_LIST NewList
|
|
)
|
|
{
|
|
PGENERIC_LIST p;
|
|
|
|
if (!NewList) {
|
|
return FALSE;
|
|
}
|
|
if (*List) {
|
|
for (p = *List; p->Next; p = p->Next) ;
|
|
p->Next = NewList;
|
|
} else {
|
|
*List = NewList;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DeleteFileEnumList (
|
|
IN PFILEENUMLIST NewList
|
|
)
|
|
{
|
|
PFILEENUMLIST p, q;
|
|
|
|
for (p = NewList; p; p = q) {
|
|
q = p->Next;
|
|
DeleteFileEnumCell (p);
|
|
}
|
|
}
|
|
|
|
PCTSTR
|
|
FindSubString (
|
|
IN PCTSTR String,
|
|
IN TCHAR Separator,
|
|
IN PCTSTR SubStr,
|
|
IN BOOL CaseSensitive
|
|
)
|
|
{
|
|
SIZE_T len1, len2;
|
|
PCTSTR end;
|
|
|
|
MYASSERT (Separator);
|
|
MYASSERT (!_istleadbyte (Separator));
|
|
MYASSERT (SubStr);
|
|
MYASSERT (!_tcschr (SubStr, Separator));
|
|
|
|
len1 = lstrlen (SubStr);
|
|
MYASSERT (SubStr[len1] == 0);
|
|
|
|
while (String) {
|
|
end = _tcschr (String, Separator);
|
|
if (end) {
|
|
len2 = end - String;
|
|
} else {
|
|
len2 = lstrlen (String);
|
|
}
|
|
if ((len1 == len2) &&
|
|
(CaseSensitive ?
|
|
!_tcsncmp (String, SubStr, len1) :
|
|
!_tcsnicmp (String, SubStr, len1)
|
|
)) {
|
|
break;
|
|
}
|
|
if (end) {
|
|
String = end + 1;
|
|
} else {
|
|
String = NULL;
|
|
}
|
|
}
|
|
|
|
return String;
|
|
}
|
|
|
|
VOID
|
|
GetCurrentWinnt32RegKey (
|
|
OUT PTSTR Key,
|
|
IN INT Chars
|
|
)
|
|
{
|
|
INT i = _sntprintf (
|
|
Key,
|
|
Chars,
|
|
TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\Winnt32\\%u.%u"),
|
|
VER_PRODUCTMAJORVERSION,
|
|
VER_PRODUCTMINORVERSION
|
|
);
|
|
MYASSERT (i > 0);
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetFileVersionEx (
|
|
IN PCTSTR FilePath,
|
|
OUT PTSTR FileVersion,
|
|
IN INT CchFileVersion
|
|
)
|
|
{
|
|
DWORD dwLength, dwTemp;
|
|
LPVOID lpData;
|
|
VS_FIXEDFILEINFO *VsInfo;
|
|
UINT DataLength;
|
|
BOOL b = FALSE;
|
|
|
|
if (FileExists (FilePath, NULL)) {
|
|
if (dwLength = GetFileVersionInfoSize ((PTSTR)FilePath, &dwTemp)) {
|
|
if (lpData = LocalAlloc (LPTR, dwLength)) {
|
|
if (GetFileVersionInfo ((PTSTR)FilePath, 0, dwLength, lpData)) {
|
|
if (VerQueryValue (lpData, TEXT("\\"), &VsInfo, &DataLength)) {
|
|
if (SUCCEEDED(StringCchPrintf (
|
|
FileVersion,
|
|
CchFileVersion,
|
|
TEXT("%u.%u.%u.%u"),
|
|
(UINT)HIWORD(VsInfo->dwFileVersionMS),
|
|
(UINT)LOWORD(VsInfo->dwFileVersionMS),
|
|
(UINT)HIWORD(VsInfo->dwFileVersionLS),
|
|
(UINT)LOWORD(VsInfo->dwFileVersionLS)
|
|
))) {
|
|
b = TRUE;
|
|
} else {
|
|
//
|
|
// why is the buffer so small?
|
|
//
|
|
MYASSERT (FALSE);
|
|
}
|
|
}
|
|
}
|
|
LocalFree (lpData);
|
|
}
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
BOOL
|
|
IsFileVersionLesser (
|
|
IN PCTSTR FileToCompare,
|
|
IN PCTSTR FileToCompareWith
|
|
)
|
|
{
|
|
TCHAR version[20];
|
|
|
|
if (GetFileVersion (FileToCompareWith, version) && CheckForFileVersion (FileToCompare, version)) {
|
|
DebugLog (
|
|
Winnt32LogInformation,
|
|
TEXT("File %1 has a smaller version (%2) than %3"),
|
|
0,
|
|
FileToCompare,
|
|
version,
|
|
FileToCompareWith
|
|
);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FindPathToInstallationFileEx (
|
|
IN PCTSTR FileName,
|
|
OUT PTSTR PathToFile,
|
|
IN INT PathToFileBufferSize,
|
|
OUT PBOOL Compressed OPTIONAL
|
|
)
|
|
{
|
|
DWORD i;
|
|
DWORD attr;
|
|
BOOL b;
|
|
HANDLE h;
|
|
WIN32_FIND_DATA fd;
|
|
PTSTR p, q;
|
|
|
|
if (!FileName || !*FileName) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Search for installation files in this order:
|
|
// 1. AlternateSourcePath (specified on the cmd line with /M:Path)
|
|
// 2. Setup Update files (downloaded from the web)
|
|
// 3. NativeSourcePath(s)
|
|
// 4. SourcePath(s)
|
|
//
|
|
if (AlternateSourcePath[0]) {
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, AlternateSourcePath, FileName)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_DynUpdtStatus->UpdatesPath[0]) {
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, g_DynUpdtStatus->UpdatesPath, FileName)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < SourceCount; i++) {
|
|
if (!BuildPathEx (PathToFile, PathToFileBufferSize, NativeSourcePaths[i], FileName)) {
|
|
continue;
|
|
}
|
|
p = CharPrev (PathToFile, _tcschr (PathToFile, 0));
|
|
*p = TEXT('?');
|
|
b = FALSE;
|
|
h = FindFirstFile (PathToFile, &fd);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
q = CharPrev (fd.cFileName, _tcschr (fd.cFileName, 0));
|
|
*p = *q;
|
|
if (Compressed) {
|
|
*Compressed = (*q == TEXT('_'));
|
|
}
|
|
b = TRUE;
|
|
}
|
|
FindClose (h);
|
|
}
|
|
if (b) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < SourceCount; i++) {
|
|
if (!BuildPathEx (PathToFile, PathToFileBufferSize, SourcePaths[i], FileName)) {
|
|
continue;
|
|
}
|
|
p = CharPrev (PathToFile, _tcschr (PathToFile, 0));
|
|
*p = TEXT('?');
|
|
b = FALSE;
|
|
h = FindFirstFile (PathToFile, &fd);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
q = CharPrev (fd.cFileName, _tcschr (fd.cFileName, 0));
|
|
*p = *q;
|
|
if (Compressed) {
|
|
*Compressed = (*q == TEXT('_'));
|
|
}
|
|
b = TRUE;
|
|
}
|
|
FindClose (h);
|
|
}
|
|
if (b) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FindPathToWinnt32File (
|
|
IN PCTSTR FileRelativePath,
|
|
OUT PTSTR PathToFile,
|
|
IN INT PathToFileBufferSize
|
|
)
|
|
{
|
|
DWORD i;
|
|
DWORD attr;
|
|
TCHAR cdFilePath[MAX_PATH];
|
|
PTSTR p;
|
|
|
|
if (!FileRelativePath || !*FileRelativePath || PathToFileBufferSize <= 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FileRelativePath[1] == TEXT(':') && FileRelativePath[2] == TEXT('\\') ||
|
|
FileRelativePath[0] == TEXT('\\') && FileRelativePath[1] == TEXT('\\')) {
|
|
//
|
|
// assume either a DOS or a UNC full path was supplied
|
|
//
|
|
attr = GetFileAttributes (FileRelativePath);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
if (lstrlen (FileRelativePath) >= PathToFileBufferSize) {
|
|
return FALSE;
|
|
}
|
|
lstrcpy (PathToFile, FileRelativePath);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!MyGetModuleFileName (NULL, cdFilePath, ARRAYSIZE(cdFilePath)) ||
|
|
!(p = _tcsrchr (cdFilePath, TEXT('\\')))) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// hop over the backslash
|
|
//
|
|
p++;
|
|
if (FAILED (StringCchCopy (p, cdFilePath + ARRAYSIZE(cdFilePath) - p, FileRelativePath))) {
|
|
cdFilePath[0] = 0;
|
|
}
|
|
|
|
//
|
|
// Search for winnt32 files in this order:
|
|
// 1. AlternateSourcePath (specified on the cmd line with /M:Path
|
|
// 2. Setup Update files (downloaded from the web)
|
|
// 3. NativeSourcePath(s)
|
|
// 4. SourcePath(s)
|
|
//
|
|
if (AlternateSourcePath[0]) {
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, AlternateSourcePath, FileRelativePath)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
|
|
p = _tcsrchr (PathToFile, TEXT('\\'));
|
|
if (p) {
|
|
//
|
|
// try the root of /M too, for backwards compatibility with W2K
|
|
//
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, AlternateSourcePath, p + 1)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (g_DynUpdtStatus && g_DynUpdtStatus->Winnt32Path[0]) {
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, g_DynUpdtStatus->Winnt32Path, FileRelativePath)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
//
|
|
// check file version relative to the CD version
|
|
//
|
|
if (!IsFileVersionLesser (PathToFile, cdFilePath)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
//
|
|
// on Win9x systems, first check if the file was downloaded in %windir%\winnt32
|
|
// load it from there if it's present
|
|
//
|
|
if (g_LocalSourcePath) {
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, g_LocalSourcePath, FileRelativePath)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < SourceCount; i++) {
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, NativeSourcePaths[i], FileRelativePath)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < SourceCount; i++) {
|
|
if (BuildPathEx (PathToFile, PathToFileBufferSize, SourcePaths[i], FileRelativePath)) {
|
|
attr = GetFileAttributes (PathToFile);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
attr = GetFileAttributes (cdFilePath);
|
|
if (attr != (DWORD)-1 && !(attr & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
if (SUCCEEDED (StringCchCopy (PathToFile, PathToFileBufferSize, cdFilePath))) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
PathToFile[0] = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
CreateDir (
|
|
IN PCTSTR DirName
|
|
)
|
|
{
|
|
return CreateDirectory (DirName, NULL) || GetLastError () == ERROR_ALREADY_EXISTS;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetLinkDate (
|
|
IN PCTSTR FilePath,
|
|
OUT PDWORD LinkDate
|
|
)
|
|
{
|
|
HANDLE hFile;
|
|
HANDLE hFileMapping;
|
|
PVOID pFileBase;
|
|
DWORD fileSize;
|
|
PIMAGE_DOS_HEADER dosHeader;
|
|
PIMAGE_NT_HEADERS pNtHeaders;
|
|
DWORD rc;
|
|
|
|
rc = MapFileForRead (FilePath, &fileSize, &hFile, &hFileMapping, &pFileBase);
|
|
if (rc != ERROR_SUCCESS) {
|
|
SetLastError (rc);
|
|
return FALSE;
|
|
}
|
|
|
|
__try {
|
|
if (fileSize < sizeof (IMAGE_DOS_HEADER)) {
|
|
rc = ERROR_BAD_FORMAT;
|
|
__leave;
|
|
}
|
|
dosHeader = (PIMAGE_DOS_HEADER)pFileBase;
|
|
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
rc = ERROR_BAD_FORMAT;
|
|
__leave;
|
|
}
|
|
if ((DWORD)dosHeader->e_lfanew + sizeof (IMAGE_NT_HEADERS) > fileSize) {
|
|
rc = ERROR_BAD_FORMAT;
|
|
__leave;
|
|
}
|
|
pNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)pFileBase + dosHeader->e_lfanew);
|
|
if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
|
|
rc = ERROR_BAD_FORMAT;
|
|
__leave;
|
|
}
|
|
*LinkDate = pNtHeaders->FileHeader.TimeDateStamp;
|
|
rc = ERROR_SUCCESS;
|
|
} __finally {
|
|
UnmapFile (hFileMapping, pFileBase);
|
|
CloseHandle (hFile);
|
|
SetLastError (rc);
|
|
}
|
|
|
|
return rc == ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CheckForFileVersionEx (
|
|
LPCTSTR FileName,
|
|
LPCTSTR FileVer, OPTIONAL
|
|
LPCTSTR BinProductVer, OPTIONAL
|
|
LPCTSTR LinkDate OPTIONAL
|
|
)
|
|
/*
|
|
Arguments -
|
|
|
|
FileName - Full path to the file to check
|
|
Filever - Version value to check against of the for x.x.x.x
|
|
BinProductVer - Version value to check against of the for x.x.x.x
|
|
LinkDate - Link date of executable
|
|
|
|
Function will check the actual file against the fields specified. The depth of the check
|
|
is as deep as specified in "x.x.x.x" i..e if FileVer = 3.5.1 and actual version on the file
|
|
is 3.5.1.4 we only compare upto 3.5.1.
|
|
|
|
Return values -
|
|
|
|
TRUE - If the version of the file is <= FileVer which means that the file is an incompatible one
|
|
|
|
else we return FALSE
|
|
|
|
*/
|
|
|
|
{
|
|
TCHAR Buffer[MAX_PATH];
|
|
TCHAR temp[MAX_PATH];
|
|
DWORD dwLength, dwTemp;
|
|
UINT DataLength;
|
|
LPVOID lpData;
|
|
VS_FIXEDFILEINFO *VsInfo;
|
|
LPTSTR s,e;
|
|
DWORD Vers[5],File_Vers[5];//MajVer, MinVer;
|
|
INT i, Depth;
|
|
BOOL bEqual, bError = FALSE;
|
|
DWORD linkDate, fileLinkDate;
|
|
BOOL bIncompatible;
|
|
BOOL bIncompFileVer, bIncompBinProdVer;
|
|
|
|
if (!ExpandEnvironmentStrings( FileName, Buffer, ARRAYSIZE(Buffer))) {
|
|
return FALSE;
|
|
}
|
|
|
|
if(!FileExists(Buffer, NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
bIncompatible = FALSE;
|
|
|
|
if(FileVer && *FileVer || BinProductVer && *BinProductVer) {
|
|
//
|
|
// we need to read the version info
|
|
//
|
|
if(dwLength = GetFileVersionInfoSize( Buffer, &dwTemp )) {
|
|
if(lpData = LocalAlloc( LPTR, dwLength )) {
|
|
if(GetFileVersionInfo( Buffer, 0, dwLength, lpData )) {
|
|
if (VerQueryValue( lpData, TEXT("\\"), &VsInfo, &DataLength )) {
|
|
|
|
if (FileVer && *FileVer) {
|
|
File_Vers[0] = (HIWORD(VsInfo->dwFileVersionMS));
|
|
File_Vers[1] = (LOWORD(VsInfo->dwFileVersionMS));
|
|
File_Vers[2] = (HIWORD(VsInfo->dwFileVersionLS));
|
|
File_Vers[3] = (LOWORD(VsInfo->dwFileVersionLS));
|
|
if (FAILED (StringCchCopy (temp, ARRAYSIZE(temp), FileVer))) {
|
|
MYASSERT(FALSE);
|
|
}
|
|
//
|
|
//Parse and get the depth of versioning we look for
|
|
//
|
|
s = e = temp;
|
|
bEqual = FALSE;
|
|
i = 0;
|
|
if (*e == TEXT('=')) {
|
|
bEqual = TRUE;
|
|
e++;
|
|
s++;
|
|
}
|
|
while (e) {
|
|
if (((*e < TEXT('0')) || (*e > TEXT('9'))) &&
|
|
(*e != TEXT('.')) &&
|
|
(*e != TEXT('\0'))
|
|
) {
|
|
MYASSERT (FALSE);
|
|
bError = TRUE;
|
|
break;
|
|
}
|
|
if (*e == TEXT('\0')) {
|
|
*e = 0;
|
|
Vers[i] = _ttoi(s);
|
|
break;
|
|
}
|
|
if (*e == TEXT('.')) {
|
|
*e = 0;
|
|
Vers[i++] = _ttoi(s);
|
|
s = e+1;
|
|
}
|
|
e++;
|
|
}// while
|
|
|
|
if (!bError) {
|
|
Depth = i + 1;
|
|
if (Depth > 4) {
|
|
Depth = 4;
|
|
}
|
|
for (i = 0; i < Depth; i++) {
|
|
if (File_Vers[i] == Vers[i]) {
|
|
continue;
|
|
}
|
|
if (bEqual) {
|
|
break;
|
|
}
|
|
if (File_Vers[i] > Vers[i]) {
|
|
break;
|
|
}
|
|
bIncompatible = TRUE;
|
|
break;
|
|
}
|
|
if (i == Depth) {
|
|
//
|
|
// everything matched - the file is incompatible
|
|
//
|
|
bIncompatible = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
bIncompatible = TRUE;
|
|
}
|
|
if (!bError && bIncompatible && BinProductVer && *BinProductVer) {
|
|
//
|
|
// reset status
|
|
//
|
|
bIncompatible = FALSE;
|
|
File_Vers[0] = (HIWORD(VsInfo->dwProductVersionMS));
|
|
File_Vers[1] = (LOWORD(VsInfo->dwProductVersionMS));
|
|
File_Vers[2] = (HIWORD(VsInfo->dwProductVersionLS));
|
|
File_Vers[3] = (LOWORD(VsInfo->dwProductVersionLS));
|
|
if (FAILED (StringCchCopy (temp, ARRAYSIZE(temp), BinProductVer))) {
|
|
MYASSERT(FALSE);
|
|
}
|
|
//
|
|
//Parse and get the depth of versioning we look for
|
|
//
|
|
s = e = temp;
|
|
bEqual = FALSE;
|
|
i = 0;
|
|
if (*e == TEXT('=')) {
|
|
bEqual = TRUE;
|
|
e++;
|
|
s++;
|
|
}
|
|
while (e) {
|
|
if (((*e < TEXT('0')) || (*e > TEXT('9'))) &&
|
|
(*e != TEXT('.')) &&
|
|
(*e != TEXT('\0'))
|
|
) {
|
|
MYASSERT (FALSE);
|
|
bError = TRUE;
|
|
break;
|
|
}
|
|
if (*e == TEXT('\0')) {
|
|
*e = 0;
|
|
Vers[i] = _ttoi(s);
|
|
break;
|
|
}
|
|
if (*e == TEXT('.')) {
|
|
*e = 0;
|
|
Vers[i++] = _ttoi(s);
|
|
s = e+1;
|
|
}
|
|
e++;
|
|
}// while
|
|
|
|
if (!bError) {
|
|
Depth = i + 1;
|
|
if (Depth > 4) {
|
|
Depth = 4;
|
|
}
|
|
for (i = 0; i < Depth; i++) {
|
|
if (File_Vers[i] == Vers[i]) {
|
|
continue;
|
|
}
|
|
if (bEqual) {
|
|
break;
|
|
}
|
|
if (File_Vers[i] > Vers[i]) {
|
|
break;
|
|
}
|
|
bIncompatible = TRUE;
|
|
break;
|
|
}
|
|
if (i == Depth) {
|
|
//
|
|
// everything matched - the file is incompatible
|
|
//
|
|
bIncompatible = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
LocalFree( lpData );
|
|
}
|
|
}
|
|
} else {
|
|
bIncompatible = TRUE;
|
|
}
|
|
|
|
if (!bError && bIncompatible && LinkDate && *LinkDate) {
|
|
bEqual = FALSE;
|
|
if (*LinkDate == TEXT('=')) {
|
|
LinkDate++;
|
|
bEqual = TRUE;
|
|
}
|
|
bIncompatible = FALSE;
|
|
if (StringToInt (LinkDate, &linkDate)) {
|
|
if (GetLinkDate (Buffer, &fileLinkDate)) {
|
|
if (fileLinkDate == linkDate ||
|
|
!bEqual && fileLinkDate < linkDate
|
|
) {
|
|
bIncompatible = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bError) {
|
|
bIncompatible = FALSE;
|
|
}
|
|
return bIncompatible;
|
|
}
|
|
|
|
BOOL
|
|
StringToInt (
|
|
IN PCTSTR Field,
|
|
OUT PINT IntegerValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Remarks:
|
|
|
|
Hexadecimal numbers are also supported. They must be prefixed by '0x' or '0X', with no
|
|
space allowed between the prefix and the number.
|
|
|
|
--*/
|
|
|
|
{
|
|
INT Value;
|
|
UINT c;
|
|
BOOL Neg;
|
|
UINT Base;
|
|
UINT NextDigitValue;
|
|
INT OverflowCheck;
|
|
BOOL b;
|
|
|
|
if(!Field) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return(FALSE);
|
|
}
|
|
|
|
if(*Field == TEXT('-')) {
|
|
Neg = TRUE;
|
|
Field++;
|
|
} else {
|
|
Neg = FALSE;
|
|
if(*Field == TEXT('+')) {
|
|
Field++;
|
|
}
|
|
}
|
|
|
|
if((*Field == TEXT('0')) &&
|
|
((*(Field+1) == TEXT('x')) || (*(Field+1) == TEXT('X')))) {
|
|
//
|
|
// The number is in hexadecimal.
|
|
//
|
|
Base = 16;
|
|
Field += 2;
|
|
} else {
|
|
//
|
|
// The number is in decimal.
|
|
//
|
|
Base = 10;
|
|
}
|
|
|
|
for(OverflowCheck = Value = 0; *Field; Field++) {
|
|
|
|
c = (UINT)*Field;
|
|
|
|
if((c >= (UINT)'0') && (c <= (UINT)'9')) {
|
|
NextDigitValue = c - (UINT)'0';
|
|
} else if(Base == 16) {
|
|
if((c >= (UINT)'a') && (c <= (UINT)'f')) {
|
|
NextDigitValue = (c - (UINT)'a') + 10;
|
|
} else if ((c >= (UINT)'A') && (c <= (UINT)'F')) {
|
|
NextDigitValue = (c - (UINT)'A') + 10;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
Value *= Base;
|
|
Value += NextDigitValue;
|
|
|
|
//
|
|
// Check for overflow. For decimal numbers, we check to see whether the
|
|
// new value has overflowed into the sign bit (i.e., is less than the
|
|
// previous value. For hexadecimal numbers, we check to make sure we
|
|
// haven't gotten more digits than will fit in a DWORD.
|
|
//
|
|
if(Base == 16) {
|
|
if(++OverflowCheck > (sizeof(INT) * 2)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if(Value < OverflowCheck) {
|
|
break;
|
|
} else {
|
|
OverflowCheck = Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(*Field) {
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
return(FALSE);
|
|
}
|
|
|
|
if(Neg) {
|
|
Value = 0-Value;
|
|
}
|
|
b = TRUE;
|
|
try {
|
|
*IntegerValue = Value;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
b = FALSE;
|
|
}
|
|
if(!b) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
return(b);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CheckForFileVersion (
|
|
LPCTSTR FileName,
|
|
LPCTSTR FileVer
|
|
)
|
|
{
|
|
return (BOOLEAN)CheckForFileVersionEx (FileName, FileVer, NULL, NULL);
|
|
}
|
|
|
|
|
|
VOID
|
|
FixMissingKnownDlls (
|
|
OUT PSTRINGLIST* MissingKnownDlls,
|
|
IN PCTSTR RestrictedCheckList OPTIONAL
|
|
)
|
|
{
|
|
PCTSTR regStr;
|
|
HKEY key;
|
|
DWORD rc;
|
|
DWORD index;
|
|
TCHAR dllValue[MAX_PATH];
|
|
TCHAR dllName[MAX_PATH];
|
|
DWORD type;
|
|
DWORD size1;
|
|
DWORD size2;
|
|
TCHAR systemDir[MAX_PATH];
|
|
TCHAR dllPath[MAX_PATH];
|
|
BOOL bCheck;
|
|
|
|
if (!GetSystemDirectory (systemDir, ARRAYSIZE(systemDir))) {
|
|
return;
|
|
}
|
|
|
|
#ifdef UNICODE
|
|
regStr = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs";
|
|
#else
|
|
regStr = "SYSTEM\\CurrentControlSet\\Control\\SessionManager\\KnownDLLs";
|
|
#endif
|
|
rc = RegOpenKey (HKEY_LOCAL_MACHINE, regStr, &key);
|
|
if (rc == ERROR_SUCCESS) {
|
|
index = 0;
|
|
size1 = ARRAYSIZE(dllValue);
|
|
size2 = ARRAYSIZE(dllName);
|
|
while (RegEnumValue (
|
|
key,
|
|
index++,
|
|
dllValue,
|
|
&size1,
|
|
NULL,
|
|
&type,
|
|
(LPBYTE)dllName,
|
|
&size2
|
|
) == ERROR_SUCCESS) {
|
|
if (type == REG_SZ) {
|
|
bCheck = TRUE;
|
|
if (RestrictedCheckList) {
|
|
PCTSTR fileName = RestrictedCheckList;
|
|
while (*fileName) {
|
|
if (!lstrcmpi (fileName, dllName)) {
|
|
break;
|
|
}
|
|
fileName = _tcschr (fileName, 0) + 1;
|
|
}
|
|
if (*fileName == 0) {
|
|
//
|
|
// we are not interested in this dll
|
|
//
|
|
bCheck = FALSE;
|
|
}
|
|
}
|
|
if (bCheck) {
|
|
if (!BuildPath (dllPath, systemDir, dllName) ||
|
|
!FileExists (dllPath, NULL)) {
|
|
|
|
DebugLog (
|
|
Winnt32LogWarning,
|
|
TEXT("The KnownDll %1\\%2 has a name too long or it doesn't exist"),
|
|
0,
|
|
systemDir,
|
|
dllName
|
|
);
|
|
//
|
|
// OK, we found a bogus reg entry; remove the value and remember the data
|
|
//
|
|
if (RegDeleteValue (key, dllValue) == ERROR_SUCCESS) {
|
|
InsertList (
|
|
(PGENERIC_LIST*)MissingKnownDlls,
|
|
(PGENERIC_LIST)CreateStringCell (dllValue)
|
|
);
|
|
InsertList (
|
|
(PGENERIC_LIST*)MissingKnownDlls,
|
|
(PGENERIC_LIST)CreateStringCell (dllName)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
size1 = ARRAYSIZE(dllValue);
|
|
size2 = ARRAYSIZE(dllName);
|
|
}
|
|
RegCloseKey (key);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UndoFixMissingKnownDlls (
|
|
IN PSTRINGLIST MissingKnownDlls
|
|
)
|
|
{
|
|
PCTSTR regStr;
|
|
HKEY key;
|
|
DWORD rc;
|
|
PSTRINGLIST p, q;
|
|
|
|
#ifdef UNICODE
|
|
regStr = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs";
|
|
#else
|
|
regStr = "SYSTEM\\CurrentControlSet\\Control\\SessionManager\\KnownDLLs";
|
|
#endif
|
|
rc = RegOpenKey (HKEY_LOCAL_MACHINE, regStr, &key);
|
|
if (rc == ERROR_SUCCESS) {
|
|
p = MissingKnownDlls;
|
|
while (p) {
|
|
q = p->Next;
|
|
if (q) {
|
|
RegSetValueEx (
|
|
key,
|
|
p->String,
|
|
0,
|
|
REG_SZ,
|
|
(const PBYTE)q->String,
|
|
(lstrlen (q->String) + 1) * sizeof (TCHAR)
|
|
);
|
|
p = q->Next;
|
|
} else {
|
|
p = NULL;
|
|
}
|
|
}
|
|
RegCloseKey (key);
|
|
}
|
|
DeleteStringList (MissingKnownDlls);
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
IsPatternMatch compares a string against a pattern that may contain
|
|
standard * or ? wildcards.
|
|
|
|
Arguments:
|
|
|
|
wstrPattern - A pattern possibly containing wildcards
|
|
wstrStr - The string to compare against the pattern
|
|
|
|
Return Value:
|
|
|
|
TRUE when wstrStr and wstrPattern match when wildcards are expanded.
|
|
FALSE if wstrStr does not match wstrPattern.
|
|
|
|
--*/
|
|
|
|
#define MBCHAR INT
|
|
|
|
BOOL
|
|
IsPatternMatchA (
|
|
IN PCSTR strPattern,
|
|
IN PCSTR strStr
|
|
)
|
|
{
|
|
|
|
MBCHAR chSrc, chPat;
|
|
|
|
while (*strStr) {
|
|
chSrc = _mbctolower ((MBCHAR) _mbsnextc (strStr));
|
|
chPat = _mbctolower ((MBCHAR) _mbsnextc (strPattern));
|
|
|
|
if (chPat == '*') {
|
|
|
|
// Skip all asterisks that are grouped together
|
|
while (_mbsnextc (_mbsinc (strPattern)) == '*') {
|
|
strStr = _mbsinc (strPattern);
|
|
}
|
|
|
|
// Check if asterisk is at the end. If so, we have a match already.
|
|
if (!_mbsnextc (_mbsinc (strPattern))) {
|
|
return TRUE;
|
|
}
|
|
|
|
// do recursive check for rest of pattern
|
|
if (IsPatternMatchA (_mbsinc (strPattern), strStr)) {
|
|
return TRUE;
|
|
}
|
|
|
|
// Allow any character and continue
|
|
strStr = _mbsinc (strStr);
|
|
continue;
|
|
}
|
|
if (chPat != '?') {
|
|
if (chSrc != chPat) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
strStr = _mbsinc (strStr);
|
|
strPattern = _mbsinc (strPattern);
|
|
}
|
|
|
|
//
|
|
// Fail when there is more pattern and pattern does not end in an asterisk
|
|
//
|
|
|
|
while (_mbsnextc (strPattern) == '*') {
|
|
strPattern = _mbsinc (strPattern);
|
|
}
|
|
if (_mbsnextc (strPattern)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Wierd logic here required to make builds work, as this is defined
|
|
// in another file that gets linked in on x86
|
|
|
|
#ifdef _WIN64
|
|
|
|
BOOL
|
|
IsPatternMatchW (
|
|
IN PCWSTR wstrPattern,
|
|
IN PCWSTR wstrStr
|
|
)
|
|
|
|
{
|
|
WCHAR chSrc, chPat;
|
|
|
|
while (*wstrStr) {
|
|
chSrc = towlower (*wstrStr);
|
|
chPat = towlower (*wstrPattern);
|
|
|
|
if (chPat == L'*') {
|
|
|
|
// Skip all asterisks that are grouped together
|
|
while (wstrPattern[1] == L'*')
|
|
wstrPattern++;
|
|
|
|
// Check if asterisk is at the end. If so, we have a match already.
|
|
chPat = towlower (wstrPattern[1]);
|
|
if (!chPat)
|
|
return TRUE;
|
|
|
|
// Otherwise check if next pattern char matches current char
|
|
if (chPat == chSrc || chPat == L'?') {
|
|
|
|
// do recursive check for rest of pattern
|
|
wstrPattern++;
|
|
if (IsPatternMatchW (wstrPattern, wstrStr))
|
|
return TRUE;
|
|
|
|
// no, that didn't work, stick with star
|
|
wstrPattern--;
|
|
}
|
|
|
|
//
|
|
// Allow any character and continue
|
|
//
|
|
|
|
wstrStr++;
|
|
continue;
|
|
}
|
|
|
|
if (chPat != L'?') {
|
|
|
|
//
|
|
// if next pattern character is not a question mark, src and pat
|
|
// must be identical.
|
|
//
|
|
|
|
if (chSrc != chPat)
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Advance when pattern character matches string character
|
|
//
|
|
|
|
wstrPattern++;
|
|
wstrStr++;
|
|
}
|
|
|
|
//
|
|
// Fail when there is more pattern and pattern does not end in an asterisk
|
|
//
|
|
|
|
chPat = *wstrPattern;
|
|
if (chPat && (chPat != L'*' || wstrPattern[1]))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
typedef BOOL (WINAPI * GETDISKFREESPACEEXA)(
|
|
PCSTR lpDirectoryName, // directory name
|
|
PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller
|
|
PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk
|
|
PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk
|
|
);
|
|
|
|
typedef BOOL (WINAPI * GETDISKFREESPACEEXW)(
|
|
PCWSTR lpDirectoryName, // directory name
|
|
PULARGE_INTEGER lpFreeBytesAvailable, // bytes available to caller
|
|
PULARGE_INTEGER lpTotalNumberOfBytes, // bytes on disk
|
|
PULARGE_INTEGER lpTotalNumberOfFreeBytes // free bytes on disk
|
|
);
|
|
|
|
BOOL
|
|
Winnt32GetDiskFreeSpaceNewA(
|
|
IN PCSTR DriveName,
|
|
OUT DWORD * OutSectorsPerCluster,
|
|
OUT DWORD * OutBytesPerSector,
|
|
OUT ULARGE_INTEGER * OutNumberOfFreeClusters,
|
|
OUT ULARGE_INTEGER * OutTotalNumberOfClusters
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
On Win9x GetDiskFreeSpace never return free/total space more than 2048MB.
|
|
Winnt32GetDiskFreeSpaceNew use GetDiskFreeSpaceEx to calculate real number of free/total clusters.
|
|
Has same declaration as GetDiskFreeSpaceA.
|
|
|
|
Arguments:
|
|
|
|
DriveName - supplies directory name
|
|
OutSectorsPerCluster - receive number of sectors per cluster
|
|
OutBytesPerSector - receive number of bytes per sector
|
|
OutNumberOfFreeClusters - receive number of free clusters
|
|
OutTotalNumberOfClusters - receive number of total clusters
|
|
|
|
Return Value:
|
|
|
|
TRUE if the function succeeds.
|
|
If the function fails, the return value is FALSE. To get extended error information, call GetLastError
|
|
|
|
--*/
|
|
{
|
|
ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0};
|
|
ULARGE_INTEGER TotalNumberOfBytes = {0, 0};
|
|
ULARGE_INTEGER DonotCare;
|
|
HMODULE hKernel32;
|
|
GETDISKFREESPACEEXA pGetDiskFreeSpaceExA;
|
|
ULARGE_INTEGER NumberOfFreeClusters = {0, 0};
|
|
ULARGE_INTEGER TotalNumberOfClusters = {0, 0};
|
|
DWORD SectorsPerCluster;
|
|
DWORD BytesPerSector;
|
|
|
|
if(!GetDiskFreeSpaceA(DriveName,
|
|
&SectorsPerCluster,
|
|
&BytesPerSector,
|
|
&NumberOfFreeClusters.LowPart,
|
|
&TotalNumberOfClusters.LowPart)){
|
|
DebugLog (
|
|
Winnt32LogError,
|
|
TEXT("Winnt32GetDiskFreeSpaceNewA: GetDiskFreeSpaceA failed on drive %1"),
|
|
0,
|
|
DriveName);
|
|
return FALSE;
|
|
}
|
|
|
|
hKernel32 = LoadLibraryA("kernel32.dll");
|
|
pGetDiskFreeSpaceExA = (GETDISKFREESPACEEXA)GetProcAddress(hKernel32, "GetDiskFreeSpaceExA");
|
|
if(pGetDiskFreeSpaceExA &&
|
|
pGetDiskFreeSpaceExA(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){
|
|
NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
|
|
TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
|
|
}
|
|
else{
|
|
DebugLog (
|
|
Winnt32LogWarning,
|
|
pGetDiskFreeSpaceExA?
|
|
TEXT("Winnt32GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA is failed"):
|
|
TEXT("Winnt32GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA function is not in kernel32.dll"),
|
|
0);
|
|
}
|
|
FreeLibrary(hKernel32);
|
|
|
|
if(OutSectorsPerCluster){
|
|
*OutSectorsPerCluster = SectorsPerCluster;
|
|
}
|
|
|
|
if(OutBytesPerSector){
|
|
*OutBytesPerSector = BytesPerSector;
|
|
}
|
|
|
|
if(OutNumberOfFreeClusters){
|
|
OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
|
|
}
|
|
|
|
if(OutTotalNumberOfClusters){
|
|
OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
Winnt32GetDiskFreeSpaceNewW(
|
|
IN PCWSTR DriveName,
|
|
OUT DWORD * OutSectorsPerCluster,
|
|
OUT DWORD * OutBytesPerSector,
|
|
OUT ULARGE_INTEGER * OutNumberOfFreeClusters,
|
|
OUT ULARGE_INTEGER * OutTotalNumberOfClusters
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Correct NumberOfFreeClusters and TotalNumberOfClusters out parameters
|
|
with using GetDiskFreeSpace and GetDiskFreeSpaceEx
|
|
|
|
Arguments:
|
|
|
|
DriveName - supplies directory name
|
|
OutSectorsPerCluster - receive number of sectors per cluster
|
|
OutBytesPerSector - receive number of bytes per sector
|
|
OutNumberOfFreeClusters - receive number of free clusters
|
|
OutTotalNumberOfClusters - receive number of total clusters
|
|
|
|
Return Value:
|
|
|
|
TRUE if the function succeeds.
|
|
If the function fails, the return value is FALSE. To get extended error information, call GetLastError
|
|
|
|
--*/
|
|
{
|
|
ULARGE_INTEGER TotalNumberOfFreeBytes = {0, 0};
|
|
ULARGE_INTEGER TotalNumberOfBytes = {0, 0};
|
|
ULARGE_INTEGER DonotCare;
|
|
HMODULE hKernel32;
|
|
GETDISKFREESPACEEXW pGetDiskFreeSpaceExW;
|
|
ULARGE_INTEGER NumberOfFreeClusters = {0, 0};
|
|
ULARGE_INTEGER TotalNumberOfClusters = {0, 0};
|
|
DWORD SectorsPerCluster;
|
|
DWORD BytesPerSector;
|
|
|
|
if(!GetDiskFreeSpaceW(DriveName,
|
|
&SectorsPerCluster,
|
|
&BytesPerSector,
|
|
&NumberOfFreeClusters.LowPart,
|
|
&TotalNumberOfClusters.LowPart)){
|
|
DebugLog (
|
|
Winnt32LogError,
|
|
TEXT("Winnt32GetDiskFreeSpaceNewW: GetDiskFreeSpaceW failed on drive %1"),
|
|
0,
|
|
DriveName);
|
|
return FALSE;
|
|
}
|
|
|
|
hKernel32 = LoadLibraryA("kernel32.dll");
|
|
pGetDiskFreeSpaceExW = (GETDISKFREESPACEEXW)GetProcAddress(hKernel32, "GetDiskFreeSpaceExW");
|
|
if(pGetDiskFreeSpaceExW &&
|
|
pGetDiskFreeSpaceExW(DriveName, &DonotCare, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)){
|
|
NumberOfFreeClusters.QuadPart = TotalNumberOfFreeBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
|
|
TotalNumberOfClusters.QuadPart = TotalNumberOfBytes.QuadPart / (SectorsPerCluster * BytesPerSector);
|
|
}
|
|
else{
|
|
DebugLog (
|
|
Winnt32LogWarning,
|
|
pGetDiskFreeSpaceExW?
|
|
TEXT("Winnt32GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW is failed"):
|
|
TEXT("Winnt32GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW function is not in kernel32.dll"),
|
|
0);
|
|
}
|
|
FreeLibrary(hKernel32);
|
|
|
|
if(OutSectorsPerCluster){
|
|
*OutSectorsPerCluster = SectorsPerCluster;
|
|
}
|
|
|
|
if(OutBytesPerSector){
|
|
*OutBytesPerSector = BytesPerSector;
|
|
}
|
|
|
|
if(OutNumberOfFreeClusters){
|
|
OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
|
|
}
|
|
|
|
if(OutTotalNumberOfClusters){
|
|
OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
ReplaceSubStr(
|
|
IN OUT LPTSTR SrcStr,
|
|
IN LPTSTR SrcSubStr,
|
|
IN LPTSTR DestSubStr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Replaces the source substr with the destination substr in the source
|
|
string.
|
|
|
|
NOTE : SrcSubStr needs to be longer than or equal in length to
|
|
DestSubStr.
|
|
|
|
Arguments:
|
|
|
|
SrcStr : The source to operate upon. Also receives the new string.
|
|
|
|
SrcSubStr : The source substring to search for and replace.
|
|
|
|
DestSubStr : The substring to replace with for the occurences
|
|
of SrcSubStr in SrcStr.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, otherwise FALSE.
|
|
|
|
--*/
|
|
{
|
|
BOOL Result = FALSE;
|
|
|
|
//
|
|
// Validate the arguments
|
|
//
|
|
if (SrcStr && SrcSubStr && *SrcSubStr &&
|
|
(!DestSubStr || (_tcslen(SrcSubStr) >= _tcslen(DestSubStr)))) {
|
|
if (!DestSubStr || _tcsicmp(SrcSubStr, DestSubStr)) {
|
|
ULONG SrcStrLen = _tcslen(SrcStr);
|
|
ULONG SrcSubStrLen = _tcslen(SrcSubStr);
|
|
ULONG DestSubStrLen = DestSubStr ? _tcslen(DestSubStr) : 0;
|
|
LPTSTR DestStr = malloc((SrcStrLen + 1) * sizeof(TCHAR));
|
|
|
|
if (DestStr) {
|
|
LPTSTR CurrDestStr = DestStr;
|
|
LPTSTR PrevSrcStr = SrcStr;
|
|
LPTSTR CurrSrcStr = _tcsstr(SrcStr, SrcSubStr);
|
|
|
|
while (CurrSrcStr) {
|
|
//
|
|
// Skip starting substr & copy previous unmatched pattern
|
|
//
|
|
if (PrevSrcStr != CurrSrcStr) {
|
|
_tcsncpy(CurrDestStr, PrevSrcStr, (CurrSrcStr - PrevSrcStr));
|
|
CurrDestStr += (CurrSrcStr - PrevSrcStr);
|
|
*CurrDestStr = TEXT('\0');
|
|
}
|
|
|
|
//
|
|
// Copy destination substr
|
|
//
|
|
if (DestSubStr) {
|
|
_tcscpy(CurrDestStr, DestSubStr);
|
|
CurrDestStr += DestSubStrLen;
|
|
*CurrDestStr = TEXT('\0');
|
|
}
|
|
|
|
//
|
|
// Look for next substr
|
|
//
|
|
CurrSrcStr += SrcSubStrLen;
|
|
PrevSrcStr = CurrSrcStr;
|
|
CurrSrcStr = _tcsstr(CurrSrcStr, SrcSubStr);
|
|
}
|
|
|
|
//
|
|
// Copy remaining src string if any
|
|
//
|
|
if (!_tcsstr(PrevSrcStr, SrcSubStr)) {
|
|
_tcscpy(CurrDestStr, PrevSrcStr);
|
|
}
|
|
|
|
//
|
|
// Copy the new string back to the src string
|
|
//
|
|
_tcscpy(SrcStr, DestStr);
|
|
|
|
free(DestStr);
|
|
Result = TRUE;
|
|
}
|
|
} else {
|
|
Result = TRUE;
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID
|
|
RemoveTrailingWack (
|
|
PTSTR String
|
|
)
|
|
{
|
|
if (String) {
|
|
PTSTR p = _tcsrchr (String, TEXT('\\'));
|
|
if (p && p[1] == 0) {
|
|
*p = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
ULONGLONG
|
|
SystemTimeToFileTime64 (
|
|
IN PSYSTEMTIME SystemTime
|
|
)
|
|
{
|
|
FILETIME ft;
|
|
ULARGE_INTEGER result;
|
|
|
|
SystemTimeToFileTime (SystemTime, &ft);
|
|
result.LowPart = ft.dwLowDateTime;
|
|
result.HighPart = ft.dwHighDateTime;
|
|
|
|
return result.QuadPart;
|
|
}
|
|
|
|
|
|
DWORD
|
|
MyGetFullPathName (
|
|
IN PCTSTR FileName, // file name
|
|
IN DWORD BufferLength, // size of path buffer
|
|
IN PTSTR Buffer, // path buffer
|
|
OUT PTSTR* FilePart // address of file name in path
|
|
)
|
|
{
|
|
DWORD d = GetFullPathName (FileName, BufferLength, Buffer, FilePart);
|
|
return d < BufferLength ? d : 0;
|
|
}
|
|
|
|
DWORD
|
|
MyGetModuleFileName (
|
|
IN HMODULE Module,
|
|
OUT PTSTR Buffer,
|
|
IN DWORD BufferLength
|
|
)
|
|
{
|
|
DWORD d = GetModuleFileName (Module, Buffer, BufferLength);
|
|
Buffer[BufferLength - 1] = 0;
|
|
return d < BufferLength ? d : 0;
|
|
}
|
|
|