|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 2002.
//
// File: FatNot.cxx
//
// Contents: Downlevel notification.
//
// Classes: CGenericNotify
//
// History: 2-23-96 KyleP Lifed from DLNotify.?xx
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <fatnot.hxx>
#include <pathpars.hxx>
#include <imprsnat.hxx>
#include <catalog.hxx>
#include <cicat.hxx>
#include <ciregkey.hxx>
#include <cievtmsg.h>
#include <eventlog.hxx>
#include <lm.h>
//+---------------------------------------------------------------------------
//
// Class: CRemoteNotifications
//
// Purpose: A class to impersonate and enable notifications for remote
// shares.
//
// History: 7-15-96 srikants Created
//
// Notes: When there are multiple alternatives possible for a remote
// share, we have to use the one that allows access to remote
// share (if there is one). There may be some which don't allow
// the required access and we should skip those.
//
//----------------------------------------------------------------------------
class CRemoteNotifications : public PImpersonatedWorkItem {
public:
CRemoteNotifications( WCHAR const * pwszPath, CGenericNotify & notify, OBJECT_ATTRIBUTES & objAttr ) : PImpersonatedWorkItem( pwszPath ), _notify(notify), _objAttr(objAttr), _status(STATUS_SUCCESS) {
}
NTSTATUS OpenAndStart( CImpersonateRemoteAccess & remoteAccess );
virtual BOOL DoIt();
private:
CGenericNotify & _notify; OBJECT_ATTRIBUTES & _objAttr;
NTSTATUS _status; };
//+---------------------------------------------------------------------------
//
// Member: CRemoteNotifications::DoIt
//
// Synopsis: The virtual method that does the work under an impersonated
// context.
//
// Returns: TRUE if successful;
// FALSE o/w
//
// History: 7-15-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CRemoteNotifications::DoIt() { _status = _notify.OpenDirectory( _objAttr );
if ( IsRetryableError( _status ) ) { //
// We should attempt the open under a different impersonation
// if possible.
//
return FALSE; }
if ( NT_ERROR(_status) ) THROW( CException( _status ) );
//
// Now, enable the notifications.
//
_notify.StartNotification( &_status ); // already impersonated
if ( NT_ERROR(_status) ) { _notify.CloseDirectory(); if ( IsRetryableError(_status) ) return FALSE;
THROW( CException( _status ) ); }
//
// Successfully enabled notifications.
//
return TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CRemoteNotifications::OpenAndStart
//
// Synopsis: Opens and start notifications for the remote root by trying
// various impersonation contexts if necessary.
//
// Arguments: [remoteAccess] - The object to use for remote access.
//
// Returns: NTSTATUS of the whole operation.
//
// History: 7-15-96 srikants Created
//
//----------------------------------------------------------------------------
NTSTATUS CRemoteNotifications::OpenAndStart( CImpersonateRemoteAccess & remoteAccess ) { TRY { ImpersonateAndDoWork( remoteAccess ); } CATCH( CException,e ) { vqDebugOut(( DEB_ERROR, "OpenAndStart failed with error (0x%X)\n", e.GetErrorCode() )); _status = e.GetErrorCode(); } END_CATCH
return _status; }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::CGenericNotify
//
// Synopsis: Constructor of the single scope notification object CGenericNotify.
//
// Arguments: [wcsScope] -- Scope to watch
// [cwcScope] -- Size in chars of [wcsScope]
// [fDeep] -- Set to TRUE if deep notifications are enabled.
//
// History: 1-17-96 srikants Created
//
//----------------------------------------------------------------------------
CGenericNotify::CGenericNotify( PCatalog *pCat, WCHAR const * wcsScope, unsigned cwcScope, BOOL fDeep, BOOL fLogEvents ) : _refCount(1), _pCat( pCat ), _fNotifyActive(FALSE), _fRemoteDrive(FALSE), _cwcScope(cwcScope), _fDeep(fDeep), _fLogEvents(fLogEvents), _fAbort(FALSE), _hNotify(0), _pbBuffer(0) { if ( cwcScope >= MAX_PATH ) { THROW( CException( STATUS_INVALID_PARAMETER ) ); }
RtlCopyMemory( _wcsScope, wcsScope, cwcScope * sizeof(WCHAR) ); _wcsScope[cwcScope] = 0;
CDoubleLink::Close();
//
// Bigger buffer for local scopes.
//
_fRemoteDrive = !IsFixedDrive( _wcsScope, _cwcScope );
if ( _fRemoteDrive ) _cbBuffer = CB_REMOTENOTIFYBUFFER; else _cbBuffer = CB_NOTIFYBUFFER;
//
// Client should call EnableNotification() in ctor. Delay allocating
// the buffer in case this is a USN volume and no buffer is needed.
//
}
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::~CGenericNotify
//
// Synopsis: ~dtor . Disables further notifications and frees up
// memory.
//
// History: 1-17-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
CGenericNotify::~CGenericNotify() { Win4Assert( 0 == _refCount ); Win4Assert( IsSingle() ); Win4Assert( 0 == _hNotify ); delete [] _pbBuffer; }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::OpenDirectory
//
// Synopsis: Opens a remote directory and uses the given object attributes.
//
// Arguments: [ObjectAttr] -
//
// Returns: STATUS of the operation.
//
// History: 7-15-96 srikants Created
//
//----------------------------------------------------------------------------
NTSTATUS CGenericNotify::OpenDirectory( OBJECT_ATTRIBUTES & ObjectAttr ) { BOOL fSuccess = TRUE;
ULONG cSkip = 0;
NTSTATUS Status = STATUS_SUCCESS; IO_STATUS_BLOCK IoStatus;
Status = NtOpenFile( &_hNotify, // Handle
FILE_LIST_DIRECTORY | SYNCHRONIZE, // Access
&ObjectAttr, // Object Attributes
&IoStatus, // I/O Status block
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE ); // Flags
if ( NT_ERROR(Status) ) _hNotify = 0;
return Status; }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::CloseDirectory
//
// Synopsis: Closes the directory handle if open and sets it to 0.
//
// History: 7-15-96 srikants Created
//
//----------------------------------------------------------------------------
void CGenericNotify::CloseDirectory() { if ( 0 != _hNotify ) { NtClose( _hNotify ); _hNotify = 0; } }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::EnableNotification
//
// Synopsis: Enables notifications for the current scope.
//
// History: 1-17-96 srikants Created
//
//----------------------------------------------------------------------------
void CGenericNotify::EnableNotification() { vqDebugOut(( DEB_ITRACE, "Enable notification for scope %ws this=0x%x\n", _wcsScope, this ));
if ( 0 == _pbBuffer ) _pbBuffer = new BYTE [_cbBuffer];
//
// Open file
//
NTSTATUS Status; UNICODE_STRING uScope;
if ( !RtlDosPathNameToNtPathName_U( _wcsScope, &uScope, 0, 0 ) ) { vqDebugOut(( DEB_ERROR, "Error converting %ws to Nt path\n", _wcsScope )); THROW( CException(STATUS_INSUFFICIENT_RESOURCES) ); }
XRtlHeapMem xScopeBuf( uScope.Buffer );
OBJECT_ATTRIBUTES ObjectAttr;
InitializeObjectAttributes( &ObjectAttr, // Structure
&uScope, // Name
OBJ_CASE_INSENSITIVE, // Attributes
0, // Root
0 ); // Security
CImpersonateRemoteAccess remoteAccess( GetCatalog()->GetImpersonationTokenCache() );
CRemoteNotifications remoteNotify( _wcsScope, *this, ObjectAttr ); if ( _fRemoteDrive ) { //
// Check if remote notifications are disabled.
//
if ( (GetCatalog()->GetRegParams())->GetCiCatalogFlags() & CI_FLAGS_NO_REMOTE_NOTIFY ) { vqDebugOut(( DEB_WARN, "Not enabling remote notifications because it is disabled in registry\n" )); return; }
//
// Check if the remote drive is a DFS share. If so, don't try
// to enabled notifications on the share. We have to just periodically
// scan for changed documents.
//
if ( IsDfsShare( _wcsScope, _cwcScope ) ) { vqDebugOut(( DEB_WARN, "Not enabling notifications for DFS Share (%ws) \n", _wcsScope ));
LogDfsShare(); return; }
Status = remoteNotify.OpenAndStart( remoteAccess ); } else { //
// Check if local notifications are disabled.
//
if ( (GetCatalog()->GetRegParams())->GetCiCatalogFlags() & CI_FLAGS_NO_LOCAL_NOTIFY ) { vqDebugOut(( DEB_WARN, "Not enabling local notifications because it is disabled in registry\n" )); return; }
Status = OpenDirectory( ObjectAttr );
if ( NT_ERROR( Status ) ) { vqDebugOut(( DEB_ERROR, "Notification disabled. NtOpenFile( %ws ) returned 0x%lx\n", _wcsScope, Status )); _hNotify = 0; if ( _fLogEvents ) LogNoNotifications( Status );
return; }
StartNotification( &Status ); }
if ( !_fNotifyActive && _fLogEvents ) { LogNoNotifications( Status ); } }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::DisableNotification
//
// Synopsis: Disables further notifications for this scope.
//
// History: 1-17-96 srikants Created
//
// Notes: This must be called from within the lock of the notify manager.
//
//----------------------------------------------------------------------------
void CGenericNotify::DisableNotification() { vqDebugOut(( DEB_ITRACE, "Disable notification for scope %ws this=0x%x\n", _wcsScope, this ));
if ( 0 != _hNotify ) { NtClose( _hNotify ); _hNotify = 0; }
Release(); }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::StartNotification
//
// Synopsis: Starts notifications by setting the APC for receiving
// notifications. If successful, the object will be refcounted
// and the status set to indicate that the operation is
// successful.
//
// History: 1-17-96 srikants Created
//
// Notes: This must be called from within the lock of the notify manager.
// If successful, the notify manager will also be
// refcounted. This is because the APC depends upon the mutex
// in the notify manager to be around when it is invoked.
//
//----------------------------------------------------------------------------
void CGenericNotify::StartNotification( NTSTATUS * pStatus ) { //
// Set up query directory file.
//
if ( 0 == _hNotify ) { *pStatus = STATUS_TOO_LATE; return; }
NTSTATUS Status = STATUS_SUCCESS; DWORD dwFlags = GetNotifyFlags();
Status = NtNotifyChangeDirectoryFile( _hNotify, 0, APC, this, &_iosNotify, _pbBuffer, _cbBuffer, dwFlags, (BYTE)_fDeep );
if ( NT_ERROR(Status) ) { vqDebugOut(( DEB_ERROR, "NtNotifyChangeDirectoryFile( %ws ) returned 0x%lx\n", _wcsScope, Status ));
} else { _fNotifyActive = TRUE; AddRef(); }
if ( pStatus ) *pStatus = Status; }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::AddRef
//
// History: 1-17-96 srikants Created
//
//----------------------------------------------------------------------------
void CGenericNotify::AddRef() { InterlockedIncrement(&_refCount); }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::Release
//
// Synopsis: If the refcount goes to 0, the object will be deleted.
//
// History: 1-17-96 srikants Created
//
//----------------------------------------------------------------------------
void CGenericNotify::Release() { Win4Assert( _refCount > 0 ); if ( InterlockedDecrement(&_refCount) <= 0 ) delete this; }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::AdjustForOverflow
//
// Synopsis: Increases the size of the notification buffer if it is not
// a remote drive and if the current size is < the maximum.
//
// History: 2-27-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CGenericNotify::AdjustForOverflow() { if ( !_fRemoteDrive && CB_MAXSIZE > _cbBuffer ) { unsigned cbNew = min( _cbBuffer + CB_DELTAINCR, CB_MAXSIZE );
vqDebugOut(( DEB_ITRACE, "Resizing notification buffer from 0x%X to 0x%X bytes\n", _cbBuffer, cbNew ));
BYTE * pbNew = new BYTE [cbNew];
delete [] _pbBuffer; _pbBuffer = pbNew; _cbBuffer = cbNew; } }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::APC
//
// Synopsis: Asynchronous Procedure Call invoked by the system when there
// is a change notification (or related error).
//
// Arguments: [ApcContext] - Pointer to "this"
// [IoStatusBlock] -
// [Reserved] -
//
// History: 1-17-96 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
void CGenericNotify::APC( void * ApcContext, IO_STATUS_BLOCK * IoStatusBlock, ULONG Reserved ) { Win4Assert( 0 != ApcContext );
CGenericNotify * pthis = (CGenericNotify *)ApcContext;
NTSTATUS status = STATUS_SUCCESS;
TRY { pthis->_fOverflow = FALSE;
Win4Assert( &pthis->_iosNotify == IoStatusBlock );
// DbgPrint( "notifications...\n" );
if ( NT_ERROR( IoStatusBlock->Status ) ) { if ( !pthis->_fAbort ) { // DbgPrint( "Async notification for scope %ws received error 0x%x\n",
// pthis->_wcsScope,
// IoStatusBlock->Status );
vqDebugOut(( DEB_ERROR, "Async notification for scope %ws received error 0x%x\n", pthis->_wcsScope, IoStatusBlock->Status )); vqDebugOut(( DEB_ITRACE, "CiNotification APC: ERROR 0x%x\n", pthis )); status = IoStatusBlock->Status;
//
// The I/O failed and it may be due to STATUS_DELETE_PENDING.
// In any case, just close the handle so the directory is
// freed for other apps.
//
pthis->CloseDirectory(); } } else if ( IoStatusBlock->Status == STATUS_NOTIFY_CLEANUP ) { vqDebugOut(( DEB_ITRACE, "CiNotification APC: CLOSE 0x%x\n", pthis )); } else { if ( IoStatusBlock->Status == STATUS_NOTIFY_ENUM_DIR ) { // DbgPrint( "***** CiNotification LOST UPDATES for scope %ws *****\n",
// pthis->_wcsScope );
vqDebugOut(( DEB_WARN, "***** CiNotification LOST UPDATES for scope %ws *****\n", pthis->_wcsScope )); pthis->_fOverflow = TRUE;
//
// Let us adjust the size of the buffer if possible.
//
pthis->AdjustForOverflow();
//
// But call anyway. Client is responsible for checking ::BufferOverflow.
//
pthis->DoIt(); } else { // .Information is the # of bytes written to the buffer.
// This may be 0 even when .Status is STATUS_NOTIFY_ENUM_DIR,
// and with certain builds of rdr2, STATUS_SUCCESS.
if ( 0 == IoStatusBlock->Information && 0 == IoStatusBlock->Status ) { // BrianAn says NTFS won't do this, but rdr2 might
vqDebugOut(( DEB_WARN, "CGenericNotify: invalid notification apc\n" ));
// DbgPrint( "0 info and status block\n" );
}
#if 0
if ( 0 != IoStatusBlock->Information ) #endif
{ if ( !pthis->_fRemoteDrive ) { pthis->DoIt(); } else { //
// Get sufficient impersonation context to get attributes on
// the remote share. Then process the notifications.
//
CImpersonateRemoteAccess remote( pthis->GetCatalog()->GetImpersonationTokenCache() ); CImpersonatedGetAttr getAttr( pthis->_wcsScope ); getAttr.DoWork( remote );
pthis->DoIt(); } } } } } CATCH(CException, e) { //DbgPrint( "caught exception in notifications\n" );
vqDebugOut(( DEB_ERROR, "CiNotification APC: CATCH 0x%x, iostatus: 0x%x, info: 0x%x\n", e.GetErrorCode(), IoStatusBlock->Status, IoStatusBlock->Information ));
status = e.GetErrorCode(); } END_CATCH;
if ( STATUS_SUCCESS != status ) { //DbgPrint( "clearing notify enabled\n" );
pthis->ClearNotifyEnabled(); if ( pthis->_fLogEvents ) pthis->LogNotificationsFailed( status ); }
pthis->Release(); } //APC
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::IsFixedDrive, private
//
// Arguments: [wcsScope] -- Scope to check
// [len] -- Length in chars of [wcsScope]
//
// Returns: TRUE if scope is for a fixed drive.
//
// History: 1-17-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CGenericNotify::IsFixedDrive( WCHAR const * wcsScope, ULONG len ) { CPathParser pathParser( wcsScope, len ); if ( pathParser.IsUNCName() ) return FALSE;
WCHAR wDrive[MAX_PATH]; ULONG cc=sizeof(wDrive)/sizeof(WCHAR); pathParser.GetFileName( wDrive, cc );
UINT uType = GetDriveType( wDrive );
return DRIVE_FIXED == uType; }
//+---------------------------------------------------------------------------
//
// Function: CiNetShareGetInfo
//
// Synopsis: Calls NetShareGetInfo. Loads the library so we don't
// link to netapi32.dll for the odd case of indexing remote
// volumes. Also, don't cache the function pointer since
// it's called so rarely.
//
// Arguments: Same as NetShareGetInfo
//
// Returns: Win32 / NetStatus error code
//
// History: 2-18-98 dlee Created
//
//----------------------------------------------------------------------------
typedef NET_API_STATUS (NET_API_FUNCTION * NET_SHARE_GET_INFO_FUNC)( LPTSTR servername, LPTSTR netname, DWORD level, BYTE ** bufptr );
NET_API_STATUS NET_API_FUNCTION CiNetShareGetInfo( LPTSTR servername, LPTSTR netname, DWORD level, BYTE ** bufptr ) { HINSTANCE hLib = LoadLibrary( L"netapi32.dll" ); if ( 0 == hLib ) return GetLastError();
NET_SHARE_GET_INFO_FUNC pfn = (NET_SHARE_GET_INFO_FUNC) GetProcAddress( hLib, "NetShareGetInfo" );
if ( 0 == pfn ) { FreeLibrary( hLib ); return GetLastError(); }
NET_API_STATUS status = (*pfn)( servername, netname, level, bufptr ); FreeLibrary( hLib ); return status; } //CiNetShareGetInfo
//+---------------------------------------------------------------------------
//
// Function: IsDfsShare
//
// Synopsis: Determines if the given UNC share is a DFS share.
//
// Arguments: [wcsScope] - scope
// [len] - Length
//
// Returns: TRUE if it is a DFS share. FALSE o/w
//
// History: 6-23-96 srikants Created
//
//----------------------------------------------------------------------------
BOOL CGenericNotify::IsDfsShare( WCHAR const * wcsScope, ULONG len ) { CPathParser pathParser( wcsScope, len ); if ( !pathParser.IsUNCName() ) return FALSE;
WCHAR wDrive[MAX_PATH]; ULONG cc=sizeof(wDrive)/sizeof(WCHAR); pathParser.GetFileName( wDrive, cc );
WCHAR * pwszServerName = wDrive; WCHAR * pwszShareName = 0;
//
// Locate the third backslash and replace it with a NULL char.
//
for ( unsigned i = 2; i < cc; i++ ) { if ( wDrive[i] == L'\\' ) { wDrive[i] = 0; pwszShareName = wDrive+i+1; break; } }
Win4Assert( 0 != pwszShareName ); //
// Remove any trailing backslash in the share name.
//
i = wcslen( pwszShareName ); if ( L'\\' == pwszShareName[i-1] ) { pwszShareName[i-1] = 0; }
BOOL fIsDfs = FALSE; PSHARE_INFO_1005 shi1005;
NET_API_STATUS err = CiNetShareGetInfo( pwszServerName, pwszShareName, 1005, (PBYTE *) &shi1005 );
if (err == ERROR_SUCCESS) { fIsDfs = ((shi1005->shi1005_flags & SHI1005_FLAGS_DFS) != 0);
//
// Netapi32.dll midl_user_allocate calls LocalAlloc, so use
// LocalFree to free up the stuff the stub allocated.
//
LocalFree( shi1005 ); }
return fIsDfs; }
void CGenericNotify::LogNotificationsFailed( DWORD dwError ) const { Win4Assert( 0 != dwError );
TRY { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_ERROR_TYPE, CI_SERVICE_CATEGORY, MSG_CI_NOTIFICATIONS_TURNED_OFF, 2 );
item.AddArg( _wcsScope );
//
// When a logon fails, all the other eventlog messages have the
// WIN32 error code in them. Just to keep it consistent, use the
// WIN32 error code here also.
//
if ( STATUS_LOGON_FAILURE == dwError ) dwError = ERROR_LOGON_FAILURE;
item.AddError( dwError ); eventLog.ReportEvent( item ); } CATCH( CException, e ) { vqDebugOut(( DEB_ERROR, "Exception 0x%X while writing to event log\n", e.GetErrorCode() )); } END_CATCH }
void CGenericNotify::LogNoNotifications( DWORD dwError ) const { Win4Assert( 0 != dwError );
TRY { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_NOTIFICATIONS_NOT_STARTED, 2 );
item.AddArg( _wcsScope );
//
// When a logon fails, all the other eventlog messages have the
// WIN32 error code in them. Just to keep it consistent, use the
// WIN32 error code here also.
//
if ( STATUS_LOGON_FAILURE == dwError ) dwError = ERROR_LOGON_FAILURE;
item.AddError( dwError ); eventLog.ReportEvent( item ); } CATCH( CException, e ) { vqDebugOut(( DEB_ERROR, "Exception 0x%X while writing to event log\n", e.GetErrorCode() )); } END_CATCH }
//+---------------------------------------------------------------------------
//
// Member: CGenericNotify::LogDfsShare
//
// Synopsis: Logs the the current share is a DFS aware share.
//
// History: 6-27-96 srikants Created
//
//----------------------------------------------------------------------------
void CGenericNotify::LogDfsShare() const { TRY { CEventLog eventLog( NULL, wcsCiEventSource ); CEventItem item( EVENTLOG_INFORMATION_TYPE, CI_SERVICE_CATEGORY, MSG_CI_DFS_SHARE_DETECTED, 1 );
item.AddArg( _wcsScope ); eventLog.ReportEvent( item ); } CATCH( CException, e ) { vqDebugOut(( DEB_ERROR, "Exception 0x%X while writing to event log\n", e.GetErrorCode() )); } END_CATCH }
|