2581 lines
74 KiB
C++
2581 lines
74 KiB
C++
/*++
|
||
|
||
creatfil.cxx
|
||
|
||
Exports API for creating/opening a file, given the filename.
|
||
The file handle and other information are cached for the given user handle.
|
||
|
||
History:
|
||
Heath Hunnicutt ( t-heathh) ??-??-??
|
||
|
||
Murali R. Krishnan ( MuraliK) Dec 30, 1994
|
||
Added SetLastError() to set error code as appropriate
|
||
|
||
Murali R. Krishnan ( MuraliK) Jan 4, 1994
|
||
Added ability to obtain and set the BY_HANDLE_FILE_INFORMATION
|
||
as part of TS_OPEN_FILE_INFO
|
||
--*/
|
||
|
||
#include "TsunamiP.Hxx"
|
||
#pragma hdrstop
|
||
#include <lonsi.hxx>
|
||
#include "auxctrs.h"
|
||
#include <dbgutil.h>
|
||
|
||
#include <iistypes.hxx>
|
||
#include <iisver.h>
|
||
#include <iiscnfg.h>
|
||
#include <imd.h>
|
||
#include <mb.hxx>
|
||
|
||
LONG g_IISCacheAuxCounters[ AacIISCacheMaxCounters];
|
||
|
||
GENERIC_MAPPING g_gmFile = {
|
||
FILE_GENERIC_READ,
|
||
FILE_GENERIC_WRITE,
|
||
FILE_GENERIC_EXECUTE,
|
||
FILE_ALL_ACCESS
|
||
};
|
||
|
||
//
|
||
// Routines and macros for manipulating the "init complete" state of
|
||
// a PHYS_OPEN_FILE_INFO structure.
|
||
//
|
||
|
||
VOID
|
||
WaitForPhysInitComplete(
|
||
IN PPHYS_OPEN_FILE_INFO lpPFInfo
|
||
);
|
||
|
||
#define WAIT_FOR_PHYS_INIT_COMPLETE( lpPFInfo ) \
|
||
if( !lpPFInfo->fInitComplete ) { \
|
||
WaitForPhysInitComplete( lpPFInfo ); \
|
||
} else
|
||
|
||
#define MARK_PHYS_INIT_COMPLETE( lpPFInfo ) \
|
||
if( TRUE ) { \
|
||
ASSERT( (lpPFInfo)->Signature == PHYS_OBJ_SIGNATURE ); \
|
||
(lpPFInfo)->fInitComplete = TRUE; \
|
||
} else
|
||
|
||
#define PHYS_INIT_SLEEP_START 100 // ms
|
||
#define PHYS_INIT_SLEEP_INCR 100 // ms
|
||
#define PHYS_INIT_SLEEP_MAX 1000 // ms
|
||
|
||
dllexp
|
||
PSECURITY_DESCRIPTOR
|
||
TsGetFileSecDesc(
|
||
LPTS_OPEN_FILE_INFO pFile
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the security descriptor associated to the file
|
||
To be freed using LocalFree()
|
||
|
||
Arguments:
|
||
|
||
pFile - ptr to fie object
|
||
|
||
Return Value:
|
||
|
||
ptr to security descriptor or NULL if error
|
||
|
||
--*/
|
||
{
|
||
SECURITY_INFORMATION si
|
||
= OWNER_SECURITY_INFORMATION |
|
||
GROUP_SECURITY_INFORMATION |
|
||
DACL_SECURITY_INFORMATION;
|
||
BYTE abSecDesc[ SECURITY_DESC_DEFAULT_SIZE ];
|
||
DWORD dwSecDescSize;
|
||
PSECURITY_DESCRIPTOR pAcl;
|
||
|
||
if ( GetKernelObjectSecurity(
|
||
pFile->QueryFileHandle(),
|
||
si,
|
||
(PSECURITY_DESCRIPTOR)abSecDesc,
|
||
SECURITY_DESC_DEFAULT_SIZE,
|
||
&dwSecDescSize ) )
|
||
{
|
||
if ( dwSecDescSize > SECURITY_DESC_DEFAULT_SIZE )
|
||
{
|
||
if ( !(pAcl = (PSECURITY_DESCRIPTOR)LocalAlloc( LMEM_FIXED, dwSecDescSize )) )
|
||
{
|
||
return NULL;
|
||
}
|
||
if ( !GetKernelObjectSecurity(
|
||
pFile->QueryFileHandle(),
|
||
si,
|
||
pAcl,
|
||
dwSecDescSize,
|
||
&dwSecDescSize ) )
|
||
{
|
||
LocalFree( pAcl );
|
||
|
||
return NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ( dwSecDescSize = GetSecurityDescriptorLength(abSecDesc) )
|
||
{
|
||
if ( !(pAcl = (PSECURITY_DESCRIPTOR)LocalAlloc( LMEM_FIXED,
|
||
dwSecDescSize )) )
|
||
{
|
||
return NULL;
|
||
}
|
||
memcpy( pAcl, abSecDesc, dwSecDescSize );
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Security descriptor is empty : do not return ptr to security descriptor
|
||
//
|
||
|
||
pAcl = NULL;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pAcl = NULL;
|
||
}
|
||
|
||
return pAcl;
|
||
}
|
||
|
||
BOOL
|
||
TsReInitPhysFile(
|
||
IN PPHYS_OPEN_FILE_INFO pPhysFileInfo
|
||
)
|
||
|
||
/*+++
|
||
|
||
TsReInitPhysFile
|
||
|
||
Called when we get a valid, initialized phys file info structure that
|
||
has an invalid file handle. We acquire the cache critical section and
|
||
check to see if the file is still valid and still has an invalid handle
|
||
value. If it does, we're the first to try to do this, so we'll mark
|
||
the structure and initing, and let our caller try to initialize it.
|
||
Otherwise we'll just return and let our caller try again.
|
||
|
||
Arguments:
|
||
|
||
pPhysFileInfo - Pointer to the phys file info structure.
|
||
|
||
Returns:
|
||
|
||
FALSE if we reinited, TRUE otherwise.
|
||
---*/
|
||
{
|
||
BOOL bRetVal;
|
||
|
||
EnterCriticalSection( &CacheTable.CriticalSection );
|
||
|
||
if ( pPhysFileInfo->fInitComplete &&
|
||
pPhysFileInfo->hOpenFile == INVALID_HANDLE_VALUE
|
||
)
|
||
{
|
||
pPhysFileInfo->fInitComplete = FALSE;
|
||
bRetVal = FALSE;
|
||
}
|
||
else
|
||
{
|
||
bRetVal = TRUE;
|
||
}
|
||
|
||
LeaveCriticalSection( &CacheTable.CriticalSection );
|
||
|
||
return bRetVal;
|
||
}
|
||
|
||
VOID
|
||
WaitForPhysInitComplete(
|
||
IN PPHYS_OPEN_FILE_INFO lpPFInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Waits for the specified physical open file info structure to become
|
||
fully initialized.
|
||
|
||
Arguments:
|
||
|
||
lpPFInfo - The PHYS_OPEN_FILE_INFO structure to wait for.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
DWORD sleepTime;
|
||
|
||
//
|
||
// Sanity check.
|
||
//
|
||
|
||
ASSERT( lpPFInfo != NULL );
|
||
|
||
//
|
||
// Perform a linear backoff delay while waiting for init complete.
|
||
//
|
||
|
||
sleepTime = PHYS_INIT_SLEEP_START;
|
||
|
||
while( !lpPFInfo->fInitComplete ) {
|
||
|
||
ASSERT( lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
|
||
Sleep( sleepTime );
|
||
|
||
sleepTime += PHYS_INIT_SLEEP_INCR;
|
||
|
||
if( sleepTime > PHYS_INIT_SLEEP_MAX ) {
|
||
sleepTime = PHYS_INIT_SLEEP_MAX;
|
||
}
|
||
|
||
}
|
||
|
||
} // WaitForPhysInitComplete
|
||
|
||
VOID
|
||
OplockCreateFile(
|
||
PVOID Context,
|
||
DWORD Status
|
||
)
|
||
{
|
||
POPLOCK_OBJECT lpOplock = (POPLOCK_OBJECT)Context;
|
||
PBLOB_HEADER pbhBlob;
|
||
PCACHE_OBJECT cache;
|
||
PCACHE_OBJECT pCacheTmp;
|
||
PCACHE_OBJECT TmpCache;
|
||
BOOL result = FALSE;
|
||
LIST_ENTRY ListHead;
|
||
LIST_ENTRY * pEntry;
|
||
LIST_ENTRY * pNextEntry;
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"OplockCreateFile(%08lx, %08lx) - Entered\n", Context, Status ));
|
||
}
|
||
|
||
if (lpOplock == NULL) {
|
||
return;
|
||
}
|
||
|
||
ASSERT(lpOplock->Signature == OPLOCK_OBJ_SIGNATURE);
|
||
if ( Status != OPLOCK_BREAK_NO_OPLOCK ) {
|
||
WaitForSingleObject( lpOplock->hOplockInitComplete, (DWORD)(-1) );
|
||
}
|
||
|
||
if ( Status == OPLOCK_BREAK_OPEN ) {
|
||
if ( lpOplock->lpPFInfo != NULL ) {
|
||
pbhBlob = (( PBLOB_HEADER )lpOplock->lpPFInfo ) - 1;
|
||
if ( pbhBlob->IsCached ) {
|
||
cache = pbhBlob->pCache;
|
||
|
||
InitializeListHead( &ListHead );
|
||
|
||
EnterCriticalSection( &CacheTable.CriticalSection );
|
||
EnterCriticalSection( &csVirtualRoots );
|
||
|
||
for ( pEntry = CacheTable.MruList.Flink;
|
||
pEntry != &CacheTable.MruList;
|
||
pEntry = pNextEntry ) {
|
||
|
||
pNextEntry = pEntry->Flink;
|
||
|
||
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, MruList );
|
||
|
||
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
|
||
|
||
if ( pCacheTmp != cache ) {
|
||
continue;
|
||
}
|
||
|
||
result = TRUE;
|
||
|
||
while ( !IsListEmpty( &lpOplock->lpPFInfo->OpenReferenceList ) ) {
|
||
pEntry = RemoveHeadList( &lpOplock->lpPFInfo->OpenReferenceList );
|
||
pbhBlob = CONTAINING_RECORD( pEntry, BLOB_HEADER, PFList );
|
||
TmpCache = pbhBlob->pCache;
|
||
if (!RemoveCacheObjFromLists( TmpCache, FALSE ) ) {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"OplockCreateFile(%08lx, %08lx, %08lx) - Error Processing Open Reference %08lx\n", Context, Status, pEntry, TmpCache ));
|
||
}
|
||
break;
|
||
}
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"OplockCreateFile(%08lx, %08lx, %08lx) - Processing Open Reference %08lx\n", Context, Status, pEntry, TmpCache ));
|
||
}
|
||
InsertTailList( &ListHead, pEntry );
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
LeaveCriticalSection( &csVirtualRoots );
|
||
LeaveCriticalSection( &CacheTable.CriticalSection );
|
||
|
||
if ( result ) {
|
||
while ( !IsListEmpty( &ListHead ) ) {
|
||
pEntry = RemoveHeadList( &ListHead );
|
||
pbhBlob = CONTAINING_RECORD( pEntry, BLOB_HEADER, PFList );
|
||
TmpCache = pbhBlob->pCache;
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"OplockCreateFile(%08lx, %08lx) - Processing Open Reference %08lx %08lx\n", Context, Status, pEntry, TmpCache ));
|
||
}
|
||
TsDereferenceCacheObj( TmpCache, TRUE );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
CloseHandle( lpOplock->hOplockInitComplete );
|
||
lpOplock->Signature = 0;
|
||
LocalFree(lpOplock);
|
||
}
|
||
|
||
VOID
|
||
TsRemovePhysFile(
|
||
PPHYS_OPEN_FILE_INFO lpPFInfo
|
||
)
|
||
{
|
||
PBLOB_HEADER pbhBlob;
|
||
PCACHE_OBJECT cache;
|
||
PCACHE_OBJECT pCacheTmp;
|
||
PCACHE_OBJECT TmpCache;
|
||
BOOL result = FALSE;
|
||
BOOL bSuccess;
|
||
LIST_ENTRY * pEntry;
|
||
LIST_ENTRY * pNextEntry;
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsRemovePhysFile(%08lx) - Entered\n", lpPFInfo ));
|
||
}
|
||
|
||
ASSERT( lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
|
||
TSUNAMI_TRACE( TRACE_PHYS_REMOVE, lpPFInfo );
|
||
|
||
pbhBlob = (( PBLOB_HEADER )lpPFInfo ) - 1;
|
||
if ( pbhBlob->IsCached ) {
|
||
cache = pbhBlob->pCache;
|
||
|
||
EnterCriticalSection( &CacheTable.CriticalSection );
|
||
EnterCriticalSection( &csVirtualRoots );
|
||
|
||
if ( cache->references > 1 ) {
|
||
|
||
TsCheckInCachedBlob( (PVOID)lpPFInfo );
|
||
|
||
} else {
|
||
|
||
for ( pEntry = CacheTable.MruList.Flink;
|
||
pEntry != &CacheTable.MruList;
|
||
pEntry = pNextEntry ) {
|
||
|
||
pNextEntry = pEntry->Flink;
|
||
|
||
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, MruList );
|
||
|
||
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
|
||
|
||
if ( pCacheTmp != cache ) {
|
||
continue;
|
||
}
|
||
|
||
if ( !RemoveCacheObjFromLists( cache, FALSE ) ) {
|
||
ASSERT( FALSE );
|
||
continue;
|
||
}
|
||
|
||
result = TRUE;
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
LeaveCriticalSection( &csVirtualRoots );
|
||
LeaveCriticalSection( &CacheTable.CriticalSection );
|
||
|
||
if ( result ) {
|
||
TsDereferenceCacheObj( cache, TRUE );
|
||
}
|
||
} else {
|
||
if ( pbhBlob->pfnFreeRoutine ) {
|
||
bSuccess = pbhBlob->pfnFreeRoutine( (PVOID)lpPFInfo );
|
||
} else {
|
||
bSuccess = TRUE;
|
||
}
|
||
|
||
if ( bSuccess ) {
|
||
//
|
||
// Free the memory used by the Blob.
|
||
//
|
||
|
||
FREE( pbhBlob );
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
dllexp
|
||
BOOL
|
||
TsDeleteOnClose(PW3_URI_INFO pURIInfo,
|
||
HANDLE OpeningUser,
|
||
BOOL *fDeleted)
|
||
{
|
||
PPHYS_OPEN_FILE_INFO lpPFInfo;
|
||
LPTS_OPEN_FILE_INFO lpOpenFile;
|
||
BYTE psFile[SIZE_PRIVILEGE_SET];
|
||
DWORD dwPS;
|
||
DWORD dwGrantedAccess;
|
||
BOOL fAccess;
|
||
|
||
if ( DisableSPUD || pURIInfo == NULL ) {
|
||
return FALSE;
|
||
}
|
||
|
||
#if 0
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsDeleteOnClose(%08lx) - Waiting On hFileEvent!\n", pURIInfo ));
|
||
}
|
||
WaitForSingleObject( pURIInfo->hFileEvent, (DWORD)(-1) );
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsDeleteOnClose(%08lx) - Returned from Waiting On hFileEvent!\n", pURIInfo ));
|
||
}
|
||
|
||
lpOpenFile = pURIInfo->pOpenFileInfo;
|
||
if ( lpOpenFile == NULL ) {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsDeleteOnClose(%08lx) - lpOpenFile == NULL!\n", pURIInfo ));
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
*fDeleted = FALSE;
|
||
lpPFInfo = lpOpenFile->QueryPhysFileInfo();
|
||
if ( lpPFInfo->fIsCached && lpPFInfo->fSecurityDescriptor ) {
|
||
dwPS = sizeof(psFile);
|
||
((PRIVILEGE_SET*)&psFile)->PrivilegeCount = 0;
|
||
if ( AccessCheck(
|
||
lpPFInfo->abSecurityDescriptor,
|
||
OpeningUser,
|
||
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
||
&g_gmFile,
|
||
(PRIVILEGE_SET*)psFile,
|
||
&dwPS,
|
||
&dwGrantedAccess,
|
||
&fAccess) ) {
|
||
if ( fAccess ) {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsDeleteOnClose(%08lx) - Marking file for delete!\n", lpPFInfo ));
|
||
}
|
||
lpPFInfo->fDeleteOnClose = TRUE;
|
||
*fDeleted = TRUE;
|
||
}
|
||
}
|
||
} else {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsDeleteOnClose(%08lx) - !fIsCached || !fSecurityDescriptor\n", lpPFInfo ));
|
||
}
|
||
return FALSE;
|
||
}
|
||
#endif //!oplock
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
|
||
BOOL
|
||
TsAccessCheck(
|
||
IN PPHYS_OPEN_FILE_INFO pFile,
|
||
IN HANDLE hUser
|
||
)
|
||
|
||
/*+++
|
||
|
||
TsAccessCheck
|
||
|
||
This routine validates that the user identified by hUser has permissions
|
||
to access the file associated with the input physical file open structure.
|
||
We use the cached security descriptor in pFile to do so if we can;
|
||
otherwise we'll try and read the security descriptor first.
|
||
|
||
Arguments:
|
||
|
||
pFile - A pointer to the physical file open structure
|
||
identifying the file.
|
||
|
||
hUser - User access token to be checked.
|
||
|
||
Returns:
|
||
|
||
TRUE if the file can be accessed, FALSE otherwise.
|
||
|
||
---*/
|
||
{
|
||
|
||
PSECURITY_DESCRIPTOR pSecDesc;
|
||
BOOL bHaveAccess;
|
||
CHAR cBuffer[SIZE_PRIVILEGE_SET];
|
||
DWORD dwPSSize;
|
||
PPRIVILEGE_SET pPS;
|
||
DWORD dwGrantedAccess;
|
||
|
||
|
||
// See if we've got a cached security descriptor; if not, read one
|
||
// from the file.
|
||
|
||
if ( !pFile->fSecurityDescriptor )
|
||
{
|
||
DWORD dwError;
|
||
DWORD dwInputSize;
|
||
DWORD dwSizeNeeded;
|
||
|
||
// Don't have one cached, need to read one. Allocate a buffer and
|
||
// set up the basic information we need for this call.
|
||
|
||
dwInputSize = SECURITY_DESC_DEFAULT_SIZE;
|
||
pSecDesc = (PSECURITY_DESCRIPTOR)ALLOC( dwInputSize );
|
||
|
||
if ( pSecDesc == NULL )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
for (;;)
|
||
{
|
||
// Try and read the security descriptor. If we can, then we're done
|
||
// with this loop.
|
||
if ( GetKernelObjectSecurity(
|
||
pFile->hOpenFile,
|
||
OWNER_SECURITY_INFORMATION |
|
||
GROUP_SECURITY_INFORMATION |
|
||
DACL_SECURITY_INFORMATION,
|
||
pSecDesc,
|
||
dwInputSize,
|
||
&dwSizeNeeded ) )
|
||
{
|
||
break;
|
||
}
|
||
|
||
// Couldn't read it. If it happened because our buffer was too
|
||
// small, allocate a bigger one and try again. Otherwise so other
|
||
// error happened. In that case we can't read the descriptor, so
|
||
// return failure.
|
||
|
||
dwError = GetLastError();
|
||
|
||
FREE( pSecDesc ); // Don't need this anymore, in any case.
|
||
|
||
if ( dwError != ERROR_INSUFFICIENT_BUFFER )
|
||
{
|
||
// Some other error, fail.
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// Now try and allocate a buffer of the size the API said we need.
|
||
|
||
dwInputSize = dwSizeNeeded;
|
||
|
||
pSecDesc = (PSECURITY_DESCRIPTOR)ALLOC( dwInputSize );
|
||
|
||
if ( pSecDesc == NULL )
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Have one cached, use that.
|
||
pSecDesc = pFile->abSecurityDescriptor;
|
||
}
|
||
|
||
// Have a security descriptor now, so do the check.
|
||
|
||
pPS = (PPRIVILEGE_SET)cBuffer;
|
||
dwPSSize = SIZE_PRIVILEGE_SET;
|
||
pPS->PrivilegeCount = 0;
|
||
|
||
if (
|
||
!AccessCheck(
|
||
pSecDesc,
|
||
hUser,
|
||
FILE_GENERIC_READ,
|
||
&g_gmFile,
|
||
pPS,
|
||
&dwPSSize,
|
||
&dwGrantedAccess,
|
||
&bHaveAccess )
|
||
)
|
||
{
|
||
bHaveAccess = FALSE;
|
||
}
|
||
|
||
// Free our temporary buffer if neccessary.
|
||
|
||
if ( !pFile->fSecurityDescriptor )
|
||
{
|
||
FREE( pSecDesc );
|
||
}
|
||
|
||
return bHaveAccess;
|
||
}
|
||
|
||
dllexp
|
||
LPTS_OPEN_FILE_INFO
|
||
TsCreateFile(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN LPCSTR pszName,
|
||
IN HANDLE OpeningUser,
|
||
IN DWORD dwOptions
|
||
)
|
||
{
|
||
HANDLE hFile;
|
||
PVOID pvBlob;
|
||
PPHYS_OPEN_FILE_INFO lpPFInfo;
|
||
LPTS_OPEN_FILE_INFO lpOpenFile;
|
||
POPLOCK_OBJECT lpOplock;
|
||
BOOL bSuccess;
|
||
BOOL fAtRoot;
|
||
BOOL fImpersonated = FALSE;
|
||
DWORD dwSecDescSize;
|
||
DWORD dwReadN;
|
||
SECURITY_INFORMATION si
|
||
= OWNER_SECURITY_INFORMATION
|
||
| GROUP_SECURITY_INFORMATION
|
||
| DACL_SECURITY_INFORMATION;
|
||
PSECURITY_DESCRIPTOR abSecDesc;
|
||
BOOL fDoReadObjectSecurity;
|
||
BOOL fObjectSecurityPresent;
|
||
BOOL fOplockSucceeded = TRUE;
|
||
SECURITY_ATTRIBUTES sa;
|
||
BOOL fPhysFileCacheHit = FALSE;
|
||
BOOL fNoCanon = (dwOptions & TS_USE_WIN32_CANON) == 0;
|
||
BOOL fAccess;
|
||
DWORD dwGrantedAccess;
|
||
DWORD dwPS;
|
||
DWORD cch;
|
||
DWORD cbPrefix;
|
||
LPCSTR pszPath;
|
||
WCHAR awchPath[MAX_PATH+8+1];
|
||
BYTE psFile[SIZE_PRIVILEGE_SET];
|
||
BOOL fDontUseSpud = (DisableSPUD || !fNoCanon);
|
||
DWORD err;
|
||
|
||
//
|
||
// Mask out options that are not applicable
|
||
//
|
||
|
||
dwOptions &= TsValidCreateFileOptions;
|
||
if ( TsIsWindows95() ) {
|
||
dwOptions |= (TS_NO_ACCESS_CHECK | TS_DONT_CACHE_ACCESS_TOKEN);
|
||
}
|
||
|
||
//
|
||
// Have we cached a handle to this file?
|
||
//
|
||
|
||
if ( dwOptions & TS_CACHING_DESIRED )
|
||
{
|
||
|
||
bSuccess = TsCheckOutCachedBlob( TSvcCache,
|
||
pszName,
|
||
RESERVED_DEMUX_OPEN_FILE,
|
||
&pvBlob );
|
||
|
||
if ( bSuccess )
|
||
{
|
||
|
||
ASSERT( BLOB_IS_OR_WAS_CACHED( pvBlob ) );
|
||
|
||
//
|
||
// The following is a brutal casting of PVOID to C++ object
|
||
// Well. there is no way to extract the object clearly from the
|
||
// memory map :(
|
||
//
|
||
|
||
lpOpenFile = (LPTS_OPEN_FILE_INFO )pvBlob;
|
||
|
||
//
|
||
// Make sure the user tokens match
|
||
//
|
||
|
||
if ( (OpeningUser == lpOpenFile->QueryOpeningUser()
|
||
&& NULL != lpOpenFile->QueryOpeningUser())
|
||
|| (dwOptions & TS_NO_ACCESS_CHECK) )
|
||
{
|
||
ASSERT ( lpOpenFile->IsValid());
|
||
TSUNAMI_TRACE( TRACE_OPENFILE_REFERENCE, lpOpenFile );
|
||
|
||
return( lpOpenFile);
|
||
}
|
||
|
||
//
|
||
// User token doesn't match
|
||
//
|
||
|
||
if ( !g_fCacheSecDesc )
|
||
{
|
||
//
|
||
// Check in object, will have to
|
||
// open the file with the new user access token
|
||
//
|
||
|
||
bSuccess = TsCheckInCachedBlob( pvBlob );
|
||
|
||
ASSERT( bSuccess );
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// attempt to validate access using cached
|
||
// security descriptor
|
||
//
|
||
|
||
if ( lpOpenFile->IsSecurityDescriptorValid() )
|
||
{
|
||
dwPS = sizeof( psFile );
|
||
((PRIVILEGE_SET*)&psFile)->PrivilegeCount = 0;
|
||
|
||
if ( !AccessCheck(
|
||
lpOpenFile->GetSecurityDescriptor(),
|
||
OpeningUser,
|
||
FILE_GENERIC_READ,
|
||
&g_gmFile,
|
||
(PRIVILEGE_SET*)psFile,
|
||
&dwPS,
|
||
&dwGrantedAccess,
|
||
&fAccess ) )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[TsCreateFile]: AccessCheck failed, error %d\n",
|
||
GetLastError() ));
|
||
|
||
fAccess = FALSE;
|
||
}
|
||
|
||
if ( fAccess )
|
||
{
|
||
return( lpOpenFile );
|
||
}
|
||
}
|
||
|
||
//
|
||
// not validated using cached information
|
||
//
|
||
|
||
bSuccess = TsCheckInCachedBlob( pvBlob );
|
||
|
||
ASSERT( bSuccess );
|
||
|
||
if ( lpOpenFile->IsSecurityDescriptorValid() )
|
||
{
|
||
return( NULL );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( TsIsWindows95() )
|
||
{
|
||
fNoCanon = FALSE;
|
||
}
|
||
|
||
fDoReadObjectSecurity = FALSE;
|
||
|
||
sa.nLength = sizeof(sa);
|
||
sa.lpSecurityDescriptor = NULL;
|
||
sa.bInheritHandle = FALSE;
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFile(%s) - Calling TsCheckOutCachedPhysFile\n", pszName ));
|
||
}
|
||
|
||
fPhysFileCacheHit = TsCheckOutCachedPhysFile( TSvcCache,
|
||
pszName,
|
||
(VOID **)&lpPFInfo );
|
||
|
||
while (fPhysFileCacheHit)
|
||
{
|
||
DWORD dwLastError;
|
||
|
||
ASSERT( lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
|
||
WAIT_FOR_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
|
||
hFile = lpPFInfo->hOpenFile;
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE)
|
||
{
|
||
break;
|
||
}
|
||
|
||
dwLastError = lpPFInfo->dwLastError;
|
||
|
||
if (dwLastError != ERROR_FILE_NOT_FOUND &&
|
||
dwLastError != ERROR_PATH_NOT_FOUND &&
|
||
dwLastError != ERROR_INVALID_NAME )
|
||
{
|
||
fPhysFileCacheHit = TsReInitPhysFile(lpPFInfo);
|
||
}
|
||
else
|
||
{
|
||
TsRemovePhysFile(lpPFInfo);
|
||
SetLastError(dwLastError);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
if ( fPhysFileCacheHit )
|
||
{
|
||
|
||
DBG_ASSERT( hFile != INVALID_HANDLE_VALUE );
|
||
|
||
abSecDesc = lpPFInfo->abSecurityDescriptor;
|
||
dwSecDescSize = lpPFInfo->cbSecDescMaxSize;
|
||
fObjectSecurityPresent = lpPFInfo->fSecurityDescriptor;
|
||
|
||
//
|
||
// We've got a file handle from the cache. If we're doing access
|
||
// checking make sure we can use it.
|
||
//
|
||
if (!(dwOptions & TS_NO_ACCESS_CHECK))
|
||
{
|
||
if ( !TsAccessCheck(lpPFInfo, OpeningUser) )
|
||
{
|
||
// Don't have permission to use this handle, so fail.
|
||
//
|
||
TsCheckInOrFree( (PVOID)lpPFInfo );
|
||
SetLastError(ERROR_ACCESS_DENIED);
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
if ( lpPFInfo == NULL ) {
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
return NULL;
|
||
}
|
||
|
||
ASSERT( lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
|
||
|
||
if ( (dwOptions & TS_NOT_IMPERSONATED) &&
|
||
!(dwOptions & TS_NO_ACCESS_CHECK) )
|
||
{
|
||
if ( !::ImpersonateLoggedOnUser( OpeningUser ) )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"ImpersonateLoggedOnUser[%d] failed with %d\n",
|
||
OpeningUser, GetLastError()));
|
||
|
||
err = lpPFInfo->dwLastError;
|
||
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
|
||
SetLastError( err );
|
||
return NULL;
|
||
}
|
||
fImpersonated = TRUE;
|
||
}
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFile(%s) - Not in cache, Opening!\n", pszName ));
|
||
}
|
||
|
||
abSecDesc = lpPFInfo->abSecurityDescriptor;
|
||
dwSecDescSize = lpPFInfo->cbSecDescMaxSize;
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFile(%s) - abSecDesc = %08lx, size = %08lx\n", pszName, abSecDesc, SECURITY_DESC_DEFAULT_SIZE ));
|
||
}
|
||
|
||
if ( abSecDesc == NULL ) {
|
||
if( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
return( NULL );
|
||
}
|
||
|
||
if ( fNoCanon )
|
||
{
|
||
if ( (pszName[0] == '\\') && (pszName[1] == '\\') )
|
||
{
|
||
CopyMemory(
|
||
awchPath,
|
||
L"\\\\?\\UNC\\",
|
||
(sizeof("\\\\?\\UNC\\")-1) * sizeof(WCHAR)
|
||
);
|
||
|
||
cbPrefix = sizeof("\\\\?\\UNC\\")-1;
|
||
pszPath = pszName + sizeof( "\\\\" ) -1;
|
||
}
|
||
else
|
||
{
|
||
CopyMemory(
|
||
awchPath,
|
||
L"\\\\?\\",
|
||
(sizeof("\\\\?\\")-1) * sizeof(WCHAR)
|
||
);
|
||
|
||
cbPrefix = sizeof("\\\\?\\")-1;
|
||
pszPath = pszName;
|
||
}
|
||
|
||
cch = MultiByteToWideChar( CP_ACP,
|
||
MB_PRECOMPOSED,
|
||
pszPath,
|
||
-1,
|
||
awchPath + cbPrefix,
|
||
sizeof(awchPath)/sizeof(WCHAR) - cbPrefix );
|
||
if ( !cch )
|
||
{
|
||
hFile = INVALID_HANDLE_VALUE;
|
||
}
|
||
else
|
||
{
|
||
if ( (pszName[1] == ':') && (pszName[2] == '\0') )
|
||
{
|
||
wcscat( awchPath, L"\\" );
|
||
}
|
||
|
||
sa.nLength = sizeof(sa);
|
||
sa.lpSecurityDescriptor = NULL;
|
||
sa.bInheritHandle = FALSE;
|
||
|
||
#if 1
|
||
// if ( fDontUseSpud ) {
|
||
hFile = CreateFileW( awchPath,
|
||
GENERIC_READ,
|
||
TsCreateFileShareMode,
|
||
&sa,
|
||
OPEN_EXISTING,
|
||
TsCreateFileFlags,
|
||
NULL );
|
||
#else
|
||
} else {
|
||
lpOplock = ( POPLOCK_OBJECT )LocalAlloc(LPTR, sizeof(OPLOCK_OBJECT));
|
||
|
||
if ( lpOplock == NULL ) {
|
||
DBGPRINTF((DBG_CONTEXT,"LocalAlloc lpOplock[%s] failed with %d\n",
|
||
pszName, GetLastError()));
|
||
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
|
||
ASSERT( !fImpersonated );
|
||
return( NULL );
|
||
}
|
||
|
||
lpOplock->Signature = OPLOCK_OBJ_SIGNATURE;
|
||
lpOplock->lpPFInfo = NULL;
|
||
lpOplock->hOplockInitComplete = IIS_CREATE_EVENT(
|
||
"OPLOCK_OBJECT::hOplockInitComplete",
|
||
lpOplock,
|
||
TRUE,
|
||
FALSE
|
||
);
|
||
|
||
hFile = AtqCreateFileW( awchPath,
|
||
TsCreateFileShareMode,
|
||
&sa,
|
||
TsCreateFileFlags,
|
||
si,
|
||
(PSECURITY_DESCRIPTOR)abSecDesc,
|
||
( ( g_fCacheSecDesc
|
||
&& !(dwOptions & TS_NO_ACCESS_CHECK)
|
||
&& (dwOptions & TS_CACHING_DESIRED) ) ?
|
||
SECURITY_DESC_DEFAULT_SIZE : 0 ),
|
||
&dwSecDescSize,
|
||
OplockCreateFile,
|
||
(PVOID)lpOplock );
|
||
}
|
||
#endif //!oplock
|
||
}
|
||
}
|
||
else
|
||
{
|
||
hFile = CreateFile( pszName,
|
||
GENERIC_READ,
|
||
TsCreateFileShareMode,
|
||
&sa,
|
||
OPEN_EXISTING,
|
||
TsCreateFileFlags,
|
||
NULL );
|
||
}
|
||
|
||
//
|
||
// If we're supposed to cache the security descriptor, do so now.
|
||
// This should only be done once, by the thread intializing the physical
|
||
// file entry.
|
||
//
|
||
|
||
if (DisableSPUD && hFile != INVALID_HANDLE_VALUE)
|
||
{
|
||
if ( g_fCacheSecDesc )
|
||
{
|
||
DWORD dwInputSize;
|
||
|
||
dwInputSize = dwSecDescSize;
|
||
|
||
// Loop, reading the security info each time, until we either
|
||
// read it succesfully, are unable to allocate a big enough buffer,
|
||
// or get some error other than buffer too smal.
|
||
|
||
for (;;)
|
||
{
|
||
|
||
if ( GetKernelObjectSecurity(
|
||
hFile,
|
||
si,
|
||
(PSECURITY_DESCRIPTOR)lpPFInfo->abSecurityDescriptor,
|
||
dwInputSize,
|
||
&dwSecDescSize ) )
|
||
{
|
||
lpPFInfo->fSecurityDescriptor = TRUE ;
|
||
fObjectSecurityPresent = TRUE;
|
||
abSecDesc = (PSECURITY_DESCRIPTOR)lpPFInfo->abSecurityDescriptor;
|
||
dwSecDescSize = dwInputSize;
|
||
break;
|
||
}
|
||
|
||
// Had some sort of error on the attempt to get the security
|
||
// descriptor.
|
||
|
||
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
||
{
|
||
|
||
// Need a bigger buffer for the descriptor.
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFile() - Realloc Security Desc !!!\n" ));
|
||
}
|
||
|
||
FREE( lpPFInfo->abSecurityDescriptor );
|
||
lpPFInfo->abSecurityDescriptor = (PSECURITY_DESCRIPTOR)ALLOC( dwSecDescSize );
|
||
|
||
if ( lpPFInfo->abSecurityDescriptor == NULL )
|
||
{
|
||
CloseHandle( hFile );
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
|
||
if ( fImpersonated )
|
||
{
|
||
::RevertToSelf();
|
||
}
|
||
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
return NULL;
|
||
}
|
||
|
||
dwInputSize = dwSecDescSize;
|
||
|
||
} else
|
||
{
|
||
// This wasn't too small a buffer, so quit trying.
|
||
|
||
dwSecDescSize = 0;
|
||
|
||
break;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFile(%s) - SECURITY_DESC_DEFAULT_SIZE = %08lx, dwSecDescSize = %08lx\n",
|
||
pszName, SECURITY_DESC_DEFAULT_SIZE, dwSecDescSize ));
|
||
}
|
||
|
||
if ( hFile == INVALID_HANDLE_VALUE )
|
||
{
|
||
DBGPRINTF((DBG_CONTEXT,"CreateFile[%s] failed with %d\n",
|
||
pszName, GetLastError()));
|
||
|
||
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
lpPFInfo->dwLastError = GetLastError();
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
err = lpPFInfo->dwLastError;
|
||
TsRemovePhysFile(lpPFInfo);
|
||
SetLastError( err );
|
||
return( NULL );
|
||
}
|
||
|
||
|
||
if ( !fDontUseSpud ) {
|
||
|
||
if ( GetLastError() != ERROR_SUCCESS || fPhysFileCacheHit ) {
|
||
fOplockSucceeded = FALSE;
|
||
}
|
||
if ( fPhysFileCacheHit || (dwSecDescSize == SECURITY_DESC_DEFAULT_SIZE ) ) {
|
||
fObjectSecurityPresent = TRUE;
|
||
lpPFInfo->fSecurityDescriptor = TRUE ;
|
||
} else {
|
||
if ( dwSecDescSize > SECURITY_DESC_DEFAULT_SIZE ) {
|
||
fDoReadObjectSecurity = TRUE;
|
||
|
||
dwSecDescSize = ( (dwSecDescSize + SECURITY_DESC_GRANULARITY - 1)
|
||
/ SECURITY_DESC_GRANULARITY )
|
||
* SECURITY_DESC_GRANULARITY;
|
||
|
||
} else {
|
||
dwSecDescSize = 0;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Increment the miss count after we've confirmed it's a valid resource
|
||
//
|
||
|
||
bSuccess = TsAllocateEx( TSvcCache,
|
||
sizeof( TS_OPEN_FILE_INFO ),
|
||
&pvBlob,
|
||
DisposeOpenFileInfo );
|
||
|
||
if ( !bSuccess )
|
||
{
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
return( NULL );
|
||
}
|
||
|
||
lpOpenFile = (LPTS_OPEN_FILE_INFO)pvBlob;
|
||
|
||
if ( !fPhysFileCacheHit )
|
||
{
|
||
lpPFInfo->hOpenFile = hFile;
|
||
if (BLOB_IS_OR_WAS_CACHED( lpPFInfo ))
|
||
{
|
||
INC_COUNTER( TSvcCache.GetServiceId(), CurrentOpenFileHandles );
|
||
}
|
||
}
|
||
|
||
#if 0
|
||
if ( !fDontUseSpud && !fPhysFileCacheHit ) {
|
||
if ( fOplockSucceeded ) {
|
||
lpOplock->lpPFInfo = lpPFInfo;
|
||
SetEvent( lpOplock->hOplockInitComplete );
|
||
} else {
|
||
dwOptions &= ~TS_CACHING_DESIRED;
|
||
}
|
||
}
|
||
#endif //!oplock
|
||
|
||
//
|
||
// The file must be fully qualified so it must be at least three characters
|
||
// plus the terminator
|
||
//
|
||
|
||
fAtRoot = (pszName[1] == ':' &&
|
||
((pszName[2] == '\\' && pszName[3] == '\0')
|
||
|| (pszName[2] == '\0')) );
|
||
|
||
bSuccess = lpOpenFile->SetFileInfo( lpPFInfo,
|
||
(dwOptions & TS_DONT_CACHE_ACCESS_TOKEN) ? NULL : OpeningUser,
|
||
fAtRoot,
|
||
dwSecDescSize );
|
||
|
||
if (!fDontUseSpud)
|
||
InsertHeadPhysFile( lpPFInfo, pvBlob );
|
||
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
//
|
||
// Check if security descriptor to be read.
|
||
// Failure to read it will not make TsCreateFile to fail :
|
||
// security descriptor size may have grown since previous call
|
||
//
|
||
|
||
if ( fDoReadObjectSecurity )
|
||
{
|
||
|
||
FREE( abSecDesc );
|
||
abSecDesc = (PSECURITY_DESCRIPTOR)ALLOC( dwSecDescSize );
|
||
lpPFInfo->abSecurityDescriptor = abSecDesc;
|
||
bSuccess = lpOpenFile->SetFileInfo( lpPFInfo,
|
||
(dwOptions & TS_DONT_CACHE_ACCESS_TOKEN) ? NULL : OpeningUser,
|
||
fAtRoot,
|
||
dwSecDescSize );
|
||
|
||
if ( GetKernelObjectSecurity(
|
||
hFile,
|
||
si,
|
||
lpOpenFile->GetSecurityDescriptor(),
|
||
dwSecDescSize,
|
||
&dwReadN ) )
|
||
{
|
||
sec_succ:
|
||
lpOpenFile->SetSecurityDescriptorValid( TRUE );
|
||
|
||
}
|
||
else
|
||
{
|
||
ASSERT( GetLastError() != 0 );
|
||
}
|
||
}
|
||
else if ( fObjectSecurityPresent )
|
||
{
|
||
goto sec_succ;
|
||
}
|
||
|
||
if ( !bSuccess)
|
||
{
|
||
|
||
//
|
||
// Error in setting up the file information.
|
||
//
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
|
||
#if 0
|
||
if ( !fDontUseSpud && fOplockSucceeded ) {
|
||
SetEvent( lpOplock->hOplockInitComplete );
|
||
}
|
||
#endif //oplock
|
||
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
err = lpPFInfo->dwLastError;
|
||
TsRemovePhysFile(lpPFInfo);
|
||
SetLastError( err );
|
||
return ( NULL);
|
||
}
|
||
|
||
//
|
||
// If this is a UNC connection check and make sure we haven't exceeded
|
||
// the maximum UNC handles we will cache (SMB FID limits count to 2048)
|
||
//
|
||
|
||
if ( !g_fDisableCaching &&
|
||
(dwOptions & TS_CACHING_DESIRED ) &&
|
||
(cCachedUNCHandles < MAX_CACHED_UNC_HANDLES ||
|
||
pszName[1] != '\\') )
|
||
{
|
||
bSuccess = TsCacheDirectoryBlob( TSvcCache,
|
||
pszName,
|
||
RESERVED_DEMUX_OPEN_FILE,
|
||
pvBlob,
|
||
TRUE );
|
||
|
||
//
|
||
// Only count it if we successfully added the item to the
|
||
// cache
|
||
//
|
||
|
||
if ( bSuccess )
|
||
{
|
||
if ( pszName[1] == '\\' )
|
||
{
|
||
InterlockedIncrement( (LONG *) &cCachedUNCHandles );
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Too many cached UNC handles, don't cache it. It would be nice
|
||
// to do an LRU for these handles but it's probably not generally
|
||
// worth it
|
||
//
|
||
|
||
bSuccess = FALSE;
|
||
}
|
||
|
||
#if DBG
|
||
if ( !bSuccess )
|
||
{
|
||
ASSERT( !BLOB_IS_OR_WAS_CACHED( pvBlob ) );
|
||
}
|
||
else
|
||
{
|
||
ASSERT( BLOB_IS_OR_WAS_CACHED( pvBlob ) );
|
||
}
|
||
#endif
|
||
if ( !fPhysFileCacheHit ) {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFile(%s) - Not in cache, Open Complete Causing Event!\n", pszName ));
|
||
}
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
|
||
#if 0
|
||
if ( !fDontUseSpud && fOplockSucceeded ) {
|
||
SetEvent( lpOplock->hOplockInitComplete );
|
||
}
|
||
#endif //!oplock
|
||
|
||
} else {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFile(%s) - Already in cache, Open Complete!\n", pszName ));
|
||
}
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
TSUNAMI_TRACE( TRACE_OPENFILE_CREATE, lpOpenFile );
|
||
return( lpOpenFile );
|
||
|
||
} // TsCreateFile
|
||
|
||
|
||
|
||
dllexp
|
||
LPTS_OPEN_FILE_INFO
|
||
TsCreateFileFromURI(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN PW3_URI_INFO pURIInfo,
|
||
IN HANDLE OpeningUser,
|
||
IN DWORD dwOptions,
|
||
IN DWORD *dwError
|
||
)
|
||
|
||
/*+++
|
||
|
||
TsCreateFileFromURI
|
||
|
||
This routine takes a (checked out) URI block and retrieves a file
|
||
handle from it. If the file handle in the URI info block is valid and
|
||
we have the right security for it, we'll use that. Otherwise if it's
|
||
invalid we'll create a valid handle and save it. If it's valid but we
|
||
don't have security for it we'll open a new handle but not cache it.
|
||
|
||
Not caching a new handle may become a performance problem in the future
|
||
if authenticated file access becomes common. There are several possible
|
||
solutions to this problem if this occurs. One would be to have a list
|
||
of cached TS_OPEN_FILE_INFO class structures chained off the URI block,
|
||
one for each 'distinct' security class, and then select the best one to
|
||
return. Another would be to have a second level of cache, i.e. put the
|
||
URI blocks in a second hash table and keep one OPEN_FILE_INFO in the
|
||
URI block, looking up other in the other hash table if we need to. Any
|
||
solution would have to exhibit the property of being able to handle
|
||
mapping from a single URI to multiple file handles.
|
||
|
||
Arguments:
|
||
|
||
TsvcCache -
|
||
pURIInfo - A pointer to the URI block from which we're to derive
|
||
file information.
|
||
OpeningUser - A handle identifying the opening used.
|
||
dwOptions - A set of options indicating how we're to open the file.
|
||
|
||
Returns:
|
||
|
||
A pointer to a TS_OPEN_FILE_INFO class structure if we're succesfull,
|
||
or NULL if we're not.
|
||
---*/
|
||
|
||
{
|
||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
LPTS_OPEN_FILE_INFO lpOpenFile;
|
||
POPLOCK_OBJECT lpOplock;
|
||
BOOL fAtRoot;
|
||
BOOL bSuccess;
|
||
PVOID pvBlob;
|
||
PPHYS_OPEN_FILE_INFO lpPFInfo;
|
||
BOOL fImpersonated = FALSE;
|
||
BOOL SPUDdidCall = FALSE;
|
||
DWORD dwSecDescSize;
|
||
BOOL fOplockSucceeded = FALSE;
|
||
SECURITY_INFORMATION si
|
||
= OWNER_SECURITY_INFORMATION
|
||
| GROUP_SECURITY_INFORMATION
|
||
| DACL_SECURITY_INFORMATION;
|
||
SECURITY_ATTRIBUTES sa;
|
||
BOOL fPhysFileCacheHit = FALSE;
|
||
BOOL fNoCanon = (dwOptions & TS_USE_WIN32_CANON) == 0;
|
||
BOOL fAccess;
|
||
DWORD dwGrantedAccess;
|
||
DWORD dwPS;
|
||
DWORD cch;
|
||
DWORD cbPrefix;
|
||
LPCSTR pszPath;
|
||
PCHAR pName;
|
||
DWORD dwInputSize;
|
||
WCHAR awchPath[MAX_PATH+8+1];
|
||
BYTE psFile[SIZE_PRIVILEGE_SET];
|
||
|
||
AcIncrement( CacOpenURI);
|
||
|
||
ASSERT(pURIInfo != NULL);
|
||
|
||
//
|
||
// Mask out options that are not applicable
|
||
//
|
||
|
||
dwOptions &= TsValidCreateFileOptions;
|
||
|
||
if ( TsIsWindows95() ) {
|
||
dwOptions |= (TS_NO_ACCESS_CHECK | TS_DONT_CACHE_ACCESS_TOKEN);
|
||
}
|
||
|
||
*dwError = pURIInfo->dwFileOpenError;
|
||
|
||
// Check to see if the open file info is valid. If it it, try to use it.
|
||
|
||
if ( pURIInfo->bFileInfoValid )
|
||
{
|
||
|
||
|
||
// Get the open file info. If it's non-NULL (the file exists)
|
||
// see if we have permission to access it.
|
||
|
||
lpOpenFile = pURIInfo->pOpenFileInfo;
|
||
|
||
//
|
||
// Make sure the user tokens match, or that lpOpenFile is NULL.
|
||
// In the latter case the file doesn't exist - this is a negative
|
||
// hit.
|
||
//
|
||
|
||
if ( ( lpOpenFile == NULL ) ||
|
||
(OpeningUser == lpOpenFile->QueryOpeningUser()
|
||
&& lpOpenFile->QueryOpeningUser() != NULL)
|
||
|| (dwOptions & TS_NO_ACCESS_CHECK) )
|
||
{
|
||
|
||
return( lpOpenFile);
|
||
}
|
||
|
||
//
|
||
// User token doesn't match
|
||
//
|
||
|
||
if ( g_fCacheSecDesc )
|
||
{
|
||
|
||
//
|
||
// attempt to validate access using cached
|
||
// security descriptor
|
||
//
|
||
|
||
if ( lpOpenFile->IsSecurityDescriptorValid() )
|
||
{
|
||
dwPS = sizeof( psFile );
|
||
((PRIVILEGE_SET*)&psFile)->PrivilegeCount = 0;
|
||
|
||
if ( !AccessCheck(
|
||
lpOpenFile->GetSecurityDescriptor(),
|
||
OpeningUser,
|
||
FILE_GENERIC_READ,
|
||
&g_gmFile,
|
||
(PRIVILEGE_SET*)psFile,
|
||
&dwPS,
|
||
&dwGrantedAccess,
|
||
&fAccess ) )
|
||
{
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"[TsCreateFileFromURI]: AccessCheck failed, \
|
||
error %d\n",
|
||
GetLastError() ));
|
||
|
||
fAccess = FALSE;
|
||
}
|
||
|
||
// See if we have access to the file. If we get here we know
|
||
// we have a valid security descriptor, so if we didn't get
|
||
// access on this check then there's no point in attemptint to
|
||
// open the file.
|
||
|
||
if ( fAccess )
|
||
{
|
||
return( lpOpenFile );
|
||
}
|
||
else
|
||
{
|
||
*dwError = GetLastError();
|
||
|
||
return NULL;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
// At this point, either the file info in the structure isn't
|
||
// valid, or we're not allowed to cache security descriptors, or
|
||
// the cache descriptor isn't valid. In any of these cases we need
|
||
// to try to open the file. If this succeeds and the file info in the
|
||
// URI block isn't valid, save the newly opened file info there.
|
||
//
|
||
// If we open the file but the file info isn't valid, we'll tag the
|
||
// open file info structure as non cached so that we won't try to
|
||
// check it in later.
|
||
//
|
||
// Since the file info isn't valid, the error we set in *dwError
|
||
// is bad also. All exits from this point on need to make sure to
|
||
// set that to the proper value. We also want to update the cached
|
||
// error in the URI info if we're going to make the file info valid.
|
||
//
|
||
//
|
||
// Now try to open the actual file.
|
||
//
|
||
|
||
if ( TsIsWindows95() )
|
||
{
|
||
fNoCanon = FALSE;
|
||
}
|
||
|
||
sa.nLength = sizeof(sa);
|
||
sa.lpSecurityDescriptor = NULL;
|
||
sa.bInheritHandle = FALSE;
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - Calling TsCheckOutCachedPhysFile\n", pURIInfo->pszName ));
|
||
}
|
||
|
||
fPhysFileCacheHit = TsCheckOutCachedPhysFile( TSvcCache,
|
||
pURIInfo->pszName,
|
||
(VOID **)&lpPFInfo );
|
||
|
||
while (fPhysFileCacheHit)
|
||
{
|
||
DWORD dwLastError;
|
||
|
||
ASSERT( lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
|
||
WAIT_FOR_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
|
||
hFile = lpPFInfo->hOpenFile;
|
||
|
||
if (hFile != INVALID_HANDLE_VALUE)
|
||
{
|
||
break;
|
||
}
|
||
|
||
dwLastError = lpPFInfo->dwLastError;
|
||
|
||
if (dwLastError != ERROR_FILE_NOT_FOUND &&
|
||
dwLastError != ERROR_PATH_NOT_FOUND &&
|
||
dwLastError != ERROR_INVALID_NAME )
|
||
{
|
||
fPhysFileCacheHit = TsReInitPhysFile(lpPFInfo);
|
||
}
|
||
else
|
||
{
|
||
TsRemovePhysFile(lpPFInfo);
|
||
SetLastError(dwLastError);
|
||
*dwError = dwLastError;
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
if ( fPhysFileCacheHit )
|
||
{
|
||
|
||
dwSecDescSize = lpPFInfo->cbSecDescMaxSize;
|
||
|
||
DBG_ASSERT(hFile != INVALID_HANDLE_VALUE);
|
||
|
||
//
|
||
// We've got a file handle from the cache. If we're doing access
|
||
// checking make sure we can use it.
|
||
//
|
||
if (!(dwOptions & TS_NO_ACCESS_CHECK))
|
||
{
|
||
if ( !TsAccessCheck(lpPFInfo, OpeningUser) )
|
||
{
|
||
// Don't have permission to use this handle, so fail.
|
||
//
|
||
TsCheckInOrFree( (PVOID)lpPFInfo );
|
||
SetLastError(ERROR_ACCESS_DENIED);
|
||
*dwError = ERROR_ACCESS_DENIED;
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( lpPFInfo == NULL ) {
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
*dwError = ERROR_NOT_ENOUGH_MEMORY;
|
||
return NULL;
|
||
}
|
||
|
||
ASSERT( lpPFInfo->Signature == PHYS_OBJ_SIGNATURE );
|
||
|
||
//
|
||
// If we're not impersonating right now, do that before we try to
|
||
// open the file.
|
||
//
|
||
|
||
if ( (dwOptions & TS_NOT_IMPERSONATED) &&
|
||
!(dwOptions & TS_NO_ACCESS_CHECK) )
|
||
{
|
||
if ( !::ImpersonateLoggedOnUser( OpeningUser ) )
|
||
{
|
||
*dwError = GetLastError();
|
||
|
||
DBGPRINTF(( DBG_CONTEXT,
|
||
"ImpersonateLoggedOnUser[%d] failed with %d\n",
|
||
OpeningUser, *dwError));
|
||
|
||
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
return NULL;
|
||
}
|
||
|
||
fImpersonated = TRUE;
|
||
}
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - Not in Cache, Opening!\n", pURIInfo->pszName ));
|
||
}
|
||
|
||
dwSecDescSize = lpPFInfo->cbSecDescMaxSize;
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - lpPFInfo->abSecurityDescriptor = %08lx, size = %08lx\n", pURIInfo->pszName, lpPFInfo->abSecurityDescriptor, SECURITY_DESC_DEFAULT_SIZE ));
|
||
}
|
||
|
||
if ( lpPFInfo->abSecurityDescriptor == NULL ) {
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
|
||
if ( fNoCanon )
|
||
{
|
||
if ( (pURIInfo->pszName[0] == '\\') && (pURIInfo->pszName[1] == '\\') )
|
||
{
|
||
CopyMemory(
|
||
awchPath,
|
||
L"\\\\?\\UNC\\",
|
||
(sizeof("\\\\?\\UNC\\")-1) * sizeof(WCHAR)
|
||
);
|
||
|
||
cbPrefix = sizeof("\\\\?\\UNC\\")-1;
|
||
pszPath = pURIInfo->pszName + sizeof( "\\\\" ) -1;
|
||
}
|
||
else
|
||
{
|
||
CopyMemory(
|
||
awchPath,
|
||
L"\\\\?\\",
|
||
(sizeof("\\\\?\\")-1) * sizeof(WCHAR)
|
||
);
|
||
|
||
cbPrefix = sizeof("\\\\?\\")-1;
|
||
pszPath = pURIInfo->pszName;
|
||
}
|
||
|
||
cch = MultiByteToWideChar( CP_ACP,
|
||
MB_PRECOMPOSED,
|
||
pszPath,
|
||
-1,
|
||
awchPath + cbPrefix,
|
||
sizeof(awchPath)/sizeof(WCHAR) - cbPrefix );
|
||
if ( !cch )
|
||
{
|
||
hFile = INVALID_HANDLE_VALUE;
|
||
}
|
||
else
|
||
{
|
||
if ( (pURIInfo->pszName[1] == ':') && (pURIInfo->pszName[2] == '\0') )
|
||
{
|
||
wcscat( awchPath, L"\\" );
|
||
}
|
||
|
||
sa.nLength = sizeof(sa);
|
||
sa.lpSecurityDescriptor = NULL;
|
||
sa.bInheritHandle = FALSE;
|
||
|
||
#if 1
|
||
//if ( DisableSPUD ) {
|
||
hFile = CreateFileW( awchPath,
|
||
GENERIC_READ,
|
||
TsCreateFileShareMode,
|
||
&sa,
|
||
OPEN_EXISTING,
|
||
TsCreateFileFlags,
|
||
NULL );
|
||
#else
|
||
} else {
|
||
|
||
if ( g_fCacheSecDesc &&
|
||
!(dwOptions & TS_NO_ACCESS_CHECK) )
|
||
{
|
||
// Assume we can get by with the default size, and just allocate
|
||
// that.
|
||
|
||
lpOpenFile = ( LPTS_OPEN_FILE_INFO )LocalAlloc(LPTR,
|
||
sizeof(TS_OPEN_FILE_INFO));
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - lpOpenFile = %08lx\n", pURIInfo->pszName, lpOpenFile ));
|
||
}
|
||
|
||
if (lpOpenFile == NULL)
|
||
{
|
||
// Couldn't get the memory we needed, so fail.
|
||
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
lpOpenFile->SetFileInfo( lpPFInfo,
|
||
(dwOptions & TS_DONT_CACHE_ACCESS_TOKEN) ? NULL : OpeningUser,
|
||
FALSE,
|
||
dwSecDescSize );
|
||
|
||
}
|
||
|
||
SPUDdidCall = TRUE;
|
||
|
||
lpOplock = ( POPLOCK_OBJECT )LocalAlloc(LPTR, sizeof(OPLOCK_OBJECT));
|
||
|
||
if ( lpOplock == NULL ) {
|
||
LocalFree( lpOpenFile );
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
lpOplock->Signature = OPLOCK_OBJ_SIGNATURE;
|
||
lpOplock->lpPFInfo = NULL;
|
||
lpOplock->hOplockInitComplete = IIS_CREATE_EVENT(
|
||
"OPLOCK_OBJECT::hOplockInitComplete",
|
||
lpOplock,
|
||
TRUE,
|
||
FALSE
|
||
);
|
||
|
||
hFile = AtqCreateFileW( awchPath,
|
||
TsCreateFileShareMode,
|
||
&sa,
|
||
TsCreateFileFlags,
|
||
si,
|
||
(PSECURITY_DESCRIPTOR)lpOpenFile->GetSecurityDescriptor(),
|
||
( ( g_fCacheSecDesc
|
||
&& !(dwOptions & TS_NO_ACCESS_CHECK)) ?
|
||
SECURITY_DESC_DEFAULT_SIZE : 0 ),
|
||
&dwSecDescSize,
|
||
OplockCreateFile,
|
||
(PVOID)lpOplock );
|
||
}
|
||
#endif //!oplock
|
||
}
|
||
}
|
||
else
|
||
{
|
||
hFile = CreateFile( pURIInfo->pszName,
|
||
GENERIC_READ,
|
||
TsCreateFileShareMode,
|
||
&sa,
|
||
OPEN_EXISTING,
|
||
TsCreateFileFlags,
|
||
NULL );
|
||
}
|
||
|
||
//
|
||
// If we're supposed to cache the security descriptor, do so now.
|
||
// This should only be done once, by the thread intializing the physical
|
||
// file entry.
|
||
//
|
||
|
||
if (DisableSPUD && hFile != INVALID_HANDLE_VALUE)
|
||
{
|
||
if ( g_fCacheSecDesc )
|
||
{
|
||
dwInputSize = dwSecDescSize;
|
||
|
||
// Loop, reading the security info each time, until we either
|
||
// read it succesfully, are unable to allocate a big enough buffer,
|
||
// or get some error other than buffer too smal.
|
||
|
||
for (;;)
|
||
{
|
||
|
||
if ( GetKernelObjectSecurity(
|
||
hFile,
|
||
si,
|
||
(PSECURITY_DESCRIPTOR)lpPFInfo->abSecurityDescriptor,
|
||
dwInputSize,
|
||
&dwSecDescSize ) )
|
||
{
|
||
lpPFInfo->fSecurityDescriptor = TRUE ;
|
||
dwSecDescSize = dwInputSize;
|
||
break;
|
||
}
|
||
|
||
// Had some sort of error on the attempt to get the security
|
||
// descriptor.
|
||
|
||
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
||
{
|
||
|
||
// Need a bigger buffer for the descriptor.
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - Realloc Security Desc !!!\n", pURIInfo->pszName ));
|
||
}
|
||
|
||
FREE( lpPFInfo->abSecurityDescriptor );
|
||
lpPFInfo->abSecurityDescriptor = (PSECURITY_DESCRIPTOR)ALLOC( dwSecDescSize );
|
||
|
||
if ( lpPFInfo->abSecurityDescriptor == NULL )
|
||
{
|
||
CloseHandle( hFile );
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
dwInputSize = dwSecDescSize;
|
||
|
||
} else
|
||
{
|
||
// This wasn't too small a buffer, so quit trying.
|
||
|
||
dwSecDescSize = 0;
|
||
|
||
break;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( hFile != INVALID_HANDLE_VALUE )
|
||
{
|
||
AcIncrement( AacOpenURIFiles);
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
lpPFInfo->hOpenFile = hFile;
|
||
|
||
if (BLOB_IS_OR_WAS_CACHED( lpPFInfo ))
|
||
{
|
||
INC_COUNTER( TSvcCache.GetServiceId(), CurrentOpenFileHandles );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we're supposed to cache the security descriptor we'll do that
|
||
// now. In order to do this we need to allocate the OPEN_FILE_INFO
|
||
// structure that we'll use.
|
||
//
|
||
|
||
#if 1 // Oplocks are always disabled
|
||
//if ( DisableSPUD ) {
|
||
|
||
lpOpenFile = ( LPTS_OPEN_FILE_INFO )LocalAlloc(LPTR,
|
||
sizeof(TS_OPEN_FILE_INFO));
|
||
|
||
|
||
if (lpOpenFile == NULL)
|
||
{
|
||
// Couldn't get the memory we needed, so fail.
|
||
|
||
goto not_enough_memory;
|
||
}
|
||
#else
|
||
} else {
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
if ( GetLastError() == ERROR_SUCCESS ) {
|
||
fOplockSucceeded = TRUE;
|
||
}
|
||
if ( g_fCacheSecDesc &&
|
||
!(dwOptions & TS_NO_ACCESS_CHECK) )
|
||
{
|
||
dwInputSize = dwSecDescSize;
|
||
|
||
if (dwSecDescSize > SECURITY_DESC_DEFAULT_SIZE ) {
|
||
|
||
PSECURITY_DESCRIPTOR TmpSd;
|
||
|
||
TmpSd = lpOpenFile->GetSecurityDescriptor();
|
||
|
||
FREE( TmpSd );
|
||
TmpSd = (PSECURITY_DESCRIPTOR)ALLOC( dwSecDescSize );
|
||
lpPFInfo->abSecurityDescriptor = TmpSd;
|
||
|
||
if ( TmpSd == NULL )
|
||
{
|
||
LocalFree( lpOpenFile );
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
lpOpenFile->SetFileInfo( lpPFInfo,
|
||
(dwOptions & TS_DONT_CACHE_ACCESS_TOKEN) ? NULL : OpeningUser,
|
||
FALSE,
|
||
dwSecDescSize );
|
||
|
||
// Now loop, reading the security info each time, until we either
|
||
// read it succesfully, are unable to allocate a big enough buffer,
|
||
// or get some error other than buffer too smal.
|
||
|
||
|
||
if ( !GetKernelObjectSecurity(
|
||
hFile,
|
||
si,
|
||
(PSECURITY_DESCRIPTOR)lpOpenFile->GetSecurityDescriptor(),
|
||
dwInputSize,
|
||
&dwSecDescSize ) )
|
||
{
|
||
|
||
// Had some sort of error on the attempt to get the security
|
||
// descriptor.
|
||
|
||
// This wasn't too small a buffer, so quit trying.
|
||
|
||
dwSecDescSize = 0;
|
||
|
||
}
|
||
lpPFInfo->fSecurityDescriptor = TRUE ;
|
||
|
||
} else {
|
||
lpPFInfo->fSecurityDescriptor = TRUE ;
|
||
}
|
||
|
||
} else {
|
||
lpOpenFile = ( LPTS_OPEN_FILE_INFO )LocalAlloc(LPTR,
|
||
sizeof(TS_OPEN_FILE_INFO));
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - lpOpenFile = %08lx\n", pURIInfo->pszName, lpOpenFile ));
|
||
}
|
||
|
||
if (lpOpenFile == NULL)
|
||
{
|
||
// Couldn't get the memory we needed, so fail.
|
||
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
}
|
||
} else {
|
||
lpOpenFile = ( LPTS_OPEN_FILE_INFO )LocalAlloc(LPTR,
|
||
sizeof(TS_OPEN_FILE_INFO));
|
||
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - lpOpenFile = %08lx\n", pURIInfo->pszName, lpOpenFile ));
|
||
}
|
||
|
||
if (lpOpenFile == NULL)
|
||
{
|
||
// Couldn't get the memory we needed, so fail.
|
||
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
}
|
||
}
|
||
#endif //!oplock
|
||
|
||
*dwError = ERROR_SUCCESS;
|
||
lpOpenFile->SetCachedFlag(TRUE);
|
||
|
||
//
|
||
// The file must be fully qualified so it must be at least three
|
||
// characters plus the terminator
|
||
//
|
||
|
||
pName = pURIInfo->pszName;
|
||
|
||
fAtRoot = (pName[1] == ':' &&
|
||
((pName[2] == '\\' && pName[3] == '\0')
|
||
|| (pName[2] == '\0')) );
|
||
|
||
bSuccess = lpOpenFile->SetFileInfo( lpPFInfo,
|
||
(dwOptions & TS_DONT_CACHE_ACCESS_TOKEN) ? NULL : OpeningUser,
|
||
fAtRoot,
|
||
dwSecDescSize );
|
||
|
||
if ( !bSuccess )
|
||
{
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
AcDecrement( AacOpenURIFiles);
|
||
|
||
LocalFree(lpOpenFile);
|
||
|
||
*dwError = GetLastError();
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
#if 0 //!oplock
|
||
if ( !DisableSPUD && fOplockSucceeded ) {
|
||
SetEvent( lpOplock->hOplockInitComplete );
|
||
}
|
||
#endif
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
return NULL;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
lpPFInfo->dwLastError = GetLastError();
|
||
} else {
|
||
SetLastError(lpPFInfo->dwLastError);
|
||
}
|
||
|
||
IF_DEBUG(ERROR) {
|
||
DBGPRINTF((DBG_CONTEXT,"Create file[%s] failed with %d\n",
|
||
pURIInfo->pszName, GetLastError()));
|
||
}
|
||
|
||
//
|
||
// Couldn't open the file! If the reason we failed was because
|
||
// the file or path didn't exist, cache this information.
|
||
//
|
||
|
||
*dwError = GetLastError();
|
||
|
||
//
|
||
// if this is win95 (does not support dir opens),
|
||
// do the right thing.
|
||
//
|
||
|
||
if ( TsNoDirOpenSupport ) {
|
||
|
||
DBG_ASSERT(TsIsWindows95());
|
||
goto no_dir_open_support;
|
||
}
|
||
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
if (*dwError != ERROR_FILE_NOT_FOUND &&
|
||
*dwError != ERROR_PATH_NOT_FOUND &&
|
||
*dwError != ERROR_INVALID_NAME )
|
||
{
|
||
|
||
// Not a 'not found error'. We don't cache those.
|
||
|
||
lpPFInfo->hOpenFile = INVALID_HANDLE_VALUE;
|
||
if ( !fPhysFileCacheHit ) {
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
#if 0
|
||
if ( !DisableSPUD && fOplockSucceeded ) {
|
||
SetEvent( lpOplock->hOplockInitComplete );
|
||
}
|
||
#endif //!oplock
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
return( NULL );
|
||
}
|
||
|
||
// Go ahead and set the file info structure to valid, or try to. We
|
||
// use compare and exchange to handle the race condition where the
|
||
// file is open by someone else, and has been deleted while the handle
|
||
// is still open. but we couldn't use the cached values because of
|
||
// lack of a security descriptor. In this case we could get file not
|
||
// found when the file info in the cached URI block is valid for
|
||
// someone else. We don't want to blindly stomp the information in
|
||
// that case. Note that since the cached pOpenFileInfo field is
|
||
// initialized to NULL, all we need to do is set bFileInfoValid to
|
||
// TRUE.
|
||
|
||
if (!pfnInterlockedCompareExchange( (PVOID *)&pURIInfo->bFileInfoValid,
|
||
(PVOID)TRUE,
|
||
FALSE) )
|
||
{
|
||
// The compare&exchange worked, so we now officially have
|
||
// a negatively cached file. Go ahead and save the error
|
||
// value in the URI info.
|
||
|
||
pURIInfo->dwFileOpenError = *dwError;
|
||
|
||
}
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
#if 0
|
||
if ( !DisableSPUD && fOplockSucceeded ) {
|
||
SetEvent( lpOplock->hOplockInitComplete );
|
||
}
|
||
#endif //!oplock
|
||
}
|
||
TsRemovePhysFile( lpPFInfo );
|
||
|
||
ASSERT( !fImpersonated );
|
||
lpOpenFile = NULL;
|
||
return lpOpenFile;
|
||
|
||
}
|
||
|
||
// We're all done with file operations now, so revert back to who we were.
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
// OK, at this point we have an LP_OPEN_FILE info, or the file doesn't
|
||
// exist. If the file info in the URI block isn't valid, save this now.
|
||
|
||
if ( !pURIInfo->bFileInfoValid )
|
||
{
|
||
PVOID Temp;
|
||
|
||
// Now that we've opened the file, set the information to valid.
|
||
|
||
Temp = pfnInterlockedCompareExchange( (PVOID *)&pURIInfo->pOpenFileInfo,
|
||
lpOpenFile,
|
||
NULL
|
||
);
|
||
if ( Temp == NULL )
|
||
{
|
||
// The exchange worked. A few notes are in order: if we're
|
||
// caching a negative hit, we wouldn't have come through
|
||
// this path, we'd have gone through the code above where
|
||
// we do a compate&exchange on bFileInfoValid. There is a
|
||
// race between that code and this - if one thread opens the
|
||
// file, the file is deleted, and another thread fails to
|
||
// open the file we have a race. In that race this code path
|
||
// always wins. Either we get here first and set bFileInfoValid
|
||
// to TRUE so the negative hit cache c&e fails, or the negative
|
||
// c&e succeeds in setting it to TRUE, and then we set the
|
||
// file info pointer to a valid file and set bFileInfo to TRUE
|
||
// also. In either case we end up with bFileInfo at TRUE and
|
||
// a valid pOpenFileInfo pointer. It is possible in this case
|
||
// that the cached file open error will be incorrect, but that's
|
||
// OK because this is valid only when the return from this
|
||
// function is NULL. In any case, this is a transitory state. A
|
||
// change notify should fire shortly after this race and clean
|
||
// all of this mess up.
|
||
|
||
pURIInfo->bFileInfoValid = TRUE;
|
||
|
||
pURIInfo->dwFileOpenError = ERROR_SUCCESS; // For debugging purposes.
|
||
|
||
if ( lpOpenFile != NULL )
|
||
{
|
||
TsIncreaseFileHandleCount( FALSE );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
|
||
// The exchange didn't work, which means someone else snuck
|
||
// in and set it to valid while we were doing this. In this
|
||
// case mark our file info as not cached.
|
||
|
||
ASSERT(pURIInfo->bFileInfoValid);
|
||
|
||
if (lpOpenFile != NULL)
|
||
{
|
||
lpOpenFile->SetCachedFlag(FALSE);
|
||
*dwError = ERROR_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// The cached file info is already valid. This could be because of a
|
||
// race and someone else got here first, or it could be because we
|
||
// had a valid cached file before but the security tokens don't match.
|
||
// Either way, mark the open file info as not cached so the handle gets
|
||
// closed when we're done.
|
||
|
||
|
||
if (lpOpenFile != NULL)
|
||
{
|
||
lpOpenFile->SetCachedFlag(FALSE);
|
||
*dwError = ERROR_SUCCESS;
|
||
}
|
||
|
||
}
|
||
|
||
#if 0
|
||
SetEvent( pURIInfo->hFileEvent );
|
||
|
||
// And now we're done.
|
||
if ( (lpOpenFile != NULL) && !DisableSPUD ) {
|
||
InsertHeadPhysFile( lpPFInfo, (PVOID)pURIInfo );
|
||
}
|
||
|
||
if ( !DisableSPUD && !fPhysFileCacheHit && fOplockSucceeded ) {
|
||
lpOplock->lpPFInfo = lpPFInfo;
|
||
SetEvent( lpOplock->hOplockInitComplete );
|
||
}
|
||
#endif //!oplock
|
||
|
||
if ( !fPhysFileCacheHit ) {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - Not in Cache, Open Complete Causing Event!\n", pURIInfo->pszName ));
|
||
}
|
||
if ( lpOpenFile != NULL ) {
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
}
|
||
} else {
|
||
IF_DEBUG(OPLOCKS) {
|
||
DBGPRINTF( (DBG_CONTEXT,"TsCreateFileFromURI(%s) - In Cache, Open Complete!\n", pURIInfo->pszName ));
|
||
}
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
return lpOpenFile;
|
||
|
||
not_enough_memory:
|
||
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
ASSERT( lpPFInfo != NULL );
|
||
if ( !fPhysFileCacheHit )
|
||
{
|
||
MARK_PHYS_INIT_COMPLETE( lpPFInfo );
|
||
TsRemovePhysFile(lpPFInfo);
|
||
}
|
||
else
|
||
{
|
||
TsCheckInOrFree( (PVOID)lpPFInfo );
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
|
||
*dwError = ERROR_NOT_ENOUGH_MEMORY;
|
||
return( NULL );
|
||
|
||
no_dir_open_support:
|
||
|
||
//
|
||
// This is to support win95 where opening directories are not
|
||
// allowed.
|
||
//
|
||
|
||
DWORD dwAttributes;
|
||
BOOL fDirectory = FALSE;
|
||
|
||
if ( fImpersonated ) {
|
||
::RevertToSelf();
|
||
fImpersonated = FALSE;
|
||
}
|
||
|
||
//
|
||
// if this is not a directory, fail it.
|
||
//
|
||
|
||
dwAttributes = GetFileAttributes(pURIInfo->pszName);
|
||
if( dwAttributes != (DWORD)-1) {
|
||
if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
fDirectory = TRUE;
|
||
}
|
||
}
|
||
|
||
if (!fDirectory) {
|
||
IF_DEBUG(ERROR) {
|
||
DBGPRINTF((DBG_CONTEXT,"Not a directory[%x]. Fail\n",
|
||
dwAttributes ));
|
||
}
|
||
|
||
ASSERT( !fImpersonated );
|
||
return( NULL );
|
||
}
|
||
|
||
lpOpenFile = (LPTS_OPEN_FILE_INFO)LocalAlloc(LPTR,
|
||
sizeof(TS_OPEN_FILE_INFO));
|
||
|
||
if (lpOpenFile == NULL) {
|
||
|
||
//
|
||
// Couldn't get the memory we needed, so fail.
|
||
//
|
||
|
||
DBGPRINTF((DBG_CONTEXT,"Cannot allocate memory for file info[%d]\n",
|
||
GetLastError()));
|
||
goto not_enough_memory;
|
||
}
|
||
|
||
//
|
||
// set properties
|
||
//
|
||
|
||
lpPFInfo->hOpenFile = BOGUS_WIN95_DIR_HANDLE;
|
||
|
||
lpOpenFile->SetFileInfo(lpPFInfo,
|
||
NULL,
|
||
FALSE,
|
||
0,
|
||
dwAttributes
|
||
);
|
||
|
||
ASSERT( !fImpersonated );
|
||
return lpOpenFile;
|
||
|
||
} // TsCreateFileFromURI
|
||
|
||
|
||
|
||
dllexp
|
||
BOOL
|
||
TsCloseHandle(
|
||
IN const TSVC_CACHE &TSvcCache,
|
||
IN LPTS_OPEN_FILE_INFO lpOpenFile
|
||
)
|
||
{
|
||
PVOID pvBlob;
|
||
BOOL bSuccess;
|
||
|
||
ASSERT( lpOpenFile != NULL );
|
||
TSUNAMI_TRACE( TRACE_OPENFILE_CLOSE, lpOpenFile );
|
||
|
||
pvBlob = ( PVOID )lpOpenFile;
|
||
|
||
bSuccess = TsCheckInOrFree( pvBlob );
|
||
|
||
return( bSuccess );
|
||
} // TsCloseHandle
|
||
|
||
dllexp
|
||
BOOL
|
||
TsCloseURIFile(
|
||
IN LPTS_OPEN_FILE_INFO lpOpenFile
|
||
)
|
||
{
|
||
|
||
PVOID pvBlob;
|
||
|
||
AcIncrement( CacCloseURI);
|
||
if ( lpOpenFile != NULL ) {
|
||
|
||
if ( !lpOpenFile->QueryCachedFlag() )
|
||
{
|
||
// This file isn't actually part of a URI cache block, so
|
||
// close it.
|
||
|
||
|
||
AcDecrement( AacOpenURIFiles);
|
||
|
||
if ( lpOpenFile->QueryFileHandle() != BOGUS_WIN95_DIR_HANDLE ) {
|
||
pvBlob = ( PVOID )lpOpenFile->QueryPhysFileInfo();
|
||
TsCheckInOrFree( pvBlob );
|
||
}
|
||
|
||
LocalFree( lpOpenFile);
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
} // TsCloseURIFile
|
||
|
||
dllexp
|
||
BOOL TsCreateETagFromHandle(
|
||
IN HANDLE hFile,
|
||
IN PCHAR ETag,
|
||
IN BOOL *bWeakETag
|
||
)
|
||
/*+++
|
||
|
||
TsCreateETagFromHandle
|
||
|
||
This routine takes a file handle as input, and creates an ETag in
|
||
the supplied buffer for that file handle.
|
||
|
||
Arguments:
|
||
|
||
hFile - File handle for which to create an ETag.
|
||
ETag - Where to store the ETag. This must be long
|
||
enough to hold the maximum length ETag.
|
||
bWeakETag - Set to TRUE if the newly created ETag is weak.
|
||
|
||
Returns:
|
||
|
||
TRUE if we create an ETag, FALSE otherwise.
|
||
---*/
|
||
{
|
||
BY_HANDLE_FILE_INFORMATION FileInfo;
|
||
BOOL bReturn;
|
||
PUCHAR Temp;
|
||
FILETIME ftNow;
|
||
SYSTEMTIME stNow;
|
||
MB mb( (IMDCOM*) IIS_SERVICE::QueryMDObject() );
|
||
DWORD dwChangeNumber;
|
||
|
||
|
||
bReturn = GetFileInformationByHandle(
|
||
hFile,
|
||
&FileInfo
|
||
);
|
||
|
||
if (!bReturn)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
dwChangeNumber = 0;
|
||
mb.GetSystemChangeNumber(&dwChangeNumber);
|
||
|
||
FORMAT_ETAG(ETag, FileInfo.ftLastWriteTime, dwChangeNumber );
|
||
|
||
::GetSystemTime(&stNow);
|
||
|
||
if (::SystemTimeToFileTime((CONST SYSTEMTIME *)&stNow, &ftNow))
|
||
{
|
||
__int64 iNow, iFileTime;
|
||
|
||
iNow = (__int64)*(__int64 UNALIGNED *)&ftNow;
|
||
|
||
iFileTime = (__int64)*(__int64 UNALIGNED *)&FileInfo.ftLastWriteTime;
|
||
|
||
if ((iNow - iFileTime) > STRONG_ETAG_DELTA )
|
||
{
|
||
*bWeakETag = FALSE;
|
||
}
|
||
else
|
||
{
|
||
*bWeakETag = TRUE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
dllexp
|
||
BOOL TsLastWriteTimeFromHandle(
|
||
IN HANDLE hFile,
|
||
IN FILETIME *tm
|
||
)
|
||
/*+++
|
||
|
||
TsLastWriteTimeFromHandle
|
||
|
||
This routine takes a file handle as input, and returns the last write time
|
||
for that handle.
|
||
|
||
Arguments:
|
||
|
||
hFile - File handle for which to get the last write time.
|
||
tm - Where to return the last write time.
|
||
|
||
Returns:
|
||
|
||
TRUE if we succeed, FALSE otherwise.
|
||
---*/
|
||
{
|
||
BY_HANDLE_FILE_INFORMATION FileInfo;
|
||
BOOL bReturn;
|
||
|
||
bReturn = GetFileInformationByHandle(
|
||
hFile,
|
||
&FileInfo
|
||
);
|
||
|
||
if (!bReturn)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
*tm = FileInfo.ftLastWriteTime;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
|
||
const char * g_IISAuxCounterNames[] =
|
||
{
|
||
"Aac Open URI Files",
|
||
"Cac Calls to TsOpenURI()",
|
||
"Cac Calls to TsCloseURI()",
|
||
"Max Counters"
|
||
};
|
||
|
||
|
||
|
||
extern "C"
|
||
VOID
|
||
TsDumpCacheCounters( OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
|
||
{
|
||
DWORD cb = 0;
|
||
|
||
DBG_ASSERT( NULL != lpcbBuffer);
|
||
|
||
if ( *lpcbBuffer > 30) {
|
||
cb = wsprintf( pchBuffer, " IIS Cache Aux Counters. <p> <UL>");
|
||
} else {
|
||
cb = 30;
|
||
}
|
||
|
||
for ( DWORD i = 0; i < AacIISCacheMaxCounters; i++) {
|
||
|
||
if ( *lpcbBuffer > cb + 30) {
|
||
cb += wsprintf( pchBuffer + cb, " <LI> %s = %d",
|
||
g_IISAuxCounterNames[i],
|
||
AcCounter(i));
|
||
} else {
|
||
cb += 30;
|
||
}
|
||
} // for
|
||
|
||
if ( *lpcbBuffer > cb + 5) {
|
||
cb += wsprintf( pchBuffer + cb, " </UL> ");
|
||
} else {
|
||
cb += 5;
|
||
}
|
||
|
||
*lpcbBuffer = cb;
|
||
return ;
|
||
} // TsDumpCacheCounters()
|
||
|
||
|
||
|
||
|
||
|
||
|