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.
 
 
 
 
 
 

877 lines
24 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name :
getdirp.cxx
Abstract:
This module implements the functions for getting directory listings
and transparently caching them.
( This uses OS specific functions to obtain the directory).
Author:
Murali R. Krishnan ( MuraliK ) 13-Jan-1995
Project:
Tsunami Lib
( Common caching and directory functions for Internet Services)
Functions Exported:
BOOL TsGetDirectoryListing()
BOOL TsFreeDirectoryListing()
int __cdecl
AlphaCompareFileBothDirInfo(
IN const void * pvFileInfo1,
IN const void * pvFileInfo2)
TS_DIRECTORY_HEADER::ReadWin32DirListing( IN LPCSTR pszDirectoryName)
TS_DIRECTORY_HEADER::ReadFromNtDirectoryFile(
IN LPCWSTR pwszDirectoryName
)
TS_DIRECTORY_HEADER::BuildFileInfoPointers(
IN LPCWSTR pwszDirectoryName
)
TS_DIRECTORY_HEADER::CleanupThis()
Revision History:
MuraliK 06-Dec-1995 Used Win32 apis instead of NT apis
MCourage 09-Jan-1998 Stopped caching directory information
Removed "guardian blobs"
--*/
/************************************************************
* Include Headers
************************************************************/
# include "tsunamip.hxx"
# include <stdlib.h>
# include <string.h>
# include <dbgutil.h>
/************************************************************
* Type Definitions
************************************************************/
# define DBGCODE(s) DBG_CODE(s)
#define DIRECTORY_BUFFER_SIZE 8160 /* < 8192 bytes */
/************************************************************
* Functions
************************************************************/
BOOL FreeDirectoryHeaderContents( PVOID pvOldBlock );
PTS_DIRECTORY_HEADER
TsGetFreshDirectoryHeader(
IN const TSVC_CACHE & tsCache,
IN LPCSTR pszDirectoryName,
IN HANDLE hLisingUser);
dllexp
BOOL
TsGetDirectoryListing(
IN const TSVC_CACHE &tsCache,
IN PCSTR pszDirectoryName,
IN HANDLE hListingUser,
OUT PTS_DIRECTORY_HEADER * ppTsDirectoryHeader
)
/*++
This function obtains the directory listing for dir specified
in pszDirectoryName.
Arguments:
tsCache Cache structure which is used for lookup
pszDirectoryName pointer to string containing the directory name
ListingUser Handle for the user opening the directory
ppTsDirectoryHeader
pointer to pointer to class containing directory information.
Filled on successful return. On failure this will be NULL
Returns:
TRUE on success and FALSE if there is a failure.
--*/
{
PVOID pvBlob = NULL;
ULONG ulSize = 0;
BOOL bSuccess;
ASSERT( pszDirectoryName != NULL );
ASSERT( ppTsDirectoryHeader != NULL);
//
// First, check to see if we have already cached a listing of this
// directory.
//
*ppTsDirectoryHeader = NULL;
//
// Dont try to cache directory listings
//
// bSuccess = TsCheckOutCachedBlob( tsCache,
// pszDirectoryName,
// RESERVED_DEMUX_DIRECTORY_LISTING,
// ( PVOID * )&pvBlob,
// &ulSize );
bSuccess = FALSE;
//
// The block was not present in cache.
// Obtain a fresh copy of the directory listing and cache it.
//
IF_DEBUG( DIR_LIST) {
DBGPRINTF( (DBG_CONTEXT,
"Missing DirListing (%s) in cache. Generating newly\n",
pszDirectoryName));
}
*ppTsDirectoryHeader = TsGetFreshDirectoryHeader(
tsCache,
pszDirectoryName,
hListingUser );
bSuccess = ( *ppTsDirectoryHeader != NULL);
IF_DEBUG( DIR_LIST) {
DBGCODE(
CHAR pchBuff[600];
wsprintfA( pchBuff,
"Obtained DirListing (%s). NEntries = %d\n",
pszDirectoryName,
(*ppTsDirectoryHeader)->QueryNumEntries());
OutputDebugString( pchBuff);
);
}
return ( bSuccess);
} // TsGetDirectoryListing()
dllexp
BOOL
TsFreeDirectoryListing
(
IN const TSVC_CACHE & tsCache,
IN PTS_DIRECTORY_HEADER pDirectoryHeader
)
{
BOOL fReturn;
BOOL fCached = pDirectoryHeader->IsCached;
IF_DEBUG( DIR_LIST) {
DBGPRINTF( ( DBG_CONTEXT,
"TsFreeDirectoryListing( %08p) called. Cached = %d\n",
pDirectoryHeader,
fCached));
pDirectoryHeader->Print();
}
if ( fCached )
{
fReturn = TsCheckInCachedBlob( ( PVOID )pDirectoryHeader );
}
else
{
fReturn = TsFree( tsCache, ( PVOID )pDirectoryHeader );
}
return( fReturn);
} // TsFreeDirectoryListing()
PTS_DIRECTORY_HEADER
TsGetFreshDirectoryHeader(
IN const TSVC_CACHE & tsCache,
IN LPCSTR pszDirectoryName,
IN HANDLE hListingUser)
/*++
This function obtains a fresh copy of the directory listing for the
directory specified and caches it if possible, before returning pointer
to the directory information.
Returns:
On success it returns the pointer to newly constructed directory listing.
On failure this returns a NULL
--*/
{
PTS_DIRECTORY_HEADER pDirectoryHeader;
PVOID pvGuardBlob;
BOOL bSuccess;
BOOL bCachedGuardianBlob;
#ifdef TS_USE_DIRECTORY_GUARD
//
// If we are going to cache this directory, we would like to increase the
// likelihood that it is an "atomic" snapshot. This is done as follows:
//
// We cache and hold checked-out a small blob while create the directory
// listing. If that Blob (1) could not be cached, or (2) was ejected
// from the cache while we were generating a listing, then we do not
// attempt to cache the directory.
//
// Reasoning:
//
// 1) If the Blob couldn't be cached then the directory info won't be any
// different.
//
// 2) If the Blob was ejected, the directory must have changed while we
// were reading it. If this happens, we don't want to cache possibly
// inconsistent data.
//
// If the directory changed and the Blob has not yet been ejected, the
// directory will soon be ejected anyway. Notice that the Blob is not
// DeCache()'d until after the directory has been cached.
//
if ( !TsAllocate( tsCache, sizeof( TS_DIRECTORY_HEADER),
(PVOID *) &pvGuardBlob)) {
//
// Failure to allocate and secure a guardian blob.
//
IF_DEBUG( DIR_LIST) {
DBGPRINTF( (DBG_CONTEXT,
"Allocation of Guardianblob for %s failed. Error=%d\n",
pszDirectoryName, GetLastError()));
}
return ( NULL);
}
//
// A successful guardian block allocated. Try and cache it.
//
ULONG cchDirectoryName = strlen(pszDirectoryName);
bCachedGuardianBlob = TsCacheDirectoryBlob(
tsCache,
pszDirectoryName,
cchDirectoryName,
RESERVED_DEMUX_ATOMIC_DIRECTORY_GUARD,
pvGuardBlob,
TRUE );
if ( !bCachedGuardianBlob ) {
BOOL fFreed;
//
// Already there is one such cached blob. ignore this blob.
// So free up the space used.
//
fFreed = TsFree( tsCache, pvGuardBlob );
ASSERT( fFreed);
pvGuardBlob = NULL;
}
#endif // TS_USE_DIRECTORY_GUARD
//
// Allocate space for Directory listing
//
bSuccess = TsAllocateEx( tsCache,
sizeof( TS_DIRECTORY_HEADER ),
( PVOID * )&pDirectoryHeader,
FreeDirectoryHeaderContents );
if ( bSuccess) {
BOOL fReadSuccess;
DWORD cbBlob = 0;
ASSERT( pDirectoryHeader != NULL);
pDirectoryHeader->ReInitialize(); // called since we raw alloced space
pDirectoryHeader->SetListingUser( hListingUser);
fReadSuccess = (pDirectoryHeader->ReadWin32DirListing(pszDirectoryName,
&cbBlob ) &&
pDirectoryHeader->BuildFileInfoPointers( &cbBlob )
);
if ( fReadSuccess) {
#if TS_USE_DIRECTORY_GUARD
//
// Attempt and cache the blob if the blob size is cacheable
//
if ( bCachedGuardianBlob &&
!BLOB_IS_EJECTATE( pvGuardBlob ) ) {
ASSERT( BLOB_IS_OR_WAS_CACHED( pvGuardBlob ) );
bSuccess =
TsCacheDirectoryBlob(tsCache,
pszDirectoryName,
cchDirectoryName,
RESERVED_DEMUX_DIRECTORY_LISTING,
pDirectoryHeader,
TRUE );
if ( bSuccess ) {
INC_COUNTER( tsCache.GetServiceId(), CurrentDirLists );
}
//
// Even if caching of the blob failed, that is okay!
//
if ( bSuccess && BLOB_IS_EJECTATE( pvGuardBlob ) ) {
TsExpireCachedBlob( tsCache, pDirectoryHeader );
}
}
#endif // TS_USE_DIRECTORY_GUARD
} else {
//
// Reading directory failed.
// cleanup directory related data and get out.
//
BOOL fFreed = TsFree( tsCache, pDirectoryHeader);
ASSERT( fFreed);
pDirectoryHeader = NULL;
bSuccess = FALSE;
}
} else {
//
// Allocation of Directory Header failed.
//
ASSERT( pDirectoryHeader == NULL);
}
#if TS_USE_DIRECTORY_GUARD
// Free up the guardian block and exit.
if ( bCachedGuardianBlob) {
bSuccess = TsExpireCachedBlob( tsCache, pvGuardBlob );
ASSERT( bSuccess );
bSuccess = TsCheckInCachedBlob( pvGuardBlob );
ASSERT( bSuccess );
pvGuardBlob = NULL;
}
ASSERT( pvGuardBlob == NULL);
#endif // TS_USE_DIRECTORY_GUARD
return ( pDirectoryHeader);
} // TsGetFreshDirectoryHeader
BOOL
FreeDirectoryHeaderContents( PVOID pvOldBlock )
{
PTS_DIRECTORY_HEADER pDirectoryHeader;
pDirectoryHeader = ( PTS_DIRECTORY_HEADER )pvOldBlock;
pDirectoryHeader->CleanupThis();
//
// The item may never have been added to the cache, don't
// count it in this case
//
//
// We're never caching dir blobs now
//
// if ( BLOB_IS_OR_WAS_CACHED( pvOldBlock ) ) {
//
// DEC_COUNTER( BLOB_GET_SVC_ID( pvOldBlock ), CurrentDirLists );
// }
return ( TRUE);
} // FreeDirectoryHeaderContents()
int __cdecl
AlphaCompareFileBothDirInfo(
IN const void * pvFileInfo1,
IN const void * pvFileInfo2)
{
const WIN32_FIND_DATA * pFileInfo1 =
*((const WIN32_FIND_DATA **) pvFileInfo1);
const WIN32_FIND_DATA * pFileInfo2 =
*((const WIN32_FIND_DATA **) pvFileInfo2);
ASSERT( pFileInfo1 != NULL && pFileInfo2 != NULL);
return ( lstrcmp( (LPCSTR )pFileInfo1->cFileName,
(LPCSTR )pFileInfo2->cFileName));
} // AlphaCompareFileBothDirInfo()
BOOL
SortInPlaceFileInfoPointers(
IN OUT PWIN32_FIND_DATA * prgFileInfo,
IN int nEntries,
IN PFN_CMP_WIN32_FIND_DATA pfnCompare)
/*++
This is a generic function to sort the pointers to file information
array in place using pfnCompare to compare the records for ordering.
Returns:
TRUE on success and FALSE on failure.
--*/
{
DWORD dwTime;
IF_DEBUG( DIR_LIST) {
DBGPRINTF( ( DBG_CONTEXT,
"Qsorting the FileInfo Array %08p ( Total = %d)\n",
prgFileInfo, nEntries));
dwTime = GetTickCount();
}
qsort( (PVOID ) prgFileInfo, nEntries,
sizeof( PWIN32_FIND_DATA),
pfnCompare);
IF_DEBUG( DIR_LIST) {
dwTime = GetTickCount() - dwTime;
DBGPRINTF( ( DBG_CONTEXT,
" Time to sort %d entries = %d\n",
nEntries, dwTime));
}
return ( TRUE);
} // SortInPlaceFileInfoPointers()
/**********************************************************************
* TS_DIRECTORY_HEADER related member functions
**********************************************************************/
const char PSZ_DIR_STAR_STAR[] = "*.*";
# define LEN_PSZ_DIR_STAR_STAR ( sizeof(PSZ_DIR_STAR_STAR)/sizeof(CHAR))
BOOL
TS_DIRECTORY_HEADER::ReadWin32DirListing(
IN LPCSTR pszDirectoryName,
IN OUT DWORD * pcbMemUsed
)
/*++
Opens and reads the directory file for given directory to obtain
information about files and directories in the dir.
Arguments:
pszDirectoryName - pointer to string containing directory name
with a terminating "\".
pcbMemUsed - pointer to DWORD which on successful return
will contain the memory used.
Returns:
TRUE on success and FALSE on failure.
Use GetLastError() for further error information.
--*/
{
BOOL fReturn = TRUE; // default assumed.
CHAR pchFullPath[MAX_PATH*2];
HANDLE hFindFile = INVALID_HANDLE_VALUE;
BOOL fFirstTime;
DWORD cbExtraMem = 0;
DWORD dwError = NO_ERROR;
PWIN32_FIND_DATA pFileInfo = NULL;
PTS_DIR_BUFFERS pTsDirBuffers = NULL;
DWORD startCount;
DBGCODE( CHAR pchBuff[300];
);
DBG_ASSERT( pszDirectoryName != NULL);
if ( strlen(pszDirectoryName) > MAX_PATH*2 - LEN_PSZ_DIR_STAR_STAR) {
return ( ERROR_PATH_NOT_FOUND);
}
wsprintfA( pchFullPath, "%s%s", pszDirectoryName, PSZ_DIR_STAR_STAR);
InitializeListHead( &m_listDirectoryBuffers);
//
// Get the next chunk of directory information.
//
pTsDirBuffers = (PTS_DIR_BUFFERS ) ALLOC( sizeof(TS_DIR_BUFFERS));
if ( pTsDirBuffers == NULL ) {
//
// Allocation failure.
//
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
fReturn = FALSE;
goto Failure;
}
cbExtraMem += sizeof(TS_DIR_BUFFERS);
// insert the buffer into the list so that it may be freed later.
InsertBufferInTail( &pTsDirBuffers->listEntry);
pTsDirBuffers->nEntries = 0;
pFileInfo = (PWIN32_FIND_DATA ) pTsDirBuffers->rgFindData;
hFindFile = FindFirstFile( pchFullPath, pFileInfo);
if ( hFindFile == INVALID_HANDLE_VALUE) {
dwError = GetLastError();
IF_DEBUG( DIR_LIST) {
DBGPRINTF(( DBG_CONTEXT,
"FindFirstFile(%s, %08p) failed. Error = %d\n",
pchFullPath, pFileInfo, dwError));
}
fReturn = FALSE;
goto Failure;
}
IF_DEBUG( DIR_LIST) {
DBGCODE(
wsprintf( pchBuff, "-%d-[%d](%08p) %s(%s)\t%08x\n",
MAX_DIR_ENTRIES_PER_BLOCK,
0,
pFileInfo,
pFileInfo->cFileName, pFileInfo->cAlternateFileName,
pFileInfo->dwFileAttributes);
OutputDebugString( pchBuff);
);
}
pFileInfo++;
IncrementDirEntries();
pTsDirBuffers->nEntries++;
startCount = 1;
// atleast 10 to justify overhead
DBG_ASSERT( MAX_DIR_ENTRIES_PER_BLOCK > 10);
//
// Loop through getting subsequent entries in the directory.
//
for( dwError = NO_ERROR; dwError == NO_ERROR; ) {
// loop thru and get a block of items
for( int i = startCount; i < MAX_DIR_ENTRIES_PER_BLOCK; i++ ) {
if ( !FindNextFile( hFindFile, pFileInfo)) {
dwError = GetLastError();
if ( dwError != ERROR_NO_MORE_FILES) {
DBGPRINTF(( DBG_CONTEXT,
"FindNextFile(%s(%08p), %08p) failed."
" Error = %d\n",
pchFullPath, hFindFile, pFileInfo,
dwError));
fReturn = FALSE;
goto Failure;
} else {
break;
}
}
IF_DEBUG( DIR_LIST) {
DBGCODE(
wsprintf( pchBuff, "[%d](%08p) %s(%s)\t%08x\n",
i, pFileInfo,
pFileInfo->cFileName,
pFileInfo->cAlternateFileName,
pFileInfo->dwFileAttributes);
OutputDebugString( pchBuff);
);
}
IncrementDirEntries();
pFileInfo++;
} // for all elements that fit in this block
pTsDirBuffers->nEntries = i;
if ( dwError == ERROR_NO_MORE_FILES) {
//
// info on all entries in a directory obtained. Return back.
//
dwError = NO_ERROR;
break;
} else {
//
// The buffer contains directory entries and is full now.
// Update the count of entries in this buffer and
// Allocate a new buffer and set the start counts properly.
//
IF_DEBUG( DIR_LIST) {
DBGCODE( OutputDebugString( " Allocating Next Chunck\n"););
}
pTsDirBuffers = (PTS_DIR_BUFFERS ) ALLOC(sizeof(TS_DIR_BUFFERS));
if ( pTsDirBuffers == NULL ) {
//
// Allocation failure.
//
dwError = ERROR_NOT_ENOUGH_MEMORY;
fReturn = FALSE;
break; // Get out of the loop with failure.
}
cbExtraMem += sizeof(TS_DIR_BUFFERS);
// insert the buffer into the list so that it may be freed later.
InsertBufferInTail( &pTsDirBuffers->listEntry);
pTsDirBuffers->nEntries = 0;
pFileInfo = ( PWIN32_FIND_DATA ) pTsDirBuffers->rgFindData;
startCount = 0; // start count from zero items on this buffer page
}
} // for all directory entries
Failure:
if ( hFindFile != INVALID_HANDLE_VALUE) {
DBG_REQUIRE( FindClose( hFindFile ));
hFindFile = INVALID_HANDLE_VALUE;
}
*pcbMemUsed += cbExtraMem;
return ( fReturn);
} // TS_DIRECTORY_HEADER::ReadWin32DirListing()
VOID
TS_DIRECTORY_HEADER::CleanupThis( VOID)
{
PLIST_ENTRY pEntry;
PLIST_ENTRY pNextEntry;
for ( pEntry = QueryDirBuffersListEntry()->Flink;
pEntry != QueryDirBuffersListEntry();
pEntry = pNextEntry )
{
PTS_DIR_BUFFERS pTsDirBuffer =
CONTAINING_RECORD( pEntry, TS_DIR_BUFFERS, listEntry);
// cache the next block pointer.
pNextEntry = pEntry->Flink;
// remove the current block from list.
RemoveEntryList( pEntry);
// delete the current block.
FREE( pTsDirBuffer );
}
InitializeListHead( QueryDirBuffersListEntry());
if ( m_ppFileInfo != NULL) {
FREE( m_ppFileInfo);
m_ppFileInfo = NULL;
}
m_hListingUser = INVALID_HANDLE_VALUE;
m_nEntries = 0;
return;
} // TS_DIRECTORY_HEADER::CleanupThis()
BOOL
TS_DIRECTORY_HEADER::BuildFileInfoPointers(
IN OUT DWORD * pcbMemUsed
)
/*++
This constructs the indirection pointers from the buffers containing the
file information.
This array of indirection enables faster access to the file information
structures stored.
Should be always called after ReadFromNtDirectoryFile() to construct the
appropriate pointers.
Returns:
TRUE on success and FALSE if there are any failures.
--*/
{
BOOL fReturn = FALSE;
DWORD cbAlloc;
int maxIndex;
maxIndex = QueryNumEntries();
ASSERT( maxIndex != 0); // Any directory will atleast have "."
//
// Alloc space for holding the pointers for numEntries pointers.
//
cbAlloc = maxIndex * sizeof( PWIN32_FIND_DATA );
m_ppFileInfo = (PWIN32_FIND_DATA *) ALLOC( cbAlloc );
if ( m_ppFileInfo != NULL ) {
int index;
PLIST_ENTRY pEntry;
PTS_DIR_BUFFERS pTsDirBuffers;
PWIN32_FIND_DATA pFileInfo;
//
// Get the link to first buffer and start enumeration.
//
for( pEntry = QueryDirBuffersListEntry()->Flink, index = 0;
pEntry != QueryDirBuffersListEntry();
pEntry = pEntry->Flink
) {
pTsDirBuffers = CONTAINING_RECORD( pEntry, TS_DIR_BUFFERS,
listEntry);
pFileInfo = pTsDirBuffers->rgFindData;
for (int i = 0;
i < pTsDirBuffers->nEntries;
i++ ) {
m_ppFileInfo[index++] = pFileInfo; // store the pointer.
IF_DEBUG( DIR_LIST) {
DBGCODE(
CHAR pchBuff[300];
wsprintf( pchBuff, "[%d](%08p) %s(%s)\t%08x\n",
index, pFileInfo,
pFileInfo->cFileName,
pFileInfo->cAlternateFileName,
pFileInfo->dwFileAttributes);
OutputDebugString( pchBuff);
);
}
pFileInfo++;
} // for all elements in a buffer
} // for ( all buffers in the list)
ASSERT( index == maxIndex);
fReturn = SortInPlaceFileInfoPointers( m_ppFileInfo,
maxIndex,
AlphaCompareFileBothDirInfo);
} // valid alloc of the pointers.
*pcbMemUsed += cbAlloc;
return ( fReturn);
} // TS_DIRECTORY_HEADER::BuildFileInfoPointers()
# if DBG
VOID
TS_DIRECTORY_HEADER::Print( VOID) const
{
DBGPRINTF( ( DBG_CONTEXT,
"Printing TS_DIRECTORY_HEADER ( %08p).\n"
"ListingUser Handle = %08p\t Num Entries = %08x\n"
"Pointer to array of indirection pointers %08p\n",
this,
m_hListingUser, m_nEntries,
m_ppFileInfo));
//
// The buffers containing the data of the file information not printed
//
return;
} // TS_DIRECTORY_HEADER::Print()
# endif // DBG
/************************ End of File ***********************/