/*++ Copyright (c) 1994 Microsoft Corporation Module Name: util.cxx Abstract: Contains the class implementation of UTILITY classes. Author: Madan Appiah (madana) 16-Nov-1994 Environment: User Mode - Win32 Revision History: Ahsan Kabir (akabir) 24-Nov-1997 --*/ #include #include typedef BOOL (WINAPI *PFNGETFILEATTREX)(LPCTSTR, GET_FILEEX_INFO_LEVELS, LPVOID); static char vszDot[] = "."; static char vszDotDot[] = ".."; #ifdef UNIX static char vszIndexFile[] = "index.dat"; #endif /* UNIX */ static char vszSHClassInfo[]=".ShellClassInfo"; static char vszCLSIDKey[]="CLSID"; static char vszCLSID[]="{FF393560-C2A7-11CF-BFF4-444553540000}"; static char vszUICLSIDKey[]="UICLSID"; static char vszUICLSID[]="{7BD29E00-76C1-11CF-9DD0-00A0C9034933}"; typedef HRESULT (*PFNSHFLUSHCACHE)(VOID); #ifdef UNIX extern void UnixGetValidParentPath(LPTSTR szDevice); #endif /* UNIX */ /*----------------------------------------------------------------------------- DeleteOneCachedFile Deletes a file belonging to the cache. Arguments: lpszFileName: Fully qualified filename Return Value: TRUE if successful. If FALSE, GetLastError() returns the error code. Comments: ---------------------------------------------------------------------------*/ BOOL DeleteOneCachedFile( LPSTR lpszFileName, DWORD dostEntry) { if (dostEntry) { DWORD dostCreate = 0; LPWORD pwCreate = (LPWORD) &dostCreate; WIN32_FILE_ATTRIBUTE_DATA FileAttrData; switch (GetFileSizeAndTimeByName(lpszFileName, &FileAttrData)) { case ERROR_SUCCESS: break; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: return TRUE; default: return FALSE; } FileTimeToDosDateTime(&FileAttrData.ftCreationTime, pwCreate, pwCreate+1); if (dostCreate != dostEntry) return TRUE; // not our file, so consider it done! } if(!DeleteFile(lpszFileName)) { TcpsvcsDbgPrint (( DEBUG_ERRORS, "DeleteFile failed on %s, Error=%ld\n", lpszFileName, GetLastError())); switch (GetLastError()) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: return TRUE; default: return FALSE; } } else { TcpsvcsDbgPrint(( DEBUG_ERRORS, "Deleted %s\n", lpszFileName )); return TRUE; } } /*----------------------------------------------------------------------------- DeleteCachedFilesInDir ---------------------------------------------------------------------------*/ DWORD DeleteCachedFilesInDir( LPSTR lpszPath, DWORD dwLevel ) { TCHAR PathFiles[MAX_PATH+1]; TCHAR FullFileName[MAX_PATH+1]; LPTSTR FullFileNamePtr; WIN32_FIND_DATA FindData; HANDLE FindHandle = INVALID_HANDLE_VALUE; // Since this has become a recursive call, we don't want to go more than 6 levels. if (dwLevel>5) { INET_ASSERT(FALSE); return ERROR_INVALID_PARAMETER; } DWORD Error, len, cbUsed; BOOL fFindSuccess; DWORD cb = strlen(lpszPath); memcpy(PathFiles, lpszPath, cb + 1); if(!AppendSlashIfNecessary(PathFiles, &cb)) { Error = ERROR_INVALID_NAME; goto Cleanup; } memcpy(FullFileName, PathFiles, cb + 1); memcpy(PathFiles + cb, ALLFILES_WILDCARD_STRING, sizeof(ALLFILES_WILDCARD_STRING)); FullFileNamePtr = FullFileName + lstrlen( (LPTSTR)FullFileName ); if ( IsValidCacheSubDir( lpszPath)) DisableCacheVu( lpszPath); FindHandle = FindFirstFile( (LPTSTR)PathFiles, &FindData ); if( FindHandle == INVALID_HANDLE_VALUE ) { Error = GetLastError(); goto Cleanup; } cbUsed = (unsigned int)(FullFileNamePtr-FullFileName); FullFileName[MAX_PATH] = '\0'; do { cb = strlen(FindData.cFileName); if (cb+cbUsed+1 > MAX_PATH) { // Subtracting 1 extra so that the null terminator doesn't get overwritten cb = MAX_PATH - cbUsed - 2; } memcpy(FullFileNamePtr, FindData.cFileName, cb+1); #ifndef UNIX if (!(!strnicmp(FindData.cFileName, vszDot, sizeof(vszDot)-1) || !strnicmp(FindData.cFileName, vszDotDot, sizeof(vszDotDot)-1))) #else if (!(!strnicmp(FindData.cFileName, vszDot, sizeof(vszDot)-1) || !strnicmp(FindData.cFileName, vszIndexFile, sizeof(vszIndexFile)-1) || !strnicmp(FindData.cFileName, vszDotDot, sizeof(vszDotDot)-1))) #endif /* UNIX */ { if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { Error = DeleteCachedFilesInDir(FullFileName, dwLevel + 1); if (Error!=ERROR_SUCCESS) { goto Cleanup; } SetFileAttributes(FullFileName, FILE_ATTRIBUTE_DIRECTORY); RemoveDirectory(FullFileName); } else { DeleteOneCachedFile( (LPTSTR)FullFileName, 0); } } // // find next file. // } while (FindNextFile( FindHandle, &FindData )); Error = GetLastError(); if( Error == ERROR_NO_MORE_FILES) { Error = ERROR_SUCCESS; } Cleanup: if( FindHandle != INVALID_HANDLE_VALUE ) { FindClose( FindHandle ); } if( Error != ERROR_SUCCESS ) { TcpsvcsDbgPrint(( DEBUG_ERRORS, "DeleteCachedFilesInDir failed, %ld.\n", Error )); } return( Error ); } /*----------------------------------------------------------------------------- AppendSlashIfNecessary ---------------------------------------------------------------------------*/ BOOL AppendSlashIfNecessary(LPSTR szPath, DWORD* pcbPath) { if (*pcbPath > (MAX_PATH-2)) return FALSE; if (szPath[*pcbPath-1] != DIR_SEPARATOR_CHAR) { szPath[*pcbPath] = DIR_SEPARATOR_CHAR; (*pcbPath)++; szPath[*pcbPath] = '\0'; } return TRUE; } /*----------------------------------------------------------------------------- EnableCachevu ---------------------------------------------------------------------------*/ BOOL EnableCacheVu(LPSTR szPath, DWORD dwContainer) { DWORD cbPath = strlen(szPath); CHAR szDesktopIni[MAX_PATH]; DWORD dwFileAttributes; HMODULE hInstShell32 = 0; PFNSHFLUSHCACHE pfnShFlushCache = NULL; #define DESIRED_ATTR (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM) // Calls with non-existant directory allowed and return false. dwFileAttributes = GetFileAttributes(szPath); if (dwFileAttributes == 0xFFFFFFFF) return FALSE; // Always be set to enable cachevu. SetFileAttributes(szPath, FILE_ATTRIBUTE_SYSTEM); // Path to DESKTOP_INI_FILENAME memcpy(szDesktopIni, szPath, cbPath + 1); AppendSlashIfNecessary(szDesktopIni, &cbPath); // Correct location for desktop.ini. memcpy(szDesktopIni + cbPath, DESKTOPINI_FILE_NAME, sizeof(DESKTOPINI_FILE_NAME)); // Check for existing desktop.ini dwFileAttributes = GetFileAttributes(szDesktopIni); if (dwFileAttributes == 0xFFFFFFFF) { dwFileAttributes = 0; // Always write out the UICLSID WritePrivateProfileString(vszSHClassInfo, vszUICLSIDKey, vszUICLSID, szDesktopIni); // HISTORY requires an additional CLSID. if (dwContainer == HISTORY) WritePrivateProfileString(vszSHClassInfo, vszCLSIDKey, vszCLSID, szDesktopIni); // Flush buffer - problems on Win95 if you don't. WritePrivateProfileString(NULL, NULL, NULL, szDesktopIni); } if ((dwFileAttributes & DESIRED_ATTR) != DESIRED_ATTR) { // Should be hidden, read-only and system for cachevu to work correctly. SetFileAttributes(szDesktopIni, DESIRED_ATTR); } /* BUGBUG - taking this code out for raid # 45710. // We now need to notify the shell that a new desktop.ini has been created. hInstShell32 = GetModuleHandle("shell32.dll"); if (hInstShell32) { pfnShFlushCache = (PFNSHFLUSHCACHE) GetProcAddress(hInstShell32, (LPSTR) 526); if (pfnShFlushCache) { __try { (*pfnShFlushCache)(); } __except(EXCEPTION_EXECUTE_HANDLER) { } ENDEXCEPT } } */ return TRUE; } /*----------------------------------------------------------------------------- IsValidCacheSubDir ---------------------------------------------------------------------------*/ BOOL IsValidCacheSubDir(LPSTR szPath) { DWORD dwFileAttributes, cb, cbPath; CHAR szDesktopIni[MAX_PATH]; CHAR szCLSID [MAX_PATH]; CHAR szWindowsDir[MAX_PATH]; CHAR szSystemDir [MAX_PATH]; cbPath = strlen(szPath); // Root, Windows or System directories // are decidedly not cache subdirectories. cb = GetWindowsDirectory(szWindowsDir, MAX_PATH); if (!cb || cb>MAX_PATH) { INET_ASSERT(FALSE); return FALSE; } AppendSlashIfNecessary(szWindowsDir, &cb); cb = GetSystemDirectory(szSystemDir, MAX_PATH); AppendSlashIfNecessary(szSystemDir, &cb); if (cbPath < 4 || !strnicmp(szPath, szWindowsDir, cbPath) || !strnicmp(szPath, szSystemDir, cbPath)) { INET_ASSERT(FALSE); return FALSE; } // Path to DESKTOP_INI_FILENAME memcpy(szDesktopIni, szPath, cbPath + 1); AppendSlashIfNecessary(szDesktopIni, &cbPath); // Correct location for desktop.ini. memcpy(szDesktopIni + cbPath, DESKTOPINI_FILE_NAME, sizeof(DESKTOPINI_FILE_NAME)); // Check for existing desktop.ini dwFileAttributes = GetFileAttributes(szDesktopIni); // No desktop.ini found or system attribute not set. if (dwFileAttributes == 0xFFFFFFFF) { return FALSE; } // Found UICLSID (CONTENT cachevu) ? if (GetPrivateProfileString(vszSHClassInfo, vszUICLSIDKey, "", szCLSID, MAX_PATH, szDesktopIni) && !strcmp(szCLSID, vszUICLSID)) { return TRUE; } return FALSE; } /*----------------------------------------------------------------------------- DisableCachevu ---------------------------------------------------------------------------*/ BOOL DisableCacheVu(LPSTR szPath) { DWORD cbPath = strlen(szPath); CHAR szDesktopIni[MAX_PATH]; // Path to DESKTOP_INI_FILENAME memcpy(szDesktopIni, szPath, cbPath + 1); AppendSlashIfNecessary(szDesktopIni, &cbPath); // Correct location for desktop.ini. memcpy(szDesktopIni + cbPath, DESKTOPINI_FILE_NAME, sizeof(DESKTOPINI_FILE_NAME)); SetFileAttributes(szDesktopIni, FILE_ATTRIBUTE_NORMAL); DeleteFile(szDesktopIni); return TRUE; } /*----------------------------------------------------------------------------- StripTrailingWhiteSpace ---------------------------------------------------------------------------*/ VOID StripTrailingWhiteSpace(LPSTR szString, LPDWORD pcb) { INET_ASSERT(szString); if (*pcb == 0) return; CHAR* ptr = szString + *pcb - 1; while (*ptr == ' ') { ptr--; if (--(*pcb) == 0) break; } *(ptr+1) = '\0'; } /* PerformOperationOverUrlCache----------------------- The purpose of this function is to iterate through the content cache and perform the same action (here, called an operation) on each entry in the cache. This function takes all the parameters that FindFirstUrlCacheEntryEx accepts, plus two more: op -- This is of type CACHE_OPERATOR, discussed below pOperatorData -- a pointer to an array of data that the calling process and op use to collect/maintain info */ /* CACHE_OPERATOR is a pointer to function, that takes three arguments(pointer to a cache entry, cache entry size, and a pointer to state data. The operator can perform whatever operation (move/copy/data collection) it wishes on the supplied cache entry. It must return TRUE if the operation has succeeded and PerformOperationOverUrlCache can continue to iterate through the cache, FALSE otherwise. pOpData can be null, or a cast pointer to whatever structure the operator will use to maintain state information. PerformOperationOverUrlCache guarantees that each cache entry will have sufficient space for its information. */ typedef BOOL (*CACHE_OPERATOR)(INTERNET_CACHE_ENTRY_INFO* pcei, PDWORD pcbcei, PVOID pOpData); // hAdjustMemory is a helper function // that ensures that the buffer used by PerformOperationOverUrlCache // is large enough to hold all of a cache entry's info BOOL hAdjustMemory(PBYTE pbSrc, PDWORD pcbAvail, LPINTERNET_CACHE_ENTRY_INFO* pbNew, PDWORD pcbNeeded) { if ((PBYTE)*pbNew!=pbSrc) { FREE_MEMORY(*pbNew); } do { *pcbAvail += 1024; } while (*pcbAvail < *pcbNeeded); *pcbNeeded = *pcbAvail; *pbNew = (LPINTERNET_CACHE_ENTRY_INFO)ALLOCATE_FIXED_MEMORY(*pcbAvail); return (*pbNew!=NULL); } // PerformOperationOverUrlCache // described above // uses FindFirstUrlCacheEntryEx and FindNext as any other wininet client would. // and passes a complete cache entry to the operator for processing BOOL PerformOperationOverUrlCacheA( IN PCSTR pszUrlSearchPattern, IN DWORD dwFlags, IN DWORD dwFilter, IN GROUPID GroupId, OUT PVOID pReserved1, IN OUT PDWORD pdwReserved2, IN PVOID pReserved3, IN CACHE_OPERATOR op, IN OUT PVOID pOperatorData ) { BOOL fResult = FALSE; BYTE buffer[sizeof(INTERNET_CACHE_ENTRY_INFO) + 1024]; DWORD cbAvail = sizeof(buffer); DWORD cbCEI = cbAvail; LPINTERNET_CACHE_ENTRY_INFO pCEI = (LPINTERNET_CACHE_ENTRY_INFO)buffer; HANDLE hFind = NULL; hFind = FindFirstUrlCacheEntryEx(pszUrlSearchPattern, dwFlags, dwFilter, GroupId, pCEI, &cbCEI, pReserved1, pdwReserved2, pReserved3); if (!hFind && (GetLastError()!=ERROR_INSUFFICIENT_BUFFER) && hAdjustMemory(buffer, &cbAvail, &pCEI, &cbCEI)) { hFind = FindFirstUrlCacheEntryEx(pszUrlSearchPattern, dwFlags, dwFilter, GroupId, pCEI, &cbCEI, pReserved1, pdwReserved2, pReserved3); } if (hFind!=NULL) { do { fResult = op(pCEI, &cbCEI, pOperatorData); if (fResult) { cbCEI = cbAvail; fResult = FindNextUrlCacheEntryEx(hFind, pCEI, &cbCEI, NULL, NULL, NULL); if (!fResult && (GetLastError()==ERROR_INSUFFICIENT_BUFFER) && hAdjustMemory(buffer, &cbAvail, &pCEI, &cbCEI)) { fResult = FindNextUrlCacheEntryEx(hFind, pCEI, &cbCEI, NULL, NULL, NULL); } } } while (fResult); FindCloseUrlCache(hFind); if (GetLastError()==ERROR_NO_MORE_ITEMS) { fResult = TRUE; } } if (pCEI!=(LPINTERNET_CACHE_ENTRY_INFO)buffer) { FREE_MEMORY(pCEI); } return fResult; } // ------ MoveCachedFiles --------------------------------------------------------------------------------------- // Purpose: Moves as many files as possible from the current Temporary Internet Files to the new location // State information required for the move operation struct MOVE_OP_STATE { TCHAR szNewPath[MAX_PATH]; TCHAR szOldPath[MAX_PATH]; DWORD ccNewPath; DWORD ccOldPath; DWORDLONG dlCacheSize; DWORD dwClusterSizeMinusOne; DWORD dwClusterSizeMask; }; // Helper function that, // given a string pointer, // returns the next occurrence of DIR_SEPARATOR_CHAR ('/' || '\\') PTSTR hScanPastSeparator(PTSTR pszPath) { while (*pszPath && *pszPath!=DIR_SEPARATOR_CHAR) { pszPath++; } if (*pszPath) { return pszPath+1; } return NULL; } // Helper function that, // given a path, // ensures that all the directories in the path exist BOOL hConstructSubDirs(PTSTR pszBase) { PTSTR pszLast = hScanPastSeparator(pszBase); if (NULL == pszLast) return TRUE; // returning TRUE on purpose PTSTR pszNext = pszLast; while ((pszNext=hScanPastSeparator(pszNext))!=NULL) { *(pszNext-1) = '\0'; CreateDirectory(pszBase, NULL); *(pszNext-1) = DIR_SEPARATOR_CHAR; pszLast = pszNext; } return TRUE; } // MoveOperation // actually moves a cached file to the new location BOOL MoveOperation(LPINTERNET_CACHE_ENTRY_INFO pCEI, PDWORD pcbCEI, PVOID pOpData) { MOVE_OP_STATE* pmos = (MOVE_OP_STATE*)pOpData; BOOL fResult = TRUE; if (pCEI->lpszLocalFileName) { if (!strnicmp(pCEI->lpszLocalFileName, pmos->szOldPath, pmos->ccOldPath)) { // Copy the file lstrcpy(pmos->szNewPath + pmos->ccNewPath, pCEI->lpszLocalFileName + pmos->ccOldPath); fResult = CopyFile(pCEI->lpszLocalFileName, pmos->szNewPath, FALSE); if (!fResult && GetLastError()==ERROR_PATH_NOT_FOUND) { if (hConstructSubDirs(pmos->szNewPath)) { fResult = CopyFile(pCEI->lpszLocalFileName, pmos->szNewPath, FALSE); } } // If the move was successful, we need to adjust the size of the new cache if (fResult) { fResult = FALSE; HANDLE h1 = CreateFile(pCEI->lpszLocalFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // If we can't open the original file, then the new file will never // get scavenged because we'll never be able to match creation times if (h1!=INVALID_HANDLE_VALUE) { HANDLE h2 = CreateFile(pmos->szNewPath, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (h2!=INVALID_HANDLE_VALUE) { FILETIME ft; if (GetFileTime(h1, &ft, NULL, NULL)) { fResult = SetFileTime(h2, &ft, NULL, NULL); } CloseHandle(h2); } CloseHandle(h1); } } // If we haven't been able to set the create time, then we've got a problem // we'd sooner not deal with. if (!fResult) { DeleteUrlCacheEntry(pCEI->lpszSourceUrlName); DeleteFile(pmos->szNewPath); } else { pmos->dlCacheSize += ((LONGLONG) (pCEI->dwSizeLow + pmos->dwClusterSizeMinusOne) & pmos->dwClusterSizeMask); } // Delete the old one DeleteFile(pCEI->lpszLocalFileName); } } return TRUE; } DWORD MoveCachedFiles( LPSTR pszOldPath, LPSTR pszNewPath ) { MOVE_OP_STATE mos; INET_ASSERT(pszOldPath && pszNewPath); mos.ccNewPath = lstrlen(pszNewPath); memcpy(mos.szNewPath, pszNewPath, mos.ccNewPath*sizeof(TCHAR)); AppendSlashIfNecessary(mos.szNewPath, &mos.ccNewPath); memcpy(mos.szNewPath + mos.ccNewPath, CONTENT_VERSION_SUBDIR, sizeof(CONTENT_VERSION_SUBDIR)*sizeof(TCHAR)); mos.ccNewPath += sizeof(CONTENT_VERSION_SUBDIR)-1; AppendSlashIfNecessary(mos.szNewPath, &mos.ccNewPath); mos.ccOldPath = lstrlen(pszOldPath); memcpy(mos.szOldPath, pszOldPath, mos.ccOldPath*sizeof(TCHAR)); AppendSlashIfNecessary(mos.szOldPath, &mos.ccOldPath); mos.dlCacheSize = 0; GetDiskInfo(mos.szNewPath, &mos.dwClusterSizeMinusOne, NULL, NULL); mos.dwClusterSizeMinusOne--; mos.dwClusterSizeMask = ~mos.dwClusterSizeMinusOne; GlobalUrlContainers->WalkLeakList(CONTENT); // We don't need to get all the information about each and every entry. PerformOperationOverUrlCacheA( NULL, FIND_FLAGS_RETRIEVE_ONLY_FIXED_AND_FILENAME, NORMAL_CACHE_ENTRY | STICKY_CACHE_ENTRY | SPARSE_CACHE_ENTRY, NULL, NULL, NULL, NULL, MoveOperation, (PVOID)&mos); GlobalUrlContainers->SetCacheSize(CONTENT, mos.dlCacheSize); // Copy desktop.ini and index.dat, since these aren't cached TCHAR szFile[MAX_PATH]; DWORD ccOldPath = lstrlen(pszOldPath); memcpy(szFile, pszOldPath, ccOldPath); AppendSlashIfNecessary(szFile, &ccOldPath); memcpy(szFile + ccOldPath, MEMMAP_FILE_NAME, sizeof(MEMMAP_FILE_NAME)); memcpy(mos.szNewPath + mos.ccNewPath, MEMMAP_FILE_NAME, sizeof(MEMMAP_FILE_NAME)); CopyFile(szFile, mos.szNewPath, FALSE); memcpy(szFile + ccOldPath, DESKTOPINI_FILE_NAME, sizeof(DESKTOPINI_FILE_NAME)); memcpy(mos.szNewPath + mos.ccNewPath, DESKTOPINI_FILE_NAME, sizeof(DESKTOPINI_FILE_NAME)); CopyFile(szFile, mos.szNewPath, FALSE); return ERROR_SUCCESS; } /*----------------------------------------------------------------------------- IsCorrectUser Routine Description: checks to see from the headers whether there is any username in there and whether it matches the currently logged on user. If no one is logged on a default username string is used Arguments: lpszHeaderInfo: headers to check dwheaderSize: size of the headers buffer Return Value: BOOL ---------------------------------------------------------------------------*/ BOOL IsCorrectUserPrivate( IN LPSTR lpszHeaderInfo, IN DWORD dwHeaderSize ) { LPSTR lpTemp, lpTemp2; INET_ASSERT (lpszHeaderInfo); lpTemp = lpszHeaderInfo+dwHeaderSize-1; // start searching backwards while (lpTemp >= lpszHeaderInfo) { if (*lpTemp ==':') { // If this is less than the expected header: // then we know that there is no such usernameheader // i.e. it's not a peruseritem so allow access if ((DWORD)PtrDifference((lpTemp+1), lpszHeaderInfo) < (sizeof(vszUserNameHeader)-1)) { TcpsvcsDbgPrint((DEBUG_CONTAINER, "IsCorrectUser (Util.cxx): Didn't find header \r\n", lpTemp, lpTemp, lpszHeaderInfo, lpszHeaderInfo, vszCurrentUser, PtrDifference(lpTemp, lpszHeaderInfo), (sizeof(vszUserNameHeader)-1) )); return (TRUE); // No such header. just ay it is OK } // point this puppy to the expected header start lpTemp2 = lpTemp - (sizeof(vszUserNameHeader)-2); // if the earlier char is not a white space [0x9-0xd or 0x20] // then this is not the beginning of the header // Also need to check for the first header which would not // have whitespace preceding it. Want to first check lpTemp2 == // lpszheaderInfo to prevent underflowing when dereferencing. if (((lpTemp2) == lpszHeaderInfo) || isspace(*(lpTemp2-1))) { // we have the beginning of a header if (!strnicmp(lpTemp2 , vszUserNameHeader , sizeof(vszUserNameHeader)-1)) { // right header, let us see whether this is the right person if(!strnicmp(lpTemp+1, vszCurrentUser, vdwCurrentUserLen)) { TcpsvcsDbgPrint((DEBUG_CONTAINER, "IsCorrectUser (Util.cxx): Match!! %s header == %s current user.\r\n", lpTemp+1, vszCurrentUser )); return (TRUE); // right guy } else { TcpsvcsDbgPrint((DEBUG_CONTAINER, "IsCorrectUser (Util.cxx): No match!! %s header != %s current user.\r\n", lpTemp+1, vszCurrentUser )); } return(FALSE); // wrong guy } } } --lpTemp; } return (TRUE); // there was no UserName header, just say it is OK } BOOL IsCorrectUser( IN LPSTR lpszHeaderInfo, IN DWORD dwHeaderSize ) { BOOL fRet = FALSE; __try { fRet = IsCorrectUserPrivate(lpszHeaderInfo, dwHeaderSize); } // __try __except(EXCEPTION_EXECUTE_HANDLER) { INET_ASSERT(FALSE); fRet = FALSE; } ENDEXCEPT return fRet; } #ifndef UNICODE #define SZ_GETDISKFREESPACEEX "GetDiskFreeSpaceExA" #define SZ_WNETUSECONNECTION "WNetUseConnectionA" #define SZ_WNETCANCELCONNECTION "WNetCancelConnectionA" #else #define SZ_GETDISKFREESPACEEX "GetDiskFreeSpaceExW" #define SZ_WNETUSECONNECTION "WNetUseConnectionW" #define SZ_WNETCANCELCONNECTION "WNetCancelConnectionW" #endif typedef BOOL (WINAPI *PFNGETDISKFREESPACEEX)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); typedef BOOL (WINAPI *PFNWNETUSECONNECTION)(HWND, LPNETRESOURCE, PSTR, PSTR, DWORD, PSTR, PDWORD, PDWORD); typedef BOOL (WINAPI *PFNWNETCANCELCONNECTION)(LPCTSTR, BOOL); BOOL EstablishFunction(PTSTR pszModule, PTSTR pszFunction, PFN* pfn) { if (*pfn==(PFN)-1) { *pfn = NULL; HMODULE ModuleHandle = GetModuleHandle(pszModule); if (ModuleHandle) { *pfn = (PFN)GetProcAddress(ModuleHandle, pszFunction); } } return (*pfn!=NULL); } // GetPartitionClusterSize // GetDiskFreeSpace has the annoying habit of lying about the layout // of the drive; thus we've been ending up with bogus sizes for the cluster size. // You can't imagine how annoying it is to think you've a 200 MB cache, but it // starts scavenging at 20MB. // This function will, if given reason to doubt the veracity of GDFS, go straight // to the hardware and get the information for itself, otherwise return the passed-in // value. // The code that follows is heavily doctored from msdn sample code. Copyright violation? I think not. static PFNGETDISKFREESPACEEX pfnGetDiskFreeSpaceEx = (PFNGETDISKFREESPACEEX)-1; #define VWIN32_DIOC_DOS_DRIVEINFO 6 typedef struct _DIOC_REGISTERS { DWORD reg_EBX; DWORD reg_EDX; DWORD reg_ECX; DWORD reg_EAX; DWORD reg_EDI; DWORD reg_ESI; DWORD reg_Flags; } DIOC_REGISTERS, *PDIOC_REGISTERS; // Important: All MS_DOS data structures must be packed on a // one-byte boundary. #pragma pack(1) typedef struct _DPB { BYTE dpb_drive; // Drive number (1-indexed) BYTE dpb_unit; // Unit number WORD dpb_sector_size; // Size of sector in bytes BYTE dpb_cluster_mask; // Number of sectors per cluster, minus 1 BYTE dpb_cluster_shift; // The stuff after this, we don't really care about. WORD dpb_first_fat; BYTE dpb_fat_count; WORD dpb_root_entries; WORD dpb_first_sector; WORD dpb_max_cluster; WORD dpb_fat_size; WORD dpb_dir_sector; DWORD dpb_reserved2; BYTE dpb_media; BYTE dpb_first_access; DWORD dpb_reserved3; WORD dpb_next_free; WORD dpb_free_cnt; WORD extdpb_free_cnt_hi; WORD extdpb_flags; WORD extdpb_FSInfoSec; WORD extdpb_BkUpBootSec; DWORD extdpb_first_sector; DWORD extdpb_max_cluster; DWORD extdpb_fat_size; DWORD extdpb_root_clus; DWORD extdpb_next_free; } DPB, *PDPB; #pragma pack() DWORD GetPartitionClusterSize(PTSTR szDevice, DWORD dwClusterSize) { switch (GlobalPlatformType) { case PLATFORM_TYPE_WIN95: // If GetDiskFreeSpaceEx is present _and_ we're running Win9x, this implies // that we must be doing OSR2 or later. We can trust earlier versions // of the GDFS (we think; this assumption may be invalid.) // Since Win95 can't read NTFS drives, we'll freely assume we're reading a FAT drive. // Basically, we're performing an MSDOS INT21 call to get the drive partition record. Joy. if (pfnGetDiskFreeSpaceEx) { HANDLE hDevice; DIOC_REGISTERS reg; BYTE buffer[sizeof(WORD)+sizeof(DPB)]; PDPB pdpb = (PDPB)(buffer + sizeof(WORD)); BOOL fResult; DWORD cb; // We must always have a drive letter in this case int nDrive = *szDevice - TEXT('A') + 1; // Drive number, 1-indexed hDevice = CreateFile("\\\\.\\vwin32", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL); if (hDevice!=INVALID_HANDLE_VALUE) { reg.reg_EDI = PtrToUlong(buffer); reg.reg_EAX = 0x7302; reg.reg_ECX = sizeof(buffer); reg.reg_EDX = (DWORD) nDrive; // drive number (1-based) reg.reg_Flags = 0x0001; // assume error (carry flag is set) fResult = DeviceIoControl(hDevice, VWIN32_DIOC_DOS_DRIVEINFO, ®, sizeof(reg), ®, sizeof(reg), &cb, 0); if (fResult && !(reg.reg_Flags & 0x0001)) { // no error if carry flag is clear dwClusterSize = DWORD((pdpb->dpb_cluster_mask+1)*pdpb->dpb_sector_size); } CloseHandle(hDevice); } } break; default: // Do nothing. Trust the value we've been passed. // UNIX guys will have to treat this separately. // For NT, however, this might be another issue. We can't use the DOS INT21. // Questions: // NT5 (but not NT4) supports FAT32; will we get honest answers? Apparently, yes. // NT4/5: NTFS drives and other FAT drives -- do we still get honest answers? Investigation // so far says, Yes. break; } return dwClusterSize; } /* GetDiskInfo A nice way to get volume information */ BOOL GetDiskInfoA(PTSTR pszPath, PDWORD pdwClusterSize, PDWORDLONG pdlAvail, PDWORDLONG pdlTotal) { static PFNWNETUSECONNECTION pfnWNetUseConnection = (PFNWNETUSECONNECTION)-1; static PFNWNETCANCELCONNECTION pfnWNetCancelConnection = (PFNWNETCANCELCONNECTION)-1; if (!pszPath) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } INET_ASSERT(pdwClusterSize || pdlAvail || pdlTotal); // If GetDiskFreeSpaceExA is available, we can be confident we're running W95OSR2+ || NT4 EstablishFunction(TEXT("KERNEL32"), SZ_GETDISKFREESPACEEX, (PFN*)&pfnGetDiskFreeSpaceEx); BOOL fRet = FALSE; TCHAR szDevice[MAX_PATH]; PTSTR pszGDFSEX = NULL; if (*pszPath==DIR_SEPARATOR_CHAR) { // If we're dealing with a cache that's actually located on a network share, // that's fine so long as we have GetDiskFreeSpaceEx at our disposal. // _However_, if we need the cluster size on Win9x, we'll need to use // INT21 stuff (see above), even if we have GDFSEX available, so we need to map // the share to a local drive. if (pfnGetDiskFreeSpaceEx && !((GlobalPlatformType==PLATFORM_TYPE_WIN95) && pdwClusterSize)) { DWORD cbPath = lstrlen(pszPath); cbPath -= ((pszPath[cbPath-1]==DIR_SEPARATOR_CHAR) ? 1 : 0); if (cbPath>MAX_PATH-2) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } memcpy(szDevice, pszPath, cbPath); szDevice[cbPath] = DIR_SEPARATOR_CHAR; cbPath++; szDevice[cbPath] = '\0'; pszGDFSEX = szDevice; } else { if (!(EstablishFunction(TEXT("MPR"), SZ_WNETUSECONNECTION, (PFN*)&pfnWNetUseConnection) && EstablishFunction(TEXT("MPR"), SZ_WNETCANCELCONNECTION, (PFN*)&pfnWNetCancelConnection))) { return FALSE; } // If it's a UNC, map it to a local drive for backwards compatibility NETRESOURCE nr = { 0, RESOURCETYPE_DISK, 0, 0, szDevice, pszPath, NULL, NULL }; DWORD cbLD = sizeof(szDevice); DWORD dwNull; if (pfnWNetUseConnection(NULL, &nr, NULL, NULL, CONNECT_INTERACTIVE | CONNECT_REDIRECT, szDevice, &cbLD, &dwNull)!=ERROR_SUCCESS) { SetLastError(ERROR_NO_MORE_DEVICES); return FALSE; } } } else { memcpy(szDevice, pszPath, sizeof(TEXT("?:\\"))); szDevice[3] = '\0'; pszGDFSEX = pszPath; } if (*szDevice!=DIR_SEPARATOR_CHAR) { *szDevice = (TCHAR)CharUpper((LPTSTR)*szDevice); } #ifdef UNIX /* On Unix, GetDiskFreeSpace and GetDiskFreeSpaceEx will work successfully * only if the path exists. So, let us pass a path that exists */ UnixGetValidParentPath(szDevice); #endif /* UNIX */ // I hate goto's, and this is a way to avoid them... for (;;) { DWORDLONG cbFree = 0, cbTotal = 0; if (pfnGetDiskFreeSpaceEx && (pdlTotal || pdlAvail)) { ULARGE_INTEGER ulFree, ulTotal; // BUG BUG BUG Is the following problematic? Also, we'll need to add checks to make sure that // the cKBlimit fits a DWORD (in the obscene if unlikely case drive spaces grow that large) // For instance, if this is a per user system with a non-shared cache, we might want to change // the ratios. INET_ASSERT(pszGDFSEX); fRet = pfnGetDiskFreeSpaceEx(pszGDFSEX, &ulFree, &ulTotal, NULL); // HACK Some versions of GetDiskFreeSpaceEx don't accept the whole directory; they // take only the drive letter. Pfft. if (!fRet) { fRet = pfnGetDiskFreeSpaceEx(szDevice, &ulFree, &ulTotal, NULL); } if (fRet) { cbFree = ulFree.QuadPart; cbTotal = ulTotal.QuadPart; } } if ((!fRet) || pdwClusterSize) { DWORD dwSectorsPerCluster, dwBytesPerSector, dwFreeClusters, dwClusters, dwClusterSize; if (!GetDiskFreeSpace(szDevice, &dwSectorsPerCluster, &dwBytesPerSector, &dwFreeClusters, &dwClusters)) { fRet = FALSE; break; } dwClusterSize = dwBytesPerSector * dwSectorsPerCluster; if (!fRet) { cbFree = (DWORDLONG)dwClusterSize * (DWORDLONG)dwFreeClusters; cbTotal = (DWORDLONG)dwClusterSize * (DWORDLONG)dwClusters; } if (pdwClusterSize) { *pdwClusterSize = GetPartitionClusterSize(szDevice, dwClusterSize); } } if (pdlTotal) { *pdlTotal = cbTotal; } if (pdlAvail) { *pdlAvail = cbFree; } fRet = TRUE; break; }; // We've got the characteristics. Now delete local device connection, if any. if (*pszPath==DIR_SEPARATOR_CHAR && !pfnGetDiskFreeSpaceEx) { pfnWNetCancelConnection(szDevice, FALSE); } return fRet; } // -- ScanToLastSeparator // Given a path, and a pointer within the path, discover where the path separator prior to the path // is located and return the pointer to it. If there is none, return NULL. BOOL ScanToLastSeparator(PTSTR pszPath, PTSTR* ppszCurrent) { PTSTR pszActual = *ppszCurrent; pszActual--; while ((pszActual>(pszPath+1)) && (*pszActual!=DIR_SEPARATOR_CHAR)) { pszActual--; } if ((*pszActual==DIR_SEPARATOR_CHAR) && (pszActual!=*ppszCurrent)) { *ppszCurrent = pszActual; return TRUE; } return FALSE; } // -- Centralised method of tracking mutexes // class MUTEX_HOLDER MUTEX_HOLDER::MUTEX_HOLDER() { _hHandle = NULL; _dwState = WAIT_FAILED; } MUTEX_HOLDER::~MUTEX_HOLDER() { if (_hHandle) { INET_ASSERT(FALSE); TcpsvcsDbgPrint((DEBUG_CONTAINER, "ERROR: Releasing ownership of mutex %d\r\n", _hHandle)); ReleaseMutex(_hHandle); } } VOID MUTEX_HOLDER::Grab(HANDLE hHandle, DWORD dwTime) { INET_ASSERT(hHandle); _hHandle = hHandle; _dwState = WaitForSingleObject(_hHandle, dwTime); if (_dwState==WAIT_ABANDONED) { TcpsvcsDbgPrint((DEBUG_CONTAINER, "Gained ownership of abandoned mutex %d\r\n", hHandle)); } else if (_dwState==WAIT_OBJECT_0) { TcpsvcsDbgPrint((DEBUG_CONTAINER, "Gained ownership of mutex %d\r\n", hHandle)); } else { INET_ASSERT(FALSE); TcpsvcsDbgPrint((DEBUG_CONTAINER, "Unable to gain ownership of mutex %d\r\n", hHandle)); } } VOID MUTEX_HOLDER::Release() { if (_hHandle) { if (_dwState==WAIT_ABANDONED || _dwState==WAIT_OBJECT_0) { ReleaseMutex(_hHandle); TcpsvcsDbgPrint((DEBUG_CONTAINER, "Released ownership of mutex %d\r\n", _hHandle)); } else { TcpsvcsDbgPrint((DEBUG_CONTAINER, "Would release ownership of mutex %d, except we don't own it\r\n", _hHandle)); } } _hHandle = NULL; }