/*++ 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; }