Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1524 lines
46 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name: filemgr.cxx
Abstract:
Manages cache file & directory creation/deletion.
Author:
Adriaan Canter (adriaanc) 04-02-97
Modifications:
Ahsan Kabir (akabir) 25-Sept-97 made minor alterations.
--*/
#include <cache.hxx>
#define WWW_DOT "www."
#define MAP_KEY_TO_PATH 0
#define MAP_PATH_TO_KEY 1
//
//==================== CFileMgr Public Functions =============================
//
/*-----------------------------------------------------------------------------
CFileMgr::CFileMgr
----------------------------------------------------------------------------*/
CFileMgr::CFileMgr(MEMMAP_FILE* mmFile, DWORD dwOptions) : _mmFile(mmFile), _dwOptions(dwOptions)
{
INET_ASSERT(_mmFile);
// GetFullPathNameLen includes trailing backslash.
_cbBasePathLen = _mmFile->GetFullPathNameLen();
}
/*-----------------------------------------------------------------------------
CFileMgr::~CFileMgr
----------------------------------------------------------------------------*/
CFileMgr::~CFileMgr()
{}
/*-----------------------------------------------------------------------------
virtual CFileMgr::Init
----------------------------------------------------------------------------*/
BOOL CFileMgr::Init()
{
return TRUE;
}
/*-----------------------------------------------------------------------------
virtual GetDirLen
Returns length of cache dir path.
----------------------------------------------------------------------------*/
DWORD CFileMgr::GetDirLen(DWORD nKey)
{
return _cbBasePathLen;
}
/*-----------------------------------------------------------------------------
virtual CFileMgr::CreateUniqueFile
Generates cache files.
----------------------------------------------------------------------------*/
DWORD CFileMgr::CreateUniqueFile(LPCSTR szUrl, LPTSTR szFileName,
LPTSTR szFileExtension, HANDLE *phfHandle, BOOL fCreatePerUser)
{
return CreateUniqueFile((LPCSTR) szUrl, (LPTSTR) _mmFile->GetFullPathName(),
(LPTSTR) szFileName, (LPTSTR) szFileExtension, (HANDLE*) phfHandle, fCreatePerUser);
}
/*-----------------------------------------------------------------------------
virtual CFileMgr::NotifyCommit
No-op.
----------------------------------------------------------------------------*/
BOOL CFileMgr::NotifyCommit(DWORD nDirIndex)
{
return TRUE;
}
/*-----------------------------------------------------------------------------
CFileMgr::DeleteCache
----------------------------------------------------------------------------*/
BOOL CFileMgr::DeleteCache(LPSTR szRoot)
{
BOOL fHasCacheVu = IsValidCacheSubDir(szRoot);
if ( fHasCacheVu)
DisableCacheVu(szRoot);
if (DeleteCachedFilesInDir(szRoot) == ERROR_SUCCESS)
{
SetFileAttributes(szRoot, FILE_ATTRIBUTE_DIRECTORY);
RemoveDirectory(szRoot);
}
if( fHasCacheVu)
EnableCacheVu( szRoot);
return TRUE;
}
/*-----------------------------------------------------------------------------
CFileMgr::Cleanup
----------------------------------------------------------------------------*/
BOOL CFileMgr::Cleanup()
{
return TRUE;
}
/*-----------------------------------------------------------------------------
virtual CFileMgr::GetDirIndex
----------------------------------------------------------------------------*/
BOOL CFileMgr::GetDirIndex(LPSTR szFilePath, LPDWORD pnIndex)
{
*pnIndex = NOT_A_CACHE_SUBDIRECTORY;
return TRUE;
}
/*-----------------------------------------------------------------------------
virtual CFileMgr::GetFilePathFromEntry
Retrieves the full path to the cache subdirectory for a cache entry.
Maps the directory index from the URL_FILEMAP_ENTRY pointer passed in
to a string containing the full path.
----------------------------------------------------------------------------*/
BOOL CFileMgr::GetFilePathFromEntry(URL_FILEMAP_ENTRY *pEntry,
LPSTR szSubDirPath, LPDWORD pcb)
{
INET_ASSERT(pEntry && szSubDirPath && pcb && *pcb);
// "[email protected]"
LPTSTR szFile = (LPTSTR) OFFSET_TO_POINTER(pEntry, pEntry->InternalFileNameOffset);
// Lengths of path and file.
DWORD cbFile = strlen(szFile);
DWORD cbPath = _mmFile->GetFullPathNameLen();
// Don't overflow output buffer.
DWORD cbSubDirPath = cbPath + cbFile;
if (cbSubDirPath + 1 > *pcb)
{
INET_ASSERT(FALSE);
return FALSE;
}
// "C:\Windows\Profiles\anyuser\Cookies\"
memcpy(szSubDirPath, _mmFile->GetFullPathName(), cbPath);
// "C:\Windows\Profiles\anyuser\Cookies\[email protected]"
memcpy(szSubDirPath + cbPath, szFile, cbFile + 1);
*pcb = cbSubDirPath;
return TRUE;
}
/*-----------------------------------------------------------------------------
virtual CFileMgr::DeleteOneCachedFile
Deletes a single cache file given the absolute path.
----------------------------------------------------------------------------*/
BOOL CFileMgr::DeleteOneCachedFile(LPSTR lpszFileName,
DWORD dostEntry, DWORD nIndex)
{
return ::DeleteOneCachedFile(lpszFileName, dostEntry);
}
/*-----------------------------------------------------------------------------
virtual BOOL CreateDirWithSecureName( LPSTR);
Creates a cache directory with a given name to allow existing directories
to be copied into another cache file. Just the eight letters of the new
directory are given.
----------------------------------------------------------------------------*/
BOOL CFileMgr::CreateDirWithSecureName( LPSTR szDirName)
{
return _mmFile->CreateDirWithSecureName( szDirName);
}
//
//================== CFileMgr Protected Functions =============================
//
/*-----------------------------------------------------------------------------
CFileMgr::GetStoreDirectory
Returns "%windir%\web\" - ie "C:\Windows\Web\" and length. There
is currently only ONE store directory and this is it.
----------------------------------------------------------------------------*/
BOOL CFileMgr::GetStoreDirectory(LPSTR szPath, LPDWORD pcbPath)
{
DWORD cb;
// Hardwired to "%windir%\Web\"
if ((cb = GetWindowsDirectory(szPath, MAX_PATH)) && cb<=MAX_PATH)
{
AppendSlashIfNecessary(szPath, &cb);
memcpy(szPath + cb, WEBDIR_STRING, sizeof(WEBDIR_STRING));
cb += sizeof(WEBDIR_STRING) - 1;
*pcbPath = cb;
return TRUE;
}
INET_ASSERT(FALSE);
return FALSE;
}
/*-----------------------------------------------------------------------------
CFileMgr::MapStoreKey
Maps path to storage directory key (stored in the FILEMAP_ENTRY::DirIndex)
or storage directory key to path (ie C:\Windows\Web\). There is currently
only one key and it is INSTALLED_DIRECTORY_KEY. Mapping depends on dwFlag.
----------------------------------------------------------------------------*/
BOOL CFileMgr::MapStoreKey(LPSTR szPath, LPDWORD pcbPath,
LPDWORD dwKey, DWORD dwFlag)
{
DWORD cb;
BOOL fReturn = FALSE;
CHAR szStoreDir[MAX_PATH];
// Must be able to get store dir in any case.
if (GetStoreDirectory(szStoreDir, &cb))
{
// Mapping a path to a key requested.
if (dwFlag == MAP_PATH_TO_KEY)
{
// Path matches?
if ((*pcbPath == cb)
&& !strnicmp(szStoreDir, szPath, cb))
{
// We only map one directory for now.
*dwKey = INSTALLED_DIRECTORY_KEY;
fReturn = TRUE;
}
}
// Mapping a key to a path requested.
else if (dwFlag == MAP_KEY_TO_PATH)
{
if (*dwKey == INSTALLED_DIRECTORY_KEY)
{
memcpy(szPath, szStoreDir, cb+1);
*pcbPath = cb;
fReturn = TRUE;
}
}
}
//INET_ASSERT(fReturn);
return fReturn;
}
/*-----------------------------------------------------------------------------
CreateUniqueFile
Routine Description:
Arguments:
UrlName : pointer to url name.
Path : pointer to cache path.
FileName : pointer to a buffer that receives the full path name of the
newly created file.
Extension : if specified the extension is used to make random file.
Return Value:
Windows Error Code.
----------------------------------------------------------------------------*/
DWORD CFileMgr::CreateUniqueFile(LPCSTR UrlName, LPTSTR Path,
LPTSTR FileName, LPTSTR Extension,
HANDLE *phfHandle, BOOL fCreatePerUser)
{
DWORD cbPath, cbName, cbFull;
cbPath = strlen(Path);
DWORD Error=ERROR_SUCCESS, len, lenExt=0;
TCHAR RandomFileName[MAX_PATH];
TCHAR FullFileName[MAX_PATH];
HANDLE FileHandle;
DWORD dwCollision = 0, dwTotalCollissionCount;
char szHost[MAX_PATH], szExtraInfo[MAX_PATH];
URL_COMPONENTS sUrl;
char *pszUrlPath = (char *) ALLOCATE_FIXED_MEMORY(INTERNET_MAX_PATH_LENGTH);
LPTSTR FileNamePtr = FileName, lpT;
BOOL fUseFileName = FALSE, fPrettyName = FALSE;
DWORD cbFileName;
CHAR szExt[MAX_PATH];
*szExt = '\0';
if (phfHandle)
*phfHandle = INVALID_HANDLE_VALUE;
if (pszUrlPath == NULL)
{
Error = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
// If a filename has been passed in attempt to use it.
if (FileName[0] != '\0')
{
// Various checks to determine validity of name.
// First strip any trailing whitespace.
cbFileName = strlen(FileName);
StripTrailingWhiteSpace(FileName, &cbFileName);
// Check length.
if (cbFileName < MAX_PATH)
{
// '.' and '..' are illegal.
if (memcmp(FileName, ".", sizeof("."))
&& memcmp(FileName, "..", sizeof("..")))
{
// slashes and backslashes are illegal.
LPTSTR ptr = FileName;
while (*ptr != '\0')
{
if (IsDBCSLeadByte(*ptr))
++ptr;
else
if (*ptr == '\\' || *ptr == '/')
break;
ptr++;
}
// Filename has no slashes in it.
if (!*ptr)
{
// Preliminary judgment. Creating
// this file could possibly fail,
// depending on further tests.
fUseFileName = TRUE;
}
}
}
}
// Preliminary checks indicate valid filename.
if (fUseFileName)
{
// Attempt to parse a file extension.
CHAR* pExt = StrChr(FileName, '.');
// Found a file extension delimiter.
if (pExt)
{
// '.' but no extension (eg "foo.")
if (*(pExt + 1) == '\0')
{
*pExt = '\0';
len = cbFileName - 1;
}
// '.' at beginning (eg ".foo") Valid file, no extension.
else if (pExt == FileName)
{
len = cbFileName;
}
// Common case (eg foo.bar)
else
{
// Separate out the file extension w/o '.'
lenExt = (DWORD) (cbFileName - (pExt - FileName) - 1); // 64BIT
memcpy(szExt, pExt+1, lenExt + 1);
// Filename without extension.
*pExt = '\0';
len = (DWORD) (pExt - FileName); // 64BIT
}
}
// No file extension found
else
{
len = cbFileName;
}
fPrettyName = TRUE;
goto have_file_name;
}
// No or bad filename passed in.
else
{
// Copy over any extension passed in,
// limiting the length as necessary.
if (Extension)
{
lenExt = strlen(Extension);
if (lenExt >= MAX_PATH)
{
lenExt = MAX_PATH - 1;
}
memcpy(szExt, Extension, lenExt);
szExt[lenExt] = '\0';
}
else
{
*szExt = '\0';
lenExt = 3;
}
}
memset(&sUrl, 0, sizeof(sUrl));
sUrl.dwStructSize = sizeof(sUrl);
sUrl.lpszHostName = szHost;
sUrl.dwHostNameLength = sizeof(szHost);
sUrl.lpszUrlPath = pszUrlPath;
sUrl.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
sUrl.lpszExtraInfo = szExtraInfo;
sUrl.dwExtraInfoLength = sizeof(szExtraInfo);
if (InternetCrackUrl(UrlName, lstrlen(UrlName), 0, &sUrl)) {
fPrettyName = TRUE;
if ((sUrl.dwUrlPathLength == 1) && (pszUrlPath[0] == '/')) {
FileNamePtr = szHost;
len = sUrl.dwHostNameLength;
// strip out www., this info is redundant
if (!strnicmp(FileNamePtr, WWW_DOT, sizeof(WWW_DOT)-1)) {
len -= (sizeof(WWW_DOT)-1);
// copy the NULL terminator too
memmove(FileNamePtr, FileNamePtr+sizeof(WWW_DOT)-1,len+1);
}
}
else {
FileNamePtr = pszUrlPath;
len = sUrl.dwUrlPathLength;
// if there is a terminating slash let us fix it.
// len-1 wont' break because we know the url is more than 1 char
// Above assumption not valid.
if (len && (FileNamePtr[len-1] == '/'))
{
FileNamePtr[len-1] = 0;
--len;
}
// get the tail
if (lpT=StrRChrA(FileNamePtr, FileNamePtr+len-1, '/'))
{
++lpT;
len = len - (DWORD)PtrDifference(lpT, FileNamePtr);
//
// truncate the FileNamePtr if it is too long -
// the "fudge-factor" number is to allow room for stuff like "[%d]"
// in the URL -- this number doesn't have to be accurate since
// the worst-case scenario is us using a random (ugly) filename.
//
unsigned int newlen;
if(GlobalTruncateFileName)
newlen = /*excel limit*/218 - (cbPath + lenExt + 2 + /*fudge-factor*/5);
else
newlen = MAX_PATH - (cbPath + lenExt + 2 + /*fudge-factor*/5);
if ((newlen > 2) && (newlen < len))
{
// For UTF-8, we don't want to chop in the middle of a %XX
if (lpT[newlen - 2] == '%')
newlen -= 2;
else if (lpT[newlen - 1] == '%')
newlen -= 1;
len = newlen;
lpT[len] = '\0';
}
// copy the NULL terminator as well
memmove(FileNamePtr, lpT, len+1);
}
// Special hack for cookies: Ensure that the username is
// prepended on to the filename. The preceding filename
// generation code does not generate this for cookies
// which specify paths in addition to domains.
if (!memcmp(UrlName, COOKIE_PREFIX, sizeof(COOKIE_PREFIX) - 1))
{
// This is a cookie url of the form Cookie:username@domain/path
if (GetWininetUserName())
{
DWORD cb = vdwCurrentUserLen;
if (len + cb + 1 < MAX_PATH)
{
if (memcmp(FileNamePtr, vszCurrentUser, cb)
|| FileNamePtr[cb] != '@'
|| FileNamePtr[cb+1] == '\0')
{
memmove(FileNamePtr + cb + 1, FileNamePtr, len+1);
FileNamePtr[cb] = '@';
memcpy(FileNamePtr, vszCurrentUser, cb);
len += cb + 1;
}
}
}
}
}
if (lpT=StrRChrA(FileNamePtr, FileNamePtr+len-1, '.'))
{
*lpT = 0;
len = (DWORD) PtrDifference(lpT, FileNamePtr);
}
// convert all invalid char (including '%') to '_'
for(lpT = FileNamePtr; *lpT; ++lpT)
{
if (IsDBCSLeadByte(*lpT))
++lpT;
else if ((strchr(vszInvalidFilenameChars, *lpT)))
*lpT = '_';
}
if ((cbPath+len+lenExt+2) > MAX_PATH) {
fPrettyName = FALSE;
}
}
else {
fPrettyName = FALSE;
}
have_file_name:
for(dwTotalCollissionCount = 0;
dwTotalCollissionCount < MAX_COLLISSION_ATTEMPTS;
dwTotalCollissionCount++) {
//
// make a random file name.
//
if (!fPrettyName)
{
// If fUseFileName is TRUE, it means we've attempted to create
// a file using the filename passed in and failed. We still want
// to create a cache file, but since the extension parsed from the
// filename is also suspect, we want to create a cache filename
// without any passed in extension, or NULL.
if (fUseFileName)
{
if (Extension)
{
lenExt = strlen(Extension);
memcpy(szExt, Extension, lenExt+1);
}
else
{
lenExt = 0;
*szExt = '\0';
}
}
Error = MakeRandomFileName(UrlName, RandomFileName, szExt);
}
else {
DWORD digit;
DWORD cb = strlen(FileNamePtr);
memcpy(RandomFileName, FileNamePtr, cb+1);
lpT = RandomFileName+len;
// Always decorate the cache file name with [1-99]
// We use square brackets instead of parens because
// NT cmd shell barfs on parens.
if (++dwCollision > 99)
{
fPrettyName = FALSE;
continue;
}
#ifndef UNIX
if (fCreatePerUser && GlobalIdentity)
{
lpT += wsprintf (lpT, "[%d][%d]", GlobalIdentity, dwCollision);
}
else
{
lpT += wsprintf (lpT, "[%d]", dwCollision);
}
#else
/* Square brackets cause problems on UNIX */
lpT += wsprintf (lpT, "(%d)", dwCollision);
#endif /* UNIX */
if (*szExt)
{
*lpT++ = '.';
memcpy(lpT, szExt, lenExt + 1);
}
Error = ERROR_SUCCESS;
}
if (Error != ERROR_SUCCESS) {
INET_ASSERT(FALSE);
goto Cleanup;
}
cbName = strlen(RandomFileName);
cbFull = cbPath + cbName + 1;
if (cbFull > MAX_PATH)
{
INET_ASSERT(FALSE);
Error = ERROR_FILENAME_EXCED_RANGE;
goto Cleanup;
}
#ifndef UNIX
// Hack for special DOS filenames:
// aux.*, com[0-9].*, con.*, lpt[0-9].*,
// nul.* and prn.* on non-NT platforms.
if (!IsPlatformWinNT())
{
DWORD cbMajor = cbName - lenExt;
if (cbMajor == 4 || cbMajor == 5)
{
switch(tolower(*RandomFileName))
{
// Test for aux.*
case 'a':
if (!strnicmp(RandomFileName + 1, "ux.", 3))
{
continue;
}
break;
// Test for com[0-9].* and con.*
case 'c':
if (tolower(RandomFileName[1]) == 'o')
{
CHAR c = tolower(RandomFileName[2]);
if (c == 'm')
{
if (isdigit(RandomFileName[3])
&& RandomFileName[4] == '.')
{
continue;
}
}
else if (c == 'n')
{
if (RandomFileName[3] == '.')
{
continue;
}
}
}
break;
// Test for lpt[0-9].*
case 'l':
{
if (!strnicmp(RandomFileName + 1, "pt", 2)
&& isdigit(RandomFileName[3])
&& RandomFileName[4] == '.')
{
continue;
}
break;
}
// Test for nul.*
case 'n':
{
if (!strnicmp(RandomFileName + 1, "ul.", 3))
{
continue;
}
break;
}
// Test for prn.*
case 'p':
{
if (!strnicmp(RandomFileName + 1, "rn.", 3))
{
continue;
}
break;
}
}
}
}
#endif /* !UNIX */
// Make full path name.
memcpy(FullFileName, Path, cbPath);
memcpy(FullFileName + cbPath, RandomFileName, cbName + 1);
// Check if this file exists.
if (GetFileAttributes(FullFileName)!=0xffffffff)
{
// A file or dir by this name exists.
// This will also take care of special DOS filenames
// on NT, which return !0xffffffff.
continue;
}
FileHandle = CreateFile(
FullFileName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( FileHandle != INVALID_HANDLE_VALUE )
{
// successfully create a new file either return handle or close it and return.
if ( phfHandle )
*phfHandle = FileHandle;
else
CloseHandle( FileHandle );
break;
}
else
{
// Couldn't create the file. This is possibly due to the file
// already existing or to the fact that the directory was deleted.
// Check for the existance of the directory:
if (GetFileAttributes(Path) == 0xffffffff)
{
// Directory was deleted. Create one and then
// create the file.
if (CreateDirectory(Path, NULL))
{
// Set system attribute.
SetFileAttributes(Path, FILE_ATTRIBUTE_SYSTEM);
// Enable cachevu in this directory
if (!(GetOptions() & INTERNET_CACHE_CONTAINER_NODESKTOPINIT))
EnableCacheVu(Path);
FileHandle = CreateFile(
FullFileName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL );
// We just created the directory and the
// child file, so the file handle should
// be valid.
if( FileHandle != INVALID_HANDLE_VALUE )
{
// successfully create a new file either return handle or close it and return.
if ( phfHandle )
*phfHandle = FileHandle;
else
CloseHandle( FileHandle );
break;
}
}
}
}
Error = GetLastError();
if( Error != ERROR_FILE_EXISTS )
{
if (!fPrettyName)
{
INET_ASSERT(FALSE);
goto Cleanup;
}
else
{
fPrettyName = FALSE;
Error = ERROR_SUCCESS;
}
}
else {
// We found that the file exists
// if it is zero size, let us just use it.
// this in itself is an unlikely occurrence
// but we any way try to work around the IBM virus software
// ACHTUNG!!! this is a special hack for IBM antivirus software
FileHandle = CreateFile(
FullFileName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (FileHandle != INVALID_HANDLE_VALUE) {
// this looks usable
if (GetFileSize(FileHandle, NULL) == 0)
{
if (phfHandle)
*phfHandle = FileHandle;
else
CloseHandle( FileHandle );
break;
}
CloseHandle( FileHandle );
INET_ASSERT(FALSE);
}
Error = ERROR_DISK_FULL;
goto Cleanup;
}
//
// try another random file.
//
} // end of the for loop
if (dwTotalCollissionCount < MAX_COLLISSION_ATTEMPTS) {
memcpy(FileName, FullFileName, cbFull);
Error = ERROR_SUCCESS;
}
else {
INET_ASSERT(FALSE);
Error = ERROR_DISK_OPERATION_FAILED;
}
Cleanup:
if (pszUrlPath)
FREE_MEMORY(pszUrlPath);
return Error;
}
/*-----------------------------------------------------------------------------
MakeRandomFileName
Routine Description:
Creates a random 8.3 file name. The format of the name will be as
below:
ca(0-99999).(0-999)
Ex ca19200.340
Ex ca19354.tmp - if an extension (tmp) is specified.
Arguments:
UrlName : pointer to an URL string
FileName : pointer to a string buffer where the random file name is
returned. The buffer length must be atleast 8+3+1+1= 13 wchars.
Extension : pointer to an extension string. if this is non-NULL, then
the specified extension is used otherwise random extension as
explained above is used.
Return Value:
none.
----------------------------------------------------------------------------*/
DWORD CFileMgr::MakeRandomFileName(LPCSTR UrlName,
LPTSTR FileName, LPTSTR Extension)
{
DWORD RandNum;
LPTSTR FileNamePtr = FileName;
static Counter;
DWORD i;
DWORD cbExtension = 0;
if (Extension)
cbExtension = lstrlen(Extension) + 1;
if (cbExtension > (MAX_PATH-(8+1)))
{
return(ERROR_FILENAME_EXCED_RANGE);
}
// Additional special hack for cookie urls.
if (!memcmp(UrlName, COOKIE_PREFIX, sizeof(COOKIE_PREFIX) - 1))
{
// This is a cookie url of the form Cookie:username@domain/path
if (GetWininetUserName())
{
DWORD cb = vdwCurrentUserLen;
if (cb + 8 + cbExtension + 1 < MAX_PATH)
{
memcpy(FileName, vszCurrentUser, cb);
FileName[cb] = '@';
FileNamePtr += (cb + 1);
}
}
}
// Check that the total name doesn't exceed MAX_PATH
// Our total name is 8 chars basename + a dot + the extension + 0
*FileNamePtr++ = L'C';
*FileNamePtr++ = L'A';
//
// generate a six digits random string;
//
//
// We can't use rand() alone to generate a random number because it will
// repeat the same sequence for each new thread that comes in. We can't
// use the TickCount alone because it is a little too predicable. But
// the two combined should be nice. Adding in Counter will take care of
// the case of two brand-new threads coming in at the same time.
//
for ( i = 0; i < 6; i++) {
UINT digit;
RandNum = (GetTickCount() * rand()) + Counter++;
digit = RandNum % 36; // 10 digits + 26 letters
*FileNamePtr++ = ( digit < 10 ) ? (CHAR)('0' + digit) : (CHAR)('A' + (digit - 10));
}
*FileNamePtr++ = L'.';
//
// if an extension is specified, use it.
//
if( Extension != NULL )
{
// if a 0 extension if provided, we will create a
// file with no extension
memcpy(FileNamePtr, Extension, cbExtension);
return ERROR_SUCCESS;
}
// Append default file extension.
memcpy(FileNamePtr, DEFAULT_FILE_EXTENSION, sizeof(DEFAULT_FILE_EXTENSION));
return ERROR_SUCCESS;
}
//
//===================== CSecFileMgr Public Functions ==========================
//
/*-----------------------------------------------------------------------------
CSecFileMgr::CSecFileMgr
----------------------------------------------------------------------------*/
CSecFileMgr::CSecFileMgr(MEMMAP_FILE* mmFile, DWORD dwOptions) : CFileMgr(mmFile, dwOptions)
{
INET_ASSERT(_mmFile);
// BUGBUG - have to guard against getting out of sync with dirs.
if (_mmFile->GetDirCount() == 0)
Init();
}
/*-----------------------------------------------------------------------------
CSecFileMgr::~CSecFileMgr
----------------------------------------------------------------------------*/
CSecFileMgr::~CSecFileMgr()
{}
/*-----------------------------------------------------------------------------
CSecFileMgr::Init
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::Init()
{
if (!(GetOptions() & INTERNET_CACHE_CONTAINER_NODESKTOPINIT))
EnableCacheVu(_mmFile->GetFullPathName());
return CreateAdditionalSubDirectories(DEFAULT_DIR_TABLE_GROW_SIZE);
}
/*-----------------------------------------------------------------------------
GetDirLen()
Returns cache dir path length.
----------------------------------------------------------------------------*/
DWORD CSecFileMgr::GetDirLen(DWORD nKey)
{
DWORD cb = 0;
if (nKey < DEFAULT_MAX_DIRS)
{
// + 1 to account for trailing backslash.
cb = _cbBasePathLen + DIR_NAME_SIZE + 1;
}
else
{
CHAR szStoreDir[MAX_PATH];
GetStoreDirectory(szStoreDir, &cb);
}
INET_ASSERT(cb);
return cb;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::CreateUniqueFile
Creates a cache file.
----------------------------------------------------------------------------*/
DWORD CSecFileMgr::CreateUniqueFile(LPCSTR szUrl, LPTSTR szFileName,
LPTSTR szFileExtension, HANDLE *phfHandle, BOOL fCreatePerUser)
{
DWORD nDir, nFiles;
DWORD nDirCount = _mmFile->GetDirCount();
INET_ASSERT(nDirCount <= DEFAULT_MAX_DIRS);
FindMinFilesSubDir(nDir, nFiles);
if (nFiles >= MAX_FILES_PER_CACHE_DIRECTORY
&& nDirCount < DEFAULT_MAX_DIRS)
{
if (CreateAdditionalSubDirectories(DEFAULT_DIR_TABLE_GROW_SIZE))
nDir++;
}
// Get the cache path and subdirectory
// from the memory mapped file
CHAR szSubDirPath[MAX_PATH];
DWORD cb = _mmFile->GetFullPathNameLen();
memcpy(szSubDirPath, _mmFile->GetFullPathName(), cb);
_mmFile->GetDirName(nDir, szSubDirPath + cb);
memcpy(szSubDirPath + cb + DIR_NAME_SIZE, DIR_SEPARATOR_STRING, sizeof(DIR_SEPARATOR_STRING));
return CFileMgr::CreateUniqueFile((LPCSTR) szUrl, (LPTSTR) szSubDirPath,
(LPTSTR) szFileName, (LPTSTR) szFileExtension,
(HANDLE*) phfHandle, fCreatePerUser);
}
/*-----------------------------------------------------------------------------
CSecFileMgr::NotifyCommit
Tracks committed cache file counts.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::NotifyCommit(DWORD nDirIndex)
{
INET_ASSERT(_mmFile->GetDirCount() <= DEFAULT_MAX_DIRS);
// Regular random subdir
if (nDirIndex < _mmFile->GetDirCount())
{
_mmFile->IncrementFileCount(nDirIndex);
return TRUE;
}
// Not a directory.
else if (nDirIndex == NOT_A_CACHE_SUBDIRECTORY)
{
//INET_ASSERT(FALSE);
//return FALSE;
// May be an absolute path EDITED_CACHE_ENTRY so pass
return TRUE;
}
// Otherwise this had better be an installed directory.
INET_ASSERT(nDirIndex == INSTALLED_DIRECTORY_KEY);
return TRUE;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::Cleanup
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::Cleanup()
{
CHAR szPath[MAX_PATH];
DWORD cb = _mmFile->GetFullPathNameLen();
memcpy(szPath, _mmFile->GetFullPathName(), cb+1);
if (!AppendSlashIfNecessary(szPath, &cb))
return FALSE;
memcpy(szPath + cb, "*.*", sizeof("*.*"));
WIN32_FIND_DATA fd;
HANDLE handle = FindFirstFile(szPath, &fd);
if (handle == INVALID_HANDLE_VALUE)
return FALSE;
do
{
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
&& (strcmp(fd.cFileName, ".") && strcmp(fd.cFileName, "..")))
{
memcpy(szPath + cb, fd.cFileName, strlen(fd.cFileName) + 1);
// Only delete directory if it is a valid subdirectory.
if (IsValidCacheSubDir(szPath))
{
if (_mmFile->GetDirIndex(szPath) == NOT_A_CACHE_SUBDIRECTORY)
{
DisableCacheVu(szPath);
if (DeleteCachedFilesInDir(szPath) == ERROR_SUCCESS)
{
SetFileAttributes(szPath, FILE_ATTRIBUTE_DIRECTORY);
RemoveDirectory(szPath);
}
}
}
}
} while (FindNextFile(handle, &fd));
FindClose(handle);
return TRUE;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::GetDirIndex
Returns index of random cache subdirectory from an absolute file path.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::GetDirIndex(LPSTR szAbsPath, LPDWORD pnIndex)
{
BOOL fReturn = FALSE;
DWORD idx;
INET_ASSERT(pnIndex);
if (szAbsPath && *szAbsPath)
{
// First look in mem map file for regular dir.
idx = _mmFile->GetDirIndex(szAbsPath);
// If didn't find it in the mem map file,
// check if it is the storage directory.
if (idx == NOT_A_CACHE_SUBDIRECTORY)
{
// First we need to find the path to the file sans \filename.ext
DWORD cbAbsPath = strlen(szAbsPath);
LPSTR ptr = StrRChr(szAbsPath, szAbsPath + cbAbsPath, DIR_SEPARATOR_CHAR);
if (ptr)
{
// Separate path from filename and attempt to map.
// Note - trailing slash is included in path mapped.
DWORD cbPath = (DWORD) (ptr - szAbsPath + 1); // 64BIT
if (MapStoreKey(szAbsPath, &cbPath, &idx, MAP_PATH_TO_KEY))
{
*pnIndex = idx;
fReturn = TRUE;
}
// Must be an EDITED_CACHE_ENTRY set at an absolute path so just update the idx and fail
else
{
*pnIndex = NOT_A_CACHE_SUBDIRECTORY;
}
}
}
// Otherwise, this should be a valid cache subdirectory.
else
{
*pnIndex = idx;
fReturn = TRUE;
}
}
else
{
*pnIndex = NOT_A_CACHE_SUBDIRECTORY;
}
return fReturn;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::GetFilePathFromEntry
Retrieves the full path to the cache subdirectory for a cache entry.
Maps the directory index from the URL_FILEMAP_ENTRY pointer passed in
to a string containing the full path.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::GetFilePathFromEntry(URL_FILEMAP_ENTRY *pEntry,
LPSTR szAbsPath, LPDWORD pcb)
{
DWORD cbSubDirPath, cbFile;
INET_ASSERT(pEntry && szAbsPath && pcb && *pcb);
// Get file name and length - eg "default.html"
LPTSTR szFile = (LPTSTR) OFFSET_TO_POINTER(pEntry, pEntry->InternalFileNameOffset);
cbFile = strlen(szFile);
// Make real time check?
if (cbFile && (cbFile < MAX_PATH))
{
// Does entry reside in on of the random subdirs?
if (pEntry->DirIndex != NOT_A_CACHE_SUBDIRECTORY
&& pEntry->DirIndex < DEFAULT_MAX_DIRS)
{
// Path length.
DWORD cbFull = _mmFile->GetFullPathNameLen();
// Don't overflow output buffer.
cbSubDirPath =
cbFull
+ DIR_NAME_SIZE
+ sizeof(DIR_SEPARATOR_STRING) - 1
+ cbFile;
if (cbSubDirPath + 1 > *pcb)
{
// INET_ASSERT(FALSE);
return FALSE;
}
// "C:\Windows\Profiles\anyuser\Temporary Internet Files\"
memcpy(szAbsPath, _mmFile->GetFullPathName(), cbFull);
// "C:\Windows\Profiles\anyuser\Temporary Internet Files\XAQRTLY7"
_mmFile->GetDirName(pEntry->DirIndex, szAbsPath + cbFull);
// "C:\Windows\Profiles\anyuser\Temporary Internet Files\XAQRTLY7\"
memcpy(szAbsPath + cbFull + DIR_NAME_SIZE, DIR_SEPARATOR_STRING, sizeof (DIR_SEPARATOR_STRING));
// "C:\Windows\Profiles\anyuser\Temporary Internet Files\XAQRTLY7\default.htm"
memcpy(szAbsPath + cbFull + DIR_NAME_SIZE + sizeof(DIR_SEPARATOR_STRING) - 1, szFile, cbFile + 1);
}
// There is no cache subdirectory which has been can be mapped
// from the index. See if there is an existing store mapping.
else
{
if (pEntry->DirIndex == NOT_A_CACHE_SUBDIRECTORY)
// Assume an ECE absolute path item as AddURL only writes NACS entries with ECE set
{
cbSubDirPath = cbFile + 1; // Add terminator to size
if (cbSubDirPath > *pcb)
{
INET_ASSERT(FALSE);
return FALSE;
}
memcpy(szAbsPath, szFile, cbSubDirPath);
}
else
{
INET_ASSERT(pEntry->DirIndex == INSTALLED_DIRECTORY_KEY);
DWORD cbPath = 0;
DWORD dwIndex = INSTALLED_DIRECTORY_KEY;
if (MapStoreKey(szAbsPath, &cbPath, &dwIndex, MAP_KEY_TO_PATH))
{
// "C:\Winnt\Web\"
cbSubDirPath = cbPath + cbFile;
if (cbSubDirPath + 1 > *pcb)
{
INET_ASSERT(FALSE);
return FALSE;
}
// "C:\Winnt\Web\default.html"
memcpy(szAbsPath + cbPath, szFile, cbFile + 1);
}
else
{
INET_ASSERT(FALSE);
return FALSE;
}
}
}
// Hand out the absolute path to the file.
*pcb = cbSubDirPath;
return TRUE;
}
INET_ASSERT(FALSE);
return FALSE;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::DeleteOneCachedFile
Deletes one cache file and decrements the file count.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::DeleteOneCachedFile(LPSTR lpszFileName,
DWORD dostEntry, DWORD nDirIndex)
{
if (!::DeleteOneCachedFile(lpszFileName, dostEntry))
return FALSE;
INET_ASSERT(nDirIndex != NOT_A_CACHE_SUBDIRECTORY);
_mmFile->DecrementFileCount(nDirIndex);
return TRUE;
}
//
//==================== CSecFileMgr Protected Functions =======================
//
/*-----------------------------------------------------------------------------
CSecFileMgr::CreateRandomDirName
Creates a random subdirectory name under the root container path.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::CreateRandomDirName(LPSTR szDirName)
{
DWORD RandNum;
LPSTR ptr = szDirName;
static Counter;
INET_ASSERT(szDirName);
// Stolen from MakeRandomFileName.
for (DWORD i = 0; i < DIR_NAME_SIZE; i++)
{
UINT digit;
RandNum = (GetTickCount() * rand()) + Counter++;
// 10 digits + 26 letters
digit = RandNum % 36;
*ptr++ = ( digit < 10 ) ? (CHAR)('0' + digit) : (CHAR)('A' + (digit - 10));
}
*ptr = '\0';
return TRUE;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::CreateAdditionalSubDirectories
Creates nAdditionalDirs random subdirectories, up to DEFAULT_MAX_DIRS.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::CreateAdditionalSubDirectories(DWORD nAdditionalDirs)
{
DWORD nTotalDirs;
DWORD nDirCount = _mmFile->GetDirCount();
BOOL bSuccess = TRUE;
INET_ASSERT(nDirCount <= DEFAULT_MAX_DIRS);
// Don't create more than the max allowed dirs.
nTotalDirs = nAdditionalDirs + nDirCount;
INET_ASSERT(nTotalDirs <= DEFAULT_MAX_DIRS);
// Create the dir and set the file count to 0.
DWORD i = nDirCount;
DWORD nTotalTries = 0;
do
{
if (CreateSubDirectory(i))
{
_mmFile->SetFileCount(i, 0);
_mmFile->IncrementDirCount();
i++;
}
else
{
INET_ASSERT(FALSE);
bSuccess = FALSE;
}
if (nTotalTries++ > 100)
{
bSuccess = FALSE;
break;
}
} while (i < nTotalDirs);
return bSuccess;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::CreateSubDirectory(DWORD nIdx)
Actual creation of subdirectory.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::CreateSubDirectory(DWORD nIdx)
{
CHAR szCacheDir[MAX_PATH];
CHAR szSubDir[DIR_NAME_SIZE + 1];
// Generate full path to random dir.
CreateRandomDirName(szSubDir);
DWORD cb = _mmFile->GetFullPathNameLen();
memcpy(szCacheDir, _mmFile->GetFullPathName(), cb);
memcpy(szCacheDir + cb, szSubDir, DIR_NAME_SIZE + 1);
// Create the directory and add it to
// the list of directories in the index.
if (CreateDirectory(szCacheDir, NULL))
{
_mmFile->SetDirName(nIdx, szSubDir);
// For cachevu must be hidden and system.
// BUGBUG - sure it must be hidden?
SetFileAttributes(szCacheDir, FILE_ATTRIBUTE_SYSTEM);
if (!(GetOptions() & INTERNET_CACHE_CONTAINER_NODESKTOPINIT))
EnableCacheVu(szCacheDir);
}
else
{
// Couldn't create the directory.
INET_ASSERT(FALSE);
return FALSE;
}
return TRUE;
}
/*-----------------------------------------------------------------------------
CSecFileMgr::FindMinFilesSubDir
Determines the cache subdirectory with the minimum file count for load balancing.
----------------------------------------------------------------------------*/
BOOL CSecFileMgr::FindMinFilesSubDir(DWORD& nMinFileDir, DWORD& nFiles)
{
DWORD nDirCount = _mmFile->GetDirCount();
if (nDirCount == 0 || nDirCount > DEFAULT_MAX_DIRS)
{
INET_ASSERT(FALSE);
_mmFile->SetDirCount(0);
CreateAdditionalSubDirectories(DEFAULT_DIR_TABLE_GROW_SIZE);
nDirCount = _mmFile->GetDirCount();
}
nMinFileDir = 0;
DWORD nMinFiles = _mmFile->GetFileCount(0);
for (DWORD i = 1; i < nDirCount; i++)
{
if (_mmFile->GetFileCount(i) < nMinFiles)
{
nMinFiles = _mmFile->GetFileCount(i);
nMinFileDir = i;
}
}
nFiles = _mmFile->GetFileCount(nMinFileDir);
return TRUE;
}