NT4/private/net/otnboot/dirutils.c
2020-09-30 17:12:29 +02:00

1868 lines
53 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
DirUtils.C
Abstract:
directory, file, filename and network utility functions.
Author:
Bob Watson (a-robw)
Revision History:
24 Jun 94 Written
--*/
//
// Windows Include Files
//
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <tchar.h> // unicode macros
#include <stdlib.h> // string to number conversions
#include <lmcons.h> // lanman API constants
#include <lmerr.h> // lanman error returns
#include <lmshare.h> // sharing API prototypes
#include <lmapibuf.h> // lanman buffer API prototypes
#include <commdlg.h> // common dialog include
//
// app include files
//
#include "otnboot.h"
#include "otnbtdlg.h"
LPCTSTR
GetRootFromPath (
IN LPCTSTR szPath
)
{
static TCHAR szReturnBuffer[MAX_PATH];
LPTSTR szNextChar;
LONG lIndex;
if (IsUncPath(szPath)) {
// return server & share
lstrcpy (szReturnBuffer, cszDoubleBackslash);
szNextChar = &szReturnBuffer[2];
if (GetServerFromUnc (szPath, szNextChar)) {
lstrcat (szReturnBuffer, cszBackslash);
szNextChar = &szReturnBuffer[lstrlen(szReturnBuffer)];
if (GetShareFromUnc(szPath, szNextChar)) {
// append trailing backslash
lstrcat (szReturnBuffer, cszBackslash);
} else {
szReturnBuffer[0] = 0;
}
} else {
szReturnBuffer[0] = 0;
}
} else {
// dos path name
for (lIndex = 0; lIndex < 3; lIndex++) {
if (szPath[lIndex] > cSpace) {
szReturnBuffer[lIndex] = szPath[lIndex];
} else {
break;
}
}
szReturnBuffer[lIndex] = 0;
}
return (LPCTSTR)&szReturnBuffer[0];
}
BOOL
GetShareFromUnc (
IN LPCTSTR szPath,
OUT LPTSTR szShare
)
/*++
Routine Description:
Parses a UNC path name and returns the Sharepoint name in the buffer
supplied by the caller. The buffer size is not checked and is
assumed to be at least (MAX_SHARENAME+1) characters
long.
Arguments:
IN LPCTSTR szPath UNC path name to parse
OUT LPTSTR szShare buffer supplied by caller to receive the server
Return Value:
TRUE if a sharename is returned, otherwise...
FALSE if unable to parse out a share name
--*/
{
int nSlashCount = 0; // count of backslashes found in string
LPTSTR szPathPtr; // pointer to char in szPath
LPTSTR szSharePtr; // pointer to char in szShare
szPathPtr = (LPTSTR)szPath;
szSharePtr = szShare;
if (IsUncPath(szPath)) {
while (*szPathPtr != 0) {
if (nSlashCount < 3) {
if (*szPathPtr++ == cBackslash) nSlashCount++;
} else {
if (*szPathPtr == cBackslash) {
szPathPtr++;
break;
} else {
*szSharePtr++ = *szPathPtr++;
}
}
}
}
*szSharePtr = 0; // terminate share name
if (szSharePtr == szShare) {
return FALSE;
} else {
return TRUE;
}
}
BOOL
GetNetPathInfo (
IN LPCTSTR szPath,
OUT LPTSTR szServer,
OUT LPTSTR szRemotePath
)
/*++
Routine Description:
Processes a path and returns the machine the files are on and the
local path on that machine. UNC, Redirected and local paths may
be processed by this routine. On remote machine paths (e.g.
redir'd dos paths and UNC paths on another machine) the shared
path on the remote machine is returned if there user has sufficient
permission to access that information. On local paths, the
local computer name and root path is returned.
Arguments:
IN LPCTSTR szPath
the input path to analyze.
OUT LPTSTR szServer
the server/computername that the path references. The buffer size
is NOT checked and must be at least MAX_COMPUTERNAME_LENGTH+1 char's
long.
OUT LPTSTR szRemotePath
the path on the "szServer" machine that corresponds to the
input path. The buffer size is NOT checked and must be at least
MAX_PATH + 1 characters long.
Return Value:
TRUE if a path was processed and data returned
FALSE if an error occurred or unable to process the path
--*/
{
DWORD dwBufLen; // buffer length value used in fn. calls
DWORD dwStatus; // status value returned by fn. calls
TCHAR szShareName[NNLEN+1]; // net share name from path
LPTSTR szUncPath; // buffer to get path from redir'd drive
TCHAR szDrive[4]; // buffer to build drive string in
LPTSTR szSubDirs; // pointer to start of dirs not in share
LONG lBsCount; // count of backslashes found parsing UNC
BOOL bReturn = FALSE; // return valie
PSHARE_INFO_2 psi2Data; // pointer to Net data buffer
// check args
if ((szServer == NULL) || (szRemotePath == NULL)) {
// an invalid argument was passed
return FALSE;
}
szUncPath = GlobalAlloc (GPTR, MAX_PATH_BYTES);
if (szUncPath == NULL) {
*szServer = 0;
*szRemotePath = 0;
return FALSE;
}
// args ok, so take a look at the path passed in to process
if (IsUncPath(szPath)) {
// extract the server from the path string
if (GetServerFromUnc(szPath, szServer)) {
// that worked, so extract the share name.
if (GetShareFromUnc(szPath, szShareName)) {
// find end of UNC so sub dirs may be appended to final path
for (szSubDirs = (LPTSTR)szPath, lBsCount = 0;
(lBsCount < 4) && (*szSubDirs != 0);
szSubDirs++) {
if (*szSubDirs == cBackslash) lBsCount++;
}
// now look up the share on the server to get the
// source path on the server machine
if (NetShareGetInfo (szServer, (LPWSTR)szShareName, 2L, (LPBYTE *)&psi2Data) == NERR_Success) {
// successful call so copy the data to the user's buffer
lstrcpy (szRemotePath, psi2Data->shi2_path);
NetApiBufferFree (psi2Data);
// add this only if there's a subdir to append
if (*szSubDirs != 0) {
if (szRemotePath[lstrlen(szRemotePath)-1] != cBackslash)
lstrcat(szRemotePath, cszBackslash);
lstrcat (szRemotePath, szSubDirs);
}
bReturn = TRUE;
} else {
// unable to get path on server
*szRemotePath = 0;
bReturn = FALSE;
}
} else {
// unable to get parse share name
*szRemotePath = 0;
bReturn = FALSE;
}
} else {
// unable to parse server name
*szServer = 0;
*szRemotePath = 0;
bReturn = FALSE;
}
} else {
// it's a dos pathname so see if it's a redirected drive
if (OnRemoteDrive(szPath)) {
// yes it's been redirected so look up the information
// get drive letter of redirected drive
szDrive[0] = szPath[0];
szDrive[1] = szPath[1];
szDrive[2] = 0;
szSubDirs = (LPTSTR)&szPath[3];
dwBufLen = MAX_PATH;
dwStatus = WNetGetConnection (
szDrive,
szUncPath,
&dwBufLen);
if (dwStatus == NO_ERROR) {
// UNC of shared dir looked up ok, now proccess that to
// find the server and path on the server
if (GetServerFromUnc(szUncPath, szServer)) {
if (GetShareFromUnc(szUncPath, szShareName)) {
// now look up the share on the server
if (NetShareGetInfo (szServer, (LPWSTR)szShareName, 2L, (LPBYTE *)&psi2Data) == NERR_Success) {
// successful call so return pointer to path string
lstrcpy (szRemotePath, psi2Data->shi2_path);
NetApiBufferFree (psi2Data);
if (*szSubDirs != 0) {
if (szRemotePath[lstrlen(szRemotePath)-1] != cBackslash)
lstrcat(szRemotePath, cszBackslash);
lstrcat (szRemotePath, szSubDirs);
}
bReturn = TRUE;
} else {
// unable to get path on server
*szRemotePath = 0;
bReturn = FALSE;
}
} else {
// unable to get parse share name
*szRemotePath = 0;
bReturn = FALSE;
}
} else {
// unable to parse server name
*szServer = 0;
*szRemotePath = 0;
bReturn = FALSE;
}
}
} else {
// this is a local path so return the local machine name
// and the path passed in
dwBufLen = MAX_COMPUTERNAME_LENGTH+1;
GetComputerName (szServer, &dwBufLen);
lstrcpy (szRemotePath, szPath);
bReturn = TRUE;
}
}
FREE_IF_ALLOC (szUncPath);
return bReturn;
}
BOOL
ComputerPresent (
IN LPCTSTR szMachine
)
/*++
Routine Description:
Try to connect to the registry on the machine name in order to
determine if the machine is available on the network.
Arguments:
IN LPCTSTR szMachine
computer name to try (must be in the form of \\machine)
Return Value:
TRUE "szMachine" is present
FALSE unable to connect to machine
--*/
{
LONG lStatus; // status of function call
HKEY hKey; // handle to registry key opened
lStatus = RegConnectRegistry (
(LPTSTR)szMachine,
HKEY_LOCAL_MACHINE,
&hKey);
if (lStatus == ERROR_SUCCESS) {
RegCloseKey (hKey);
return TRUE;
} else {
return FALSE;
}
}
BOOL
GetServerFromUnc (
IN LPCTSTR szPath,
OUT LPTSTR szServer
)
/*++
Routine Description:
reads a UNC path and returns the name of the server referenced in
the buffer provided by the caller
Arguments:
IN LPCTSTR szPath
UNC path to parse
OUT LPTSTR szServer
buffer to receive name of server (must be large enough!)
Return Value:
TRUE if successful,
FALSE if not
--*/
{
LPTSTR szPathPtr; // pointer to char in szPath
LPTSTR szServPtr; // pointer to char in szServer
int nSlashCount = 0; // count of backslash chars found
szPathPtr = (LPTSTR)szPath;
szServPtr = szServer;
if (IsUncPath(szPath)) {
while (szPathPtr != 0) {
if (*szPathPtr == cBackslash) nSlashCount++;
if ((nSlashCount == 2) && (*szPathPtr != cBackslash)) {
*szServPtr++ = *szPathPtr;
} else {
if (nSlashCount == 3) break; // exit loop
}
szPathPtr++;
}
}
*szServPtr = 0; // terminate server name
if (szServPtr == szServer) {
return FALSE;
} else {
return TRUE;
}
}
BOOL
LookupLocalShare (
IN LPCTSTR szDrivePath,
IN BOOL bExactMatch,
OUT LPTSTR szLocalPath,
IN PDWORD pdwBuffLen
)
/*++
Routine Description:
Searches registry to see if a file is already shared and returns
the UNC name of the path if it is. (note this function is
satisfied by the first match it finds, it doesn't try to
find the "Best" match, just "A" match.
FIXFIX: Should this use WNetEnumResource?
Arguments:
IN LPCTSTR szDrivePath
DOS file path to look up
IN BOOL bExactMatch
TRUE find a share that matches the szDrivePath EXACTLY
FALSE find a share that has any higher path in szDrivePath
(e.g. if c:\dir is shared as XXX, then XXX would satisfy
the lookup if the path was c:\dir\subdir\etc)
OUT LPTSTR szLocalPath
buffer to receive UNC version of path if a share exists
IN PDWORD pdwBuffLen
length of buffer referenced by szLocalPath
Return Value:
TRUE if a match is found,
FALSE if not
--*/
{
HKEY hkeyShareList; // handle to registry listing shares
LONG lStatus; // called function status return value
BOOL bReturn = FALSE; // routine status to return to caller
BOOL bMatch; // true when strings match
DWORD dwIndex; // index used to increment through search
LPTSTR szValueBuffer; // value name string to examine
LPTSTR szValueName; // value value string
DWORD dwBufferSize; // buffer length variable
DWORD dwNameSize; // Name Buffer size
DWORD dwDataType; // data type read from registry
LPTSTR szThisItem; // pointer to value in enumerated list of values
LPTSTR szThisItemChar; // char in name value to compare to path
LPTSTR szThisMatchChar; // matching character found
LPTSTR szFilePath; // pointer into path buffer where filename is
TCHAR szLocalMachine[MAX_COMPUTERNAME_LENGTH + 1];
szValueName = GlobalAlloc (GPTR, MAX_PATH_BYTES);
szValueBuffer = GlobalAlloc (GPTR, (SMALL_BUFFER_SIZE * sizeof(TCHAR)));
if ((szValueName != NULL) && (szValueBuffer != NULL)) {
// read the registry on the machine the user is
// running the app on, not the server
lStatus = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
cszLanmanServerShares,
0L,
KEY_READ,
&hkeyShareList);
if (lStatus == ERROR_SUCCESS) {
// loop through all values under this key until
// a match is found or all have been examined
dwIndex = 0;
dwIndex = 0;
dwNameSize = MAX_PATH;
dwBufferSize = SMALL_BUFFER_SIZE;
dwNameSize = MAX_PATH;
dwBufferSize = SMALL_BUFFER_SIZE;
while ((RegEnumValue(hkeyShareList, dwIndex,
szValueName, &dwNameSize,
0L, &dwDataType,
(LPBYTE)szValueBuffer, &dwBufferSize) == ERROR_SUCCESS) && !bReturn) {
// value read in so scan multi-sz returned to find path entry
for (szThisItem = szValueBuffer;
*szThisItem != 0;
szThisItem += (lstrlen(szThisItem)+1)) {
// compare the current entry to the "Path" string.
// if the characters don't match then exit the loop
// after the loop, if the match char == 0 then all
// characters in the "item" matched so this is the
// Path entry. otherwise, it's not the path so go to the
// the next item.
for (szThisItemChar = szThisItem, szThisMatchChar = (LPTSTR)cszPath;
(*szThisMatchChar != 0) && (*szThisItemChar != 0);
szThisItemChar++, szThisMatchChar++) {
if (*szThisMatchChar != *szThisItemChar) break;
}
if (*szThisMatchChar == 0) {
// this is a match so see what the path that is
// shared.
while (*szThisItemChar != cEqual) szThisItemChar++;
szThisItemChar++;
// szThisItemChar now points to the path that is shared
// compare it to the path that was passed in. if it matches,
// then return the local machine and share name in a UNC
// format
if (bExactMatch) {
if (lstrcmpi(szThisItemChar, szDrivePath) == 0) {
bMatch = TRUE;
} else {
bMatch = FALSE;
}
} else {
bMatch = MatchFirst(szThisItemChar, szDrivePath);
}
if (bMatch) {
// it's a match so format return value
dwBufferSize = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerName(
szLocalMachine,
&dwBufferSize);
if (szLocalPath != NULL) {
//
// format name by replacing drive letter and colon
// with UNC format \\server\path
//
lstrcpy (szLocalPath, cszDoubleBackslash);
lstrcat (szLocalPath, szLocalMachine);
lstrcat (szLocalPath, cszBackslash);
lstrcat (szLocalPath, szValueName);
szFilePath = (LPTSTR)&szDrivePath[lstrlen(szThisItemChar)];
if (*szFilePath != cBackslash) lstrcat (szLocalPath, cszBackslash);
lstrcat (szLocalPath, szFilePath);
}
bReturn = TRUE;
}
} else {
// this is not the path entry so break and go to the
// next one.
}
}
// read next value
dwIndex++;
dwNameSize = MAX_PATH;
dwBufferSize = SMALL_BUFFER_SIZE;
}
RegCloseKey (hkeyShareList);
} else {
// unable to open registry key
bReturn = FALSE;
}
} else {
// unable to allocate memory
bReturn = FALSE;
}
FREE_IF_ALLOC (szValueName);
FREE_IF_ALLOC (szValueBuffer);
return bReturn;
}
BOOL
LookupRemotePath (
IN LPCTSTR szDrivePath,
OUT LPTSTR szRemotePath,
IN PDWORD pdwBuffLen
)
/*++
Routine Description:
Looks up a path on a remote server to see if a Dos file path is really
on a remote drive and returns the UNC path if it is.
Arguments:
IN LPCTSTR szDrivePath
DOS file path to examine
OUT LPTSTR szRemotePath
buffer to get UNC version of path if one exists
IN PDWORD pdwBuffLen
length of szRemotePath buffer in bytes
Return Value:
TRUE if match found
FALSE if no match
--*/
{
HKEY hkeyNetDrive; // handle to registry describing remote drive
LONG lStatus; // function status returned by called fn's
LPTSTR szKeyName; // buffer to build name string in
TCHAR szLocalDrive[4]; // buffer to build local drive string in
LPTSTR szFilePath; // pointer into buffer where file path goes
DWORD dwValueType; // buffer for registry query
BOOL bReturn; // return value of this function
szKeyName = GlobalAlloc (GPTR, MAX_PATH_BYTES);
if (szKeyName == NULL) return FALSE;
lstrcpy (szKeyName, cszNetDriveListKeyName);
szLocalDrive[0] = szDrivePath[0];
szLocalDrive[1] = 0;
lstrcat (szKeyName, szLocalDrive);
szFilePath = (LPTSTR)&szDrivePath[2];
lStatus = RegOpenKeyEx (
HKEY_CURRENT_USER,
szKeyName,
0L,
KEY_READ,
&hkeyNetDrive);
if (lStatus == ERROR_SUCCESS) {
lStatus = RegQueryValueEx (
hkeyNetDrive,
(LPTSTR)cszRemotePathValue,
0L,
&dwValueType,
(LPBYTE)szRemotePath,
pdwBuffLen);
RegCloseKey (hkeyNetDrive);
if (lStatus == ERROR_SUCCESS) {
//append the rest of the path and exit
lstrcat (szRemotePath, szFilePath);
bReturn = TRUE;
} else {
bReturn = FALSE;
}
} else {
// unable to find drive or open key
bReturn = FALSE;
}
FREE_IF_ALLOC (szKeyName);
return bReturn;
}
BOOL
OnRemoteDrive (
IN LPCTSTR szPath
)
/*++
Routine Description:
Returns TRUE if drive referenced is a network drive as opposed to
on on the local machine
Arguments:
IN LPCTSTR szPath
path to look up
Return Value:
TRUE if szPath references a remote drive
FALSE if szPath references a local drive
--*/
{
TCHAR szRootDir[4]; // drive string
TCHAR szServerName[MAX_COMPUTERNAME_LENGTH*2]; // server name buffer
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1]; // local machine name
DWORD dwBufLen; // buffer len. var.
if (IsUncPath(szPath)) {
if (GetServerFromUnc(szPath, szServerName)) {
dwBufLen = MAX_COMPUTERNAME_LENGTH+1;
GetComputerName (szComputerName, &dwBufLen);
if (lstrcmpi(szServerName, szComputerName) == 0) {
// shared on this machine so not remote
return FALSE;
} else {
// not on this machine so it must be remote
return TRUE;
}
}
} else {
// check the DOS path
// see if this is a network or local drive
szRootDir[0] = szPath[0]; // letter
szRootDir[1] = szPath[1]; // colon
szRootDir[2] = cBackslash;
szRootDir[3] = 0;
if (GetDriveType(szRootDir) == DRIVE_REMOTE) {
return TRUE;
} else {
return FALSE;
}
}
return FALSE;
}
DWORD
ComputeFreeSpace (
IN LPCTSTR szPath
)
/*++
Routine Description:
Function to return free space on drive referenced in path
(NOTE: assumes DOS file path specification)
Arguments:
IN LPCTSTR szPath
Path including Drive to check free space of. (MUST BE A DOS
FILE Path)
Return Value:
Free space found in BYTES
--*/
{
DWORD dwSectorsPerCluster;
DWORD dwBytesPerSector;
DWORD dwFreeClusters;
DWORD dwClusters;
TCHAR szRoot[MAX_PATH]; // root dir of path
DWORD dwFreeBytes = 0;
UINT nErrorMode;
// disable windows error message popup
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
lstrcpy (szRoot, GetRootFromPath(szPath));
if (lstrlen(szRoot) > 0) {
// we have a path to process so get info from the root
if (GetDiskFreeSpace(szRoot, &dwSectorsPerCluster,
&dwBytesPerSector, &dwFreeClusters, &dwClusters)) {
dwFreeBytes = dwFreeClusters * dwSectorsPerCluster * dwBytesPerSector;
}
} else {
dwFreeBytes = 0xFFFFFFFF;
}
SetErrorMode (nErrorMode); // restore old error mode
return dwFreeBytes;
}
BOOL
IsShareNameUsed (
IN LPCTSTR szServerName,
IN LPCTSTR szShareName,
IN OUT PDWORD pdwType,
IN OUT LPTSTR pszPath
)
/*++
Routine Description:
Looks up a share name on a sever to determine if the name is in use
and if so, what type of directory is shared.
Arguments:
IN LPCTSTR szServerName
server name to look up share name on (NULL == the local machine)
IN LPCTSTR szShareName
share name to look up
IN OUT PDWORD pdwType
buffer to return share type in options are:
NCDU_SHARE_IS_NOT_USED: name not found on server
NCDU_SHARE_IS_CLIENT_TREE: name is a client distribution tree
NCDU_SHARE_IS_SERVER_TOOLS: name is a server tool tree
NCDU_SHARE_IS_OTHER_DIR: name is shared to some other dir.
IN OUT LPTSTR pszPath
if path is shared, then this is the path on the server that
is shared
Return Value:
TRUE if share name is in use
FALSE if the share name is not found on the server.
--*/
{
LPTSTR szLocalPath; // local buffer to build path string in
LPTSTR szLocalName; // pointer into path where sharename starts
DWORD dwShareAttrib; // file attributes of share point
DWORD dwBufLen; // buffer length variable
BOOL bReturn = FALSE; // function return value
DWORD dwLocalType; // share dir type
PSHARE_INFO_2 psi2Data = NULL; // net info buffer pointer
LPTSTR szReturnPath
= (LPTSTR)cszEmptyString; // pointer to path string to return
szLocalPath = GlobalAlloc (GPTR, MAX_PATH_BYTES);
if (szLocalPath != NULL) {
// make share name into a UNC name using the appropriate machine
lstrcpy (szLocalPath, cszDoubleBackslash);
if (szServerName == NULL) {
dwBufLen = MAX_COMPUTERNAME_LENGTH + 1;
GetComputerName (&szLocalPath[2], &dwBufLen);
} else {
lstrcat (szLocalPath, szServerName);
}
lstrcat (szLocalPath, cszBackslash);
szLocalName = &szLocalPath[lstrlen(szLocalPath)];
lstrcpy (szLocalName, szShareName);
TrimSpaces (szLocalName);
// see if it's a valid path
dwShareAttrib = QuietGetFileAttributes (szLocalPath);
if (dwShareAttrib == 0xFFFFFFFF) {
// it's not a valid file or dir, so
bReturn = FALSE; // share name is not used
szReturnPath = (LPTSTR)cszEmptyString;
dwLocalType = NCDU_SHARE_IS_NOT_USED;
} else {
// it's a valid path so see if it's anything we want
bReturn = TRUE;
// terminate local path after server name for use in net API Call
*(szLocalName - 1) = 0;
// get share info
if (NetShareGetInfo ((LPWSTR)szLocalPath, (LPWSTR)szShareName,
2L, (LPBYTE *)&psi2Data) == NERR_Success) {
// successful call so return pointer to path string
szReturnPath = psi2Data->shi2_path;
} else {
// function failed so return empty path
szReturnPath = (LPTSTR)cszEmptyString;
}
// restore local path string
*(szLocalName - 1) = cBackslash;
// share is used, so see if it's a clients path
if (*szReturnPath != 0) {
if (ValidSharePath (szLocalPath) == 0) {
dwLocalType = NCDU_SHARE_IS_CLIENT_TREE;
} else if (ValidSrvToolsPath (szLocalPath) == 0) {
dwLocalType = NCDU_SHARE_IS_SERVER_TOOLS;
} else {
dwLocalType = NCDU_SHARE_IS_OTHER_DIR;
}
} else {
// no path name
bReturn = FALSE;
dwLocalType = NCDU_SHARE_IS_NOT_USED;
}
}
FREE_IF_ALLOC (szLocalPath);
} else {
// unable to allocate memory
bReturn = FALSE;
dwLocalType = 0;
}
if (pdwType != NULL) {
*pdwType = dwLocalType;
}
if (pszPath != NULL) {
lstrcpy (pszPath, szReturnPath);
}
// free buffer created by net call
if (psi2Data != NULL) NetApiBufferFree (psi2Data);
return bReturn;
}
DWORD
QuietGetFileAttributes (
IN LPCTSTR lpszFileName
)
/*++
Routine Description:
Reads the attributes of the path in lpzsFileName without triggering
any Windows error dialog if there's an OS error (e.g. drive not
ready)
Arguments:
IN LPCTSTR lpszFileName
path to retrieve attributes from
Return Value:
file attributes DWORD returned from GetFileAttributes or
0xFFFFFFFF if unable to open path.
--*/
{
DWORD dwReturn;
UINT nErrorMode;
// disable windows error message popup
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
dwReturn = GetFileAttributes (lpszFileName);
SetErrorMode (nErrorMode); // restore old error mode
return dwReturn;
}
BOOL
GetSizeOfDirs (
IN LPCTSTR szPath,
IN BOOL bFlags,
IN OUT PDWORD pdwSize
)
/*++
Routine Description:
Scans a directory and optionally a subdirectory to total the sizes
of the files found.
NOTE: This function will only process files that are < 4GB in size
and dir paths that total less then 4GB total.
Arguments:
IN LPCTSTR szPath
top dir or dir tree to scan.
IN BOOL bFlags
operation modification flags:
0 : specified path only.
GSOD_INCLUDE_SUBDIRS: include sub dirs
IN OUT PDWORD pdwSize
buffer to return size in.
Return Value:
TRUE size returned in pdwSize
FALSE error occurred
--*/
{
HANDLE hSearch;
HANDLE hFile;
WIN32_FIND_DATA fdSearch;
PDWORD pdwSizeBuffer;
DWORD dwLocalSize = 0;
DWORD dwPathAttrib;
BOOL bSearching;
LPTSTR szLocalPath;
LPTSTR szSearchPath;
LPTSTR szLocalFileName;
DWORD dwFileSizeLow;
DWORD dwFileSizeHigh;
LPTSTR szThisFileName;
BOOL bReturn;
//
// check path to see if it's a directory
//
dwPathAttrib = QuietGetFileAttributes (szPath);
// if an invalid attribute or it's not a directory then return FALSE and set error
if ((dwPathAttrib == 0xFFFFFFFF) ||
((dwPathAttrib & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)) {
SetLastError (ERROR_BAD_PATHNAME);
return FALSE;
}
//
// Initialize size buffer
//
if (pdwSize == NULL) {
pdwSizeBuffer = &dwLocalSize;
} else {
pdwSizeBuffer = pdwSize;
}
// get a local buffer for the path
szSearchPath = GlobalAlloc (GPTR, MAX_PATH_BYTES);
szLocalPath = GlobalAlloc (GPTR, MAX_PATH_BYTES);
if ((szLocalPath != NULL) && (szSearchPath != NULL)) {
// make a local copy of the input path
lstrcpy (szLocalPath, szPath);
lstrcpy (szSearchPath, szLocalPath);
// make full path prefix for sub dir searches & filenames
if (szLocalPath[lstrlen(szLocalPath)-1] != cBackslash) lstrcat (szLocalPath, cszBackslash);
szLocalFileName = &szLocalPath[lstrlen(szLocalPath)];
// make wildcard path for searching this directory
lstrcat (szSearchPath, cszWildcardFile);
//
// start search of files in this path
//
hSearch = FindFirstFile (szSearchPath, &fdSearch);
if (hSearch != INVALID_HANDLE_VALUE) {
bSearching = TRUE;
while (bSearching) {
// if an alternate filename is defined, then use it, otherwise
// use the regular name
szThisFileName = ((fdSearch.cAlternateFileName[0] == 0) ?
fdSearch.cFileName : fdSearch.cAlternateFileName);
// if it's a dot or a dot dot (. or ..) dir, then skip
if (!DotOrDotDotDir(szThisFileName)) {
lstrcpy (szLocalFileName, szThisFileName);
// if it's a dir and the SUB DIR flag is set, then
// recurse through the next directory, otherwise
// get the file size of this file and continue
dwPathAttrib = QuietGetFileAttributes (szLocalPath);
if ((dwPathAttrib & FILE_ATTRIBUTE_DIRECTORY) ==
FILE_ATTRIBUTE_DIRECTORY) {
// this is a directory, so call this routine again
// if the sub-dir flag is set.
if ((bFlags & GSOD_INCLUDE_SUBDIRS) == GSOD_INCLUDE_SUBDIRS) {
if (!GetSizeOfDirs (szLocalPath, bFlags, pdwSizeBuffer)) {
// this call returned an errror so propogate it
// up the call stack and exit the loop here
bSearching = FALSE;
continue;
}
} else {
// the caller doesn't want sub dirs so ignore it
}
} else {
// this is a file so get the size of it and add
// it to the total
hFile = CreateFile (
szLocalPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
// then the file was opened successfully
// go get it's size and add it to the total
dwFileSizeLow = GetFileSize (hFile, &dwFileSizeHigh);
if (dwFileSizeLow != 0xFFFFFFFF) {
*pdwSizeBuffer += dwFileSizeLow;
}
// now close the file handle
CloseHandle (hFile);
} else {
// unable to open file so skip it
}
}
}
bSearching = FindNextFile (hSearch, &fdSearch);
}
FindClose (hSearch);
} else {
// unable to start search so return 0 size
}
bReturn = TRUE;
} else {
// unable to allocate memory
SetLastError (ERROR_OUTOFMEMORY);
bReturn = FALSE;
}
FREE_IF_ALLOC (szLocalPath);
FREE_IF_ALLOC (szSearchPath);
return bReturn;
}
BOOL
MediaPresent (
IN LPCTSTR szPath,
IN BOOL bPresentAndValid
)
/*++
Routine Description:
determines if the specified path is present and available
Arguments:
IN LPCTSTR szPath
path to examine (Must be a DOS path)
Return Value:
TRUE: path is available
FALSE: unable to find/open path
--*/
{
TCHAR szDev[8];
DWORD dwBytes = 0;
DWORD dwAttrib;
DWORD dwLastError = ERROR_SUCCESS;
BOOL bMediaPresent = FALSE;
UINT nErrorMode;
if (!IsUncPath(szPath)) {
// build device name string
szDev[0] = szPath[0];
szDev[1] = cColon;
szDev[2] = cBackslash;
szDev[3] = 0;
// disable windows error message popup
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
dwAttrib = QuietGetFileAttributes (szDev);
if ((dwAttrib != 0xFFFFFFFF) && ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
// if the root dir is a dir, then it must be present and formatted
bMediaPresent = TRUE;
} else {
// otherwise see if it's present and not formatted or not present
dwLastError = GetLastError();
if (dwLastError == ERROR_NOT_READY) {
// then no disk in drive
bMediaPresent = FALSE;
} else if ((dwLastError == ERROR_FILE_NOT_FOUND) ||
(dwLastError == ERROR_UNRECOGNIZED_MEDIA)) {
// then and UNFORMATTED disk is in drive
if (bPresentAndValid) {
// this isn't good enough if it's supposed to be formatted
bMediaPresent = FALSE;
} else {
// we're just looking for a disk so this is OK
bMediaPresent = TRUE;
}
}
}
SetErrorMode (nErrorMode); // restore old error mode
} else {
// assume UNC path devices are present
bMediaPresent = TRUE;
}
return bMediaPresent;
}
BOOL
FileExists (
IN LPCTSTR szFileName
)
/*++
Routine Description:
Opens the file to test for it's presences and returns TRUE if the
open was successful and FALSE if not.
Arguments:
file name to open
ReturnValue:
TRUE if file found
FALSE if not.
--*/
{
BOOL bMediaPresent;
UINT nDriveType;
DWORD dwAttr;
nDriveType = GetDriveType((LPTSTR)szFileName);
if ((nDriveType == DRIVE_REMOVABLE) || (nDriveType == DRIVE_CDROM)) {
// see if a formatted drive is really there
bMediaPresent = MediaPresent(szFileName, TRUE);
} else {
// if drive is not removable, then assume it's there
bMediaPresent = TRUE;
}
// try to get inforation on the file
dwAttr = QuietGetFileAttributes ((LPTSTR)szFileName);
if (dwAttr == 0xFFFFFFFF) {
// unable to obtain attributes, so assume it's not there
// or we can't access it
return FALSE;
} else {
// found, so close it and return TRUE
return TRUE;
}
}
BOOL
IsBootDisk (
IN LPCTSTR szPath
)
/*++
Routine Description:
examines the path (which should be the root dir of a floppy drive)
and looks for the "signature" files that indicate it's a bootable
(i.e. system) disk.
Arguments:
IN LPCTSTR szPath
drive path to look at
Return Value:
TRUE if one of the signature files was found
FALSE if not.
--*/
{
TCHAR szDrive[16];
LPTSTR szTestFile;
LPTSTR *szTryFile;
szTestFile = GlobalAlloc (GPTR, MAX_PATH_BYTES);
if (szTestFile == NULL) {
SetLastError (ERROR_OUTOFMEMORY);
return FALSE; // unable to continue
}
if (!IsUncPath(szPath)) {
szDrive[0] = szPath[0]; // drive letter
szDrive[1] = szPath[1]; // colon
szDrive[2] = cBackslash;
szDrive[3] = 0; // terminator
for (szTryFile = (LPTSTR *)szBootIdFiles;
*szTryFile != NULL;
szTryFile++) {
lstrcpy (szTestFile, szDrive);
lstrcat (szTestFile, *szTryFile);
if (FileExists(szTestFile)) return TRUE;
}
// if we didn't bail out already, it must not be a bootable disk
return FALSE;
} else {
// unc path name automatically is not a bootable drive
return FALSE;
}
FREE_IF_ALLOC (szTestFile);
}
MEDIA_TYPE
GetDriveTypeFromPath (
IN LPCTSTR szPath
)
/*++
Routine Description:
returns the drive type of the drive specified in the path argument
Arguments:
IN LPCTSTR szPath
path on drive to examine
Return Value:
MEDIA_TYPE value identifying drive type
--*/
{
HANDLE hFloppy;
DWORD dwRetSize;
DISK_GEOMETRY dgFloppy;
TCHAR szDevicePath[16];
UINT nDriveType;
UINT nErrorMode;
nDriveType = GetDriveType((LPTSTR)szPath);
// see if this is a remote disk and exit if it is.
if (nDriveType == DRIVE_REMOTE) return Unknown;
if ((nDriveType == DRIVE_REMOVABLE) || (nDriveType == DRIVE_CDROM)) {
// see if it's a UNC path and return unknown if so
if (IsUncPath(szPath)) return Unknown;
// it should be local and a DOS pathname so
// make device name from path
szDevicePath[0] = cBackslash;
szDevicePath[1] = cBackslash;
szDevicePath[2] = cPeriod;
szDevicePath[3] = cBackslash;
szDevicePath[4] = szPath[0]; // drive letter
szDevicePath[5] = szPath[1]; // colon
szDevicePath[6] = 0; // null terminator
// disable windows error message popup
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
// open device to get type
hFloppy = CreateFile (
szDevicePath,
GENERIC_READ,
(FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFloppy != INVALID_HANDLE_VALUE) {
// get drive information
if (!DeviceIoControl (hFloppy,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL, 0,
&dgFloppy,
sizeof(DISK_GEOMETRY),
&dwRetSize,
NULL) ){
// unable to get data so set to unknown
dgFloppy.MediaType = Unknown;
} // else return data from returned structure
CloseHandle (hFloppy);
} else {
// unable to open handle to device
dgFloppy.MediaType = Unknown;
}
SetErrorMode (nErrorMode); // reset error mode
}
return dgFloppy.MediaType;
}
BOOL
DotOrDotDotDir (
IN LPCTSTR szFileName
)
/*++
Routine Description:
examines the filename in the parameter list to see if it is one
of the default subdirectories (e.g. the . or the .. dirs). This
routine assumes that the argument is a filename only. (i.e. NO
PATH!)
Arguments:
Filename to compare
Return Value:
TRUE if filename is either the . or the .. dir
FALSE if it is any other string
--*/
{
if (szFileName != NULL) { // check for null parameter
if (lstrcmp(szFileName, cszDot) == 0) {
return TRUE; // it's the . dir
} else if (lstrcmp(szFileName, cszDotDot) == 0) {
return TRUE; // it's the .. dir
} else {
return FALSE; // it's neither
}
} else {
return FALSE; // null filename, so not a . or .. dir
}
}
UINT
ValidSharePath (
IN LPCTSTR szPath
)
/*++
Routine Description:
examines the path to see if it contains the "signature" file
that identifies the path as as that of a valid client
distribution directory tree.
Arguments:
IN LPCTSTR szPath
path to examine
Return Value:
ERROR_SUCCESS if it is
otherwise the ID of a string resource that describes
what was found and why it isn't a distribution tree
--*/
{
DWORD dwPathAttr;
UINT nReturn = 0;
LPTSTR szInfName;
szInfName = GlobalAlloc (GPTR, MAX_PATH_BYTES);
if (szInfName == NULL) {
SetLastError (ERROR_OUTOFMEMORY);
return NCDU_ERROR_NOMEMORY;
}
// validate share path by looking for INF file, display
// error box if necessary.
// Return 0 if valid
// return message string ID if not valid, for what ever reason.
//
dwPathAttr = QuietGetFileAttributes((LPTSTR)szPath);
if (dwPathAttr == 0xFFFFFFFF) {
// an error occured...
nReturn = NCDU_UNABLE_READ_DIR;
} else {
if ((dwPathAttr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
// so far so good!
lstrcpy (szInfName, szPath);
if (szInfName[lstrlen(szInfName)-1] != cBackslash) lstrcat(szInfName, cszBackslash);
lstrcat (szInfName, cszAppInfName);
dwPathAttr = QuietGetFileAttributes(szInfName);
if (dwPathAttr != 0xFFFFFFFF) {
nReturn = 0;
} else {
nReturn = NCDU_NOT_DIST_TREE;
}
} else {
nReturn = NCDU_PATH_NOT_DIR;
}
}
FREE_IF_ALLOC (szInfName);
return (nReturn);
}
UINT
ValidSrvToolsPath (
IN LPCTSTR szPath
)
/*++
Routine Description:
examines the path to see if it contains the "signature" file
that identifies the path as as that of a valid server tools
distribution directory tree.
Arguments:
IN LPCTSTR szPath
path to examine
Return Value:
ERROR_SUCCESS if it is
otherwise the ID of a string resource that describes
what was found and why it isn't a server tools tree
--*/
{
UINT nReturn = 0;
DWORD dwAttrib;
LPTSTR szClientPath;
szClientPath = GlobalAlloc (GPTR, MAX_PATH_BYTES);
if (szClientPath == NULL) {
SetLastError (ERROR_OUTOFMEMORY);
return NCDU_ERROR_NOMEMORY;
}
// make local copy of the path
lstrcpy (szClientPath, szPath);
// get file attributes of dir path to make sure it's really a dir
dwAttrib = QuietGetFileAttributes (szClientPath);
if ((dwAttrib != 0xFFFFFFFF) && ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
if (szClientPath[lstrlen(szClientPath)-1] != cBackslash) lstrcat(szClientPath, cszBackslash);
lstrcat (szClientPath, cszSrvToolSigFile);
// now get the file attributes of the signature file to see if this is a valid path
dwAttrib = QuietGetFileAttributes (szClientPath);
if (dwAttrib != 0xFFFFFFFF) {
nReturn = 0;
} else {
nReturn = NCDU_NOT_TOOL_TREE;
}
} else {
nReturn = NCDU_PATH_NOT_DIR;
}
FREE_IF_ALLOC (szClientPath);
return (nReturn);
}
BOOL
IsPathADir (
IN LPCTSTR szPath
)
/*++
Routine Description:
Checks the path passed in to see if it's a directory or not
Arguments:
IN LPCTSTR szPath
path to check
Return Value:
TRUE if path is a valid directory
FALSE if unable to find path or if path is not a directory
--*/
{
DWORD dwAttrib;
dwAttrib = QuietGetFileAttributes (szPath);
if ((dwAttrib != 0xffffffff) &&
((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
return TRUE;
} else {
return FALSE;
}
}
static
DWORD
GetSectorSize (
IN LPTSTR szDriveName
)
/*++
Routine Description:
Returns the sector size in bytes of the disk drive specified in the
argument list.
Arguments:
IN LPTSTR szDriveName ASCIZ of drive to return data for (e.g. "C:")
Return Value:
size of disk sector in bytes,
0xFFFFFFFF ((DWORD)-1) if error, error retrieved using GetLastError()
--*/
{
BOOL bStatus;
LONG lReturn = 0xFFFFFFFF;
DWORD dwBytesReturned;
HANDLE hDrive;
DISK_GEOMETRY dgBuffer;
// open existing file (root drive)
hDrive = CreateFile (
szDriveName,
GENERIC_READ,
(FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL,
OPEN_EXISTING,
0L,
NULL);
if (hDrive != INVALID_HANDLE_VALUE) {
// get disk inoformation
bStatus = DeviceIoControl(
hDrive,
IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL,
0L,
&dgBuffer,
sizeof(dgBuffer),
&dwBytesReturned,
NULL);
// if data was returned by IOCTL, then return
// sector size other wise return error value
if (bStatus) {
lReturn = dgBuffer.BytesPerSector;
} else {
lReturn = 0xFFFFFFFF;
}
CloseHandle (hDrive); // don't forget to close the handle
} else {
// unable to open device
lReturn = 0xFFFFFFFF;
}
return lReturn;
}
static
LONG
CopyFatBootSectorToBuffer (
IN LPTSTR szBootDrive,
IN LPBYTE *pOutputBuffer
)
/*++
Routine Description:
Copies the first sector of the volume to the specified file
Arguments:
IN LPTSTR szBootDrive volume to locate boot sector on
IN LPBYTE *pOutputBuffer address of pointer to buffer created
Return Value:
Win 32 Status Value:
ERROR_SUCCESS The routine complete all operations normally
--*/
{
LONG lStatus = ERROR_SUCCESS;
BOOL bStatus = FALSE;
DWORD dwByteCount;
DWORD dwBytesRead;
HANDLE hDrive;
LPBYTE pBootSector;
dwByteCount = GetSectorSize (szBootDrive);
if (dwByteCount != 0xFFFFFFFF) {
// allocate buffer to read data into
pBootSector = (LPBYTE)GlobalAlloc(GPTR, dwByteCount);
} else {
pBootSector = NULL;
lStatus = GetLastError();
}
if (pBootSector != NULL) {
// if memory allocated successfully, then open drive
hDrive = CreateFile (
szBootDrive,
GENERIC_READ,
(FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hDrive != INVALID_HANDLE_VALUE) {
SetFilePointer (hDrive, 0, NULL, FILE_BEGIN); // go to beginning
bStatus = ReadFile (
hDrive,
pBootSector,
dwByteCount,
&dwBytesRead,
NULL);
if (bStatus) {
*pOutputBuffer = pBootSector;
lStatus = ERROR_SUCCESS;
} else {
// unable to read the file (drive) so return error
*pOutputBuffer = NULL;
lStatus = GetLastError();
}
// close handle now that we're done with it.
CloseHandle (hDrive);
} else {
// unable to open drive
*pOutputBuffer = NULL;
lStatus = GetLastError();
}
} else {
lStatus = ERROR_OUTOFMEMORY;
}
return lStatus;
}
DWORD
GetBootDiskDosVersion (
IN LPCTSTR szPath
)
/*++
Routine Description:
examines the boot sector of the disk in the drive described in the
path variable and returns the MAJOR version of the OS that
formatted the disk.
Arguments:
IN LPCTSTR szPath
path containing drive to examine
Return Value:
Major version of MS-DOS that formatted the disk.
Zero is returned when:
-- the boot sector on the drive cannot be read
-- the drive is not a bootable drive
--*/
{
PDOS_BOOT_SECTOR pBootSector = NULL;
TCHAR szDrive[8];
DWORD dwReturn;
if (IsBootDisk(szPath)) {
szDrive[0] = cBackslash;
szDrive[1] = cBackslash;
szDrive[2] = cPeriod;
szDrive[3] = cBackslash;
szDrive[4] = szPath[0];
szDrive[5] = szPath[1];
szDrive[6] = 0;
szDrive[7] = 0;
if (CopyFatBootSectorToBuffer (
szDrive, (LPBYTE *)&pBootSector) == ERROR_SUCCESS) {
dwReturn = (pBootSector->bsOemName[5] - '0');
} else {
dwReturn = 0;
}
// free buffer allocated in CopyFatBootSectorToBuffer call.
FREE_IF_ALLOC (pBootSector);
} else {
dwReturn = 0;
}
return dwReturn;
}
DWORD
GetClusterSizeOfDisk (
IN LPCTSTR szPath
)
/*++
Routine Description:
returns the cluster size (in bytes) of the disk in the path
Arguments:
Path containing disk drive to examine
Return Value:
cluster size (allocation unit size) of disk in bytes.
if the function cannot determine the cluster size, then
a value of 512 for floppy disk or 4096 for hard disks will
be returned.
--*/
{
DWORD dwSectorsPerCluster;
DWORD dwBytesPerSector;
DWORD dwFreeClusters;
DWORD dwClusters;
TCHAR szRoot[4]; // root dir of path
DWORD dwClusterSize = 0;
UINT nErrorMode;
// disable windows error message popup
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
if (!IsUncPath(szPath)) {
szRoot[0] = szPath[0]; // drive letter
szRoot[1] = szPath[1]; // colon
szRoot[2] = cBackslash; // backslash
szRoot[3] = 0; // null terminator
if (GetDiskFreeSpace(szRoot, &dwSectorsPerCluster,
&dwBytesPerSector, &dwFreeClusters, &dwClusters)) {
dwClusterSize = dwSectorsPerCluster * dwBytesPerSector;
} else {
if (GetDriveType (szRoot) == DRIVE_FIXED) {
dwClusterSize = 4096;
} else {
dwClusterSize = 512;
}
}
} else {
dwClusterSize = 4096;
}
SetErrorMode (nErrorMode); // restore old error mode
return dwClusterSize;
}
DWORD
QuietGetFileSize (
IN LPCTSTR szPath
)
/*++
Routine Description:
Returns the size of the file passed in the arg list ( file sizz < 4GB)
while supressing any windows system error messages.
Arguments:
path to file to size
Return Value:
size of the file (if less then 4GB) or 0xFFFFFFFF if error
--*/
{
HANDLE hFile;
DWORD dwFileSizeLow, dwFileSizeHigh;
DWORD dwReturn;
UINT nErrorMode;
// disable windows error message popup
nErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
// get the size of the file and add it to the total
hFile = CreateFile (
szPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
// then the file was opened successfully
// go get it's size
dwFileSizeLow = GetFileSize (hFile, &dwFileSizeHigh);
dwReturn = dwFileSizeLow;
// now close the file handle
CloseHandle (hFile);
} else {
// unable to open file so return error
dwReturn = 0xFFFFFFFF;
}
SetErrorMode (nErrorMode); // restore old error mode
return dwReturn;
}