www.pudn.com > FileGuard.zip > FGHOOK.cpp


// FGHOOK.cpp - main module for VxD FGHOOK 
 
#define DEVICE_MAIN 
#include "FGHOOK.h" 
Declare_Virtual_Device(FGHOOK) 
#undef DEVICE_MAIN 
 
ppIFSFileHookFunc PrevHookProc=NULL;			//Previos hook function. 
 
KProtectedFileList protFileList; 
KProtectedFileList recycledPathList;        //Path of Recycle. 
 
KHandleTable	findHTable, openHTable; 
KHotKey		*pHotKey; 
 
HANDLE hIniFile,hVM; 
HANDLE hAppWnd=NULL;										//Handle of win32 app. 
char iniFilePath[MAX_PATH+1]=""; 
ParsedPath *pPPath; 
 
BOOL bBusy, bAppReqBusy=FALSE;							//bAppReqBusy--win32 app is about be write file. 
WORD lastError=FG_ERR_SUCCESS; 
UINT maxBakNum=DEF_MAX_BAK_NUM;		//max backup file number. 
 
//NOTICE: I don't know whether more than one object files will be linked properly, 
//(And I haven't try it.) So I just include other cpp files in the main cpp file.  
//This did not cause any problem. 
#include "..\HookShr\KProtectedFileList.cpp" 
#include "..\HookShr\HookCom.cpp" 
#include "KHandleTable.cpp" 
#include "KHotKey.cpp" 
 
/////////////////////////////////////////////////////////////////// 
//Main interface: 
FGHookVM::FGHookVM(VMHANDLE hVM) : VVirtualMachine(hVM) {} 
 
FGHookThread::FGHookThread(THREADHANDLE hThread) : VThread(hThread) {} 
 
 
BOOL FGHookDevice::OnSysDynamicDeviceInit() 
{ 
#ifdef DEBUG 
		dout << "Hello from FGHook!" << endl; 
#endif 
	 
	bBusy=FALSE; 
 
	if(!(pPPath=(ParsedPath *)malloc(MAX_PATH*sizeof(WCHAR)+sizeof(ParsedPath)))) 
	{ 
		ErrorHandler(FG_ERR_NOT_ENOUGH_MEMORY); 
		ShellUnloadDevice(); 
		return FALSE; 
	} 
 
	//Add recycled path. 
	int i; 
	char recycledPath[MAX_PATH]="A:\\RECYCLED\\*"; 
	for(i=1; i<=32; i++)		//Windows supports up to 32 drives. 
	{ 
		recycledPath[0]=i+'A'-1; 
		recycledPathList.Add(recycledPath); 
	} 
 
	//intall key board hook: 
	pHotKey=new KHotKey; 
	pHotKey->hook(); 
 
	return TRUE; 
} 
 
BOOL FGHookDevice::OnSysDynamicDeviceExit() 
{ 
	free(pPPath); 
	if(PrevHookProc) 
		IFSMgr_RemoveFileSystemApiHook(HookProc); 
 
	delete pHotKey; 
 
#ifdef DEBUG 
	dout<<"Hook exit."<dioc_IOCtlCode) 
	{ 
		case DIOC_OPEN: 
			//SHELL_Message(pDIOCParams->dioc_hvm,MB_OK,"Device initialize.","FGHook message:",0,0,0); 
			break; 
 
#ifdef DEBUG 
		case FG_DIOC_DEBUG:	 
			break; 
#endif 
 
		case FG_DIOC_INSTALL_HOOK: 
#ifdef DEBUG 
			dout<<"Now install hook..."<dioc_InBuf & 0x1;						//Get del & write proteciton options. 
			protFileList.bWriteBak=(((UINT)pDIOCParams->dioc_InBuf & 0x2)== 0x2); 
			break; 
 
		case FG_DIOC_UNINSTALL_HOOK: 
			if(PrevHookProc && IFSMgr_RemoveFileSystemApiHook(HookProc)==0) 
					PrevHookProc=NULL; 
			break; 
 
		case FG_DIOC_GET_HWND: 
			hAppWnd=*((HANDLE *)pDIOCParams->dioc_InBuf);		//Retrieve hwnd of main window of win32 app. 
			break; 
 
		case FG_DIOC_FLUSH_PROTECTED_FILE_INFO: //==FG_DIOC_READ_PROTECTED_FILE_INFO 
			//Flush file information by reread from ini file. 
			protFileList.Empty(); 
			if(pDIOCParams->dioc_InBuf) 
				strcpy(iniFilePath,(char *)pDIOCParams->dioc_InBuf); 
			else if(!*iniFilePath)  strcpy(iniFilePath, "ProtFile.ini");			//default ini path. 
#ifdef DEBUG 
			dout<<"INI Path:"<dioc_InBuf); 
			break; 
 
		case FG_DIOC_REMOVE_PROTECTED_FILE: 
			protFileList.Remove((char *)pDIOCParams->dioc_InBuf); 
			break; 
 
		case FG_DIOC_EXTRA_INFO: 
			protFileList.bDelBak=(UINT)pDIOCParams->dioc_InBuf & 0x1;						//Get del & write proteciton options. 
			protFileList.bWriteBak=(((UINT)pDIOCParams->dioc_InBuf & 0x2)== 0x2); 
			maxBakNum=(UINT)pDIOCParams->dioc_OutBuf; 
			break; 
 
		case FG_SET_DEVICE_BUSY: 
			bAppReqBusy=(BOOL)pDIOCParams->dioc_InBuf; 
			break; 
	} 
 
	_asm clc 
	return 0; 
} 
 
VOID FGHookDevice::PM_API_Entry(VMHANDLE hVM, CLIENT_STRUCT* pRegs) 
{ 
} 
 
//Hook Procedure:	all protection is done though this function. 
// Argument         : pIFSFunc pfn 
//		address of the function which will perform this file operation. 
// Argument         : int fn 
//		number of the function which will perform this file operation. 
// Argument         : int CodePage 
//		current code page. 
// Argument         : pioreq pir 
//		It is the parameter to be passed to FSD function pfn.  
//		In many cases, pir->ir_ppath contain ParsedPath. 
int _cdecl HookProc(pIFSFunc pfn, int fn, int Drive, int ResType, int CodePage, pioreq pir) 
{ 
	struct _QWORD res; 
	BOOL bLogHandle=FALSE;		//Determine whether to add a handle to handle table(use in IFSFN_OPEN); 
 
	//bAppReqBusy: Win32 application of fileguard request busy.		bBusy: used to prevent reentry. 
	if(bAppReqBusy || bBusy)  
		goto EXIT; 
 
	bBusy=TRUE; 
 
	if(protFileList.bIsHideOn && (fn==IFSFN_FILEATTRIB || fn==IFSFN_DIR || fn==IFSFN_SEARCH || fn==IFSFN_RENAME)) 
	{ 
		//Hide a file: 
		if(protFileList.IsUnderProtection(Drive, pir->ir_ppath, PT_HIDE))  
			{ 
				bBusy=FALSE; 
				return (IFSFN_DIR? ERROR_PATH_NOT_FOUND : ERROR_FILE_NOT_FOUND);	 
			} 
	} 
	 
 
	switch(fn) 
	{ 
	case IFSFN_DELETE: 
		{ 
			//do delete protection: 
 
			if(!protFileList.bIsDelOn && !protFileList.bIsHideOn) 
				//If there is not protected file has protection type of PT_DELETE or PT_HIDE, skip. 
				break; 
		 
			if(DoDeleteProtection(pir, Drive, CodePage)!=ERROR_SUCCESS) 
			{ 
				bBusy=FALSE; 
				return pir->ir_error; 
			} 
			break; 
		}		//case IFSFN_DELETE 
 
	case IFSFN_RENAME: 
		{ 
			//prevent moving protected file to recycled. 
 
			if(!protFileList.bIsDelOn && !protFileList.bIsHideOn)	 
				//If there is not protected file has protection type of PT_DELETE or PT_HIDE, skip. 
				break; 
		 
			//Is the file being renamed to the recycle? 
			if(!recycledPathList.IsUnderProtection(Drive, pir->ir_ppath2)  
				|| DoDeleteProtection(pir, Drive, CodePage)==ERROR_SUCCESS) 
				break; 
 
			bBusy=FALSE; 
			return pir->ir_error; 
		}		//case IFSFN_RENAME 
 
	case IFSFN_OPEN: 
		{ 
			//If a file is opened with ACTION_REPLACEEXISTING, content of the file will be lost. 
			//so prevent this. 
			//And prevent opening hidden file. 
 
			//Determine whether or not to log open handle: 
			PROTECTED_FILE *pProtFile; 
			if(!(pProtFile=protFileList.IsUnderProtection(Drive, pir->ir_ppath))) 
				break; 
			else if(pProtFile->PF_type&(PT_READ|PT_DELETE)) 
				bLogHandle=TRUE; 
 
			//determine whether the file is under protection. 
			if(!(protFileList.bIsHideOn || protFileList.bIsDelOn) || !(pProtFile=protFileList.IsUnderProtection(Drive, pir->ir_ppath, PT_DELETE|PT_HIDE))) 
				break; 
 
			if(!(ACTION_REPLACEEXISTING&pir->ir_options) && !(pProtFile->PF_type&PT_HIDE)) 
				//the file is not to be hidden and it is not opened with ACTION_REPLACEEXISTING, skip. 
				break; 
 
			if(pProtFile->PF_type&PT_HIDE)		 
			{ 
				//protect hidden file. 
				bBusy=FALSE; 
				pir->ir_error=ERROR_FILE_NOT_FOUND; 
				return pir->ir_error; 
			} 
			else if(!protFileList.bDelBak)		 
			{ 
				//deny file operation. 
				ShellPostError(FG_ERR_WARNING, FG_WARN_PT_DELETE);           //Send a warning. 
				bBusy=FALSE; 
				pir->ir_error=ERROR_ACCESS_DENIED; 
				return pir->ir_error; 
			} 
 
			//protect the file by rename it to *.BAK . 
			char pBCSPath[MAX_PATH+1]; 
			UniToBCSPathEx((PUCHAR)pBCSPath,pir->ir_ppath->pp_elements,Drive,MAX_PATH,CodePage,&res);	 
			if(FileRename(pBCSPath)==ERROR_SUCCESS) 
			{ 
				//The file exists, so recreate it.  
				WORD error; 
				BYTE action; 
				HANDLE h=R0_OpenCreateFile(TRUE,pBCSPath,0,ATTR_NORMAL,ACTION_IFEXISTS_FAIL|ACTION_IFNOTEXISTS_CREATE,0,&error,&action); 
				R0_CloseFile(h,&error); 
			} 
			break; 
		}	//case IFSFN_OPEN	 
 
		case IFSFN_WRITE: 
		{ 
			//do write protection. 
 
			if(!protFileList.bIsWriteOn) 
				//If there is not protected file has protection type of PT_WRITE, skip. 
				break; 
 
			//sometimes, a process writes zero bytes only to verify. 
			if(pir->ir_length==0) break; 
        	 
			//get full path. 
			int drive=Drive; 
			if(!GetFullNameByHandle(pir,drive,ResType,CodePage,pPPath, FALSE))  
				break; 
 
			//determine whether the file is under protection. 
			if(!protFileList.IsUnderProtection(drive, pPPath, PT_WRITE)) 
				break; 
			 
			if(!protFileList.bWriteBak)		 
			{ 
				//Deny write. 
				ShellPostError(FG_ERR_WARNING, FG_WARN_PT_WRITE);           //Send a warning. 
				bBusy=FALSE; 
				pir->ir_error=ERROR_ACCESS_DENIED; 
				return pir->ir_error; 
			} 
 
			//protect the file by copying it to the .BAK file. 
			char pBCSPath[MAX_PATH+1]; 
			UniToBCSPathEx((PUCHAR)pBCSPath,pPPath->pp_elements,Drive,MAX_PATH,CodePage,&res);	 
			if(FileBackupByHandle(pir,drive,ResType,CodePage,pBCSPath)!=ERROR_SUCCESS && FileBackup(pBCSPath)!=ERROR_SUCCESS) 
			{ 
				//if fail to back up the file then deny access. 
				bBusy=FALSE; 
				pir->ir_error=ERROR_ACCESS_DENIED; 
				return pir->ir_error; 
			} 
			break; 
		}		//case IFSFN_WRITE 
 
	case IFSFN_READ: 
		{ 
			//prevent process to read protected file. 
 
			//Read protection is not on or it is a pagging file. 
			if(!protFileList.bIsReadOn) 
				break; 
 
			//Get full path. 
			int drive=Drive; 
			if(!GetFullNameByHandle(pir,drive,ResType,CodePage,pPPath, FALSE))  
				break; 
 
			//determine whether the file is under protection. 
			if(!protFileList.IsUnderProtection(drive, pPPath, PT_READ)) 
				break; 
 
			//deny read 
			ShellPostError(FG_ERR_WARNING, FG_WARN_PT_READ);           //Send a warning. 
			bBusy=FALSE; 
			pir->ir_error=ERROR_ACCESS_DENIED; 
			return pir->ir_error; 
 
			break; 
		}		//case IFSFN_READ 
 
	} 
 
	bBusy=FALSE;	 
	EXIT:	 
	int nRetVal=(*PrevHookProc)(pfn,fn,Drive,ResType,CodePage,pir); 
 
	//Win32 application of fileguard request busy. 
	if(bAppReqBusy) 
		return nRetVal; 
 
	//log open handle. 
	if(bLogHandle) 
		openHTable.Add(pir->ir_fh, Drive, pir->ir_ppath); 
 
	//if find or file is closed, delete the handle in handle table. 
	if(fn==IFSFN_FINDCLOSE) 
		findHTable.Delete(pir->ir_fh); 
	else if(fn==IFSFN_CLOSE && pir->ir_flags==CLOSE_FINAL) 
		openHTable.Delete(pir->ir_fh); 
 
	if(!bBusy && protFileList.bIsHideOn && !nRetVal && (fn==IFSFN_FINDNEXT || fn==IFSFN_FINDOPEN)) 
	{ 
		//Hide file in file operation FINDOPEN & FINDNEXT.  
 
		bBusy=TRUE; 
 
		int drive=Drive; 
		_WIN32_FIND_DATA   *findData; 
 
		//Get search path: 
		switch(fn) 
		{ 
			case IFSFN_FINDOPEN: 
				memcpy(pPPath, pir->ir_ppath, IFSPathSize(pir->ir_ppath)+sizeof(WCHAR)); 
				findHTable.Add(pir->ir_fh, Drive, pPPath); 
				break; 
			case IFSFN_FINDNEXT: 
				if(!GetFullNameByHandle(pir, drive, ResType, CodePage, pPPath, TRUE)) 
				{ 
					bBusy=FALSE; 
					return nRetVal; 
				} 
				break; 
		} 
		//Now we will change the search path to file path which is found by FINDOPEN or FINDNEXT. 
 
		//get file directory from search path by removing the last component: 
		pPPath->pp_totalLength-=IFSLastElement(pPPath)->pe_length; 
 
		//get full path by adding file name to file directory. 
		findData=(_WIN32_FIND_DATA *)pir->ir_data; 
		IFSLastElement(pPPath)->pe_length=wstrlen(findData->cFileName)+sizeof(WCHAR); 
		memcpy(IFSLastElement(pPPath)->pe_unichars, findData->cFileName, IFSLastElement(pPPath)->pe_length); 
		StringUpper((WCHAR *)IFSLastElement(pPPath)->pe_unichars, IFSLastElement(pPPath)->pe_length-sizeof(WCHAR)); 
		pPPath->pp_totalLength+=IFSLastElement(pPPath)->pe_length; 
		*(WCHAR *)((char *)pPPath+pPPath->pp_totalLength)=NULL; 
 
		//determine whether the file is under protection. 
		if(protFileList.IsUnderProtection(drive, pPPath, PT_HIDE))  
		{ 
			bBusy=FALSE; 
 
			//now we are going to hide the protected file... 
			if(fn==IFSFN_FINDOPEN) 
			{ 
				//if it is FINDOPEN operation, we should return the next file. 
 
				//using IFSMgr_Ring0_FileIO to find the next file. 
				_WIN32_FIND_DATA_BCS bcsFindData; 
				BOOL retVal; 
				retVal=R0_FindNextFile(pir->ir_fh, &bcsFindData, (PWORD)&pir->ir_error); 
 
				if(retVal) 
				{ 
					//if the next file is found, convert _WIN32_FIND_DATA_BCS (the find data return by IFSMgr_Ring0_FileIO) to _WIN32_FIND_DATA ( the find data used by FS_FINDOPEN). 
					memcpy(pir->ir_data, &bcsFindData, sizeof(_WIN32_FIND_DATA_BCS)-sizeof(bcsFindData.cFileName)-sizeof(bcsFindData.cAlternateFileName)); 
					BCSToUni(((_WIN32_FIND_DATA *)pir->ir_data)->cFileName, bcsFindData.cFileName, strlen((char *)bcsFindData.cFileName), CodePage, &res); 
					((_WIN32_FIND_DATA *)pir->ir_data)->cFileName[res.ddLower]=0; 
					BCSToUni(((_WIN32_FIND_DATA *)pir->ir_data)->cAlternateFileName, bcsFindData.cAlternateFileName, strlen((char *)bcsFindData.cAlternateFileName), CodePage, &res); 
					((_WIN32_FIND_DATA *)pir->ir_data)->cAlternateFileName[res.ddLower]=0; 
				} 
				return retVal;	 
			} 
			else goto EXIT;		//if it is FINDNEXT operation, go on to find next file. 
		} 
	bBusy=FALSE; 
	} 
 
	return nRetVal; 
} 
 
//Protected file on file delete, rename operation. 
//Return value:   return ERROR_SUCCESS indicates that the protected file has been back up, and  
//						   the file operation can be continue. Otherwise the file operation should stop. 
WORD DoDeleteProtection(ioreq *pir, int Drive, int CodePage) 
{ 
	struct _QWORD res; 
	char pBCSPath[MAX_PATH+1]; 
	PROTECTED_FILE *pProtFile; 
	if(pir->ir_attr&FILE_FLAG_WILDCARDS || pir->ir_attr&FILE_FLAG_HAS_STAR) 
	{ 
		//Deal with file name with wildcards. 
 
		//Convert from ParsedPath to BCS path. 
		UniToBCSPathEx((PUCHAR)pBCSPath, pir->ir_ppath->pp_elements, Drive, MAX_PATH, CodePage, &res); 
 
		//find first file. 
		_WIN32_FIND_DATA_BCS bcsFindData; 
		WORD error; 
		HANDLE hFind; 
		hFind=R0_FindFirstFile((PCHAR)pBCSPath, pir->ir_attr, &bcsFindData, &error); 
 
		CHAR pFindPath[MAX_PATH]; 
		strcpy(pFindPath, pBCSPath); 
		_strupr(pFindPath); 
 
		while(error==ERROR_SUCCESS) 
		{ 
			//Get full path by adding file name to file directory. 
			GetDir(pFindPath); 
			_strupr((PCHAR)bcsFindData.cFileName); 
			strcat(pFindPath, (PCHAR)bcsFindData.cFileName); 
 
			if((pProtFile=protFileList.IsUnderProtection(pFindPath, PT_DELETE|PT_HIDE))!=NULL) 
			{ 
				if(pProtFile->PF_type&PT_HIDE)							 
				{ 
					//Protect hidden file. 
					pir->ir_error=ERROR_FILE_NOT_FOUND; 
					return pir->ir_error; 
				} 
				else if(protFileList.bDelBak) 
				{ 
					//Deny delete. 
					if(!FileRename(pFindPath)==ERROR_SUCCESS)			//Cannot rename the file, so deny access to the file. 
						return ERROR_ACCESS_DENIED; 
				} 
				else 
				{ 
					//Back up on delete. 
					pir->ir_error=ERROR_ACCESS_DENIED; 
					ShellPostError(FG_ERR_WARNING, FG_WARN_PT_DELETE);	//Send a warning. 
					return pir->ir_error; 
				} 
			} 
 
			//go on to find next file 
			R0_FindNextFile(hFind, &bcsFindData, &error); 
 
		}		//while(error==ERROR_SUCCESS) 
 
	}		//if(pir->ir_attr&FILE_FLAG_WILDCARDS || pir->ir_attr&FILE_FLAG_HAS_STAR) 
 
	else if((pProtFile=protFileList.IsUnderProtection(Drive, pir->ir_ppath, PT_DELETE|PT_HIDE))!=NULL) 
	{ 
		//Deal with file name without wildcards: 
 
		if(pProtFile->PF_type&PT_HIDE)						 
			pir->ir_error=ERROR_FILE_NOT_FOUND;		//Protect hidden file. 
		if(!protFileList.bDelBak)						 
		{ 
			//Deny delete 
			pir->ir_error=ERROR_ACCESS_DENIED; 
			ShellPostError(FG_ERR_WARNING, FG_WARN_PT_DELETE);	//Send a warning. 
		} 
		else  
		{ 
			//Back up on delete. 
			UniToBCSPathEx((PUCHAR)pBCSPath,pir->ir_ppath->pp_elements,Drive,MAX_PATH,CodePage,&res);	 
			pir->ir_error=FileRename(pBCSPath); 
		} 
		return pir->ir_error; 
	} 
 
	return ERROR_SUCCESS; 
} 
 
//Append a extension like ".BAK0" or ".BAK1" to the file name. 
//(the maximum number appended at the tail depends on MAX_BAK_NUM) 
//If every optional back up file name have been used, return FALSE,  
//else return TRUE. 
//No checking for the parameters. pBCSNewName must be long enough. 
BOOL  AppendBakExtension(const char *pBCSOldName, char *pBCSNewName) 
{ 
	strcpy(pBCSNewName,pBCSOldName); 
	strcat(pBCSNewName,".BAK1");      //Rename the file. 
 
	WORD error; 
	_WIN32_FIND_DATA_BCS findData; 
 
	char i; 
	for(i='2'; i<='1'+maxBakNum-1; i++) 
	{ 
		R0_FindFirstFile(pBCSNewName, NULL, &findData, &error); 
		if(error==ERROR_FILE_NOT_FOUND) 
			return TRUE; 
		else  
			//if the file name has already been used, make some change. 
			pBCSNewName[strlen(pBCSNewName)-1]=i;					 
	} 
 
	return FALSE; 
} 
 
//Backup a file by renaming it: 
//e.g. "A.txt"  to "A.txt.BAK". 
WORD FileRename(char *pBCSOldName) 
{ 
	char pBCSNewName[MAX_PATH+1]; 
	WORD error; 
	 
    //Name the bak file. 
	AppendBakExtension(pBCSOldName, pBCSNewName); 
 
	R0_RenameFile(pBCSOldName,pBCSNewName,&error); 
	 
	return error; 
} 
 
// Function name	: FileBackupByHandle 
//							Backup a file identified by pir->ir_fh & pBCSName. 
//							Using pir->ir_fh to read from the file. 
//							if	the handle is open with out Generic_Read, this function will fail. 
//							Only call this function in HookProc. 
WORD FileBackupByHandle(pioreq pir,int Drive, int ResType, int CodePage,const char *pBCSName) 
{ 
	char pBCSNewName[MAX_PATH+1]; 
	HANDLE hNewFile; 
	BYTE action; 
	WORD error, retVal=ERROR_SUCCESS; 
	ifsreq ifsr; 
	 
	//Name the bak file. 
	AppendBakExtension(pBCSName, pBCSNewName); 
	 
	hNewFile=R0_OpenCreateFile(FALSE, pBCSNewName,OPEN_ACCESS_WRITEONLY | OPEN_SHARE_COMPATIBLE, 
		ATTR_NORMAL, ACTION_IFEXISTS_TRUNCATE | ACTION_IFNOTEXISTS_CREATE,0 ,&error,&action); 
	if(error!=ERROR_SUCCESS) return error; 
	 
	char buf[BUF_LEN+1]; 
	DWORD nBytesRead=0,nBytesWrite=0,offset=0; 
	memcpy(&ifsr,pir,sizeof(ifsreq)); 
	pIFSFunc  readFunc = ifsr.ifs_hndl->hf_read; 
	while(1) 
	{ 
		ifsr.ifsir.ir_length=BUF_LEN; 
		ifsr.ifsir.ir_options=R0_NO_CACHE; 
		ifsr.ifsir.ir_data=buf; 
		ifsr.ifsir.ir_pos=offset;	 
		if((*PrevHookProc)(readFunc, IFSFN_READ,Drive, ResType, CodePage,(pioreq) &ifsr)==STATUS_PENDING) 
			IFSMgr_CompleteAsync((pioreq) &ifsr);   
 
		nBytesRead=ifsr.ifsir.ir_length; 
		if(ifsr.ifsir.ir_error!=ERROR_SUCCESS || !nBytesRead)  
		{ 
			retVal=ifsr.ifsir.ir_error; 
			break; 
		} 
 
		nBytesWrite=R0_WriteFile(FALSE,hNewFile,buf,nBytesRead,offset,&error); 
 
		if(error!=ERROR_SUCCESS) 
		{ 
			retVal=error; 
			break; 
		} 
		 
		offset+=nBytesRead; 
	} 
	 
	R0_CloseFile(hNewFile,&error); 
 
	return retVal; 
} 
 
// Function name	: FileBackup 
//							Back up a file identify by pBCSName. 
//							If the file is currently open by other user with DENY_READ, this function will fail. 
WORD FileBackup(char *pBCSName) 
{ 
	char pBCSNewName[MAX_PATH+1]; 
	HANDLE hOldFile, hNewFile; 
	BYTE action; 
	WORD error, retVal=ERROR_SUCCESS; 
	 
	//Name the bak file. 
	AppendBakExtension(pBCSName, pBCSNewName); 
 
	hOldFile=R0_OpenCreateFile(FALSE, pBCSName,OPEN_ACCESS_READONLY	 | OPEN_SHARE_COMPATIBLE, 
		ATTR_NORMAL, ACTION_IFEXISTS_OPEN | ACTION_IFNOTEXISTS_FAIL	,0 ,&error,&action); 
	if(error!=ERROR_SUCCESS) 
	{ 
		return error; 
	} 
	hNewFile=R0_OpenCreateFile(FALSE, pBCSNewName,OPEN_ACCESS_WRITEONLY | OPEN_SHARE_COMPATIBLE, 
		ATTR_NORMAL, ACTION_IFEXISTS_TRUNCATE | ACTION_IFNOTEXISTS_CREATE,0 ,&error,&action); 
	if(error!=ERROR_SUCCESS) return error; 
 
	char buf[BUF_LEN+1]; 
	DWORD nBytesRead=0,nBytesWrite=0,offset=0; 
	while(1) 
	{ 
		nBytesRead=R0_ReadFile(FALSE, hOldFile, buf, BUF_LEN, offset, &error); 
		if(error!=ERROR_SUCCESS || !nBytesRead) 
		{ 
			retVal=error; 
			break; 
		} 
 
		nBytesWrite=R0_WriteFile(FALSE,hNewFile,buf,nBytesRead,offset,&error); 
		if(error!=ERROR_SUCCESS)  
		{ 
			retVal=error; 
			break; 
		} 
		 
		offset+=nBytesRead; 
	} 
	 
	R0_CloseFile(hOldFile, &error); 
	R0_CloseFile(hNewFile,&error); 
 
	return retVal; 
} 
 
//Similar to UniToBCSPath(...) 
void  UniToBCSPathEx(PUCHAR pBCSPath, PathElement* pUniPath, int Drive, DWORD maxLength, int charSet, _QWORD* pResult) 
{ 
	//Get driver volume: 
	pBCSPath[0]=(char)Drive-1+'A'; 
	pBCSPath[1]=':'; 
	 
	UniToBCSPath(pBCSPath+2,pUniPath,maxLength,charSet,pResult); 
	pBCSPath[pResult->ddLower+2]=NULL; 
} 
 
//Get full Canonicalized Path from pir->ir_fh. 
BOOL GetFullNameByHandle(pioreq pir,int &Drive, int ResType, int CodePage,path_t pPPath, BOOL bFindHandle) 
{ 
	//Search in handle table first. 
	if((bFindHandle && findHTable.Find(pir->ir_fh, Drive, pPPath, MAX_PATH)) || openHTable.Find(pir->ir_fh, Drive, pPPath, MAX_PATH)) 
		return TRUE; 
 
	//if not found in handle table, call FS_EnumerateHandle to get file path. 
    ifsreq      ifsr; 
    memcpy( &ifsr, pir, sizeof(ifsreq)); 
    ifsr.ifsir.ir_flags = bFindHandle ?  ENUMH_GETFINDINFO : ENUMH_GETFILENAME; 
    ifsr.ifsir.ir_ppath = pPPath; 
   	pIFSFunc    enumFunc = ifsr.ifs_hndl->hf_misc->hm_func[HM_ENUMHANDLE];		//get address of FS_EnumerateHandle from struct hndlfunc. 
 
    if((*PrevHookProc)(enumFunc, IFSFN_ENUMHANDLE, Drive, ResType, CodePage,(pioreq) &ifsr)!= ERROR_SUCCESS) return FALSE; 
 
	return TRUE; 
} 
 
void SetLastError(WORD errorCode) 
{ 
	lastError=errorCode; 
} 
 
WORD GetLastError() 
{ 
	return lastError; 
} 
 
UINT refData;	 
SHELLMessage_THUNK thunk; 
 
//Gobal error handler for device: 
//err: error code.	severity: 0 means a fatal error, 1 a minor one. 
//return value: 0 ---the moduel where the error occurs must stop processing. 
//						1 ---processing can go on. 
int ErrorHandler(unsigned int err, int severity/*=1*/) 
{ 
	ShellPostError(err,severity); 
	return 1; 
} 
 
//Callback function for user's response of shell_message. 
void __stdcall MsgCallBack(DWORD ResponseCode,PVOID Refdata) 
{ 
	switch(*((UINT *)Refdata)) 
	{ 
		case ON_MSG_FILE_NOT_FOUND: 
			if(ResponseCode==IDNO) 
				ShellUnloadDevice();		//call on FileGuard win32 GUI to stop protection. 
			break; 
	} 
} 
 
//Read protected file information: 
//DO NOT call this function from within HookProc. 
BOOL ReadProtectedFileInfo(char *pFileName) 
{ 
	//Prevent reentry: 
	bBusy=TRUE; 
 
	HANDLE hIniFile; 
	WORD error; 
	BYTE action; 
	hIniFile=R0_OpenCreateFile(TRUE,pFileName,OPEN_ACCESS_READONLY|OPEN_SHARE_COMPATIBLE|OPEN_FLAGS_COMMIT,ATTR_NORMAL,ACTION_IFEXISTS_OPEN|ACTION_IFNOTEXISTS_FAIL,R0_NO_CACHE,&error,&action); 
	if(error!=ERROR_SUCCESS) 
	{ 
		ErrorHandler(FG_ERR_INI_FILE_NOT_FOUND); 
		return FALSE; 
	} 
	 
	//INI file format: each record consist of a '@' followed by length of the path 
	//(4 char, including a whitespace),a '#' followed by protection type(3 char, 
	//including a whitespace) and anther '#' followed 
	//by the path of a file under protection. 
	const UINT BYTES_TO_READ=MAX_PATH*5; 
	char buf[BYTES_TO_READ+1]; 
	char path[MAX_PATH+1]; 
	UINT offset=0; 
	do 
	{ 
		BYTE nBytesRead=0; 
		for(int i=0;i<=BYTES_TO_READ;i++)	buf[i]=0;  
		nBytesRead=R0_ReadFile(FALSE,hIniFile,buf,BYTES_TO_READ,offset,&error); 
		if(nBytesRead==0) break; 
		if(error!=ERROR_SUCCESS) 
			continue; 
		 
		UINT pos=0; 
		//find first '@': 
		while(*(buf+pos)!='@' && pos<=BYTES_TO_READ-1)     
			pos++; 
		offset+=pos; 
		do 
		{ 
			PROTECTED_FILE protFile; 
 
			if(StringToFileInfo(buf+pos, &protFile,path)!=1)	 
				break;			//not more record in the string. Go on to read next. 
			else 
			{ 
				pos+=(strlen(path)+RECORD_LEN);     
				offset+=(strlen(path)+RECORD_LEN);         //make sure offset is always pointing to the beginning of a new record. 
				 
				#ifdef DEBUG 
					dout <PF_pPath),pProtFile->PF_type,pProtFile->PF_pPath); 
		stringLen++;   //add the final char '\0'. 
		if((stringLen>R0_WriteFile(FALSE, hIniFile, buf, stringLen, offset, &error)) || error!=ERROR_SUCCESS) 
		{ 
			ErrorHandler(FG_ERR_CANNOT_WRITE_RECORD); 
			#ifdef DEBUG 
				dout << "Save file list: can't write an record." << endl; 
			#endif 
		} 
		else offset+=stringLen;  //writing success. 
	} 
	else   //if not item found 
	{ 
		R0_CloseFile(hIniFile, &error);  
		free(buf); 
		return TRUE;    
	} 
 
	while((pProtFile=protFileList.FindNext())!=NULL) 
	{ 
		UINT stringLen=sprintf(buf,"@ %3u, %2u,%s\0\n",strlen(pProtFile->PF_pPath),pProtFile->PF_type,pProtFile->PF_pPath); 
		stringLen++;   //add the final char '\0'. 
		if((stringLen>R0_WriteFile(FALSE, hIniFile, buf, stringLen, offset, &error)) || error!=ERROR_SUCCESS) 
		{ 
			ErrorHandler(FG_ERR_CANNOT_WRITE_RECORD); 
			#ifdef DEBUG 
				dout << "Save file list: can't write an record." << endl; 
			#endif 
		} 
		else offset+=stringLen;  //writing success. 
	} 
 
	R0_CloseFile(hIniFile, &error);  
	free(buf); 
 
	bBusy=FALSE; 
	return TRUE;    //end of list. 
} 
 
//call for win32 GUI to stop protection. 
BOOL ShellUnloadDevice() 
{ 
	if(!hAppWnd) 
		return FALSE; 
	return SHELL_PostMessage(hAppWnd, SM_DEVICE_FATAL_ERROR, 0, 0, 0, 0); 
} 
 
//Post errer code to win32 app. 
BOOL ShellPostError(DWORD err, int severity) 
{ 
	if(!hAppWnd) 
		return FALSE; 
	return SHELL_PostMessage(hAppWnd, SM_DEVICE_ERROR, (WORD)severity, err, 0, 0);		//Error message sended to win32 app. 
																															 //wParam is severity, lParam is error code. 
} 
 
int wstrlen(unsigned short *uniString) 
{ 
    int i = 0; 
    int len = 0; 
   
    while( uniString[i++] != 0 ) len+=2; 
 
    return len; 
} 
 
//Make characters in a wide character string to upper case. 
//Return Value: string length. 
int StringUpper(WCHAR *s, int len) 
{ 
	int i=-1; 
 
	while(++i<=len) 
		if(s[i]>='a' && s[i]<='z') 
			s[i]-=32; 
 
	return i; 
} 
 
//Convert BCS path to file system device's Canonicalized Path. 
BOOL BCSToFSDPath(FSD_PATH *fp, const char *pBCSPath) 
{ 
	fp->ppath=(struct ParsedPath *)malloc((strlen(pBCSPath)+1)*sizeof(WCHAR)); 
	if(!fp->ppath)	return FALSE; 
 
	fp->flag=0; 
	fp->drive=pBCSPath[0]-'A'+1;		//used first character as drive volume. 
	 
	//i: index;  prefix: prefix in parsed element(pe_length);  elemLength: length of an element; 
	unsigned int i=3,prefix=4, elemLength=0, totalLenghth=0;		 
	_QWORD res; 
	do 
	{ 
		if(*(pBCSPath+i)=='\\') 
		{	 
			BCSToUni((WCHAR *)((char *)fp->ppath+prefix+sizeof(WCHAR)), (PUCHAR)pBCSPath+i-elemLength, elemLength, BCS_OEM, &res); 
			*(short *)((char*)fp->ppath+prefix)=sizeof(WCHAR)+res.ddLower;		//set pe_length.				 
			totalLenghth+=*((char*)fp->ppath+prefix); 
			prefix+=*((char*)fp->ppath+prefix);		//pointing to current '\' (pe_length). 
			elemLength=0; 
			i++; 
			continue; 
		} 
		else if(*(pBCSPath+i)=='*') 
			fp->flag|=FG_FILE_FLAG_HAS_STAR; 
		else if(*(pBCSPath+i)=='?') 
			fp->flag|=FG_FILE_FLAG_HAS_QUERYMARK; 
		 
		elemLength++; 
		i++; 
	} 
	while(*(pBCSPath+i)); 
 
	BCSToUni((WCHAR *)((char *)fp->ppath+prefix+sizeof(WCHAR)), (PUCHAR)pBCSPath+i-elemLength, elemLength, BCS_OEM, &res); 
	*(short *)((char*)fp->ppath+prefix)=sizeof(WCHAR)+res.ddLower;		//set pe_length for last element. 
	totalLenghth+=*((char*)fp->ppath+prefix); 
	fp->ppath->pp_totalLength=2*sizeof(WCHAR)+totalLenghth;				//set total length. 
	fp->ppath->pp_prefixLength=prefix;		//pointing to last path element. 
	*(short *)((char *)fp->ppath+fp->ppath->pp_totalLength)=NULL; 
 
	return TRUE; 
} 
 
//Compare two parsed element. 
BOOL _inline ElementMatch(PathElement *pPattern, PathElement *pMatcher, int flag) 
{ 
	if(!(flag&FG_FILE_FLAG_HAS_STAR) && pPattern->pe_length!=pMatcher->pe_length)		//if pattern has not stars, and lengths don't equal, then match fail. 
		return FALSE; 
	 
	return PatternMatch((WCHAR *)pPattern->pe_unichars, (WCHAR *)pMatcher->pe_unichars, pPattern->pe_length/sizeof(WCHAR)-1, pMatcher->pe_length/sizeof(WCHAR)-1); 
} 
 
//Compare two FSD_PATH struct. 
BOOL _inline FSDPathMatch(FSD_PATH *pPattern, FSD_PATH *pMatcher) 
{ 
	//compare path. 
	if((pPattern->drive!=pMatcher->drive) || (!(pPattern->flag&FG_FILE_FLAG_HAS_STAR) && pPattern->ppath->pp_totalLength!=pMatcher->ppath->pp_totalLength)) 
		return FALSE; 
 
	PathElement *ppe, *mpe; 
	ppe=pPattern->ppath->pp_elements; 
	mpe=pMatcher->ppath->pp_elements; 
 
	while(ppe->pe_length) 
	{ 
		//compare each parsed element. 
 
		if(!ElementMatch(ppe, mpe, pPattern->flag)) 
			return FALSE; 
 
		ppe=IFSNextElement(ppe); 
		mpe=IFSNextElement(mpe); 
	} 
 
	return TRUE; 
}