4745 lines
123 KiB
C
4745 lines
123 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
file.c
|
|
|
|
Abstract:
|
|
|
|
General file-related functions.
|
|
|
|
Author:
|
|
|
|
Mike Condra 16-Aug-1996
|
|
|
|
Revision History:
|
|
|
|
calinn 23-Sep-1998 Additional options to file enum
|
|
jimschm 05-Feb-1998 File enumeration code
|
|
jimschm 30-Sep-1997 WriteFileString routines
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
//
|
|
// Function accepts paths within the limit of our stack variable size
|
|
//
|
|
BOOL
|
|
IsPathLengthOkA (
|
|
IN PCSTR FileSpec
|
|
)
|
|
{
|
|
return SizeOfStringA(FileSpec) < MAX_MBCHAR_PATH;
|
|
}
|
|
|
|
BOOL
|
|
IsPathLengthOkW (
|
|
IN PCWSTR FileSpec
|
|
)
|
|
{
|
|
return SizeOfStringW(FileSpec) < (MAX_WCHAR_PATH * sizeof (WCHAR));
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsPathOnFixedDriveA (
|
|
IN PCSTR FileSpec OPTIONAL
|
|
)
|
|
{
|
|
static CHAR root[] = "?:\\";
|
|
UINT type;
|
|
static BOOL lastResult;
|
|
|
|
if (!FileSpec) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!FileSpec[0]) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (FileSpec[1] != ':' || FileSpec[2] != '\\') {
|
|
return FALSE;
|
|
}
|
|
|
|
if (root[0] == FileSpec[0]) {
|
|
return lastResult;
|
|
}
|
|
|
|
root[0] = FileSpec[0];
|
|
type = GetDriveTypeA (root);
|
|
|
|
if (type != DRIVE_FIXED && type != DRIVE_REMOTE) {
|
|
DEBUGMSGA_IF ((
|
|
type != DRIVE_REMOVABLE && type != DRIVE_NO_ROOT_DIR,
|
|
DBG_VERBOSE,
|
|
"Path %s is on unexpected drive type %u",
|
|
FileSpec,
|
|
type
|
|
));
|
|
|
|
lastResult = FALSE;
|
|
} else {
|
|
lastResult = TRUE;
|
|
}
|
|
|
|
return lastResult;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsPathOnFixedDriveW (
|
|
IN PCWSTR FileSpec OPTIONAL
|
|
)
|
|
{
|
|
static WCHAR root[] = L"?:\\";
|
|
UINT type;
|
|
static BOOL lastResult;
|
|
PCWSTR p;
|
|
|
|
if (!FileSpec) {
|
|
return FALSE;
|
|
}
|
|
|
|
p = FileSpec;
|
|
if (p[0] == L'\\' && p[1] == L'\\' && p[2] == L'?' && p[3] == L'\\') {
|
|
p += 4;
|
|
}
|
|
|
|
MYASSERT (ISNT());
|
|
|
|
if (!p[0]) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (p[1] != L':' || p[2] != L'\\') {
|
|
return FALSE;
|
|
}
|
|
|
|
if (root[0] == p[0]) {
|
|
return lastResult;
|
|
}
|
|
|
|
root[0] = p[0];
|
|
type = GetDriveTypeW (root);
|
|
|
|
if (type != DRIVE_FIXED && type != DRIVE_REMOTE) {
|
|
DEBUGMSGW_IF ((
|
|
type != DRIVE_REMOVABLE && type != DRIVE_NO_ROOT_DIR,
|
|
DBG_VERBOSE,
|
|
"Path %s is on unexpected drive type %u",
|
|
FileSpec,
|
|
type
|
|
));
|
|
|
|
lastResult = FALSE;
|
|
} else {
|
|
lastResult = TRUE;
|
|
}
|
|
|
|
return lastResult;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CopyFileSpecToLongA (
|
|
IN PCSTR FullFileSpecIn,
|
|
OUT PSTR OutPath
|
|
)
|
|
{
|
|
CHAR fullFileSpec[MAX_MBCHAR_PATH];
|
|
WIN32_FIND_DATAA findData;
|
|
HANDLE findHandle;
|
|
PSTR end;
|
|
PSTR currentIn;
|
|
PSTR currentOut;
|
|
PSTR outEnd;
|
|
PSTR maxOutPath = OutPath + MAX_PATH - 1;
|
|
BOOL result = FALSE;
|
|
UINT oldMode;
|
|
BOOL forceCopy = FALSE;
|
|
|
|
oldMode = SetErrorMode (SEM_FAILCRITICALERRORS);
|
|
|
|
__try {
|
|
//
|
|
// Limit length
|
|
//
|
|
if (!IsPathLengthOkA (FullFileSpecIn)) {
|
|
DEBUGMSGA ((DBG_ERROR, "Inbound file spec is too long: %s", FullFileSpecIn));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If path is on removable media, don't touch the disk
|
|
//
|
|
|
|
if (!IsPathOnFixedDriveA (FullFileSpecIn)) {
|
|
forceCopy = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Make a copy of the file spec so we can truncate it at the wacks
|
|
//
|
|
|
|
StringCopyA (fullFileSpec, FullFileSpecIn);
|
|
|
|
//
|
|
// Begin building the path
|
|
//
|
|
|
|
OutPath[0] = fullFileSpec[0];
|
|
OutPath[1] = fullFileSpec[1];
|
|
OutPath[2] = fullFileSpec[2];
|
|
OutPath[3] = 0;
|
|
|
|
currentIn = fullFileSpec + 3;
|
|
currentOut = OutPath + 3;
|
|
|
|
for (;;) {
|
|
|
|
end = _mbschr (currentIn, '\\');
|
|
|
|
if (end == (currentIn + 1)) {
|
|
currentIn++;
|
|
continue;
|
|
}
|
|
|
|
if (end) {
|
|
*end = 0;
|
|
}
|
|
|
|
findHandle = FindFirstFileA (fullFileSpec, &findData);
|
|
|
|
if (findHandle != INVALID_HANDLE_VALUE) {
|
|
FindClose (findHandle);
|
|
|
|
//
|
|
// Copy long file name obtained from FindFirstFile
|
|
//
|
|
|
|
outEnd = currentOut + TcharCountA (findData.cFileName);
|
|
if (outEnd > maxOutPath) {
|
|
|
|
#ifdef DEBUG
|
|
*currentOut = 0;
|
|
DEBUGMSGA ((
|
|
DBG_WARNING,
|
|
"Path %s too long to append to %s",
|
|
findData.cFileName,
|
|
OutPath
|
|
));
|
|
#endif
|
|
|
|
__leave;
|
|
}
|
|
|
|
StringCopyA (currentOut, findData.cFileName);
|
|
currentOut = outEnd;
|
|
|
|
} else {
|
|
//
|
|
// Copy the rest of the path since it doesn't exist
|
|
//
|
|
|
|
if (end) {
|
|
*end = '\\';
|
|
}
|
|
|
|
outEnd = currentOut + TcharCountA (currentIn);
|
|
|
|
if (outEnd > maxOutPath) {
|
|
|
|
#ifdef DEBUG
|
|
DEBUGMSGA ((
|
|
DBG_WARNING,
|
|
"Path %s too long to append to %s",
|
|
currentIn,
|
|
OutPath
|
|
));
|
|
#endif
|
|
|
|
__leave;
|
|
}
|
|
|
|
StringCopyA (currentOut, currentIn);
|
|
break; // done with path
|
|
}
|
|
|
|
if (!end) {
|
|
MYASSERT (*currentOut == 0);
|
|
break; // done with path
|
|
}
|
|
|
|
*currentOut++ = '\\';
|
|
*currentOut = 0;
|
|
*end = '\\';
|
|
|
|
currentIn = end + 1;
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
SetErrorMode (oldMode);
|
|
}
|
|
|
|
if (forceCopy) {
|
|
StringCopyA (OutPath, FullFileSpecIn);
|
|
return TRUE;
|
|
}
|
|
|
|
if (!result) {
|
|
StringCopyTcharCountA (OutPath, FullFileSpecIn, MAX_PATH);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
CopyFileSpecToLongW (
|
|
IN PCWSTR FullFileSpecIn,
|
|
OUT PWSTR OutPath
|
|
)
|
|
{
|
|
WCHAR fullFileSpec[MAX_WCHAR_PATH];
|
|
WIN32_FIND_DATAW findData;
|
|
HANDLE findHandle;
|
|
PWSTR end;
|
|
PWSTR currentIn;
|
|
PWSTR currentOut;
|
|
PWSTR outEnd;
|
|
PWSTR maxOutPath = OutPath + MAX_PATH - 1;
|
|
BOOL result = FALSE;
|
|
UINT oldMode;
|
|
BOOL forceCopy = FALSE;
|
|
|
|
MYASSERT (ISNT());
|
|
|
|
oldMode = SetErrorMode (SEM_FAILCRITICALERRORS);
|
|
|
|
__try {
|
|
//
|
|
// Limit length
|
|
//
|
|
if (!IsPathLengthOkW (FullFileSpecIn)) {
|
|
DEBUGMSGW ((DBG_ERROR, "Inbound file spec is too long: %s", FullFileSpecIn));
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If path is on removable media, don't touch the disk
|
|
//
|
|
|
|
if (!IsPathOnFixedDriveW (FullFileSpecIn)) {
|
|
forceCopy = TRUE;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Make a copy of the file spec so we can truncate it at the wacks
|
|
//
|
|
|
|
StringCopyW (fullFileSpec, FullFileSpecIn);
|
|
|
|
//
|
|
// Begin building the path
|
|
//
|
|
|
|
OutPath[0] = fullFileSpec[0];
|
|
OutPath[1] = fullFileSpec[1];
|
|
OutPath[2] = fullFileSpec[2];
|
|
OutPath[3] = 0;
|
|
|
|
currentIn = fullFileSpec + 3;
|
|
currentOut = OutPath + 3;
|
|
|
|
for (;;) {
|
|
|
|
end = wcschr (currentIn, L'\\');
|
|
|
|
if (end == (currentIn + 1)) {
|
|
currentIn++;
|
|
continue;
|
|
}
|
|
|
|
if (end) {
|
|
*end = 0;
|
|
}
|
|
|
|
findHandle = FindFirstFileW (fullFileSpec, &findData);
|
|
|
|
if (findHandle != INVALID_HANDLE_VALUE) {
|
|
FindClose (findHandle);
|
|
|
|
//
|
|
// Copy long file name obtained from FindFirstFile
|
|
//
|
|
|
|
outEnd = currentOut + TcharCountW (findData.cFileName);
|
|
if (outEnd > maxOutPath) {
|
|
|
|
DEBUGMSGW ((
|
|
DBG_WARNING,
|
|
"Path %s too long to append to %s",
|
|
findData.cFileName,
|
|
OutPath
|
|
));
|
|
|
|
__leave;
|
|
}
|
|
|
|
StringCopyW (currentOut, findData.cFileName);
|
|
currentOut = outEnd;
|
|
|
|
} else {
|
|
//
|
|
// Copy the rest of the path since it doesn't exist
|
|
//
|
|
|
|
if (end) {
|
|
*end = L'\\';
|
|
}
|
|
|
|
outEnd = currentOut + TcharCountW (currentIn);
|
|
|
|
if (outEnd > maxOutPath) {
|
|
|
|
DEBUGMSGW ((
|
|
DBG_WARNING,
|
|
"Path %s too long to append to %s",
|
|
currentIn,
|
|
OutPath
|
|
));
|
|
|
|
__leave;
|
|
}
|
|
|
|
StringCopyW (currentOut, currentIn);
|
|
break; // done with path
|
|
}
|
|
|
|
if (!end) {
|
|
MYASSERT (*currentOut == 0);
|
|
break; // done with path
|
|
}
|
|
|
|
*currentOut++ = L'\\';
|
|
*currentOut = 0;
|
|
*end = L'\\';
|
|
|
|
currentIn = end + 1;
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
__finally {
|
|
SetErrorMode (oldMode);
|
|
}
|
|
|
|
if (forceCopy) {
|
|
StringCopyW (OutPath, FullFileSpecIn);
|
|
return TRUE;
|
|
}
|
|
|
|
if (!result) {
|
|
StringCopyTcharCountW (OutPath, FullFileSpecIn, MAX_PATH);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DoesFileExistExA(
|
|
IN PCSTR FileName,
|
|
OUT PWIN32_FIND_DATAA 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_DATAA ourFindData;
|
|
HANDLE FindHandle;
|
|
UINT OldMode;
|
|
DWORD Error;
|
|
|
|
if (!FindData) {
|
|
return GetFileAttributesA (FileName) != 0xffffffff;
|
|
}
|
|
|
|
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
FindHandle = FindFirstFileA(FileName, &ourFindData);
|
|
|
|
if (FindHandle == INVALID_HANDLE_VALUE) {
|
|
Error = GetLastError();
|
|
} else {
|
|
FindClose(FindHandle);
|
|
*FindData = ourFindData;
|
|
Error = NO_ERROR;
|
|
}
|
|
|
|
SetErrorMode(OldMode);
|
|
|
|
SetLastError(Error);
|
|
return (Error == NO_ERROR);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DoesFileExistExW (
|
|
IN PCWSTR FileName,
|
|
OUT PWIN32_FIND_DATAW 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_DATAW ourFindData;
|
|
HANDLE FindHandle;
|
|
UINT OldMode;
|
|
DWORD Error = NO_ERROR;
|
|
UINT length;
|
|
BOOL result = FALSE;
|
|
PCWSTR longFileName = NULL;
|
|
|
|
__try {
|
|
if (FileName[0] != TEXT('\\')) {
|
|
length = TcharCountW (FileName);
|
|
if (length >= MAX_PATH) {
|
|
longFileName = JoinPathsW (L"\\\\?", FileName);
|
|
MYASSERT (longFileName);
|
|
FileName = longFileName;
|
|
}
|
|
}
|
|
|
|
if (!FindData) {
|
|
result = (GetLongPathAttributesW (FileName) != 0xffffffff);
|
|
__leave;
|
|
}
|
|
|
|
OldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
FindHandle = FindFirstFileW(FileName,&ourFindData);
|
|
|
|
if (FindHandle == INVALID_HANDLE_VALUE) {
|
|
Error = GetLastError();
|
|
} else {
|
|
FindClose(FindHandle);
|
|
*FindData = ourFindData;
|
|
}
|
|
|
|
SetErrorMode(OldMode);
|
|
SetLastError(Error);
|
|
|
|
result = (Error == NO_ERROR);
|
|
}
|
|
__finally {
|
|
if (longFileName) {
|
|
FreePathStringW (longFileName);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
DWORD
|
|
MakeSurePathExistsA(
|
|
IN PCSTR FullFileSpec,
|
|
IN BOOL PathOnly
|
|
)
|
|
{
|
|
CHAR Buffer[MAX_MBCHAR_PATH];
|
|
PCHAR p,q;
|
|
BOOL Done;
|
|
BOOL isUnc;
|
|
DWORD d;
|
|
WIN32_FIND_DATAA FindData;
|
|
|
|
isUnc = (FullFileSpec[0] == '\\') && (FullFileSpec[1] == '\\');
|
|
|
|
//
|
|
// Locate and strip off the final component
|
|
//
|
|
|
|
_mbssafecpy(Buffer,FullFileSpec,sizeof(Buffer));
|
|
|
|
p = _mbsrchr(Buffer, '\\');
|
|
|
|
if (p) {
|
|
if (!PathOnly) {
|
|
*p = 0;
|
|
}
|
|
//
|
|
// If it's a drive root, nothing to do.
|
|
//
|
|
if(Buffer[0] && (Buffer[1] == ':') && !Buffer[2]) {
|
|
return(NO_ERROR);
|
|
}
|
|
} else {
|
|
//
|
|
// Just a relative filename, nothing to do.
|
|
//
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//
|
|
// If it already exists do nothing.
|
|
//
|
|
if (DoesFileExistExA (Buffer,&FindData)) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
p = Buffer;
|
|
|
|
//
|
|
// Compensate for drive spec.
|
|
//
|
|
if(p[0] && (p[1] == ':')) {
|
|
p += 2;
|
|
}
|
|
|
|
//
|
|
// Compensate for UNC paths.
|
|
//
|
|
if (isUnc) {
|
|
|
|
//
|
|
// Skip the initial wack wack before machine name.
|
|
//
|
|
p += 2;
|
|
|
|
|
|
//
|
|
// Skip to the share.
|
|
//
|
|
p = _mbschr(p, '\\');
|
|
if (p==NULL) {
|
|
return ERROR_BAD_PATHNAME;
|
|
}
|
|
|
|
//
|
|
// Skip past the share.
|
|
//
|
|
p = _mbschr(p, '\\');
|
|
if (p==NULL) {
|
|
return ERROR_BAD_PATHNAME;
|
|
}
|
|
}
|
|
|
|
Done = FALSE;
|
|
do {
|
|
//
|
|
// Skip path sep char.
|
|
//
|
|
while(*p == '\\') {
|
|
p++;
|
|
}
|
|
|
|
//
|
|
// Locate next path sep char or terminating nul.
|
|
//
|
|
if(q = _mbschr(p, '\\')) {
|
|
*q = 0;
|
|
} else {
|
|
q = GetEndOfStringA(p);
|
|
Done = TRUE;
|
|
}
|
|
|
|
//
|
|
// Create this portion of the path.
|
|
//
|
|
if(!CreateDirectoryA(Buffer,NULL)) {
|
|
d = GetLastError();
|
|
if(d != ERROR_ALREADY_EXISTS) {
|
|
return(d);
|
|
}
|
|
}
|
|
|
|
if(!Done) {
|
|
*q = '\\';
|
|
p = q+1;
|
|
}
|
|
|
|
} while(!Done);
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
VOID
|
|
DestPathCopyW (
|
|
OUT PWSTR DestPath,
|
|
IN PCWSTR SrcPath
|
|
)
|
|
{
|
|
PCWSTR p;
|
|
PWSTR q;
|
|
PCWSTR end;
|
|
PCWSTR maxStart;
|
|
UINT len;
|
|
UINT count;
|
|
|
|
len = TcharCountW (SrcPath);
|
|
|
|
if (len < MAX_PATH) {
|
|
StringCopyW (DestPath, SrcPath);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Path is too long -- try to truncate it
|
|
//
|
|
|
|
wsprintfW (DestPath, L"%c:\\Long", SrcPath[0]);
|
|
CreateDirectoryW (DestPath, NULL);
|
|
|
|
p = SrcPath;
|
|
end = SrcPath + len;
|
|
maxStart = end - 220;
|
|
|
|
while (p < end) {
|
|
if (*p == '\\') {
|
|
if (p >= maxStart) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
p++;
|
|
}
|
|
|
|
if (p == end) {
|
|
p = maxStart;
|
|
}
|
|
|
|
MYASSERT (TcharCountW (p) <= 220);
|
|
|
|
StringCopyW (AppendWackW (DestPath), p);
|
|
q = (PWSTR) GetEndOfStringW (DestPath);
|
|
|
|
//
|
|
// Verify there is no collision
|
|
//
|
|
|
|
for (count = 1 ; count < 1000000 ; count++) {
|
|
if (GetFileAttributesW (DestPath) == INVALID_ATTRIBUTES) {
|
|
break;
|
|
}
|
|
|
|
wsprintfW (q, L" (%u)", count);
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
MakeSurePathExistsW(
|
|
IN LPCWSTR FullFileSpec,
|
|
IN BOOL PathOnly
|
|
)
|
|
{
|
|
PWSTR buffer;
|
|
WCHAR *p, *q;
|
|
BOOL Done;
|
|
DWORD d;
|
|
WIN32_FIND_DATAW FindData;
|
|
DWORD result = NO_ERROR;
|
|
|
|
if (FullFileSpec[0] != L'\\') {
|
|
if (TcharCountW (FullFileSpec) >= MAX_PATH) {
|
|
if (PathOnly || ((wcsrchr (FullFileSpec, L'\\') - FullFileSpec) >= MAX_PATH)) {
|
|
LOGW ((LOG_ERROR, "Can't create path %s because it is too long", FullFileSpec));
|
|
return ERROR_FILENAME_EXCED_RANGE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Locate and strip off the final component
|
|
//
|
|
buffer = DuplicatePathStringW (FullFileSpec, 0);
|
|
__try {
|
|
|
|
p = wcsrchr(buffer, L'\\');
|
|
|
|
if (p) {
|
|
if (!PathOnly) {
|
|
*p = 0;
|
|
}
|
|
} else {
|
|
//
|
|
// Just a relative filename, nothing to do.
|
|
//
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// If it already exists do nothing.
|
|
//
|
|
if (DoesFileExistExW (buffer, &FindData)) {
|
|
result = ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? NO_ERROR : ERROR_DIRECTORY);
|
|
__leave;
|
|
}
|
|
|
|
p = buffer;
|
|
|
|
//
|
|
// Compensate for drive spec.
|
|
//
|
|
if (p[0] == L'\\' && p[1] == L'\\' && p[2] == L'?' && p[3] == L'\\') {
|
|
p += 4;
|
|
}
|
|
|
|
if (p[0] && (p[1] == L':')) {
|
|
p += 2;
|
|
}
|
|
|
|
if ((p[0] == 0) || (p[0] == L'\\' && p[1] == 0)) {
|
|
//
|
|
// Root -- leave
|
|
//
|
|
|
|
__leave;
|
|
}
|
|
|
|
Done = FALSE;
|
|
do {
|
|
//
|
|
// Skip path sep char.
|
|
//
|
|
while(*p == L'\\') {
|
|
p++;
|
|
}
|
|
|
|
//
|
|
// Locate next path sep char or terminating nul.
|
|
//
|
|
q = wcschr(p, L'\\');
|
|
|
|
if(q) {
|
|
*q = 0;
|
|
} else {
|
|
q = GetEndOfStringW (p);
|
|
Done = TRUE;
|
|
}
|
|
|
|
//
|
|
// Create this portion of the path.
|
|
//
|
|
if(!CreateDirectoryW(buffer,NULL)) {
|
|
d = GetLastError();
|
|
if(d != ERROR_ALREADY_EXISTS) {
|
|
result = d;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
if(!Done) {
|
|
*q = L'\\';
|
|
p = q+1;
|
|
}
|
|
|
|
} while(!Done);
|
|
}
|
|
__finally {
|
|
FreePathStringW (buffer);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
//
|
|
// ...PACKFILE HANDLING...
|
|
//
|
|
|
|
#define PACKFILE_BUFFERSIZE 2048
|
|
|
|
|
|
|
|
BOOL
|
|
pFillEnumStructureA (
|
|
IN PPACKFILEENUMA Enum
|
|
)
|
|
{
|
|
|
|
BOOL rSuccess = TRUE;
|
|
DWORD numBytesRead;
|
|
HANDLE savedHandle;
|
|
|
|
MYASSERT(Enum && Enum -> Handle != INVALID_HANDLE_VALUE && Enum -> Handle != NULL);
|
|
|
|
//
|
|
// Save this away.. The read below blows it away.
|
|
//
|
|
savedHandle = Enum -> Handle;
|
|
|
|
//
|
|
// Read the header information from the current position in the file.
|
|
//
|
|
rSuccess = ReadFile(
|
|
Enum -> Handle,
|
|
Enum,
|
|
sizeof(PACKFILEENUMA),
|
|
&numBytesRead,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Restore the handle member.
|
|
//
|
|
Enum -> Handle = savedHandle;
|
|
|
|
return rSuccess && numBytesRead == sizeof(PACKFILEENUMA);
|
|
}
|
|
|
|
|
|
BOOL
|
|
PackedFile_ExtractFileUsingEnumA (
|
|
IN PPACKFILEENUMA Enum,
|
|
IN LPCSTR FileName OPTIONAL
|
|
)
|
|
{
|
|
BOOL rSuccess = TRUE;
|
|
HANDLE newFileHandle;
|
|
LONGLONG numberOfBytesToExtract;
|
|
LONGLONG numberOfBytesExtracted;
|
|
DWORD numberOfBytesToRead;
|
|
DWORD numberOfBytesRead;
|
|
BYTE buffer[PACKFILE_BUFFERSIZE];
|
|
DWORD fileHigh;
|
|
DWORD fileLow;
|
|
|
|
MYASSERT(Enum && Enum -> Handle != INVALID_HANDLE_VALUE && Enum -> Handle != NULL);
|
|
|
|
//
|
|
// First, attempt to create the new file. If FileName was not specified, we'll use the
|
|
// Identifier name in the enum struct.
|
|
//
|
|
if (!FileName) {
|
|
FileName = Enum -> Identifier;
|
|
}
|
|
|
|
newFileHandle = CreateFileA (
|
|
FileName,
|
|
GENERIC_WRITE,
|
|
0, // No sharing.
|
|
NULL, // No inheritance.
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL // No template file.
|
|
);
|
|
|
|
if (newFileHandle == INVALID_HANDLE_VALUE) {
|
|
|
|
rSuccess = FALSE;
|
|
DEBUGMSG((DBG_ERROR,"PackedFile_ExtractFromEnum: Could not open file %s. FileName"));
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// File was successfully opened. Extract the data starting from the current file position
|
|
//
|
|
|
|
//
|
|
// Create a LONGLONG value out of the size of the file.
|
|
//
|
|
numberOfBytesToExtract = ((LONGLONG)Enum -> SizeHigh) << 32 | ((LONGLONG) Enum -> SizeLow);
|
|
numberOfBytesExtracted = 0;
|
|
|
|
do {
|
|
|
|
//
|
|
// Figure out how much to read from the file.
|
|
//
|
|
if (numberOfBytesToExtract - numberOfBytesExtracted > PACKFILE_BUFFERSIZE) {
|
|
numberOfBytesToRead = PACKFILE_BUFFERSIZE;
|
|
}
|
|
else {
|
|
numberOfBytesToRead = (DWORD) (numberOfBytesToExtract - numberOfBytesExtracted);
|
|
}
|
|
|
|
if (!ReadFile(
|
|
Enum -> Handle,
|
|
buffer,
|
|
numberOfBytesToRead,
|
|
&numberOfBytesRead,
|
|
NULL
|
|
) ||
|
|
!WriteFile(
|
|
newFileHandle,
|
|
buffer,
|
|
numberOfBytesRead,
|
|
&numberOfBytesRead,
|
|
NULL
|
|
)) {
|
|
|
|
numberOfBytesExtracted += (LONGLONG) numberOfBytesRead;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Update the count of bytes extracted.
|
|
//
|
|
numberOfBytesExtracted += (LONGLONG) numberOfBytesRead;
|
|
|
|
} while (numberOfBytesExtracted < numberOfBytesToExtract);
|
|
|
|
//
|
|
// Close the handle to the new file we created.
|
|
//
|
|
CloseHandle(newFileHandle);
|
|
|
|
//
|
|
// Reset the file pointer.
|
|
//
|
|
fileHigh = (DWORD) (numberOfBytesExtracted >> 32);
|
|
fileLow = (DWORD) (numberOfBytesExtracted & 0xffffffff);
|
|
SetFilePointer(Enum -> Handle,fileLow,&fileHigh,FILE_CURRENT);
|
|
|
|
if (numberOfBytesExtracted != numberOfBytesToExtract) {
|
|
rSuccess = FALSE;
|
|
DEBUGMSG((DBG_ERROR,"PackedFile_ExtractFromEnum: Could not extract file."));
|
|
}
|
|
}
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
|
|
VOID
|
|
PackedFile_AbortEnum (
|
|
IN OUT PPACKFILEENUMA Enum
|
|
)
|
|
{
|
|
MYASSERT(Enum -> Handle != INVALID_HANDLE_VALUE && Enum -> Handle != NULL);
|
|
//
|
|
// Nothing to do except close the handle.
|
|
//
|
|
CloseHandle(Enum -> Handle);
|
|
}
|
|
|
|
BOOL
|
|
PackedFile_EnumNextA (
|
|
IN PPACKFILEENUMA Enum
|
|
)
|
|
{
|
|
BOOL rSuccess = TRUE;
|
|
|
|
MYASSERT(Enum && Enum -> Handle != INVALID_HANDLE_VALUE && Enum -> Handle != NULL);
|
|
|
|
//
|
|
// Move the file pointer ahead by the size contained in the current enum structure.
|
|
//
|
|
if (SetFilePointer(Enum -> Handle,Enum -> SizeLow,&Enum -> SizeHigh,FILE_CURRENT) == 0xffffffff &&
|
|
GetLastError() != NO_ERROR) {
|
|
|
|
rSuccess = FALSE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Fill in the enum structure with the fresh data.
|
|
//
|
|
rSuccess = pFillEnumStructureA(Enum);
|
|
}
|
|
|
|
if (!rSuccess) {
|
|
PackedFile_AbortEnum(Enum);
|
|
}
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
PackedFile_EnumFirstA (
|
|
IN LPCSTR PackFile,
|
|
OUT PPACKFILEENUMA Enum
|
|
)
|
|
{
|
|
BOOL rSuccess = FALSE;
|
|
|
|
MYASSERT(Enum && PackFile);
|
|
|
|
//
|
|
// Clean out the Enum structure.
|
|
//
|
|
ZeroMemory(Enum,sizeof(PACKFILEENUMA));
|
|
|
|
//
|
|
// Open a handle to the PackFile.
|
|
//
|
|
Enum -> Handle = CreateFileA (
|
|
PackFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL, // No inheritance.
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL // No template file.
|
|
);
|
|
|
|
|
|
//
|
|
// If that succeeds, trust EnumNext to handle the dirty work.
|
|
//
|
|
if (Enum -> Handle != INVALID_HANDLE_VALUE) {
|
|
rSuccess = pFillEnumStructureA(Enum);
|
|
}
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
BOOL
|
|
PackedFile_ExtractFileA (
|
|
IN LPCSTR PackFile,
|
|
IN LPCSTR FileIdentifier,
|
|
IN LPCSTR FileName OPTIONAL
|
|
)
|
|
{
|
|
|
|
PACKFILEENUMA e;
|
|
BOOL rSuccess = FALSE;
|
|
|
|
MYASSERT(PackFile && FileIdentifier);
|
|
|
|
if (PackedFile_EnumFirstA(PackFile,&e)) {
|
|
|
|
do {
|
|
if (StringIMatchA (e.Identifier,FileIdentifier)) {
|
|
//
|
|
// We found the one they were looking for..
|
|
//
|
|
rSuccess = PackedFile_ExtractFileUsingEnumA(&e,FileName);
|
|
PackedFile_AbortEnum(&e);
|
|
break;
|
|
}
|
|
|
|
} while (PackedFile_EnumNextA(&e));
|
|
}
|
|
|
|
return rSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
PackedFile_AddFileA (
|
|
IN LPCSTR PackFile,
|
|
IN LPCSTR NewFile,
|
|
IN LPCSTR Identifier OPTIONAL
|
|
)
|
|
{
|
|
HANDLE packFileHandle = INVALID_HANDLE_VALUE;
|
|
HANDLE newFileHandle = INVALID_HANDLE_VALUE;
|
|
BOOL rSuccess = TRUE;
|
|
PACKFILEENUMA fileHeader;
|
|
DWORD numBytes;
|
|
BYTE buffer[PACKFILE_BUFFERSIZE];
|
|
|
|
//
|
|
// Now, attempt to open the new file.
|
|
//
|
|
newFileHandle = CreateFileA (
|
|
NewFile,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (newFileHandle == INVALID_HANDLE_VALUE) {
|
|
|
|
DEBUGMSG((DBG_ERROR,"PackedFile_AddFile could not open %s and therefore cannot add it.",NewFile));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Open or create the packfile. If it does not already exist, then it will be created.
|
|
//
|
|
packFileHandle = CreateFileA (
|
|
PackFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, // No sharing.
|
|
NULL, // No inheritance.
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL // No template file.
|
|
);
|
|
|
|
if (packFileHandle == INVALID_HANDLE_VALUE) {
|
|
rSuccess = FALSE;
|
|
DEBUGMSG((DBG_ERROR,"PackedFile_AddFile Could not open or create the packed file %s.",PackFile));
|
|
|
|
} else {
|
|
|
|
//
|
|
// Both files have been successfully opened. Add the new file to the packfile.
|
|
//
|
|
|
|
//
|
|
// First, we need to set the file pointer to the end of the packed file.
|
|
//
|
|
SetFilePointer(packFileHandle,0,NULL,FILE_END);
|
|
|
|
//
|
|
// Ok, Fill in a header structure for this file. It is a simple header. All it contains
|
|
// is the Identifier (or the file path if Identifier is NULL) and the size of the file.
|
|
// There is another parameter (Handle) in the structure, but this is only used
|
|
// for enumeration purposes and is always NULL when written into the file.
|
|
//
|
|
if (!Identifier) {
|
|
Identifier = NewFile;
|
|
}
|
|
|
|
fileHeader.Handle = NULL; // This member is only used in enumeration.
|
|
|
|
_mbssafecpy(fileHeader.Identifier,Identifier,MAX_MBCHAR_PATH);
|
|
|
|
fileHeader.SizeLow = GetFileSize(newFileHandle,&fileHeader.SizeHigh);
|
|
|
|
if (fileHeader.SizeLow == 0xffffffff && GetLastError() != NO_ERROR) {
|
|
rSuccess = FALSE;
|
|
DEBUGMSG((DBG_ERROR,"PackedFile_AddFile Could not get the file size for %s.",NewFile));
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Now, write the header into the packed file.
|
|
//
|
|
if (!WriteFile(
|
|
packFileHandle,
|
|
&fileHeader,
|
|
sizeof(PACKFILEENUMA),
|
|
&numBytes,
|
|
NULL // Not overlapped.
|
|
)) {
|
|
|
|
DEBUGMSG((DBG_ERROR,"PackedFile_AddFile Could not write a file header to the pack file %s.",PackFile));
|
|
rSuccess = FALSE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// At this point, all that is left to do is to write the bits from the packed file into
|
|
// the packed file.
|
|
//
|
|
do {
|
|
|
|
if (!ReadFile(
|
|
newFileHandle,
|
|
buffer,
|
|
PACKFILE_BUFFERSIZE,
|
|
&numBytes,
|
|
NULL
|
|
) ||
|
|
!WriteFile(
|
|
packFileHandle,
|
|
buffer,
|
|
numBytes,
|
|
&numBytes,
|
|
NULL
|
|
)) {
|
|
|
|
rSuccess = FALSE;
|
|
DEBUGMSG((DBG_ERROR,"PackedFile_AddFile encountered an error writing the file %s to the packed file %s.",NewFile,PackFile));
|
|
break;
|
|
}
|
|
} while (numBytes == PACKFILE_BUFFERSIZE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Close the handles to both files. We are done with them.
|
|
//
|
|
CloseHandle(packFileHandle);
|
|
}
|
|
|
|
CloseHandle(newFileHandle);
|
|
|
|
return rSuccess;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// The W versions of these files are unimplemented.
|
|
//
|
|
BOOL
|
|
PackedFile_AddFileW (
|
|
IN LPCSTR PackFile,
|
|
IN LPCSTR NewFile,
|
|
IN LPCSTR Identifier OPTIONAL
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
PackedFile_ExtractFileW (
|
|
IN LPCSTR PackFile,
|
|
IN LPCSTR FileIdentifier,
|
|
IN LPCSTR FileName OPTIONAL
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
PackedFile_EnumFirstW (
|
|
IN LPCSTR PackFile,
|
|
OUT PPACKFILEENUMA Enum
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
PackedFile_EnumNextW (
|
|
IN PPACKFILEENUMA Enum
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
PackedFile_ExtractFileUsingEnumW (
|
|
IN PPACKFILEENUMA Enum,
|
|
IN LPCSTR FileName OPTIONAL
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
WriteFileStringA (
|
|
IN HANDLE File,
|
|
IN PCSTR String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes a DBCS string to the specified file.
|
|
|
|
Arguments:
|
|
|
|
File - Specifies the file handle that was opened with write access.
|
|
|
|
String - Specifies the nul-terminated string to write to the file.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if an error occurred. Call GetLastError
|
|
for error condition.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD DontCare;
|
|
|
|
return WriteFile (File, String, ByteCountA (String), &DontCare, NULL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
WriteFileStringW (
|
|
IN HANDLE File,
|
|
IN PCWSTR String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a UNICODE string to DBCS, then Writes it to the specified file.
|
|
|
|
Arguments:
|
|
|
|
File - Specifies the file handle that was opened with write access.
|
|
|
|
String - Specifies the UNICODE nul-terminated string to convert and
|
|
write to the file.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if an error occurred. Call GetLastError for
|
|
error condition.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD DontCare;
|
|
PCSTR AnsiVersion;
|
|
BOOL b;
|
|
|
|
AnsiVersion = ConvertWtoA (String);
|
|
if (!AnsiVersion) {
|
|
return FALSE;
|
|
}
|
|
|
|
b = WriteFile (File, AnsiVersion, ByteCountA (AnsiVersion), &DontCare, NULL);
|
|
|
|
FreeConvertedStr (AnsiVersion);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
pFindShortNameA is a helper function for OurGetLongPathName. It
|
|
obtains the short file name, if it exists, using FindFirstFile.
|
|
|
|
Arguments:
|
|
|
|
WhatToFind - Specifies the short or long name of a file to locate
|
|
|
|
Buffer - Receives the matching file name
|
|
|
|
Return Value:
|
|
|
|
TRUE if the file was found and Buffer was updated, or FALSE if the
|
|
file was not found and Buffer was not updated.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
pFindShortNameA (
|
|
IN PCSTR WhatToFind,
|
|
OUT PSTR Buffer,
|
|
OUT INT *BufferSizeInBytes
|
|
)
|
|
{
|
|
WIN32_FIND_DATAA fd;
|
|
HANDLE hFind;
|
|
|
|
hFind = FindFirstFileA (WhatToFind, &fd);
|
|
if (hFind == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
FindClose (hFind);
|
|
|
|
*BufferSizeInBytes -= ByteCountA (fd.cFileName);
|
|
if (*BufferSizeInBytes >= sizeof (CHAR)) {
|
|
StringCopyA (Buffer, fd.cFileName);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pFindShortNameW (
|
|
IN PCWSTR WhatToFind,
|
|
OUT PWSTR Buffer,
|
|
OUT INT *BufferSizeInBytes
|
|
)
|
|
{
|
|
WIN32_FIND_DATAA fdA;
|
|
WIN32_FIND_DATAW fdW;
|
|
PCWSTR UnicodeVersion;
|
|
PCSTR AnsiVersion;
|
|
HANDLE hFind;
|
|
|
|
if (ISNT ()) {
|
|
hFind = FindFirstFileW (WhatToFind, &fdW);
|
|
if (hFind == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
FindClose (hFind);
|
|
} else {
|
|
AnsiVersion = ConvertWtoA (WhatToFind);
|
|
hFind = FindFirstFileA (AnsiVersion, &fdA);
|
|
FreeConvertedStr (AnsiVersion);
|
|
if (hFind == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
FindClose (hFind);
|
|
fdW.dwFileAttributes = fdA.dwFileAttributes;
|
|
fdW.ftCreationTime = fdA.ftCreationTime;
|
|
fdW.ftLastAccessTime = fdA.ftLastAccessTime;
|
|
fdW.ftLastWriteTime = fdA.ftLastWriteTime;
|
|
fdW.nFileSizeHigh = fdA.nFileSizeHigh;
|
|
fdW.nFileSizeLow = fdA.nFileSizeLow;
|
|
fdW.dwReserved0 = fdA.dwReserved0;
|
|
fdW.dwReserved1 = fdA.dwReserved1;
|
|
UnicodeVersion = ConvertAtoW (fdA.cFileName);
|
|
StringCopyTcharCountW (fdW.cFileName, UnicodeVersion, MAX_PATH);
|
|
FreeConvertedStr (UnicodeVersion);
|
|
UnicodeVersion = ConvertAtoW (fdA.cAlternateFileName);
|
|
StringCopyTcharCountW (fdW.cAlternateFileName, UnicodeVersion, 14);
|
|
FreeConvertedStr (UnicodeVersion);
|
|
}
|
|
|
|
*BufferSizeInBytes -= ByteCountW (fdW.cFileName);
|
|
if (*BufferSizeInBytes >= sizeof (WCHAR)) {
|
|
StringCopyW (Buffer, fdW.cFileName);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
OurGetLongPathName locates the long name for a file. It first locates the
|
|
full path if a path is not explicitly provided, and then uses FindFirstFile
|
|
to get the long file name. NOTE: This is exactly what the Win32 function
|
|
GetLongPathName does, but unfortunately the Win32 API is not available on Win95.
|
|
|
|
This routine resolves each piece of a short path name using recursion.
|
|
|
|
Arguments:
|
|
|
|
ShortPath - Specifies the file name or full file path to locate
|
|
|
|
Buffer - Receives the full file path. This buffer must be big enough to
|
|
handle the maximum file name size.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the file is found and Buffer contains the long name, or FALSE
|
|
if the file is not found and Buffer is not modified.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
OurGetLongPathNameA (
|
|
IN PCSTR ShortPath,
|
|
OUT PSTR Buffer,
|
|
IN INT BufferSizeInBytes
|
|
)
|
|
{
|
|
CHAR FullPath[MAX_MBCHAR_PATH];
|
|
PCSTR SanitizedPath;
|
|
PSTR FilePart;
|
|
PSTR BufferEnd;
|
|
PSTR p, p2;
|
|
MBCHAR c;
|
|
BOOL result = TRUE;
|
|
|
|
MYASSERT (BufferSizeInBytes >= MAX_MBCHAR_PATH);
|
|
|
|
if (ShortPath[0] == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
__try {
|
|
|
|
SanitizedPath = SanitizePathA (ShortPath);
|
|
if (!SanitizedPath) {
|
|
SanitizedPath = DuplicatePathStringA (ShortPath, 0);
|
|
}
|
|
|
|
if (!_mbschr (SanitizedPath, '\\')) {
|
|
if (!SearchPathA (NULL, SanitizedPath, NULL, sizeof (FullPath), FullPath, &FilePart)) {
|
|
|
|
result = FALSE;
|
|
__leave;
|
|
}
|
|
} else {
|
|
GetFullPathNameA (SanitizedPath, sizeof (FullPath), FullPath, &FilePart);
|
|
}
|
|
|
|
//
|
|
// Convert short paths to long paths
|
|
//
|
|
|
|
p = FullPath;
|
|
|
|
if (!IsPathOnFixedDriveA (FullPath)) {
|
|
_mbssafecpy (Buffer, FullPath, BufferSizeInBytes);
|
|
__leave;
|
|
}
|
|
|
|
p += 3;
|
|
|
|
// Copy drive letter to buffer
|
|
StringCopyABA (Buffer, FullPath, p);
|
|
BufferEnd = GetEndOfStringA (Buffer);
|
|
BufferSizeInBytes -= p - FullPath;
|
|
|
|
// Convert each portion of the path
|
|
do {
|
|
// Locate end of this file or dir
|
|
p2 = _mbschr (p, '\\');
|
|
if (!p2) {
|
|
p = GetEndOfStringA (p);
|
|
} else {
|
|
p = p2;
|
|
}
|
|
|
|
// Look up file
|
|
c = *p;
|
|
*p = 0;
|
|
if (!pFindShortNameA (FullPath, BufferEnd, &BufferSizeInBytes)) {
|
|
DEBUGMSG ((DBG_VERBOSE, "OurGetLongPathNameA: %s does not exist", FullPath));
|
|
result = FALSE;
|
|
__leave;
|
|
}
|
|
*p = (CHAR)c;
|
|
|
|
// Move on to next part of path
|
|
if (*p) {
|
|
p = _mbsinc (p);
|
|
if (BufferSizeInBytes >= sizeof (CHAR) * 2) {
|
|
BufferEnd = _mbsappend (BufferEnd, "\\");
|
|
BufferSizeInBytes -= sizeof (CHAR);
|
|
}
|
|
}
|
|
} while (*p);
|
|
}
|
|
__finally {
|
|
FreePathStringA (SanitizedPath);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
DWORD
|
|
OurGetShortPathNameW (
|
|
IN PCWSTR LongPath,
|
|
OUT PWSTR ShortPath,
|
|
IN DWORD CharSize
|
|
)
|
|
{
|
|
PCSTR LongPathA;
|
|
PSTR ShortPathA;
|
|
PCWSTR ShortPathW;
|
|
DWORD result;
|
|
|
|
if (ISNT()) {
|
|
return GetShortPathNameW (LongPath, ShortPath, CharSize);
|
|
} else {
|
|
LongPathA = ConvertWtoA (LongPath);
|
|
|
|
if (!IsPathOnFixedDriveA (LongPathA)) {
|
|
StringCopyTcharCountW (ShortPath, LongPath, CharSize);
|
|
FreeConvertedStr (LongPathA);
|
|
return TcharCountW (ShortPath);
|
|
}
|
|
|
|
ShortPathA = AllocPathStringA (CharSize);
|
|
result = GetShortPathNameA (LongPathA, ShortPathA, CharSize);
|
|
if (result) {
|
|
ShortPathW = ConvertAtoW (ShortPathA);
|
|
StringCopyTcharCountW (ShortPath, ShortPathW, CharSize);
|
|
FreeConvertedStr (ShortPathW);
|
|
} else {
|
|
StringCopyTcharCountW (ShortPath, LongPath, CharSize);
|
|
}
|
|
FreePathStringA (ShortPathA);
|
|
FreeConvertedStr (LongPathA);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
OurGetFullPathNameW (
|
|
PCWSTR FileName,
|
|
DWORD CharSize,
|
|
PWSTR FullPath,
|
|
PWSTR *FilePtr
|
|
)
|
|
{
|
|
PCSTR FileNameA;
|
|
PSTR FullPathA;
|
|
PSTR FilePtrA;
|
|
PCWSTR FullPathW;
|
|
DWORD result;
|
|
DWORD err;
|
|
|
|
if (ISNT()) {
|
|
return GetFullPathNameW (FileName, CharSize, FullPath, FilePtr);
|
|
} else {
|
|
FileNameA = ConvertWtoA (FileName);
|
|
FullPathA = AllocPathStringA (CharSize);
|
|
result = GetFullPathNameA (FileNameA, CharSize, FullPathA, &FilePtrA);
|
|
FullPathW = ConvertAtoW (FullPathA);
|
|
StringCopyTcharCountW (FullPath, FullPathW, CharSize);
|
|
err = GetLastError ();
|
|
*FilePtr = (PWSTR)GetFileNameFromPathW (FullPath);
|
|
FreeConvertedStr (FullPathW);
|
|
FreePathStringA (FullPathA);
|
|
FreeConvertedStr (FileNameA);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
OurGetLongPathNameW (
|
|
IN PCWSTR ShortPath,
|
|
OUT PWSTR Buffer,
|
|
IN INT BufferSizeInChars
|
|
)
|
|
{
|
|
WCHAR FullPath[MAX_WCHAR_PATH];
|
|
PWSTR FilePart;
|
|
PWSTR BufferEnd;
|
|
PWSTR p, p2;
|
|
WCHAR c;
|
|
INT BufferSizeInBytes;
|
|
|
|
MYASSERT (BufferSizeInChars >= MAX_WCHAR_PATH);
|
|
|
|
if (ShortPath[0] == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
BufferSizeInBytes = BufferSizeInChars * sizeof (WCHAR);
|
|
|
|
if (!wcschr (ShortPath, L'\\')) {
|
|
if (!SearchPathW (NULL, ShortPath, NULL, MAX_WCHAR_PATH, FullPath, &FilePart)) {
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
if (OurGetFullPathNameW (ShortPath, MAX_WCHAR_PATH, FullPath, &FilePart) == 0) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Convert short paths to long paths
|
|
//
|
|
|
|
p = FullPath;
|
|
|
|
if (!IsPathOnFixedDriveW (FullPath)) {
|
|
StringCopyTcharCountW (Buffer, FullPath, BufferSizeInChars);
|
|
return TRUE;
|
|
}
|
|
|
|
p += 3;
|
|
|
|
// Copy drive letter to buffer
|
|
StringCopyABW (Buffer, FullPath, p);
|
|
BufferEnd = GetEndOfStringW (Buffer);
|
|
BufferSizeInBytes -= sizeof (WCHAR) * 3;
|
|
|
|
// Convert each portion of the path
|
|
do {
|
|
// Locate end of this file or dir
|
|
p2 = wcschr (p, L'\\');
|
|
if (!p2) {
|
|
p = GetEndOfStringW (p);
|
|
} else {
|
|
p = p2;
|
|
}
|
|
|
|
// Look up file
|
|
c = *p;
|
|
*p = 0;
|
|
if (!pFindShortNameW (FullPath, BufferEnd, &BufferSizeInBytes)) {
|
|
DEBUGMSG ((DBG_VERBOSE, "OurGetLongPathNameW: %ls does not exist", FullPath));
|
|
return FALSE;
|
|
}
|
|
*p = c;
|
|
|
|
// Move on to next part of path
|
|
if (*p) {
|
|
p++;
|
|
if (BufferSizeInBytes >= sizeof (WCHAR) * 2) {
|
|
BufferEnd = _wcsappend (BufferEnd, L"\\");
|
|
BufferSizeInBytes -= sizeof (WCHAR);
|
|
}
|
|
}
|
|
} while (*p);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
UINT g_FileEnumResourcesInUse;
|
|
#endif
|
|
|
|
VOID
|
|
pTrackedFindClose (
|
|
HANDLE FindHandle
|
|
)
|
|
{
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse--;
|
|
#endif
|
|
|
|
FindClose (FindHandle);
|
|
}
|
|
|
|
BOOL
|
|
EnumFirstFileInTreeExA (
|
|
OUT PTREE_ENUMA EnumPtr,
|
|
IN PCSTR RootPath,
|
|
IN PCSTR FilePattern, OPTIONAL
|
|
IN BOOL EnumDirsFirst,
|
|
IN BOOL EnumDepthFirst,
|
|
IN INT MaxLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumFirstFileInTree begins an enumeration of a directory tree. The
|
|
caller supplies an uninitialized enum structure, a directory path to
|
|
enumerate, and an optional file pattern. On return, the caller
|
|
receives all files and directories that match the pattern.
|
|
|
|
If a file pattern is supplied, directories that do not match the
|
|
file pattern are enumerated anyway.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Receives the enumerated file or directory
|
|
|
|
RootPath - Specifies the full path of the directory to enumerate
|
|
|
|
FilePattern - Specifies a pattern of files to limit the search to
|
|
|
|
EnumDirsFirst - Specifies TRUE if the directories should be enumerated
|
|
before the files, or FALSE if the directories should
|
|
be enumerated after the files.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a file or directory was enumerated, or FALSE if enumeration is complete
|
|
or an error occurred. (Use GetLastError to determine the result.)
|
|
|
|
--*/
|
|
|
|
{
|
|
ZeroMemory (EnumPtr, sizeof (TREE_ENUMA));
|
|
|
|
EnumPtr->State = TREE_ENUM_INIT;
|
|
|
|
_mbssafecpy (EnumPtr->RootPath, RootPath, MAX_MBCHAR_PATH);
|
|
|
|
if (FilePattern) {
|
|
_mbssafecpy (EnumPtr->FilePattern, FilePattern, MAX_MBCHAR_PATH);
|
|
} else {
|
|
StringCopyA (EnumPtr->FilePattern, "*.*");
|
|
}
|
|
|
|
EnumPtr->EnumDirsFirst = EnumDirsFirst;
|
|
EnumPtr->EnumDepthFirst = EnumDepthFirst;
|
|
|
|
EnumPtr->Level = 1;
|
|
EnumPtr->MaxLevel = MaxLevel;
|
|
|
|
return EnumNextFileInTreeA (EnumPtr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumFirstFileInTreeExW (
|
|
OUT PTREE_ENUMW EnumPtr,
|
|
IN PCWSTR RootPath,
|
|
IN PCWSTR FilePattern, OPTIONAL
|
|
IN BOOL EnumDirsFirst,
|
|
IN BOOL EnumDepthFirst,
|
|
IN INT MaxLevel
|
|
)
|
|
{
|
|
ZeroMemory (EnumPtr, sizeof (TREE_ENUMW));
|
|
|
|
EnumPtr->State = TREE_ENUM_INIT;
|
|
|
|
_wcssafecpy (EnumPtr->RootPath, RootPath, MAX_PATH);
|
|
|
|
if (FilePattern) {
|
|
_wcssafecpy (EnumPtr->FilePattern, FilePattern, MAX_PATH);
|
|
} else {
|
|
StringCopyW (EnumPtr->FilePattern, L"*.*");
|
|
}
|
|
|
|
EnumPtr->EnumDirsFirst = EnumDirsFirst;
|
|
EnumPtr->EnumDepthFirst = EnumDepthFirst;
|
|
|
|
EnumPtr->Level = 1;
|
|
EnumPtr->MaxLevel = MaxLevel;
|
|
|
|
return EnumNextFileInTreeW (EnumPtr);
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumNextFileInTreeA (
|
|
IN OUT PTREE_ENUMA EnumPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
EnumNextFileInTree continues an enumeration of a directory tree,
|
|
returning the files that match the pattern specified in EnumFirstFileInTree,
|
|
and also returning all directories.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Specifies the enumeration in progress, receives the enumerated file
|
|
or directory
|
|
|
|
Return Value:
|
|
|
|
TRUE if a file or directory was enumerated, or FALSE if enumeration is complete
|
|
or an error occurred. (Use GetLastError to determine the result.)
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR p;
|
|
|
|
for (;;) {
|
|
switch (EnumPtr->State) {
|
|
|
|
case TREE_ENUM_INIT:
|
|
//
|
|
// Get rid of wack at the end of root path, if it exists
|
|
//
|
|
|
|
p = GetEndOfStringA (EnumPtr->RootPath);
|
|
p = _mbsdec2 (EnumPtr->RootPath, p);
|
|
if (!p) {
|
|
DEBUGMSGA ((DBG_ERROR, "Path spec %s is incomplete", EnumPtr->RootPath));
|
|
EnumPtr->State = TREE_ENUM_FAILED;
|
|
break;
|
|
}
|
|
|
|
if (_mbsnextc (p) == '\\') {
|
|
*p = 0;
|
|
}
|
|
|
|
//
|
|
// Initialize enumeration structure
|
|
//
|
|
|
|
EnumPtr->FilePatternSize = SizeOfStringA (EnumPtr->FilePattern);
|
|
|
|
StringCopyA (EnumPtr->FileBuffer, EnumPtr->RootPath);
|
|
EnumPtr->EndOfFileBuffer = GetEndOfStringA (EnumPtr->FileBuffer);
|
|
|
|
StringCopyA (EnumPtr->Pattern, EnumPtr->RootPath);
|
|
EnumPtr->EndOfPattern = GetEndOfStringA (EnumPtr->Pattern);
|
|
|
|
EnumPtr->FullPath = EnumPtr->FileBuffer;
|
|
|
|
EnumPtr->RootPathSize = ByteCountA (EnumPtr->RootPath);
|
|
|
|
//
|
|
// Allocate first find data sturct
|
|
//
|
|
|
|
EnumPtr->Current = (PFIND_DATAA) GrowBuffer (
|
|
&EnumPtr->FindDataArray,
|
|
sizeof (FIND_DATAA)
|
|
);
|
|
if (!EnumPtr->Current) {
|
|
EnumPtr->State = TREE_ENUM_FAILED;
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse++; // account for grow buffer
|
|
#endif
|
|
|
|
EnumPtr->State = TREE_ENUM_BEGIN;
|
|
break;
|
|
|
|
case TREE_ENUM_BEGIN:
|
|
//
|
|
// Initialize current find data struct
|
|
//
|
|
|
|
EnumPtr->Current->SavedEndOfFileBuffer = EnumPtr->EndOfFileBuffer;
|
|
EnumPtr->Current->SavedEndOfPattern = EnumPtr->EndOfPattern;
|
|
|
|
//
|
|
// Limit the length of the pattern string
|
|
//
|
|
|
|
if ((EnumPtr->EndOfPattern - EnumPtr->Pattern) +
|
|
EnumPtr->FilePatternSize >= MAX_MBCHAR_PATH
|
|
) {
|
|
|
|
LOGA ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->Pattern, EnumPtr->FilePattern));
|
|
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Enuemrate the files or directories
|
|
//
|
|
|
|
if (EnumPtr->EnumDirsFirst) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_FILES_BEGIN;
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_FILES_BEGIN:
|
|
//
|
|
// Begin enumeration of files
|
|
//
|
|
|
|
StringCopyA (EnumPtr->EndOfPattern, "\\");
|
|
StringCopyA (EnumPtr->EndOfPattern + 1, EnumPtr->FilePattern);
|
|
|
|
EnumPtr->Current->FindHandle = FindFirstFileA (
|
|
EnumPtr->Pattern,
|
|
&EnumPtr->Current->FindData
|
|
);
|
|
|
|
if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
|
|
if (EnumPtr->EnumDirsFirst) {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse++; // account for creation of find handle
|
|
#endif
|
|
//
|
|
// Skip directories
|
|
//
|
|
|
|
if (EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
EnumPtr->State = TREE_ENUM_FILES_NEXT;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TREE_ENUM_RETURN_ITEM:
|
|
//
|
|
// Update pointers to current item
|
|
//
|
|
|
|
EnumPtr->FindData = &EnumPtr->Current->FindData;
|
|
EnumPtr->Name = EnumPtr->FindData->cFileName;
|
|
EnumPtr->Directory = (EnumPtr->FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
|
|
//
|
|
// Limit the length of the resulting full path
|
|
//
|
|
|
|
if ((EnumPtr->EndOfFileBuffer - EnumPtr->FileBuffer) +
|
|
SizeOfStringA (EnumPtr->Name) >= MAX_MBCHAR_PATH
|
|
) {
|
|
|
|
LOGA ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Name));
|
|
|
|
if (EnumPtr->Directory) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_FILES_NEXT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Generate the full path
|
|
//
|
|
|
|
StringCopyA (EnumPtr->EndOfFileBuffer, "\\");
|
|
StringCopyA (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Name);
|
|
|
|
if (EnumPtr->Directory) {
|
|
if ((EnumPtr->MaxLevel == FILE_ENUM_ALL_LEVELS) ||
|
|
(EnumPtr->Level < EnumPtr->MaxLevel)
|
|
) {
|
|
if (EnumPtr->EnumDepthFirst) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_PUSH;
|
|
}
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_FILES_NEXT;
|
|
}
|
|
|
|
EnumPtr->SubPath = (PCSTR) ((PBYTE) EnumPtr->FileBuffer + EnumPtr->RootPathSize);
|
|
if (*EnumPtr->SubPath) {
|
|
EnumPtr->SubPath++;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
case TREE_ENUM_FILES_NEXT:
|
|
if (FindNextFileA (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
|
|
//
|
|
// Return files only
|
|
//
|
|
|
|
if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
} else {
|
|
if (!EnumPtr->EnumDirsFirst) {
|
|
pTrackedFindClose (EnumPtr->Current->FindHandle);
|
|
EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_DIRS_FILTER:
|
|
if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
|
|
} else if (StringMatchA (EnumPtr->Current->FindData.cFileName, ".") ||
|
|
StringMatchA (EnumPtr->Current->FindData.cFileName, "..")
|
|
) {
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
|
|
} else {
|
|
|
|
if (EnumPtr->EnumDepthFirst) {
|
|
EnumPtr->State = TREE_ENUM_PUSH;
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_DIRS_BEGIN:
|
|
//
|
|
// Begin enumeration of directories
|
|
//
|
|
|
|
StringCopyA (EnumPtr->EndOfPattern, "\\");
|
|
StringCopyA (EnumPtr->EndOfPattern + 1, "*.*");
|
|
|
|
EnumPtr->Current->FindHandle = FindFirstFileA (
|
|
EnumPtr->Pattern,
|
|
&EnumPtr->Current->FindData
|
|
);
|
|
|
|
if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
} else {
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse++; // account for creation of find handle
|
|
#endif
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_FILTER;
|
|
}
|
|
|
|
break;
|
|
|
|
case TREE_ENUM_DIRS_NEXT:
|
|
if (FindNextFileA (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
|
|
//
|
|
// Return directories only, then recurse into directory
|
|
//
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_FILTER;
|
|
|
|
} else {
|
|
//
|
|
// Directory enumeration complete.
|
|
//
|
|
|
|
if (EnumPtr->EnumDirsFirst) {
|
|
pTrackedFindClose (EnumPtr->Current->FindHandle);
|
|
EnumPtr->State = TREE_ENUM_FILES_BEGIN;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_PUSH:
|
|
|
|
//
|
|
// Limit the length of the resulting full path
|
|
//
|
|
|
|
if ((EnumPtr->EndOfFileBuffer - EnumPtr->FileBuffer) +
|
|
SizeOfStringA (EnumPtr->Current->FindData.cFileName) >= MAX_MBCHAR_PATH
|
|
) {
|
|
|
|
LOGA ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Name));
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Tack on directory name to strings and recalcuate end pointers
|
|
//
|
|
|
|
StringCopyA (EnumPtr->EndOfPattern + 1, EnumPtr->Current->FindData.cFileName);
|
|
StringCopyA (EnumPtr->EndOfFileBuffer, "\\");
|
|
StringCopyA (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Current->FindData.cFileName);
|
|
|
|
EnumPtr->EndOfPattern = GetEndOfStringA (EnumPtr->EndOfPattern);
|
|
EnumPtr->EndOfFileBuffer = GetEndOfStringA (EnumPtr->EndOfFileBuffer);
|
|
|
|
//
|
|
// Allocate another find data struct
|
|
//
|
|
|
|
EnumPtr->Current = (PFIND_DATAA) GrowBuffer (
|
|
&EnumPtr->FindDataArray,
|
|
sizeof (FIND_DATAA)
|
|
);
|
|
if (!EnumPtr->Current) {
|
|
EnumPtr->State = TREE_ENUM_FAILED;
|
|
break;
|
|
}
|
|
|
|
EnumPtr->Level++;
|
|
EnumPtr->State = TREE_ENUM_BEGIN;
|
|
break;
|
|
|
|
case TREE_ENUM_POP:
|
|
//
|
|
// Free the current resources
|
|
//
|
|
|
|
pTrackedFindClose (EnumPtr->Current->FindHandle);
|
|
EnumPtr->Level--;
|
|
|
|
//
|
|
// Get the previous find data struct
|
|
//
|
|
|
|
MYASSERT (EnumPtr->FindDataArray.End >= sizeof (FIND_DATAA));
|
|
EnumPtr->FindDataArray.End -= sizeof (FIND_DATAA);
|
|
if (!EnumPtr->FindDataArray.End) {
|
|
EnumPtr->State = TREE_ENUM_DONE;
|
|
break;
|
|
}
|
|
|
|
EnumPtr->Current = (PFIND_DATAA) (EnumPtr->FindDataArray.Buf +
|
|
(EnumPtr->FindDataArray.End - sizeof (FIND_DATAA)));
|
|
|
|
//
|
|
// Restore the settings of the parent directory
|
|
//
|
|
|
|
EnumPtr->EndOfPattern = EnumPtr->Current->SavedEndOfPattern;
|
|
EnumPtr->EndOfFileBuffer = EnumPtr->Current->SavedEndOfFileBuffer;
|
|
|
|
if (EnumPtr->EnumDepthFirst) {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_DONE:
|
|
AbortEnumFileInTreeA (EnumPtr);
|
|
SetLastError (ERROR_SUCCESS);
|
|
return FALSE;
|
|
|
|
case TREE_ENUM_FAILED:
|
|
PushError();
|
|
AbortEnumFileInTreeA (EnumPtr);
|
|
PopError();
|
|
return FALSE;
|
|
|
|
case TREE_ENUM_CLEANED_UP:
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumNextFileInTreeW (
|
|
IN OUT PTREE_ENUMW EnumPtr
|
|
)
|
|
{
|
|
PWSTR p;
|
|
|
|
for (;;) {
|
|
switch (EnumPtr->State) {
|
|
|
|
case TREE_ENUM_INIT:
|
|
|
|
//
|
|
// Get rid of wack at the end of root path, if it exists
|
|
//
|
|
|
|
p = GetEndOfStringW (EnumPtr->RootPath);
|
|
p = _wcsdec2 (EnumPtr->RootPath, p);
|
|
if (!p) {
|
|
DEBUGMSG ((DBG_ERROR, "Path spec %ls is incomplete", EnumPtr->RootPath));
|
|
EnumPtr->State = TREE_ENUM_FAILED;
|
|
break;
|
|
}
|
|
|
|
if (*p == L'\\') {
|
|
*p = 0;
|
|
}
|
|
|
|
//
|
|
// Initialize enumeration structure
|
|
//
|
|
|
|
EnumPtr->FilePatternSize = SizeOfStringW (EnumPtr->FilePattern);
|
|
|
|
StringCopyW (EnumPtr->FileBuffer, EnumPtr->RootPath);
|
|
EnumPtr->EndOfFileBuffer = GetEndOfStringW (EnumPtr->FileBuffer);
|
|
|
|
StringCopyW (EnumPtr->Pattern, EnumPtr->RootPath);
|
|
EnumPtr->EndOfPattern = GetEndOfStringW (EnumPtr->Pattern);
|
|
|
|
EnumPtr->FullPath = EnumPtr->FileBuffer;
|
|
|
|
EnumPtr->RootPathSize = ByteCountW (EnumPtr->RootPath);
|
|
|
|
//
|
|
// Allocate first find data sturct
|
|
//
|
|
|
|
EnumPtr->Current = (PFIND_DATAW) GrowBuffer (
|
|
&EnumPtr->FindDataArray,
|
|
sizeof (FIND_DATAW)
|
|
);
|
|
if (!EnumPtr->Current) {
|
|
EnumPtr->State = TREE_ENUM_FAILED;
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse++; // account for grow buffer
|
|
#endif
|
|
|
|
EnumPtr->State = TREE_ENUM_BEGIN;
|
|
break;
|
|
|
|
case TREE_ENUM_BEGIN:
|
|
//
|
|
// Initialize current find data struct
|
|
//
|
|
|
|
EnumPtr->Current->SavedEndOfFileBuffer = EnumPtr->EndOfFileBuffer;
|
|
EnumPtr->Current->SavedEndOfPattern = EnumPtr->EndOfPattern;
|
|
|
|
//
|
|
// Limit the length of the pattern string
|
|
//
|
|
|
|
if (((PBYTE) EnumPtr->EndOfPattern - (PBYTE) EnumPtr->Pattern) +
|
|
EnumPtr->FilePatternSize >= (MAX_PATH * 2 * sizeof (WCHAR))
|
|
) {
|
|
|
|
LOGW ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->Pattern, EnumPtr->FilePattern));
|
|
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Enuemrate the files or directories
|
|
//
|
|
|
|
if (EnumPtr->EnumDirsFirst) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_FILES_BEGIN;
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_FILES_BEGIN:
|
|
//
|
|
// Begin enumeration of files
|
|
//
|
|
|
|
StringCopyW (EnumPtr->EndOfPattern, L"\\");
|
|
StringCopyW (EnumPtr->EndOfPattern + 1, EnumPtr->FilePattern);
|
|
|
|
EnumPtr->Current->FindHandle = FindFirstFileW (
|
|
EnumPtr->Pattern,
|
|
&EnumPtr->Current->FindData
|
|
);
|
|
|
|
if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
|
|
if (EnumPtr->EnumDirsFirst) {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse++; // account for creation of find handle
|
|
#endif
|
|
//
|
|
// Skip directories
|
|
//
|
|
|
|
if (EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
EnumPtr->State = TREE_ENUM_FILES_NEXT;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case TREE_ENUM_RETURN_ITEM:
|
|
//
|
|
// Update pointers to current item
|
|
//
|
|
|
|
EnumPtr->FindData = &EnumPtr->Current->FindData;
|
|
EnumPtr->Name = EnumPtr->FindData->cFileName;
|
|
EnumPtr->Directory = (EnumPtr->FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
|
|
//
|
|
// Limit the length of the resulting full path
|
|
//
|
|
|
|
if (((PBYTE) EnumPtr->EndOfFileBuffer - (PBYTE) EnumPtr->FileBuffer) +
|
|
SizeOfStringW (EnumPtr->Name) >= (MAX_PATH * 2 * sizeof (WCHAR))
|
|
) {
|
|
|
|
LOGW ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Name));
|
|
|
|
if (EnumPtr->Directory) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_FILES_NEXT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Generate the full path
|
|
//
|
|
|
|
StringCopyW (EnumPtr->EndOfFileBuffer, L"\\");
|
|
StringCopyW (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Name);
|
|
|
|
if (EnumPtr->Directory) {
|
|
if ((EnumPtr->MaxLevel == FILE_ENUM_ALL_LEVELS) ||
|
|
(EnumPtr->Level < EnumPtr->MaxLevel)
|
|
) {
|
|
if (EnumPtr->EnumDepthFirst) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_PUSH;
|
|
}
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_FILES_NEXT;
|
|
}
|
|
|
|
EnumPtr->SubPath = (PCWSTR) ((PBYTE) EnumPtr->FileBuffer + EnumPtr->RootPathSize);
|
|
if (*EnumPtr->SubPath) {
|
|
EnumPtr->SubPath++;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
case TREE_ENUM_FILES_NEXT:
|
|
if (FindNextFileW (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
|
|
//
|
|
// Return files only
|
|
//
|
|
|
|
if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
} else {
|
|
if (!EnumPtr->EnumDirsFirst) {
|
|
pTrackedFindClose (EnumPtr->Current->FindHandle);
|
|
EnumPtr->State = TREE_ENUM_DIRS_BEGIN;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_DIRS_FILTER:
|
|
if (!(EnumPtr->Current->FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
|
|
} else if (StringMatchW (EnumPtr->Current->FindData.cFileName, L".") ||
|
|
StringMatchW (EnumPtr->Current->FindData.cFileName, L"..")
|
|
) {
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
|
|
} else {
|
|
|
|
if (EnumPtr->EnumDepthFirst) {
|
|
EnumPtr->State = TREE_ENUM_PUSH;
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_DIRS_BEGIN:
|
|
//
|
|
// Begin enumeration of directories
|
|
//
|
|
|
|
StringCopyW (EnumPtr->EndOfPattern, L"\\*.*");
|
|
|
|
EnumPtr->Current->FindHandle = FindFirstFileW (
|
|
EnumPtr->Pattern,
|
|
&EnumPtr->Current->FindData
|
|
);
|
|
|
|
if (EnumPtr->Current->FindHandle == INVALID_HANDLE_VALUE) {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
} else {
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse++; // account for creation of find handle
|
|
#endif
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_FILTER;
|
|
}
|
|
|
|
break;
|
|
|
|
case TREE_ENUM_DIRS_NEXT:
|
|
if (FindNextFileW (EnumPtr->Current->FindHandle, &EnumPtr->Current->FindData)) {
|
|
//
|
|
// Return directories only, then recurse into directory
|
|
//
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_FILTER;
|
|
|
|
} else {
|
|
//
|
|
// Directory enumeration complete.
|
|
//
|
|
|
|
if (EnumPtr->EnumDirsFirst) {
|
|
pTrackedFindClose (EnumPtr->Current->FindHandle);
|
|
EnumPtr->State = TREE_ENUM_FILES_BEGIN;
|
|
} else {
|
|
EnumPtr->State = TREE_ENUM_POP;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_PUSH:
|
|
|
|
//
|
|
// Limit the length of the resulting full path
|
|
//
|
|
|
|
if (((PBYTE) EnumPtr->EndOfFileBuffer - (PBYTE) EnumPtr->FileBuffer) +
|
|
SizeOfStringW (EnumPtr->Current->FindData.cFileName) >= (MAX_PATH * 2 * sizeof (WCHAR))
|
|
) {
|
|
|
|
LOGW ((LOG_ERROR, "Path %s\\%s is too long", EnumPtr->FileBuffer, EnumPtr->Name));
|
|
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Tack on directory name to strings and recalcuate end pointers
|
|
//
|
|
|
|
StringCopyW (EnumPtr->EndOfPattern + 1, EnumPtr->Current->FindData.cFileName);
|
|
StringCopyW (EnumPtr->EndOfFileBuffer, L"\\");
|
|
StringCopyW (EnumPtr->EndOfFileBuffer + 1, EnumPtr->Current->FindData.cFileName);
|
|
|
|
EnumPtr->EndOfPattern = GetEndOfStringW (EnumPtr->EndOfPattern);
|
|
EnumPtr->EndOfFileBuffer = GetEndOfStringW (EnumPtr->EndOfFileBuffer);
|
|
|
|
//
|
|
// Allocate another find data struct
|
|
//
|
|
|
|
EnumPtr->Current = (PFIND_DATAW) GrowBuffer (
|
|
&EnumPtr->FindDataArray,
|
|
sizeof (FIND_DATAW)
|
|
);
|
|
if (!EnumPtr->Current) {
|
|
EnumPtr->State = TREE_ENUM_FAILED;
|
|
break;
|
|
}
|
|
|
|
EnumPtr->Level++;
|
|
EnumPtr->State = TREE_ENUM_BEGIN;
|
|
break;
|
|
|
|
case TREE_ENUM_POP:
|
|
//
|
|
// Free the current resources
|
|
//
|
|
|
|
pTrackedFindClose (EnumPtr->Current->FindHandle);
|
|
EnumPtr->Level--;
|
|
|
|
//
|
|
// Get the previous find data struct
|
|
//
|
|
|
|
MYASSERT (EnumPtr->FindDataArray.End >= sizeof (FIND_DATAW));
|
|
EnumPtr->FindDataArray.End -= sizeof (FIND_DATAW);
|
|
if (!EnumPtr->FindDataArray.End) {
|
|
EnumPtr->State = TREE_ENUM_DONE;
|
|
break;
|
|
}
|
|
|
|
EnumPtr->Current = (PFIND_DATAW) (EnumPtr->FindDataArray.Buf +
|
|
(EnumPtr->FindDataArray.End - sizeof (FIND_DATAW)));
|
|
|
|
//
|
|
// Restore the settings of the parent directory
|
|
//
|
|
|
|
EnumPtr->EndOfPattern = EnumPtr->Current->SavedEndOfPattern;
|
|
EnumPtr->EndOfFileBuffer = EnumPtr->Current->SavedEndOfFileBuffer;
|
|
|
|
if (EnumPtr->EnumDepthFirst) {
|
|
EnumPtr->State = TREE_ENUM_RETURN_ITEM;
|
|
}
|
|
else {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
break;
|
|
|
|
case TREE_ENUM_DONE:
|
|
AbortEnumFileInTreeW (EnumPtr);
|
|
SetLastError (ERROR_SUCCESS);
|
|
return FALSE;
|
|
|
|
case TREE_ENUM_FAILED:
|
|
PushError();
|
|
AbortEnumFileInTreeW (EnumPtr);
|
|
PopError();
|
|
return FALSE;
|
|
|
|
case TREE_ENUM_CLEANED_UP:
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
AbortEnumFileInTreeA (
|
|
IN OUT PTREE_ENUMA EnumPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
AbortEnumFileInTree cleans up all resources used by an enumeration started
|
|
by EnumFirstFileInTree. This routine must be called if file enumeration
|
|
will not be completed by calling EnumNextFileInTree until it returns FALSE.
|
|
|
|
If EnumNextFileInTree returns FALSE, it is unnecessary to call this
|
|
function.
|
|
|
|
Arguments:
|
|
|
|
EnumPtr - Specifies the enumeration in progress, receives the enumerated file
|
|
or directory
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT Pos;
|
|
PGROWBUFFER g;
|
|
PFIND_DATAA Current;
|
|
|
|
if (EnumPtr->State == TREE_ENUM_CLEANED_UP) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Close any currently open handles
|
|
//
|
|
|
|
g = &EnumPtr->FindDataArray;
|
|
for (Pos = 0 ; Pos < g->End ; Pos += sizeof (FIND_DATAA)) {
|
|
Current = (PFIND_DATAA) (g->Buf + Pos);
|
|
pTrackedFindClose (Current->FindHandle);
|
|
}
|
|
|
|
FreeGrowBuffer (&EnumPtr->FindDataArray);
|
|
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse--;
|
|
#endif
|
|
|
|
EnumPtr->State = TREE_ENUM_CLEANED_UP;
|
|
}
|
|
|
|
|
|
VOID
|
|
AbortEnumFileInTreeW (
|
|
IN OUT PTREE_ENUMW EnumPtr
|
|
)
|
|
{
|
|
UINT Pos;
|
|
PGROWBUFFER g;
|
|
PFIND_DATAW Current;
|
|
|
|
if (EnumPtr->State == TREE_ENUM_CLEANED_UP) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Close any currently open handles
|
|
//
|
|
|
|
g = &EnumPtr->FindDataArray;
|
|
for (Pos = 0 ; Pos < g->End ; Pos += sizeof (FIND_DATAW)) {
|
|
Current = (PFIND_DATAW) (g->Buf + Pos);
|
|
pTrackedFindClose (Current->FindHandle);
|
|
}
|
|
|
|
FreeGrowBuffer (&EnumPtr->FindDataArray);
|
|
|
|
#ifdef DEBUG
|
|
g_FileEnumResourcesInUse--;
|
|
#endif
|
|
|
|
EnumPtr->State = TREE_ENUM_CLEANED_UP;
|
|
}
|
|
|
|
|
|
VOID
|
|
AbortEnumCurrentDirA (
|
|
IN OUT PTREE_ENUMA EnumPtr
|
|
)
|
|
{
|
|
if (EnumPtr->State == TREE_ENUM_PUSH) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
AbortEnumCurrentDirW (
|
|
IN OUT PTREE_ENUMW EnumPtr
|
|
)
|
|
{
|
|
if (EnumPtr->State == TREE_ENUM_PUSH) {
|
|
EnumPtr->State = TREE_ENUM_DIRS_NEXT;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumFirstFileA (
|
|
OUT PFILE_ENUMA EnumPtr,
|
|
IN PCSTR RootPath,
|
|
IN PCSTR FilePattern OPTIONAL
|
|
)
|
|
{
|
|
ZeroMemory (EnumPtr, sizeof (FILE_ENUMA));
|
|
|
|
EnumPtr->FileName = EnumPtr->fd.cFileName;
|
|
EnumPtr->FullPath = EnumPtr->RootPath;
|
|
|
|
StringCopyA (EnumPtr->RootPath, RootPath);
|
|
EnumPtr->EndOfRoot = AppendWackA (EnumPtr->RootPath);
|
|
StringCopyA (EnumPtr->EndOfRoot, FilePattern ? FilePattern : "*.*");
|
|
|
|
EnumPtr->Handle = FindFirstFileA (EnumPtr->RootPath, &EnumPtr->fd);
|
|
|
|
if (EnumPtr->Handle != INVALID_HANDLE_VALUE) {
|
|
|
|
if (StringMatchA (EnumPtr->FileName, ".") ||
|
|
StringMatchA (EnumPtr->FileName, "..")
|
|
) {
|
|
return EnumNextFileA (EnumPtr);
|
|
}
|
|
|
|
StringCopyA (EnumPtr->EndOfRoot, EnumPtr->FileName);
|
|
EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumFirstFileW (
|
|
OUT PFILE_ENUMW EnumPtr,
|
|
IN PCWSTR RootPath,
|
|
IN PCWSTR FilePattern OPTIONAL
|
|
)
|
|
{
|
|
ZeroMemory (EnumPtr, sizeof (FILE_ENUMW));
|
|
|
|
EnumPtr->FileName = EnumPtr->fd.cFileName;
|
|
EnumPtr->FullPath = EnumPtr->RootPath;
|
|
|
|
StringCopyW (EnumPtr->RootPath, RootPath);
|
|
EnumPtr->EndOfRoot = AppendWackW (EnumPtr->RootPath);
|
|
StringCopyW (EnumPtr->EndOfRoot, FilePattern ? FilePattern : L"*.*");
|
|
|
|
EnumPtr->Handle = FindFirstFileW (EnumPtr->RootPath, &EnumPtr->fd);
|
|
|
|
if (EnumPtr->Handle != INVALID_HANDLE_VALUE) {
|
|
|
|
if (StringMatchW (EnumPtr->FileName, L".") ||
|
|
StringMatchW (EnumPtr->FileName, L"..")
|
|
) {
|
|
return EnumNextFileW (EnumPtr);
|
|
}
|
|
|
|
StringCopyW (EnumPtr->EndOfRoot, EnumPtr->FileName);
|
|
EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumNextFileA (
|
|
IN OUT PFILE_ENUMA EnumPtr
|
|
)
|
|
{
|
|
do {
|
|
if (!FindNextFileA (EnumPtr->Handle, &EnumPtr->fd)) {
|
|
AbortFileEnumA (EnumPtr);
|
|
return FALSE;
|
|
}
|
|
} while (StringMatchA (EnumPtr->FileName, ".") ||
|
|
StringMatchA (EnumPtr->FileName, "..")
|
|
);
|
|
|
|
StringCopyA (EnumPtr->EndOfRoot, EnumPtr->FileName);
|
|
EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
EnumNextFileW (
|
|
IN OUT PFILE_ENUMW EnumPtr
|
|
)
|
|
{
|
|
do {
|
|
if (!FindNextFileW (EnumPtr->Handle, &EnumPtr->fd)) {
|
|
AbortFileEnumW (EnumPtr);
|
|
return FALSE;
|
|
}
|
|
} while (StringMatchW (EnumPtr->FileName, L".") ||
|
|
StringMatchW (EnumPtr->FileName, L"..")
|
|
);
|
|
|
|
if (!FindNextFileW (EnumPtr->Handle, &EnumPtr->fd)) {
|
|
AbortFileEnumW (EnumPtr);
|
|
return FALSE;
|
|
}
|
|
|
|
StringCopyW (EnumPtr->EndOfRoot, EnumPtr->FileName);
|
|
EnumPtr->Directory = EnumPtr->fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AbortFileEnumA (
|
|
IN OUT PFILE_ENUMA EnumPtr
|
|
)
|
|
{
|
|
if (EnumPtr->Handle && EnumPtr->Handle != INVALID_HANDLE_VALUE) {
|
|
FindClose (EnumPtr->Handle);
|
|
ZeroMemory (EnumPtr, sizeof (FILE_ENUMA));
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
AbortFileEnumW (
|
|
IN OUT PFILE_ENUMW EnumPtr
|
|
)
|
|
{
|
|
if (EnumPtr->Handle && EnumPtr->Handle != INVALID_HANDLE_VALUE) {
|
|
FindClose (EnumPtr->Handle);
|
|
ZeroMemory (EnumPtr, sizeof (FILE_ENUMW));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MapFileIntoMemoryA and MapFileIntoMemoryW map a file into memory. It does that
|
|
by opening the file, creating a mapping object and mapping opened file into
|
|
created mapping object. It returnes the address where the file is mapped and
|
|
also sets FileHandle and MapHandle variables to be used in order to unmap the
|
|
file when work is done.
|
|
|
|
Arguments:
|
|
|
|
FileName - the name of the file to be mapped into memory
|
|
FileHandle - will end keeping the file handle if the file was opened successfully
|
|
MapHandle - will end keeping the mapping object handle if this object was created successfully
|
|
|
|
Return Value:
|
|
|
|
NULL if function fails, a valid memory address if successfull
|
|
|
|
Comments:
|
|
|
|
If the return value is NULL you should call UnmapFile to release all allocated resources
|
|
|
|
--*/
|
|
|
|
PVOID
|
|
MapFileIntoMemoryExA (
|
|
IN PCSTR FileName,
|
|
OUT PHANDLE FileHandle,
|
|
OUT PHANDLE MapHandle,
|
|
IN BOOL WriteAccess
|
|
)
|
|
{
|
|
PVOID fileImage = NULL;
|
|
|
|
//verify function parameters
|
|
if ((FileHandle == NULL) || (MapHandle == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
//first thing. Try to open the file, read-only
|
|
*FileHandle = CreateFileA (
|
|
FileName,
|
|
WriteAccess?GENERIC_READ|GENERIC_WRITE:GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (*FileHandle == INVALID_HANDLE_VALUE) {
|
|
return NULL;
|
|
}
|
|
|
|
//now try to create a mapping object, read-only
|
|
*MapHandle = CreateFileMappingA (*FileHandle, NULL, WriteAccess?PAGE_READWRITE:PAGE_READONLY, 0, 0, NULL);
|
|
|
|
if (*MapHandle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//one more thing to do: map view of file
|
|
fileImage = MapViewOfFile (*MapHandle, WriteAccess?FILE_MAP_WRITE:FILE_MAP_READ, 0, 0, 0);
|
|
|
|
return fileImage;
|
|
}
|
|
|
|
|
|
PVOID
|
|
MapFileIntoMemoryExW (
|
|
IN PCWSTR FileName,
|
|
OUT PHANDLE FileHandle,
|
|
OUT PHANDLE MapHandle,
|
|
IN BOOL WriteAccess
|
|
)
|
|
{
|
|
PVOID fileImage = NULL;
|
|
|
|
//verify function parameters
|
|
if ((FileHandle == NULL) || (MapHandle == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
//first thing. Try to open the file, read-only
|
|
*FileHandle = CreateFileW (
|
|
FileName,
|
|
WriteAccess?GENERIC_READ|GENERIC_WRITE:GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
if (*FileHandle == INVALID_HANDLE_VALUE) {
|
|
return NULL;
|
|
}
|
|
|
|
//now try to create a mapping object, read-only
|
|
*MapHandle = CreateFileMappingW (*FileHandle, NULL, WriteAccess?PAGE_READWRITE:PAGE_READONLY, 0, 0, NULL);
|
|
|
|
if (*MapHandle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//one more thing to do: map view of file
|
|
fileImage = MapViewOfFile (*MapHandle, WriteAccess?FILE_MAP_WRITE:FILE_MAP_READ, 0, 0, 0);
|
|
|
|
return fileImage;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
UnmapFile is used to release all resources allocated by MapFileIntoMemory.
|
|
|
|
Arguments:
|
|
|
|
FileImage - image of the mapped file as returned by MapFileIntoMemory
|
|
MapHandle - handle of the mapping object as returned by MapFileIntoMemory
|
|
FileHandle - handle of the file as returned by MapFileIntoMemory
|
|
|
|
Return Value:
|
|
|
|
TRUE if successfull, FALSE if not
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
UnmapFile (
|
|
IN PVOID FileImage,
|
|
IN HANDLE MapHandle,
|
|
IN HANDLE FileHandle
|
|
)
|
|
{
|
|
BOOL result = TRUE;
|
|
|
|
//if FileImage is a valid pointer then try to unmap file
|
|
if (FileImage != NULL) {
|
|
if (UnmapViewOfFile (FileImage) == 0) {
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
//if mapping object is valid then try to delete it
|
|
if (MapHandle != NULL) {
|
|
if (CloseHandle (MapHandle) == 0) {
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
//if file handle is valid then try to close the file
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
if (CloseHandle (FileHandle) == 0) {
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RemoveCompleteDirectoryA (
|
|
IN PCSTR Dir
|
|
)
|
|
{
|
|
TREE_ENUMA e;
|
|
BOOL b = TRUE;
|
|
CHAR CurDir[MAX_MBCHAR_PATH];
|
|
CHAR NewDir[MAX_MBCHAR_PATH];
|
|
LONG rc = ERROR_SUCCESS;
|
|
DWORD Attribs;
|
|
|
|
Attribs = GetFileAttributesA (Dir);
|
|
|
|
if (Attribs == INVALID_ATTRIBUTES) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(Attribs & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
SetFileAttributesA (Dir, FILE_ATTRIBUTE_NORMAL);
|
|
return DeleteFileA (Dir);
|
|
}
|
|
|
|
GetCurrentDirectoryA (MAX_MBCHAR_PATH, CurDir);
|
|
SetCurrentDirectoryA (Dir);
|
|
GetCurrentDirectoryA (MAX_MBCHAR_PATH, NewDir);
|
|
|
|
if (EnumFirstFileInTreeA (&e, NewDir, NULL, FALSE)) {
|
|
do {
|
|
if (!e.Directory) {
|
|
SetFileAttributesA (e.FullPath, FILE_ATTRIBUTE_NORMAL);
|
|
if (!DeleteFileA (e.FullPath)) {
|
|
DEBUGMSGA ((DBG_ERROR, "Can't delete %s", e.FullPath));
|
|
if (b) {
|
|
b = FALSE;
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
} while (EnumNextFileInTreeA (&e));
|
|
}
|
|
|
|
if (EnumFirstFileInTreeExA (&e, NewDir, NULL, TRUE, TRUE, FILE_ENUM_ALL_LEVELS)) {
|
|
do {
|
|
if (e.Directory) {
|
|
SetFileAttributesA (e.FullPath, FILE_ATTRIBUTE_NORMAL);
|
|
if (!RemoveDirectoryA (e.FullPath)) {
|
|
DEBUGMSGA ((DBG_ERROR, "Can't remove %s", e.FullPath));
|
|
if (b) {
|
|
b = FALSE;
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
} while (EnumNextFileInTreeA (&e));
|
|
}
|
|
|
|
if (b) {
|
|
SetFileAttributesA (NewDir, FILE_ATTRIBUTE_NORMAL);
|
|
SetCurrentDirectoryA ("..");
|
|
b = RemoveDirectoryA (NewDir);
|
|
}
|
|
|
|
if (!b && rc == ERROR_SUCCESS) {
|
|
rc = GetLastError();
|
|
}
|
|
|
|
SetCurrentDirectoryA (CurDir);
|
|
|
|
SetLastError (rc);
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RemoveCompleteDirectoryW (
|
|
IN PCWSTR Dir
|
|
)
|
|
{
|
|
TREE_ENUMW e;
|
|
BOOL b = TRUE;
|
|
WCHAR CurDir[MAX_WCHAR_PATH];
|
|
WCHAR NewDir[MAX_WCHAR_PATH];
|
|
LONG rc = ERROR_SUCCESS;
|
|
DWORD Attribs;
|
|
|
|
Attribs = GetLongPathAttributesW (Dir);
|
|
|
|
if (Attribs == INVALID_ATTRIBUTES) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (!(Attribs & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
SetLongPathAttributesW (Dir, FILE_ATTRIBUTE_NORMAL);
|
|
return DeleteLongPathW (Dir);
|
|
}
|
|
|
|
GetCurrentDirectoryW (MAX_WCHAR_PATH, CurDir);
|
|
SetCurrentDirectoryW (Dir);
|
|
GetCurrentDirectoryW (MAX_WCHAR_PATH, NewDir);
|
|
|
|
if (EnumFirstFileInTreeW (&e, NewDir, NULL, FALSE)) {
|
|
do {
|
|
if (!e.Directory) {
|
|
SetLongPathAttributesW (e.FullPath, FILE_ATTRIBUTE_NORMAL);
|
|
if (!DeleteLongPathW (e.FullPath)) {
|
|
DEBUGMSGW ((DBG_ERROR, "Can't delete %s", e.FullPath));
|
|
if (b) {
|
|
b = FALSE;
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
} while (EnumNextFileInTreeW (&e));
|
|
}
|
|
|
|
if (EnumFirstFileInTreeExW (&e, NewDir, NULL, TRUE, TRUE, FILE_ENUM_ALL_LEVELS)) {
|
|
do {
|
|
if (e.Directory) {
|
|
SetLongPathAttributesW (e.FullPath, FILE_ATTRIBUTE_NORMAL);
|
|
if (!RemoveDirectoryW (e.FullPath)) {
|
|
DEBUGMSGW ((DBG_ERROR, "Can't remove %s", e.FullPath));
|
|
if (b) {
|
|
b = FALSE;
|
|
rc = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
} while (EnumNextFileInTreeW (&e));
|
|
}
|
|
|
|
if (b) {
|
|
SetLongPathAttributesW (NewDir, FILE_ATTRIBUTE_NORMAL);
|
|
SetCurrentDirectoryW (L"..");
|
|
b = RemoveDirectoryW (NewDir);
|
|
}
|
|
|
|
if (!b && rc == ERROR_SUCCESS) {
|
|
rc = GetLastError();
|
|
}
|
|
|
|
SetCurrentDirectoryW (CurDir);
|
|
|
|
SetLastError (rc);
|
|
return b;
|
|
}
|
|
|
|
|
|
PCMDLINEA
|
|
ParseCmdLineA (
|
|
IN PCSTR CmdLine,
|
|
IN OUT PGROWBUFFER Buffer
|
|
)
|
|
{
|
|
GROWBUFFER SpacePtrs = GROWBUF_INIT;
|
|
PCSTR p;
|
|
PSTR q;
|
|
INT Count;
|
|
INT i;
|
|
INT j;
|
|
PSTR *Array;
|
|
PCSTR Start;
|
|
CHAR OldChar = 0;
|
|
GROWBUFFER StringBuf = GROWBUF_INIT;
|
|
PBYTE CopyBuf;
|
|
PCMDLINEA CmdLineTable;
|
|
PCMDLINEARGA CmdLineArg;
|
|
UINT Base;
|
|
CHAR Path[MAX_MBCHAR_PATH];
|
|
CHAR UnquotedPath[MAX_MBCHAR_PATH];
|
|
CHAR FixedFileName[MAX_MBCHAR_PATH];
|
|
PCSTR FullPath = NULL;
|
|
DWORD Attribs = INVALID_ATTRIBUTES;
|
|
PSTR CmdLineCopy;
|
|
BOOL Quoted;
|
|
UINT OriginalArgOffset = 0;
|
|
UINT CleanedUpArgOffset = 0;
|
|
BOOL GoodFileFound = FALSE;
|
|
PSTR DontCare;
|
|
CHAR FirstArgPath[MAX_MBCHAR_PATH];
|
|
PSTR EndOfFirstArg;
|
|
BOOL QuoteMode = FALSE;
|
|
PSTR End;
|
|
|
|
CmdLineCopy = DuplicateTextA (CmdLine);
|
|
|
|
//
|
|
// Build an array of places to break the string
|
|
//
|
|
|
|
for (p = CmdLineCopy ; *p ; p = _mbsinc (p)) {
|
|
|
|
if (_mbsnextc (p) == '\"') {
|
|
|
|
QuoteMode = !QuoteMode;
|
|
|
|
} else if (!QuoteMode && (_mbsnextc (p) == ' ' || _mbsnextc (p) == '=')) {
|
|
|
|
//
|
|
// Remove excess spaces
|
|
//
|
|
|
|
q = (PSTR) p + 1;
|
|
while (_mbsnextc (q) == ' ') {
|
|
q++;
|
|
}
|
|
|
|
if (q > p + 1) {
|
|
MoveMemory ((PBYTE) p + sizeof (CHAR), q, SizeOfStringA (q));
|
|
}
|
|
|
|
GrowBufAppendDword (&SpacePtrs, (DWORD) p);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prepare the CMDLINE struct
|
|
//
|
|
|
|
CmdLineTable = (PCMDLINEA) GrowBuffer (Buffer, sizeof (CMDLINEA));
|
|
MYASSERT (CmdLineTable);
|
|
|
|
//
|
|
// NOTE: We store string offsets, then at the end resolve them
|
|
// to pointers later.
|
|
//
|
|
|
|
CmdLineTable->CmdLine = (PCSTR) StringBuf.End;
|
|
MultiSzAppendA (&StringBuf, CmdLine);
|
|
|
|
CmdLineTable->ArgCount = 0;
|
|
|
|
//
|
|
// Now test every combination, emulating CreateProcess
|
|
//
|
|
|
|
Count = SpacePtrs.End / sizeof (DWORD);
|
|
Array = (PSTR *) SpacePtrs.Buf;
|
|
|
|
i = -1;
|
|
EndOfFirstArg = NULL;
|
|
|
|
while (i < Count) {
|
|
|
|
GoodFileFound = FALSE;
|
|
Quoted = FALSE;
|
|
|
|
if (i >= 0) {
|
|
Start = Array[i] + 1;
|
|
} else {
|
|
Start = CmdLineCopy;
|
|
}
|
|
|
|
//
|
|
// Check for a full path at Start
|
|
//
|
|
|
|
if (_mbsnextc (Start) != '/') {
|
|
|
|
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
FullPath = Start;
|
|
|
|
//
|
|
// Remove quotes; continue in the loop if it has no terminating quotes
|
|
//
|
|
|
|
Quoted = FALSE;
|
|
if (_mbsnextc (Start) == '\"') {
|
|
|
|
StringCopyByteCountA (UnquotedPath, Start + 1, sizeof (UnquotedPath));
|
|
q = _mbschr (UnquotedPath, '\"');
|
|
|
|
if (q) {
|
|
*q = 0;
|
|
FullPath = UnquotedPath;
|
|
Quoted = TRUE;
|
|
} else {
|
|
FullPath = NULL;
|
|
}
|
|
}
|
|
|
|
if (FullPath && *FullPath) {
|
|
//
|
|
// Look in file system for the path
|
|
//
|
|
|
|
Attribs = GetFileAttributesA (FullPath);
|
|
|
|
if (Attribs == INVALID_ATTRIBUTES && EndOfFirstArg) {
|
|
//
|
|
// Try prefixing the path with the first arg's path.
|
|
//
|
|
|
|
StringCopyByteCountA (
|
|
EndOfFirstArg,
|
|
FullPath,
|
|
sizeof (FirstArgPath) - ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
|
|
);
|
|
|
|
FullPath = FirstArgPath;
|
|
Attribs = GetFileAttributesA (FullPath);
|
|
}
|
|
|
|
if (Attribs == INVALID_ATTRIBUTES && i < 0) {
|
|
//
|
|
// Try appending .exe, then testing again. This
|
|
// emulates what CreateProcess does.
|
|
//
|
|
|
|
StringCopyByteCountA (
|
|
FixedFileName,
|
|
FullPath,
|
|
sizeof (FixedFileName) - sizeof (".exe")
|
|
);
|
|
|
|
q = GetEndOfStringA (FixedFileName);
|
|
q = _mbsdec (FixedFileName, q);
|
|
MYASSERT (q);
|
|
|
|
if (_mbsnextc (q) != '.') {
|
|
q = _mbsinc (q);
|
|
}
|
|
|
|
StringCopyA (q, ".exe");
|
|
|
|
FullPath = FixedFileName;
|
|
Attribs = GetFileAttributesA (FullPath);
|
|
}
|
|
|
|
if (Attribs != INVALID_ATTRIBUTES) {
|
|
//
|
|
// Full file path found. Test its file status, then
|
|
// move on if there are no important operations on it.
|
|
//
|
|
|
|
OriginalArgOffset = StringBuf.End;
|
|
MultiSzAppendA (&StringBuf, Start);
|
|
|
|
if (!StringMatchA (Start, FullPath)) {
|
|
CleanedUpArgOffset = StringBuf.End;
|
|
MultiSzAppendA (&StringBuf, FullPath);
|
|
} else {
|
|
CleanedUpArgOffset = OriginalArgOffset;
|
|
}
|
|
|
|
i = j;
|
|
GoodFileFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
}
|
|
|
|
if (!GoodFileFound) {
|
|
//
|
|
// If a wack is in the path, then we could have a relative path, an arg, or
|
|
// a full path to a non-existent file.
|
|
//
|
|
|
|
if (_mbschr (Start, '\\')) {
|
|
#ifdef DEBUG
|
|
j = i + 1;
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
DEBUGMSGA ((
|
|
DBG_VERBOSE,
|
|
"%s is a non-existent path spec, a relative path, or an arg",
|
|
Start
|
|
));
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
//
|
|
// The string at Start did not contain a full path; try using
|
|
// SearchPath.
|
|
//
|
|
|
|
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
FullPath = Start;
|
|
|
|
//
|
|
// Remove quotes; continue in the loop if it has no terminating quotes
|
|
//
|
|
|
|
Quoted = FALSE;
|
|
if (_mbsnextc (Start) == '\"') {
|
|
|
|
StringCopyByteCountA (UnquotedPath, Start + 1, sizeof (UnquotedPath));
|
|
q = _mbschr (UnquotedPath, '\"');
|
|
|
|
if (q) {
|
|
*q = 0;
|
|
FullPath = UnquotedPath;
|
|
Quoted = TRUE;
|
|
} else {
|
|
FullPath = NULL;
|
|
}
|
|
}
|
|
|
|
if (FullPath && *FullPath) {
|
|
if (SearchPathA (
|
|
NULL,
|
|
FullPath,
|
|
NULL,
|
|
sizeof (Path) / sizeof (Path[0]),
|
|
Path,
|
|
&DontCare
|
|
)) {
|
|
|
|
FullPath = Path;
|
|
|
|
} else if (i < 0) {
|
|
//
|
|
// Try appending .exe and searching the path again
|
|
//
|
|
|
|
StringCopyByteCountA (
|
|
FixedFileName,
|
|
FullPath,
|
|
sizeof (FixedFileName) - sizeof (".exe")
|
|
);
|
|
|
|
q = GetEndOfStringA (FixedFileName);
|
|
q = _mbsdec (FixedFileName, q);
|
|
MYASSERT (q);
|
|
|
|
if (_mbsnextc (q) != '.') {
|
|
q = _mbsinc (q);
|
|
}
|
|
|
|
StringCopyA (q, ".exe");
|
|
|
|
if (SearchPathA (
|
|
NULL,
|
|
FixedFileName,
|
|
NULL,
|
|
sizeof (Path) / sizeof (Path[0]),
|
|
Path,
|
|
&DontCare
|
|
)) {
|
|
|
|
FullPath = Path;
|
|
|
|
} else {
|
|
|
|
FullPath = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
FullPath = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
if (FullPath && *FullPath) {
|
|
Attribs = GetFileAttributesA (FullPath);
|
|
MYASSERT (Attribs != INVALID_ATTRIBUTES);
|
|
|
|
OriginalArgOffset = StringBuf.End;
|
|
MultiSzAppendA (&StringBuf, Start);
|
|
|
|
if (!StringMatchA (Start, FullPath)) {
|
|
CleanedUpArgOffset = StringBuf.End;
|
|
MultiSzAppendA (&StringBuf, FullPath);
|
|
} else {
|
|
CleanedUpArgOffset = OriginalArgOffset;
|
|
}
|
|
|
|
i = j;
|
|
GoodFileFound = TRUE;
|
|
}
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CmdLineTable->ArgCount += 1;
|
|
CmdLineArg = (PCMDLINEARGA) GrowBuffer (Buffer, sizeof (CMDLINEARGA));
|
|
MYASSERT (CmdLineArg);
|
|
|
|
if (GoodFileFound) {
|
|
//
|
|
// We have a good full file spec in FullPath, its attributes
|
|
// are in Attribs, and i has been moved to the space beyond
|
|
// the path. We now add a table entry.
|
|
//
|
|
|
|
CmdLineArg->OriginalArg = (PCSTR) OriginalArgOffset;
|
|
CmdLineArg->CleanedUpArg = (PCSTR) CleanedUpArgOffset;
|
|
CmdLineArg->Attributes = Attribs;
|
|
CmdLineArg->Quoted = Quoted;
|
|
|
|
if (!EndOfFirstArg) {
|
|
StringCopyByteCountA (FirstArgPath, (PCSTR) (StringBuf.Buf + (UINT) CmdLineArg->CleanedUpArg), sizeof (FirstArgPath));
|
|
q = (PSTR) GetFileNameFromPathA (FirstArgPath);
|
|
if (q) {
|
|
q = _mbsdec (FirstArgPath, q);
|
|
if (q) {
|
|
*q = 0;
|
|
}
|
|
}
|
|
|
|
EndOfFirstArg = AppendWackA (FirstArgPath);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We do not have a good file spec; we must have a non-file
|
|
// argument. Put it in the table, and advance to the next
|
|
// arg.
|
|
//
|
|
|
|
j = i + 1;
|
|
if (j <= Count) {
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
CmdLineArg->OriginalArg = (PCSTR) StringBuf.End;
|
|
MultiSzAppendA (&StringBuf, Start);
|
|
|
|
Quoted = FALSE;
|
|
|
|
if (_mbschr (Start, '\"')) {
|
|
|
|
p = Start;
|
|
q = UnquotedPath;
|
|
End = (PSTR) ((PBYTE) UnquotedPath + sizeof (UnquotedPath) - sizeof (CHAR));
|
|
|
|
while (*p && q < End) {
|
|
if (IsLeadByte (*p)) {
|
|
*q++ = *p++;
|
|
*q++ = *p++;
|
|
} else {
|
|
if (*p == '\"') {
|
|
p++;
|
|
} else {
|
|
*q++ = *p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
*q = 0;
|
|
|
|
CmdLineArg->CleanedUpArg = (PCSTR) StringBuf.End;
|
|
MultiSzAppendA (&StringBuf, UnquotedPath);
|
|
Quoted = TRUE;
|
|
|
|
} else {
|
|
CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
|
|
}
|
|
|
|
CmdLineArg->Attributes = INVALID_ATTRIBUTES;
|
|
CmdLineArg->Quoted = Quoted;
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
|
|
i = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now have a command line table; transfer StringBuf to Buffer, then
|
|
// convert all offsets into pointers.
|
|
//
|
|
|
|
MYASSERT (StringBuf.End);
|
|
|
|
CopyBuf = GrowBuffer (Buffer, StringBuf.End);
|
|
MYASSERT (CopyBuf);
|
|
|
|
Base = (UINT) CopyBuf;
|
|
CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
|
|
|
|
CmdLineTable->CmdLine = (PCSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
|
|
|
|
CmdLineArg = &CmdLineTable->Args[0];
|
|
|
|
for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
|
|
CmdLineArg->OriginalArg = (PCSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
|
|
CmdLineArg->CleanedUpArg = (PCSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
|
|
|
|
CmdLineArg++;
|
|
}
|
|
|
|
FreeGrowBuffer (&StringBuf);
|
|
FreeGrowBuffer (&SpacePtrs);
|
|
|
|
return (PCMDLINEA) Buffer->Buf;
|
|
}
|
|
|
|
|
|
PCMDLINEW
|
|
ParseCmdLineW (
|
|
IN PCWSTR CmdLine,
|
|
IN OUT PGROWBUFFER Buffer
|
|
)
|
|
{
|
|
GROWBUFFER SpacePtrs = GROWBUF_INIT;
|
|
PCWSTR p;
|
|
PWSTR q;
|
|
INT Count;
|
|
INT i;
|
|
INT j;
|
|
PWSTR *Array;
|
|
PCWSTR Start;
|
|
WCHAR OldChar = 0;
|
|
GROWBUFFER StringBuf = GROWBUF_INIT;
|
|
PBYTE CopyBuf;
|
|
PCMDLINEW CmdLineTable;
|
|
PCMDLINEARGW CmdLineArg;
|
|
UINT Base;
|
|
WCHAR Path[MAX_WCHAR_PATH];
|
|
WCHAR UnquotedPath[MAX_WCHAR_PATH];
|
|
WCHAR FixedFileName[MAX_WCHAR_PATH];
|
|
PCWSTR FullPath = NULL;
|
|
DWORD Attribs = INVALID_ATTRIBUTES;
|
|
PWSTR CmdLineCopy;
|
|
BOOL Quoted;
|
|
UINT OriginalArgOffset = 0;
|
|
UINT CleanedUpArgOffset = 0;
|
|
BOOL GoodFileFound = FALSE;
|
|
PWSTR DontCare;
|
|
WCHAR FirstArgPath[MAX_MBCHAR_PATH];
|
|
PWSTR EndOfFirstArg;
|
|
BOOL QuoteMode = FALSE;
|
|
PWSTR End;
|
|
|
|
CmdLineCopy = DuplicateTextW (CmdLine);
|
|
|
|
//
|
|
// Build an array of places to break the string
|
|
//
|
|
|
|
for (p = CmdLineCopy ; *p ; p++) {
|
|
if (*p == L'\"') {
|
|
|
|
QuoteMode = !QuoteMode;
|
|
|
|
} else if (!QuoteMode && (*p == L' ' || *p == L'=')) {
|
|
|
|
//
|
|
// Remove excess spaces
|
|
//
|
|
|
|
q = (PWSTR) p + 1;
|
|
while (*q == L' ') {
|
|
q++;
|
|
}
|
|
|
|
if (q > p + 1) {
|
|
MoveMemory ((PBYTE) p + sizeof (WCHAR), q, SizeOfStringW (q));
|
|
}
|
|
|
|
GrowBufAppendDword (&SpacePtrs, (DWORD) p);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prepare the CMDLINE struct
|
|
//
|
|
|
|
CmdLineTable = (PCMDLINEW) GrowBuffer (Buffer, sizeof (CMDLINEW));
|
|
MYASSERT (CmdLineTable);
|
|
|
|
//
|
|
// NOTE: We store string offsets, then at the end resolve them
|
|
// to pointers later.
|
|
//
|
|
|
|
CmdLineTable->CmdLine = (PCWSTR) StringBuf.End;
|
|
MultiSzAppendW (&StringBuf, CmdLine);
|
|
|
|
CmdLineTable->ArgCount = 0;
|
|
|
|
//
|
|
// Now test every combination, emulating CreateProcess
|
|
//
|
|
|
|
Count = SpacePtrs.End / sizeof (DWORD);
|
|
Array = (PWSTR *) SpacePtrs.Buf;
|
|
|
|
i = -1;
|
|
EndOfFirstArg = NULL;
|
|
|
|
while (i < Count) {
|
|
|
|
GoodFileFound = FALSE;
|
|
Quoted = FALSE;
|
|
|
|
if (i >= 0) {
|
|
Start = Array[i] + 1;
|
|
} else {
|
|
Start = CmdLineCopy;
|
|
}
|
|
|
|
//
|
|
// Check for a full path at Start
|
|
//
|
|
|
|
if (*Start != L'/') {
|
|
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
FullPath = Start;
|
|
|
|
//
|
|
// Remove quotes; continue in the loop if it has no terminating quotes
|
|
//
|
|
|
|
Quoted = FALSE;
|
|
if (*Start == L'\"') {
|
|
|
|
StringCopyByteCountW (UnquotedPath, Start + 1, sizeof (UnquotedPath));
|
|
q = wcschr (UnquotedPath, L'\"');
|
|
|
|
if (q) {
|
|
*q = 0;
|
|
FullPath = UnquotedPath;
|
|
Quoted = TRUE;
|
|
} else {
|
|
FullPath = NULL;
|
|
}
|
|
}
|
|
|
|
if (FullPath && *FullPath) {
|
|
//
|
|
// Look in file system for the path
|
|
//
|
|
|
|
Attribs = GetLongPathAttributesW (FullPath);
|
|
|
|
if (Attribs == INVALID_ATTRIBUTES && EndOfFirstArg) {
|
|
//
|
|
// Try prefixing the path with the first arg's path.
|
|
//
|
|
|
|
StringCopyByteCountW (
|
|
EndOfFirstArg,
|
|
FullPath,
|
|
sizeof (FirstArgPath) - ((PBYTE) EndOfFirstArg - (PBYTE) FirstArgPath)
|
|
);
|
|
|
|
FullPath = FirstArgPath;
|
|
Attribs = GetLongPathAttributesW (FullPath);
|
|
}
|
|
|
|
if (Attribs == INVALID_ATTRIBUTES && i < 0) {
|
|
//
|
|
// Try appending .exe, then testing again. This
|
|
// emulates what CreateProcess does.
|
|
//
|
|
|
|
StringCopyByteCountW (
|
|
FixedFileName,
|
|
FullPath,
|
|
sizeof (FixedFileName) - sizeof (L".exe")
|
|
);
|
|
|
|
q = GetEndOfStringW (FixedFileName);
|
|
q--;
|
|
MYASSERT (q >= FixedFileName);
|
|
|
|
if (*q != L'.') {
|
|
q++;
|
|
}
|
|
|
|
StringCopyW (q, L".exe");
|
|
|
|
FullPath = FixedFileName;
|
|
Attribs = GetLongPathAttributesW (FullPath);
|
|
}
|
|
|
|
if (Attribs != INVALID_ATTRIBUTES) {
|
|
//
|
|
// Full file path found. Test its file status, then
|
|
// move on if there are no important operations on it.
|
|
//
|
|
|
|
OriginalArgOffset = StringBuf.End;
|
|
MultiSzAppendW (&StringBuf, Start);
|
|
|
|
if (!StringMatchW (Start, FullPath)) {
|
|
CleanedUpArgOffset = StringBuf.End;
|
|
MultiSzAppendW (&StringBuf, FullPath);
|
|
} else {
|
|
CleanedUpArgOffset = OriginalArgOffset;
|
|
}
|
|
|
|
i = j;
|
|
GoodFileFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
}
|
|
|
|
if (!GoodFileFound) {
|
|
//
|
|
// If a wack is in the path, then we could have a relative path, an arg, or
|
|
// a full path to a non-existent file.
|
|
//
|
|
|
|
if (wcschr (Start, L'\\')) {
|
|
|
|
#ifdef DEBUG
|
|
j = i + 1;
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
DEBUGMSGW ((
|
|
DBG_VERBOSE,
|
|
"%s is a non-existent path spec, a relative path, or an arg",
|
|
Start
|
|
));
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
//
|
|
// The string at Start did not contain a full path; try using
|
|
// SearchPath.
|
|
//
|
|
|
|
for (j = i + 1 ; j <= Count && !GoodFileFound ; j++) {
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
FullPath = Start;
|
|
|
|
//
|
|
// Remove quotes; continue in the loop if it has no terminating quotes
|
|
//
|
|
|
|
Quoted = FALSE;
|
|
if (*Start == L'\"') {
|
|
|
|
StringCopyByteCountW (UnquotedPath, Start + 1, sizeof (UnquotedPath));
|
|
q = wcschr (UnquotedPath, L'\"');
|
|
|
|
if (q) {
|
|
*q = 0;
|
|
FullPath = UnquotedPath;
|
|
Quoted = TRUE;
|
|
} else {
|
|
FullPath = NULL;
|
|
}
|
|
}
|
|
|
|
if (FullPath && *FullPath) {
|
|
if (SearchPathW (
|
|
NULL,
|
|
FullPath,
|
|
NULL,
|
|
sizeof (Path) / sizeof (Path[0]),
|
|
Path,
|
|
&DontCare
|
|
)) {
|
|
|
|
FullPath = Path;
|
|
|
|
} else if (i < 0) {
|
|
//
|
|
// Try appending .exe and searching the path again
|
|
//
|
|
|
|
StringCopyByteCountW (
|
|
FixedFileName,
|
|
FullPath,
|
|
sizeof (FixedFileName) - sizeof (L".exe")
|
|
);
|
|
|
|
q = GetEndOfStringW (FixedFileName);
|
|
q--;
|
|
MYASSERT (q >= FixedFileName);
|
|
|
|
if (*q != L'.') {
|
|
q++;
|
|
}
|
|
|
|
StringCopyW (q, L".exe");
|
|
|
|
if (SearchPathW (
|
|
NULL,
|
|
FixedFileName,
|
|
NULL,
|
|
sizeof (Path) / sizeof (Path[0]),
|
|
Path,
|
|
&DontCare
|
|
)) {
|
|
|
|
FullPath = Path;
|
|
|
|
} else {
|
|
|
|
FullPath = NULL;
|
|
|
|
}
|
|
} else {
|
|
|
|
FullPath = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
if (FullPath && *FullPath) {
|
|
Attribs = GetLongPathAttributesW (FullPath);
|
|
MYASSERT (Attribs != INVALID_ATTRIBUTES);
|
|
|
|
OriginalArgOffset = StringBuf.End;
|
|
MultiSzAppendW (&StringBuf, Start);
|
|
|
|
if (!StringMatchW (Start, FullPath)) {
|
|
CleanedUpArgOffset = StringBuf.End;
|
|
MultiSzAppendW (&StringBuf, FullPath);
|
|
} else {
|
|
CleanedUpArgOffset = OriginalArgOffset;
|
|
}
|
|
|
|
i = j;
|
|
GoodFileFound = TRUE;
|
|
}
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CmdLineTable->ArgCount += 1;
|
|
CmdLineArg = (PCMDLINEARGW) GrowBuffer (Buffer, sizeof (CMDLINEARGW));
|
|
MYASSERT (CmdLineArg);
|
|
|
|
if (GoodFileFound) {
|
|
//
|
|
// We have a good full file spec in FullPath, its attributes
|
|
// are in Attribs, and i has been moved to the space beyond
|
|
// the path. We now add a table entry.
|
|
//
|
|
|
|
CmdLineArg->OriginalArg = (PCWSTR) OriginalArgOffset;
|
|
CmdLineArg->CleanedUpArg = (PCWSTR) CleanedUpArgOffset;
|
|
CmdLineArg->Attributes = Attribs;
|
|
CmdLineArg->Quoted = Quoted;
|
|
|
|
if (!EndOfFirstArg) {
|
|
StringCopyByteCountW (FirstArgPath, (PCWSTR) (StringBuf.Buf + (UINT) CmdLineArg->CleanedUpArg), sizeof (FirstArgPath));
|
|
q = (PWSTR) GetFileNameFromPathW (FirstArgPath);
|
|
if (q) {
|
|
q--;
|
|
if (q >= FirstArgPath) {
|
|
*q = 0;
|
|
}
|
|
}
|
|
|
|
EndOfFirstArg = AppendWackW (FirstArgPath);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We do not have a good file spec; we must have a non-file
|
|
// argument. Put it in the table, and advance to the next
|
|
// arg.
|
|
//
|
|
|
|
j = i + 1;
|
|
if (j <= Count) {
|
|
|
|
if (j < Count) {
|
|
OldChar = *Array[j];
|
|
*Array[j] = 0;
|
|
}
|
|
|
|
CmdLineArg->OriginalArg = (PCWSTR) StringBuf.End;
|
|
MultiSzAppendW (&StringBuf, Start);
|
|
|
|
Quoted = FALSE;
|
|
if (wcschr (Start, '\"')) {
|
|
|
|
p = Start;
|
|
q = UnquotedPath;
|
|
End = (PWSTR) ((PBYTE) UnquotedPath + sizeof (UnquotedPath) - sizeof (WCHAR));
|
|
|
|
while (*p && q < End) {
|
|
if (*p == L'\"') {
|
|
p++;
|
|
} else {
|
|
*q++ = *p++;
|
|
}
|
|
}
|
|
|
|
*q = 0;
|
|
|
|
CmdLineArg->CleanedUpArg = (PCWSTR) StringBuf.End;
|
|
MultiSzAppendW (&StringBuf, UnquotedPath);
|
|
Quoted = TRUE;
|
|
|
|
} else {
|
|
CmdLineArg->CleanedUpArg = CmdLineArg->OriginalArg;
|
|
}
|
|
|
|
CmdLineArg->Attributes = INVALID_ATTRIBUTES;
|
|
CmdLineArg->Quoted = Quoted;
|
|
|
|
if (j < Count) {
|
|
*Array[j] = OldChar;
|
|
}
|
|
|
|
i = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We now have a command line table; transfer StringBuf to Buffer, then
|
|
// convert all offsets into pointers.
|
|
//
|
|
|
|
MYASSERT (StringBuf.End);
|
|
|
|
CopyBuf = GrowBuffer (Buffer, StringBuf.End);
|
|
MYASSERT (CopyBuf);
|
|
|
|
Base = (UINT) CopyBuf;
|
|
CopyMemory (CopyBuf, StringBuf.Buf, StringBuf.End);
|
|
|
|
CmdLineTable->CmdLine = (PCWSTR) ((PBYTE) CmdLineTable->CmdLine + Base);
|
|
|
|
CmdLineArg = &CmdLineTable->Args[0];
|
|
|
|
for (i = 0 ; i < (INT) CmdLineTable->ArgCount ; i++) {
|
|
CmdLineArg->OriginalArg = (PCWSTR) ((PBYTE) CmdLineArg->OriginalArg + Base);
|
|
CmdLineArg->CleanedUpArg = (PCWSTR) ((PBYTE) CmdLineArg->CleanedUpArg + Base);
|
|
|
|
CmdLineArg++;
|
|
}
|
|
|
|
FreeGrowBuffer (&StringBuf);
|
|
FreeGrowBuffer (&SpacePtrs);
|
|
|
|
return (PCMDLINEW) Buffer->Buf;
|
|
}
|
|
|
|
BOOL
|
|
GetFileSizeFromFilePathA(
|
|
IN PCSTR FilePath,
|
|
OUT ULARGE_INTEGER * FileSize
|
|
)
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA fileDataAttributes;
|
|
|
|
if(!FilePath || !FileSize){
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!IsPathOnFixedDriveA (FilePath)) {
|
|
FileSize->QuadPart = 0;
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if(!GetFileAttributesExA(FilePath, GetFileExInfoStandard, &fileDataAttributes) ||
|
|
fileDataAttributes.dwFileAttributes == INVALID_ATTRIBUTES ||
|
|
(fileDataAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
FileSize->LowPart = fileDataAttributes.nFileSizeLow;
|
|
FileSize->HighPart = fileDataAttributes.nFileSizeHigh;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
GetFileSizeFromFilePathW(
|
|
IN PCWSTR FilePath,
|
|
OUT ULARGE_INTEGER * FileSize
|
|
)
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA fileDataAttributes;
|
|
|
|
if(!FilePath || !FileSize){
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!IsPathOnFixedDriveW (FilePath)) {
|
|
FileSize->QuadPart = 0;
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
if(!GetFileAttributesExW(FilePath, GetFileExInfoStandard, &fileDataAttributes) ||
|
|
fileDataAttributes.dwFileAttributes == INVALID_ATTRIBUTES ||
|
|
(fileDataAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
|
|
MYASSERT(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
FileSize->LowPart = fileDataAttributes.nFileSizeLow;
|
|
FileSize->HighPart = fileDataAttributes.nFileSizeHigh;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
InitializeDriveLetterStructureA (
|
|
OUT PDRIVELETTERSA DriveLetters
|
|
)
|
|
{
|
|
BYTE bitPosition;
|
|
DWORD maxBitPosition = NUMDRIVELETTERS;
|
|
CHAR rootPath[] = "?:\\";
|
|
BOOL driveExists;
|
|
UINT type;
|
|
|
|
//
|
|
// GetLogicalDrives returns a bitmask of all of the drive letters
|
|
// in use on the system. (i.e. bit position 0 is turned on if there is
|
|
// an 'A' drive, 1 is turned on if there is a 'B' drive, etc.
|
|
// This loop will use this bitmask to fill in the global drive
|
|
// letters structure with information about what drive letters
|
|
// are available and what there drive types are.
|
|
//
|
|
|
|
for (bitPosition = 0; bitPosition < maxBitPosition; bitPosition++) {
|
|
|
|
//
|
|
// Initialize this drive
|
|
//
|
|
|
|
DriveLetters->ExistsOnSystem[bitPosition] = FALSE;
|
|
DriveLetters->Type[bitPosition] = 0;
|
|
DriveLetters->IdentifierString[bitPosition][0] = 0;
|
|
|
|
rootPath[0] = 'A' + bitPosition;
|
|
DriveLetters->Letter[bitPosition] = rootPath[0];
|
|
|
|
//
|
|
// Determine if there is a drive in this spot.
|
|
//
|
|
driveExists = GetLogicalDrives() & (1 << bitPosition);
|
|
|
|
if (driveExists) {
|
|
|
|
//
|
|
// There is. Now, see if it is one that we care about.
|
|
//
|
|
type = GetDriveTypeA(rootPath);
|
|
|
|
if (type == DRIVE_FIXED || type == DRIVE_REMOVABLE || type == DRIVE_CDROM) {
|
|
|
|
//
|
|
// This is a drive that we are interested in.
|
|
//
|
|
DriveLetters->ExistsOnSystem[bitPosition] = TRUE;
|
|
DriveLetters->Type[bitPosition] = type;
|
|
|
|
//
|
|
// Identifier String is not filled in this function.
|
|
//
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
InitializeDriveLetterStructureW (
|
|
OUT PDRIVELETTERSW DriveLetters
|
|
)
|
|
{
|
|
BYTE bitPosition;
|
|
DWORD maxBitPosition = NUMDRIVELETTERS;
|
|
WCHAR rootPath[] = L"?:\\";
|
|
BOOL driveExists;
|
|
UINT type;
|
|
|
|
//
|
|
// GetLogicalDrives returns a bitmask of all of the drive letters
|
|
// in use on the system. (i.e. bit position 0 is turned on if there is
|
|
// an 'A' drive, 1 is turned on if there is a 'B' drive, etc.
|
|
// This loop will use this bitmask to fill in the global drive
|
|
// letters structure with information about what drive letters
|
|
// are available and what there drive types are.
|
|
//
|
|
|
|
for (bitPosition = 0; bitPosition < maxBitPosition; bitPosition++) {
|
|
|
|
//
|
|
// Initialize this drive
|
|
//
|
|
|
|
DriveLetters->ExistsOnSystem[bitPosition] = FALSE;
|
|
DriveLetters->Type[bitPosition] = 0;
|
|
DriveLetters->IdentifierString[bitPosition][0] = 0;
|
|
|
|
rootPath[0] = L'A' + bitPosition;
|
|
DriveLetters->Letter[bitPosition] = rootPath[0];
|
|
|
|
//
|
|
// Determine if there is a drive in this spot.
|
|
//
|
|
driveExists = GetLogicalDrives() & (1 << bitPosition);
|
|
|
|
if (driveExists) {
|
|
|
|
//
|
|
// There is. Now, see if it is one that we care about.
|
|
//
|
|
type = GetDriveTypeW(rootPath);
|
|
|
|
if (type == DRIVE_FIXED || type == DRIVE_REMOVABLE || type == DRIVE_CDROM) {
|
|
|
|
//
|
|
// This is a drive that we are interested in.
|
|
//
|
|
DriveLetters->ExistsOnSystem[bitPosition] = TRUE;
|
|
DriveLetters->Type[bitPosition] = type;
|
|
|
|
//
|
|
// Identifier String is not filled in this function.
|
|
//
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
GetDiskFreeSpaceNewA(
|
|
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.
|
|
GetDiskFreeSpaceNew 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)){
|
|
DEBUGMSG((DBG_ERROR,"GetDiskFreeSpaceNewA: GetDiskFreeSpaceA failed on drive %s", 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{
|
|
DEBUGMSG((DBG_WARNING,
|
|
pGetDiskFreeSpaceExA?
|
|
"GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA is failed":
|
|
"GetDiskFreeSpaceNewA: GetDiskFreeSpaceExA function is not in kernel32.dll"));
|
|
}
|
|
FreeLibrary(hKernel32);
|
|
|
|
if(OutSectorsPerCluster){
|
|
*OutSectorsPerCluster = SectorsPerCluster;
|
|
}
|
|
|
|
if(OutBytesPerSector){
|
|
*OutBytesPerSector = BytesPerSector;
|
|
}
|
|
|
|
if(OutNumberOfFreeClusters){
|
|
OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
|
|
}
|
|
|
|
if(OutTotalNumberOfClusters){
|
|
OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
|
|
}
|
|
|
|
DEBUGMSG((DBG_VERBOSE,
|
|
"GetDiskFreeSpaceNewA: \n\t"
|
|
"SectorsPerCluster = %d\n\t"
|
|
"BytesPerSector = %d\n\t"
|
|
"NumberOfFreeClusters = %I64u\n\t"
|
|
"TotalNumberOfClusters = %I64u",
|
|
SectorsPerCluster,
|
|
BytesPerSector,
|
|
NumberOfFreeClusters.QuadPart,
|
|
TotalNumberOfClusters.QuadPart));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
GetDiskFreeSpaceNewW(
|
|
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)){
|
|
DEBUGMSG((DBG_ERROR,"GetDiskFreeSpaceNewW: GetDiskFreeSpaceW failed on drive %s", 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{
|
|
DEBUGMSG((DBG_WARNING,
|
|
pGetDiskFreeSpaceExW?
|
|
"GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW is failed":
|
|
"GetDiskFreeSpaceNewW: GetDiskFreeSpaceExW function is not in kernel32.dll"));
|
|
}
|
|
FreeLibrary(hKernel32);
|
|
|
|
if(OutSectorsPerCluster){
|
|
*OutSectorsPerCluster = SectorsPerCluster;
|
|
}
|
|
|
|
if(OutBytesPerSector){
|
|
*OutBytesPerSector = BytesPerSector;
|
|
}
|
|
|
|
if(OutNumberOfFreeClusters){
|
|
OutNumberOfFreeClusters->QuadPart = NumberOfFreeClusters.QuadPart;
|
|
}
|
|
|
|
if(OutTotalNumberOfClusters){
|
|
OutTotalNumberOfClusters->QuadPart = TotalNumberOfClusters.QuadPart;
|
|
}
|
|
|
|
DEBUGMSG((DBG_VERBOSE,
|
|
"GetDiskFreeSpaceNewW: \n\t"
|
|
"SectorsPerCluster = %d\n\t"
|
|
"BytesPerSector = %d\n\t"
|
|
"NumberOfFreeClusters = %I64u\n\t"
|
|
"TotalNumberOfClusters = %I64u",
|
|
SectorsPerCluster,
|
|
BytesPerSector,
|
|
NumberOfFreeClusters.QuadPart,
|
|
TotalNumberOfClusters.QuadPart));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
QuietGetFileAttributesA (
|
|
IN PCSTR FilePath
|
|
)
|
|
{
|
|
if (!IsPathOnFixedDriveA (FilePath)) {
|
|
return INVALID_ATTRIBUTES;
|
|
}
|
|
|
|
return GetFileAttributesA (FilePath);
|
|
}
|
|
|
|
DWORD
|
|
QuietGetFileAttributesW (
|
|
IN PCWSTR FilePath
|
|
)
|
|
{
|
|
MYASSERT (ISNT());
|
|
|
|
if (!IsPathOnFixedDriveW (FilePath)) {
|
|
return INVALID_ATTRIBUTES;
|
|
}
|
|
|
|
return GetLongPathAttributesW (FilePath);
|
|
}
|
|
|
|
|
|
DWORD
|
|
MakeSureLongPathExistsW (
|
|
IN PCWSTR Path,
|
|
IN BOOL PathOnly
|
|
)
|
|
{
|
|
PCWSTR tmp;
|
|
DWORD result;
|
|
|
|
if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
|
|
result = MakeSurePathExistsW (Path, PathOnly);
|
|
} else {
|
|
tmp = JoinPathsW (L"\\\\?", Path);
|
|
result = MakeSurePathExistsW (tmp, PathOnly);
|
|
FreePathStringW (tmp);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SetLongPathAttributesW (
|
|
IN PCWSTR Path,
|
|
IN DWORD Attributes
|
|
)
|
|
{
|
|
PCWSTR tmp;
|
|
DWORD result;
|
|
|
|
if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
|
|
result = SetFileAttributesW (Path, Attributes);
|
|
} else {
|
|
tmp = JoinPathsW (L"\\\\?", Path);
|
|
result = SetFileAttributesW (tmp, Attributes);
|
|
FreePathStringW (tmp);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetLongPathAttributesW (
|
|
IN PCWSTR Path
|
|
)
|
|
{
|
|
PCWSTR tmp;
|
|
DWORD result;
|
|
|
|
if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
|
|
result = GetFileAttributesW (Path);
|
|
} else {
|
|
tmp = JoinPathsW (L"\\\\?", Path);
|
|
result = GetFileAttributesW (tmp);
|
|
FreePathStringW (tmp);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DeleteLongPathW (
|
|
IN PCWSTR Path
|
|
)
|
|
{
|
|
PCWSTR tmp;
|
|
BOOL result;
|
|
|
|
if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
|
|
result = DeleteFileW (Path);
|
|
} else {
|
|
tmp = JoinPathsW (L"\\\\?", Path);
|
|
result = DeleteFileW (tmp);
|
|
FreePathStringW (tmp);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
BOOL
|
|
RemoveLongDirectoryPathW (
|
|
IN PCWSTR Path
|
|
)
|
|
{
|
|
PCWSTR tmp;
|
|
BOOL result;
|
|
|
|
if (Path[0] == L'\\' || TcharCountW (Path) < MAX_PATH) {
|
|
result = RemoveDirectoryW (Path);
|
|
} else {
|
|
tmp = JoinPathsW (L"\\\\?", Path);
|
|
result = RemoveDirectoryW (tmp);
|
|
FreePathStringW (tmp);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|