/*++ Copyright (c) 1989 Microsoft Corporation Module Name: HookCmmn.c Abstract: The purpose of this module is to have a common source for many routines used by the hook on win95 and on NT. In this way, there'll be only one place to change even if there's some code uglyness (ifdefs, etc.) Initially, i (jll) have not actually removed any routines from the win95 hook....i have just made copies of them here. accordingly, everything is initially under ifdef-NT. the rule that is being used is that anything that requires visibility of the Rx Fcb structures will not be included here; rather, it'll be in the minirdr part. the following are steps that need to be accomplished: 1. ensure that the win95 shadow vxd can be built from these sources. 1a. juggle the win95 vxd compile to use precomp and stdcall. 2. juggle the record manager structs so that we'll be RISCable 3. remove any routines from hook.h that are actually here. 4. remove a number of other routines that i splated around from hook.c under NT-ifdef and place them here. Author: Shishir Pardikar [Shishirp] 01-jan-1995 Revision History: Joe Linn [JoeLinn] 10-mar-97 Initial munging for NT --*/ #include "precomp.h" #pragma hdrstop #pragma code_seg("PAGE") #undef RxDbgTrace #define RxDbgTrace(a,b,__d__) {qweee __d__;} #ifdef DEBUG //cshadow dbgprint interface #define HookCmmnKdPrint(__bit,__x) {\ if (((HOOKCMMN_KDP_##__bit)==0) || FlagOn(HookCmmnKdPrintVector,(HOOKCMMN_KDP_##__bit))) {\ KdPrint (__x);\ }\ } #define HOOKCMMN_KDP_ALWAYS 0x00000000 #define HOOKCMMN_KDP_BADERRORS 0x00000001 #define HOOKCMMN_KDP_ISSPECIALAPP 0x00000002 #define HOOKCMMN_KDP_INITDATABASE 0x00000004 #define HOOKCMMN_KDP_TUNNELING 0x00000008 #define HOOKCMMN_KDP_GOOD_DEFAULT (HOOKCMMN_KDP_BADERRORS \ | 0) ULONG HookCmmnKdPrintVector = HOOKCMMN_KDP_GOOD_DEFAULT; ULONG HookCmmnKdPrintVectorDef = HOOKCMMN_KDP_GOOD_DEFAULT; #else #define HookCmmnKdPrint(__bit,__x) {NOTHING;} #endif //this is just for editing ease...... //#ifdef CSC_RECORDMANAGER_WINNT //#endif //ifdef CSC_RECORDMANAGER_WINNT #ifdef CSC_RECORDMANAGER_WINNT BOOL fInitDB = FALSE; // Database Initialized BOOL fReadInit = FALSE; // Init values have been read BOOL fSpeadOpt = TRUE; // spead option, reads the cached files even when // locks are set on the share GLOBALSTATUS sGS; // Global status used for communicating with ring3 // Semaphore used to synchronize in memory structures VMM_SEMAPHORE semHook; // This strucutre is used as temporary I/O buffer only within Shadow critical // section WIN32_FIND_DATA vsFind32; // Agent Info, obtained through IoctlRegisterAgent ULONG hthreadReint=0; // Thred ID ULONG hwndReint=0; // windows handle. ULONG heventReint; // upto 16 threads can temporarily enable and disable shadowing ULONG rghthreadTemp[16]; // Reintegration happens one share at a time. If it is going on, then BeginReint // in ioctl.c sets hShareReint to the share on which we are doing reint // if vfBlockingReint is TRUE, then all operations on that share will fail // when the share is reintegrating. // If vfBlockingReint is not true, then if the dwActivityCount is non-zero // the ioctls to change any state on any of the descendents of this share fail // causing the agent to aboort reintegration // The acitvycount is incremented based on a trigger from the redir/hook, when // any namespace mutating operation is performed. HSHARE hShareReint=0; // Share that is currently being reintegrated BOOL vfBlockingReint = TRUE; DWORD vdwActivityCount = 0; //from shadow.asm --------------------------------------------------- int fShadow = 0; #if defined(_X86_) int fLog = 1; #else int fLog = 0; #endif //tunnel cache SH_TUNNEL rgsTunnel[10] = {0}; #ifdef DEBUG ULONG cntReadHits=0; tchar pathbuff[MAX_PATH+1]; #endif BOOL FindTempAgentHandle( ULONG hthread) { int i; for (i=0; i < (sizeof(rghthreadTemp)/sizeof(ULONG)); ++i) { if (rghthreadTemp[i]==hthread) { return TRUE; } } return FALSE; } BOOL RegisterTempAgent( VOID ) { int i; ULONG hthread; hthread = GetCurThreadHandle(); for (i=0; i< (sizeof(rghthreadTemp)/sizeof(ULONG)); ++i) { if (!rghthreadTemp[i]) { rghthreadTemp[i] = hthread; return TRUE; } } return FALSE; } BOOL UnregisterTempAgent( VOID ) { int i; ULONG hthread; hthread = GetCurThreadHandle(); for (i=0; i < (sizeof(rghthreadTemp)/sizeof(ULONG)); ++i) { if (rghthreadTemp[i]==hthread) { rghthreadTemp[i] = 0; return TRUE; } } return FALSE; } BOOL IsSpecialApp ( VOID ) { ULONG hthread; hthread = GetCurThreadHandle(); if ((hthread==hthreadReint) || FindTempAgentHandle(hthread)) { HookCmmnKdPrint(ISSPECIALAPP,("This is our Special App \r\n")); return TRUE; } return FALSE; } #define SetHintsFromList(a,b) ((-1)) int InitShadowDB(VOID) { //for NT, we don't have all this hint stuff....just return success return(1); #if 0 VMMHKEY hKeyShadow; int iSize = sizeof(int), iRet = -1; DWORD dwType; extern char vszExcludeList[], vszIncludeList[]; BOOL fOpen = FALSE; char rgchList[128]; if (_RegOpenKey(HKEY_LOCAL_MACHINE, REG_KEY_SHADOW, &hKeyShadow) == ERROR_SUCCESS) { fOpen = TRUE; iSize = sizeof(rgchList); if (_RegQueryValueEx(hKeyShadow, vszExcludeList, NULL, &dwType, rgchList, &iSize)==ERROR_SUCCESS) { if (SetHintsFromList(rgchList, TRUE) < 0) goto bailout; } iSize = sizeof(rgchList); if (_RegQueryValueEx(hKeyShadow, vszIncludeList, NULL, &dwType, rgchList, &iSize)==ERROR_SUCCESS) { if (SetHintsFromList(rgchList, FALSE) < 0) goto bailout; } iRet = 1; } bailout: if (fOpen) { _RegCloseKey(hKeyShadow); } return (iRet); #endif //0 } int ReinitializeDatabase( LPSTR lpszLocation, LPSTR lpszUserName, DWORD nFileSizeHigh, DWORD nFileSizeLow, DWORD dwClusterSize ) { BOOL fDBReinited = FALSE; if(OpenShadowDB(lpszLocation, lpszUserName, nFileSizeHigh, nFileSizeLow, dwClusterSize, TRUE, &fDBReinited) >= 0) { if (InitShadowDB() >= 0) { Assert(fDBReinited == TRUE); fInitDB = 1; } else { CloseShadowDB(); } } if (fInitDB != 1) { fInitDB = -1; fShadow = 0; } return (fInitDB); } int InitDatabase( LPSTR lpszLocation, LPSTR lpszUserName, DWORD nFileSizeHigh, DWORD nFileSizeLow, DWORD dwClusterSize, BOOL fReformat, BOOL *lpfNew ) { int iRet = 1; BOOL fDBReinited = FALSE; LPSTR PrefixedLocation = NULL; HookCmmnKdPrint(INITDATABASE,("Opening database at %s for %s with size %d \r\n", lpszLocation, lpszUserName, nFileSizeLow)); // // When CSC is started by the kernel as part of a remote boot, the input path // will already be in NT format. // if (( _strnicmp(lpszLocation,"\\Device\\Harddisk",strlen("\\Device\\Harddisk")) != 0 ) && ( _strnicmp(lpszLocation,"\\ArcName",strlen("\\ArcName")) != 0 )) { //this would be NT only.......... PrefixedLocation = AllocMem(strlen(lpszLocation)+sizeof(NT_DB_PREFIX)); if (!PrefixedLocation) { return -1; } strcpy(PrefixedLocation, NT_DB_PREFIX); strcat(PrefixedLocation, lpszLocation); HookCmmnKdPrint(INITDATABASE,("Opening database at %s changing to %s \r\n", lpszLocation, PrefixedLocation)); //fortunately, this is call-by-value....so i can just overwrite the input parameter lpszLocation = PrefixedLocation; } // Do onetime init if (!fReadInit) { ReadInitValues(); memset(rghthreadTemp, 0, sizeof(rghthreadTemp)); fReadInit = TRUE; } // check if the database is not already initialized if (!fInitDB) { // open/create it iRet = OpenShadowDB( lpszLocation, lpszUserName, nFileSizeHigh, nFileSizeLow, dwClusterSize, fReformat, &fDBReinited ); // open/create DB succeeded? if (iRet < 0) { //no HookCmmnKdPrint(ALWAYS,("Error Opening/Createing shadow database \r\n")); fInitDB = -1; fShadow = 0; } else { *lpfNew = fDBReinited; // did it exist in the first place? if (fDBReinited) { // no it didn't, let use create things like the filters and such iRet = InitShadowDB(); } if (iRet >= 0) { fInitDB = 1; } else { CloseShadowDB(); } } } if (PrefixedLocation) { FreeMem(PrefixedLocation); } return (iRet); } int CloseDatabase( VOID ) { if (fInitDB) { CloseShadowDB(); fInitDB = FALSE; return (1); } return (0); } BOOL IsBusy ( HSHADOW hShadow ) { DeclareFindFromShadowOnNtVars() if (PFindFdbFromHShadow(hShadow)) return TRUE; return FALSE; } int PRIVATE DeleteShadowHelper ( BOOL fMarkDeleted, HSHADOW hDir, HSHADOW hNew ) { int iRet = -1; if (!fMarkDeleted) { if (DeleteShadow(hDir, hNew) < SRET_OK) goto bailout; } else { if (TruncateDataHSHADOW(hDir, hNew) < SRET_OK) goto bailout; // ACHTUNG!! other people depend on status being SHADOW_DELETED if (SetShadowInfo(hDir, hNew, NULL, SHADOW_DELETED, SHADOW_FLAGS_OR) < SRET_OK) goto bailout; } iRet = 0; bailout: return (iRet); } /*+-------------------------------------------------------------------------*/ /* @doc INTERNAL HOOK @func BOOL | InsertTunnelInfo | 2.0 The function inserts file names in a tunnelling table. These entries are considered useful only for STALE_TUNNEL_INFO seconds. Tunnelling is used to retain LPOTHERINFO for files that get renamed/deleted and some other file is renamed to it. This typically done by editors, spreadsheets and such while saving things. @parm HSHADOW | hDir | Directory to which this guy belongs @parm LPPE | lppe | PathELement for the file to be tunnelled @parm LPOTHERINFO | lpOI | Info to be kept with the tunnelled entry @comm >>comment_text,

etc... @rdesc This function returns TRUE if successful. Otherwise the return value is FALSE. @xref >>, ... */ BOOL InsertTunnelInfo( HSHADOW hDir, USHORT *lpcFileName, USHORT *lpcAlternateFileName, // assume 14 USHORTs LPOTHERINFO lpOI ) { int i, iHole = -1; ULONG uTime = IFSMgr_Get_NetTime(), cbFileName; cbFileName = (wstrlen(lpcFileName)+1)*sizeof(USHORT); ASSERT(hDir!=0); FreeStaleEntries(); for (i=0;i< (sizeof(rgsTunnel)/sizeof(SH_TUNNEL)); ++i) { if (!rgsTunnel[i].hDir && (iHole < 0)) { iHole = i; } if ((rgsTunnel[i].hDir==hDir) && (!wstrnicmp(lpcFileName, rgsTunnel[i].lpcFileName, MAX_PATH*sizeof(USHORT))|| !wstrnicmp( lpcFileName, rgsTunnel[i].cAlternateFileName, sizeof(rgsTunnel[i].cAlternateFileName))) ) { FreeEntry(&rgsTunnel[i]); iHole = i; break; } } if (iHole >=0) { if (!(rgsTunnel[iHole].lpcFileName = (USHORT *)AllocMem(cbFileName))) { return (FALSE); } rgsTunnel[iHole].uTime = uTime; rgsTunnel[iHole].hDir = hDir; rgsTunnel[iHole].ubHintFlags = (UCHAR)(lpOI->ulHintFlags); rgsTunnel[iHole].ubHintPri = (UCHAR)(lpOI->ulHintPri); rgsTunnel[iHole].ubIHPri = (UCHAR)(lpOI->ulIHPri); rgsTunnel[iHole].ubRefPri = (UCHAR)(lpOI->ulRefPri); // copy without the NULL, we know that allocmem will have a NULL at the end memcpy(rgsTunnel[iHole].lpcFileName, lpcFileName, cbFileName-sizeof(USHORT)); // assumes 14 USHORTS memcpy( rgsTunnel[iHole].cAlternateFileName, lpcAlternateFileName, sizeof(rgsTunnel[iHole].cAlternateFileName)); HookCmmnKdPrint(TUNNELING,("InsertTunnelInfo: Inserting %ws/%ws, Hintpri=%d, HintFlags=%d RefPri=%d \r\n", lpcFileName,lpcAlternateFileName, lpOI->ulHintPri, lpOI->ulHintFlags, lpOI->ulRefPri )); return (TRUE); } return (FALSE); } /*+-------------------------------------------------------------------------*/ /* @doc INTERNAL HOOK @func BOOL | RetrieveTunnelInfo | 2.0 The function returns to the caller OTHERINFO structure containing priorities and such about files which have been recently renamed or deleted. Entries which are older than STALE_TUNNEL_INFO seconds are thrown out. @parm HSHADOW | hDir | Directory to which this guy belongs @parm ushort *| lpcFileName| name of the file whose info is needed @parm LPOTHERINFO | lpOI | Info to be retrieved from the tunnelled entry @comm >>comment_text,

etc... @rdesc This function returns TRUE if successful. Otherwise the return value is FALSE. @xref >>, ... */ #ifndef MRXSMB_BUILD_FOR_CSC_DCON BOOL RetrieveTunnelInfo( #else RETRIEVE_TUNNEL_INFO_RETURNS RetrieveTunnelInfo( #endif HSHADOW hDir, USHORT *lpcFileName, WIN32_FIND_DATA *lpFind32, // if NULL, get only otherinfo LPOTHERINFO lpOI ) { int i; #ifdef MRXSMB_BUILD_FOR_CSC_DCON RETRIEVE_TUNNEL_INFO_RETURNS RetVal; #endif ASSERT(hDir!=0); FreeStaleEntries(); for (i=0;i< (sizeof(rgsTunnel)/sizeof(SH_TUNNEL)); ++i) { #ifndef MRXSMB_BUILD_FOR_CSC_DCON if (rgsTunnel[i].hDir && (!wstrnicmp(lpcFileName, rgsTunnel[i].lpcFileName, MAX_PATH*sizeof(USHORT))|| !wstrnicmp( lpcFileName, rgsTunnel[i].cAlternateFileName, sizeof(rgsTunnel[i].cAlternateFileName))) ) { #else if (rgsTunnel[i].hDir==hDir) { if (!wstrnicmp(lpcFileName, rgsTunnel[i].cAlternateFileName, sizeof(rgsTunnel[i].cAlternateFileName)) ) { RetVal = TUNNEL_RET_SHORTNAME_TUNNEL; } else if ( !wstrnicmp(lpcFileName, rgsTunnel[i].lpcFileName, MAX_PATH*sizeof(USHORT)) ){ RetVal = TUNNEL_RET_LONGNAME_TUNNEL; } else { continue; } #endif InitOtherInfo(lpOI); lpOI->ulHintFlags = (ULONG)(rgsTunnel[i].ubHintFlags); lpOI->ulHintPri = (ULONG)(rgsTunnel[i].ubHintPri); lpOI->ulIHPri = (ULONG)(rgsTunnel[i].ubIHPri); // don't copy the reference priority, it is assigned by the record manager // lpOI->ulRefPri = (ULONG)(rgsTunnel[i].ubRefPri); HookCmmnKdPrint(TUNNELING,("RetrieveTunnelInfo: %ws found, Hintpri=%d, HintFlags=%d RefPri=%d \r\n", lpcFileName, lpOI->ulHintPri, lpOI->ulHintFlags, lpOI->ulRefPri )); if (lpFind32) { memcpy( lpFind32->cFileName, rgsTunnel[i].lpcFileName, (wstrlen(rgsTunnel[i].lpcFileName)+1)*sizeof(USHORT) ); memcpy( lpFind32->cAlternateFileName, rgsTunnel[i].cAlternateFileName, sizeof(rgsTunnel[i].cAlternateFileName) ); HookCmmnKdPrint(TUNNELING,("Recovered LFN %ws/%ws \r\n", lpFind32->cFileName, lpFind32->cAlternateFileName)); } FreeEntry(&rgsTunnel[i]); #ifndef MRXSMB_BUILD_FOR_CSC_DCON return (TRUE); } } return (FALSE); #else return (RetVal); } } return (TUNNEL_RET_NOTFOUND); #endif } VOID FreeStaleEntries() { int i; ULONG uTime = IFSMgr_Get_NetTime(); for (i=0;i< (sizeof(rgsTunnel)/sizeof(SH_TUNNEL)); ++i) { if (rgsTunnel[i].lpcFileName && ((uTime- rgsTunnel[i].uTime)>STALE_TUNNEL_INFO)) { FreeEntry(&rgsTunnel[i]); } } } void FreeEntry(LPSH_TUNNEL lpshTunnel) { FreeMem(lpshTunnel->lpcFileName); memset(lpshTunnel, 0, sizeof(SH_TUNNEL)); } //got this from hook.c...not the same because w95 looks at the //resource flags directly BOOL IsShadowVisible( #ifdef MRXSMB_BUILD_FOR_CSC_DCON BOOLEAN Disconnected, #else PRESOURCE pResource, #endif //ifdef MRXSMB_BUILD_FOR_CSC_DCON DWORD dwAttr, ULONG uShadowStatus ) { BOOL fVisible = 1; // To a filesystem API the shadow is never visible when it is marked // as being deleted if (mShadowDeleted(uShadowStatus)) return (0); #ifdef MRXSMB_BUILD_FOR_CSC_DCON if (Disconnected) #else if (mIsDisconnected(pResource)) #endif //ifdef MRXSMB_BUILD_FOR_CSC_DCON { if (IsFile(dwAttr)) { #ifdef OFFLINE #ifdef CSC_RECORDMANAGER_WINNT ASSERT(FALSE); #endif //ifdef CSC_RECORDMANAGER_WINNT if (mIsOfflineConnection(pResource)) { // offline connection if (!mShadowOutofSync(uShadowStatus)) { fVisible = 0; } } else #endif //OFFLINE {// pure disconnected state // ignore sparse files if (mShadowSparse(uShadowStatus)) { fVisible = 0; } } } } else { // connected state, bypass all out of ssync files, doesn't include // stale, because we can handle staleness during open if (IsFile(dwAttr)) { if (mShadowOutofSync(uShadowStatus)) { fVisible = 0; } } else if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { // and locallycreated or orphaned directories if (mQueryBits(uShadowStatus, SHADOW_LOCALLY_CREATED|SHADOW_ORPHAN)) { fVisible = 0; } } } return (fVisible); } //got this from hook.c...not the same because w95 looks at the //resource flags directly //CODE.IMPROVEMENT we should define a common header for RESOURCE and FDB so that most stuff // would just work. int MarkShareDirty( PUSHORT ShareStatus, ULONG hShare ) { if (!mQueryBits(*ShareStatus, SHARE_REINT)) { SetShareStatus(hShare, SHARE_REINT, SHADOW_FLAGS_OR); mSetBits(*ShareStatus, SHARE_REINT); } return SRET_OK; } #endif //ifdef CSC_RECORDMANAGER_WINNT