/************************************************************************* * * dirwalk.c * * Walk a tree setting ACL's on an NT system. * * Copyright Microsoft, 1998 * * * *************************************************************************/ #include #include #include #include #include #include #include #include #include "security.h" #if DBG ULONG DbgPrint( PCH Format, ... ); #define DBGPRINT(x) DbgPrint x #if DBGTRACE #define TRACE0(x) DbgPrint x #define TRACE1(x) DbgPrint x #else #define TRACE0(x) #define TRACE1(x) #endif #else #define DBGPRINT(x) #define TRACE0(x) #define TRACE1(x) #endif // Global variables PWCHAR gpAvoidDir = NULL; CRITICAL_SECTION SyslibCritSect; typedef BOOLEAN (CALLBACK* NODEPROC)( PWCHAR, PWIN32_FIND_DATAW, DWORD, DWORD ); BOOLEAN EnumerateDirectory( PWCHAR pRoot, DWORD Level, NODEPROC pProc ); BOOLEAN NodeEnumProc( PWCHAR pParent, PWIN32_FIND_DATA p, DWORD Level, DWORD Index ); PWCHAR AddWildCard( PWCHAR pRoot ); PWCHAR AddBackSlash( PWCHAR pRoot ); FILE_RESULT xxxProcessFile( PWCHAR pParent, PWIN32_FIND_DATAW p, DWORD Level, DWORD Index ); /***************************************************************************** * * CtxGetSyslibCritSect * * Returns the library global critical section pointer. * Critical section will be initialised if necessary * * ENTRY: * VOID - Caller must ensure that only one threads a times calls this * function. The function will not itself guarantie mutual exclusion. * EXIT: * Pointer to critical section. NULL if failure. * ****************************************************************************/ PCRITICAL_SECTION CtxGetSyslibCritSect(void) { static BOOLEAN fInitialized = FALSE; NTSTATUS Status; if( !fInitialized ){ Status = RtlInitializeCriticalSection(&SyslibCritSect); if (NT_SUCCESS(Status)) { fInitialized = TRUE; }else{ return NULL; } } return(&SyslibCritSect); } /***************************************************************************** * * SetFileTree * * Walk the given tree calling the processing function for each node. * * ENTRY: * pRoot (input) * Full WIN32 path to directory to walk * * pVoidDir (input) * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ BOOLEAN SetFileTree( PWCHAR pRoot, PWCHAR pAvoidDir ) { BOOLEAN rc; PWCHAR pRootNew; static BOOLEAN fInitialized = FALSE; PRTL_CRITICAL_SECTION pLock = CtxGetSyslibCritSect(); // if critical section could not be created, do nothing. if (pLock == NULL) { return FALSE; } DBGPRINT(( "entering SetFileTree(pRoot=%ws,pAvoidDir=%ws)\n", pRoot, pAvoidDir )); EnterCriticalSection(pLock); // If this is the console make sure the user hasn't changed if ((NtCurrentPeb()->SessionId == 0) && fInitialized) { CheckUserSid(); } else if ( !fInitialized ) { fInitialized = TRUE; if ( !InitSecurity() ) { DBGPRINT(( "problem initializing security; we're outta here.\n" )); LeaveCriticalSection(pLock); return( 1 ); // (non-zero for error...)// Should be return FALSE!? } } LeaveCriticalSection(pLock); gpAvoidDir = pAvoidDir; // be sure to apply security to root directory pRootNew = AddBackSlash(pRoot); if(pRootNew) { DBGPRINT(( "processing file %ws\n", pRootNew )); xxxProcessFile(pRootNew, NULL, 0, 0); LocalFree(pRootNew); } rc = EnumerateDirectory( pRoot, 0, NodeEnumProc ); DBGPRINT(( "leaving SetFileTree()\n" )); return( rc ); } /***************************************************************************** * * EnumerateDirectory * * Walk the given directory calling the processing function for each file. * * ENTRY: * pRoot (input) * Full WIN32 path to directory to walk * * Level (input) * Level we are in a given tree. Useful for formating output * * pProc (input) * Procedure to call for each non-directory file * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ BOOLEAN EnumerateDirectory( PWCHAR pRoot, DWORD Level, NODEPROC pProc ) { BOOL rc; DWORD Result; HANDLE hf; DWORD Index; WIN32_FIND_DATA Data; PWCHAR pRootNew; DBGPRINT(( "entering EnumerateDirectory(pRoot=%ws,Level=%ld,pProc=NodeEnumProc)\n",pRoot,Level )); if( pRoot == NULL ) { DBGPRINT(( "leaving EnumerateDirectory(), return=FALSE\n" )); return( FALSE ); } // Make sure it is not one we want to avoid if( gpAvoidDir ) { DWORD Len = wcslen( gpAvoidDir ); if( _wcsnicmp( pRoot, gpAvoidDir, Len ) == 0 ) { DBGPRINT(( "leaving EnumerateDirectory(), return=FALSE\n" )); return( FALSE ); } } pRootNew = AddWildCard( pRoot ); if( pRootNew == NULL ) { DBGPRINT(( "leaving EnumerateDirectory(), return=FALSE\n" )); return( FALSE ); } Index = 0; DBGPRINT(("FindFirstFileW: %ws\n",pRootNew)); hf = FindFirstFileW( pRootNew, &Data ); if( hf == INVALID_HANDLE_VALUE ) { DBGPRINT(("EnumerateDirectory: Error %d opening root %ws\n",GetLastError(),pRootNew)); LocalFree( pRootNew ); DBGPRINT(( "leaving EnumerateDirectory(), return=FALSE\n" )); return(FALSE); } while( 1 ) { // pass the parent without the wildcard added pProc( pRoot, &Data, Level, Index ); rc = FindNextFile( hf, &Data ); if( !rc ) { Result = GetLastError(); if( Result == ERROR_NO_MORE_FILES ) { FindClose( hf ); LocalFree( pRootNew ); DBGPRINT(( "leaving EnumerateDirectory(), return=TRUE\n" )); return( TRUE ); } else { DBGPRINT(("EnumerateDirectory: Error %d, Index 0x%x\n",Result,Index)); FindClose( hf ); LocalFree( pRootNew ); DBGPRINT(( "leaving EnumerateDirectory(), return=FALSE\n" )); return( FALSE ); } } Index++; } // UNREACHABLE. } /***************************************************************************** * * NodeEnumProc * * Process the enumerated file * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ BOOLEAN NodeEnumProc( PWCHAR pParent, PWIN32_FIND_DATAW p, DWORD Level, DWORD Index ) { BOOLEAN rc; PWCHAR pParentNew; DWORD ParentCount, ChildCount; // // We must append the directory to our parent path to get the // new full path. // ParentCount = wcslen( pParent ); ChildCount = wcslen( p->cFileName ); pParentNew = LocalAlloc( LMEM_FIXED, (ParentCount + ChildCount + 2)*sizeof(WCHAR) ); if( pParentNew == NULL ) return( FALSE ); wcscpy( pParentNew, pParent ); wcscat( pParentNew, L"\\" ); wcscat( pParentNew, p->cFileName ); if( p->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // Skip "." and ".." if( wcscmp( L".", p->cFileName ) == 0 ) { LocalFree( pParentNew ); return( TRUE ); } if( wcscmp( L"..", p->cFileName ) == 0 ) { LocalFree( pParentNew ); return( TRUE ); } TRACE0(("%ws:\n",pParentNew)); xxxProcessFile( pParentNew, p, Level, Index ); // For directories, we recurse rc = EnumerateDirectory( pParentNew, Level+1, NodeEnumProc ); LocalFree( pParentNew ); return( rc ); } TRACE0(("%ws\n",pParentNew)); xxxProcessFile( pParentNew, p, Level, Index ); LocalFree( pParentNew ); return( TRUE ); } /***************************************************************************** * * AddWildCard * * Add the wild card search specifier * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ PWCHAR AddWildCard( PWCHAR pRoot ) { DWORD Count; PWCHAR pNew; PWCHAR WildCard = L"\\*"; Count = wcslen( pRoot ); pNew = LocalAlloc( LMEM_FIXED, (Count + wcslen(WildCard) + 1)*sizeof(WCHAR) ); if( pNew == NULL ) { return( NULL ); } wcscpy( pNew, pRoot ); wcscat( pNew, WildCard ); return( pNew ); } /***************************************************************************** * * AddBackSlash * * Add the backslash character to path * * ENTRY: * Param1 (input/output) * Comments * * EXIT: * STATUS_SUCCESS - no error * ****************************************************************************/ PWCHAR AddBackSlash( PWCHAR pRoot ) { DWORD Count; PWCHAR pNew; PWCHAR BackSlash = L"\\"; Count = wcslen( pRoot ); pNew = LocalAlloc( LMEM_FIXED, (Count + wcslen(BackSlash) + 1)*sizeof(WCHAR) ); if( pNew == NULL ) { return( NULL ); } wcscpy( pNew, pRoot ); // only add backslash if string doesn't already have it if(*(pRoot+Count-1) != L'\\') wcscat( pNew, BackSlash ); return( pNew ); }