3668 lines
109 KiB
C++
3668 lines
109 KiB
C++
/*++
|
||
|
||
(c) 1998 Seagate Software, Inc. All rights reserved.
|
||
|
||
Module Name:
|
||
|
||
fsafltr.cpp
|
||
|
||
Abstract:
|
||
|
||
This class represents a file system filter for NTFS 5.0.
|
||
|
||
Author:
|
||
|
||
Chuck Bardeen [cbardeen] 12-Feb-1997
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "stdafx.h"
|
||
extern "C" {
|
||
#include "devioctl.h"
|
||
#include <winerror.h>
|
||
#include "aclapi.h"
|
||
|
||
// #define MAC_SUPPORT // NOTE: You must define MAC_SUPPORT in fsaftrcl.cpp to enable all the code
|
||
|
||
#ifdef MAC_SUPPORT
|
||
#include <macfile.h>
|
||
#endif
|
||
}
|
||
|
||
#define WSB_TRACE_IS WSB_TRACE_BIT_FSA
|
||
|
||
#include "wsb.h"
|
||
#include "HsmConn.h"
|
||
#include "job.h"
|
||
#include "fsa.h"
|
||
#include "fsafltr.h"
|
||
#include "fsaitemr.h"
|
||
|
||
|
||
#ifdef MAC_SUPPORT
|
||
extern HANDLE FsaDllSfm;
|
||
extern BOOL FsaMacSupportInstalled;
|
||
|
||
extern "C" {
|
||
typedef DWORD (*AdminConnect) (LPWSTR lpwsServerName, PAFP_SERVER_HANDLE phAfpServer);
|
||
|
||
extern AdminConnect pAfpAdminConnect;
|
||
|
||
typedef VOID (*AdminDisconnect) (AFP_SERVER_HANDLE hAfpServer);
|
||
|
||
extern AdminDisconnect pAfpAdminDisconnect;
|
||
|
||
typedef VOID (*AdminBufferFree) (PVOID pBuffer);
|
||
|
||
extern AdminBufferFree pAfpAdminBufferFree;
|
||
|
||
typedef DWORD (*AdminSessionEnum) (AFP_SERVER_HANDLE hAfpServer, LPBYTE *lpbBuffer,
|
||
DWORD dwPrefMaxLen, LPDWORD lpdwEntriesRead, LPDWORD lpdwTotalEntries,
|
||
LPDWORD lpdwResumeHandle);
|
||
|
||
extern AdminSessionEnum pAfpAdminSessionEnum;
|
||
}
|
||
#endif
|
||
|
||
DWORD FsaIoctlThread(void *pFilterInterface);
|
||
DWORD FsaPipeThread(void *pFilterInterface);
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Cancel(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::Cancel().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
HSM_JOB_STATE oldState;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::Cancel"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
WsbAffirm((HSM_JOB_STATE_ACTIVE == m_state), E_UNEXPECTED);
|
||
|
||
oldState = m_state;
|
||
m_state = HSM_JOB_STATE_CANCELLING;
|
||
|
||
try {
|
||
|
||
// TBD - Do whatever it takes to cancel
|
||
WsbAssertHr(E_NOTIMPL);
|
||
m_state = HSM_JOB_STATE_CANCELLED;
|
||
|
||
} WsbCatchAndDo(hr, m_state = oldState;);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::Cancel"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::CancelRecall(
|
||
IN IFsaFilterRecall* pRecall
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::CancelRecall().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::CancelRecall"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
WsbAssert(pRecall != 0, E_POINTER);
|
||
|
||
// Get the private interface and tell the recall to cancel itself.
|
||
WsbAffirmHr(pRecall->QueryInterface(IID_IFsaFilterRecallPriv, (void**) &pRecallPriv));
|
||
WsbAffirmHr(pRecallPriv->Cancel());
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::CancelRecall"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::CompareTo(
|
||
IN IUnknown* pUnknown,
|
||
OUT SHORT* pResult
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IWsbCollectable::CompareTo().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IFsaFilter> pFilter;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::CompareTo"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
// Did they give us a valid item to compare to?
|
||
WsbAssert(0 != pUnknown, E_POINTER);
|
||
|
||
// We need the IWsbBool interface to get the value of the object.
|
||
WsbAffirmHr(pUnknown->QueryInterface(IID_IFsaFilter, (void**) &pFilter));
|
||
|
||
// Compare the rules.
|
||
hr = CompareToIFilter(pFilter, pResult);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::CompareTo"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::CompareToIdentifier(
|
||
IN GUID id,
|
||
OUT SHORT* pResult
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::CompareToIdentifier().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
SHORT aResult = 0;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::CompareToIdentifier"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
aResult = WsbSign( memcmp(&m_id, &id, sizeof(GUID)) );
|
||
|
||
if (0 != aResult) {
|
||
hr = S_FALSE;
|
||
}
|
||
|
||
if (0 != pResult) {
|
||
*pResult = aResult;
|
||
}
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::CompareToIdentifier"), OLESTR("hr = <%ls>, result = <%d>"), WsbHrAsString(hr), aResult);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::CompareToIFilter(
|
||
IN IFsaFilter* pFilter,
|
||
OUT SHORT* pResult
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::CompareToIFilter().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
GUID id;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::CompareToIFilter"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
// Did they give us a valid item to compare to?
|
||
WsbAssert(0 != pFilter, E_POINTER);
|
||
WsbAffirmHr(pFilter->GetIdentifier(&id));
|
||
// Either compare the name or the id.
|
||
hr = CompareToIdentifier(id, pResult);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::CompareToIFilter"), OLESTR("hr = <%ls>, result = <%ls>"), WsbHrAsString(hr), WsbPtrToShortAsString(pResult));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::CleanupClients(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
CFsaFilter::CleanupClients()
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IFsaFilterClient> pClient;
|
||
CComPtr<IWsbEnum> pEnum;
|
||
FILETIME now, last;
|
||
LARGE_INTEGER tNow, tLast;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::CleanupClients"), OLESTR(""));
|
||
|
||
EnterCriticalSection(&m_clientLock);
|
||
|
||
try {
|
||
|
||
WsbAffirmHr(m_pClients->Enum(&pEnum));
|
||
hr = pEnum->First(IID_IFsaFilterClient, (void**) &pClient);
|
||
|
||
while (S_OK == hr) {
|
||
GetSystemTimeAsFileTime(&now);
|
||
tNow.LowPart = now.dwLowDateTime;
|
||
tNow.HighPart = now.dwHighDateTime;
|
||
|
||
WsbAffirmHr(pClient->GetLastRecallTime(&last));
|
||
tLast.LowPart = last.dwLowDateTime;
|
||
tLast.HighPart = last.dwHighDateTime;
|
||
//
|
||
// Get the time (in 100 nano-second units)
|
||
// from the end of the last recall until now.
|
||
//
|
||
if (tLast.QuadPart != 0) {
|
||
tNow.QuadPart -= tLast.QuadPart;
|
||
//
|
||
// Convert to seconds and check against the expiration time
|
||
//
|
||
tNow.QuadPart /= (LONGLONG) 10000000;
|
||
if (tNow.QuadPart > (LONGLONG) FSA_CLIENT_EXPIRATION_TIME) {
|
||
//
|
||
// This client structure is old - blow it away
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::CleanupClients - cleaning up old client (%ls)\n"),
|
||
WsbLonglongAsString(tNow.QuadPart));
|
||
m_pClients->RemoveAndRelease(pClient);
|
||
pClient = NULL;
|
||
WsbAffirmHr(pEnum->Reset());
|
||
}
|
||
}
|
||
|
||
pClient = NULL;
|
||
hr = pEnum->Next(IID_IFsaFilterClient, (void**) &pClient);
|
||
}
|
||
if (hr == WSB_E_NOTFOUND) {
|
||
hr = S_OK;
|
||
}
|
||
|
||
} WsbCatch(hr);
|
||
|
||
LeaveCriticalSection(&m_clientLock);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::CleanupClients"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DeleteRecall(
|
||
IN IFsaFilterRecall* pRecall
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::DeleteRecall().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::DeleteRecall"), OLESTR(""));
|
||
|
||
try {
|
||
ULONG numEnt;
|
||
|
||
WsbAssert(pRecall != 0, E_POINTER);
|
||
|
||
// Delete the request.
|
||
WsbAffirmHr(pRecall->QueryInterface(IID_IFsaFilterRecallPriv, (void**) &pRecallPriv));
|
||
WsbAffirmHr(pRecallPriv->Delete());
|
||
|
||
// Remove it from our collection.
|
||
EnterCriticalSection(&m_recallLock);
|
||
m_pRecalls->RemoveAndRelease(pRecall);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
|
||
m_pRecalls->GetEntries(&numEnt);
|
||
WsbTrace(OLESTR("CFsaFilter::DeleteRecall: Recall queue has %u entries after delete.\n"),
|
||
numEnt);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::DeleteRecall"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::EnumRecalls(
|
||
OUT IWsbEnum** ppEnum
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::EnumRecalls().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != ppEnum, E_POINTER);
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->Enum(ppEnum);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
WsbAffirmHr(hr);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::FinalConstruct(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
CComObjectRoot::FinalConstruct().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAffirmHr(CWsbCollectable::FinalConstruct());
|
||
|
||
WsbAffirmHr(CoCreateGuid(&m_id));
|
||
m_state = HSM_JOB_STATE_IDLE;
|
||
|
||
//
|
||
// Start out enabled
|
||
//
|
||
|
||
m_isEnabled = TRUE;
|
||
//
|
||
// These are the default parameters.
|
||
// The max recalls and admin exemption setting are configurable via the admin gui.
|
||
//
|
||
m_minRecallInterval = 10;
|
||
m_maxRecallBuffers = RP_MAX_RECALL_BUFFERS;
|
||
m_maxRecalls = RP_DEFAULT_RUNAWAY_RECALL_LIMIT;
|
||
m_exemptAdmin = FALSE; // By default admin is NOT exempt
|
||
|
||
m_ioctlHandle = INVALID_HANDLE_VALUE;
|
||
m_pipeHandle = INVALID_HANDLE_VALUE;
|
||
|
||
InitializeCriticalSection(&m_clientLock);
|
||
InitializeCriticalSection(&m_recallLock);
|
||
InitializeCriticalSection(&m_stateLock);
|
||
|
||
// Create the Client List collection.
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CWsbOrderedCollection, NULL, CLSCTX_SERVER, IID_IWsbCollection, (void**) &m_pClients));
|
||
|
||
// Create the Recall List collection.
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CWsbOrderedCollection, NULL, CLSCTX_SERVER, IID_IWsbCollection, (void**) &m_pRecalls));
|
||
|
||
|
||
#ifdef MAC_SUPPORT
|
||
//
|
||
// Attempt to load the Mac support
|
||
//
|
||
FsaDllSfm = LoadLibrary(L"SFMAPI.DLL");
|
||
if (FsaDllSfm != NULL) {
|
||
//
|
||
// The DLL is there - try importing the functions we will need
|
||
//
|
||
try {
|
||
WsbAffirmPointer((pAfpAdminConnect = (AdminConnect) GetProcAddress((HMODULE) FsaDllSfm, "AfpAdminConnect")));
|
||
WsbAffirmPointer((pAfpAdminDisconnect = (AdminDisconnect) GetProcAddress((HMODULE) FsaDllSfm, "AfpAdminDisconnect")));
|
||
WsbAffirmPointer((pAfpAdminBufferFree = (AdminBufferFree) GetProcAddress((HMODULE) FsaDllSfm, "AfpAdminBufferFree")));
|
||
WsbAffirmPointer((pAfpAdminSessionEnum = (AdminSessionEnum) GetProcAddress((HMODULE) FsaDllSfm, "AfpAdminSessionEnum")));
|
||
FsaMacSupportInstalled = TRUE;
|
||
} WsbCatchAndDo(hr,
|
||
FreeLibrary((HMODULE) FsaDllSfm);
|
||
FsaDllSfm = NULL;
|
||
);
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::FinalRelease(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
CComObjectRoot::FinalRelease().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
HANDLE pThreadHandles[THREAD_HANDLE_COUNT];
|
||
DWORD nThreadCount = 0;
|
||
BOOL bThreads = TRUE;
|
||
BOOL bTerminated = TRUE;
|
||
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::FinalRelease"), OLESTR(""));
|
||
#ifdef MAC_SUPPORT
|
||
if (FsaDllSfm != NULL) {
|
||
FreeLibrary((HMODULE) FsaDllSfm);
|
||
FsaDllSfm = NULL;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Stop the ioctl and pipe threads
|
||
//
|
||
m_state = HSM_JOB_STATE_SUSPENDING;
|
||
|
||
// But wait until they've completed first.
|
||
if ((m_pipeThread) && (m_ioctlThread)) {
|
||
pThreadHandles[0] = m_pipeThread;
|
||
pThreadHandles[1] = m_ioctlThread;
|
||
nThreadCount = 2;
|
||
} else if (m_pipeThread) {
|
||
pThreadHandles[0] = m_pipeThread;
|
||
nThreadCount = 1;
|
||
} else if (m_ioctlThread) {
|
||
pThreadHandles[0] = m_ioctlThread;
|
||
nThreadCount = 1;
|
||
} else{
|
||
// neither of the threads exist, skip wait.
|
||
bThreads = FALSE;
|
||
}
|
||
|
||
if (bThreads) {
|
||
|
||
switch (WaitForMultipleObjects(nThreadCount, pThreadHandles, TRUE, 20000)) {
|
||
case WAIT_FAILED: {
|
||
WsbTrace(OLESTR("CFsaFilter::FinalRelease: WaitforMultipleObjects returned error %lu\n"),
|
||
GetLastError());
|
||
}
|
||
// fall through...
|
||
|
||
case WAIT_TIMEOUT: {
|
||
WsbTrace(OLESTR("CFsaFilter::FinalRelease: force-terminating threads.\n"));
|
||
|
||
// after timeout, force termination on threads
|
||
// bTerminated specify if the force-termination succeeds
|
||
DWORD dwExitCode;
|
||
if (GetExitCodeThread( m_ioctlThread, &dwExitCode)) {
|
||
if (dwExitCode == STILL_ACTIVE) { // thread still active
|
||
if (!TerminateThread (m_ioctlThread, 0)) {
|
||
WsbTrace(OLESTR("CFsaFilter::FinalRelease: TerminateThread returned error %lu\n"),
|
||
GetLastError());
|
||
bTerminated = FALSE;
|
||
}
|
||
}
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::FinalRelease: GetExitCodeThread returned error %lu\n"),
|
||
GetLastError());
|
||
bTerminated = FALSE;
|
||
}
|
||
|
||
if (GetExitCodeThread( m_pipeThread, &dwExitCode)) {
|
||
if (dwExitCode == STILL_ACTIVE) { // thread still active
|
||
if (!TerminateThread (m_pipeThread, 0)) {
|
||
WsbTrace(OLESTR("CFsaFilter::FinalRelease: TerminateThread returned error %lu\n"),
|
||
GetLastError());
|
||
bTerminated = FALSE;
|
||
}
|
||
}
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::FinalRelease: GetExitCodeThread returned error %lu\n"),
|
||
GetLastError());
|
||
bTerminated = FALSE;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (m_pipeHandle != INVALID_HANDLE_VALUE) {
|
||
CloseHandle(m_pipeHandle);
|
||
m_pipeHandle = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
if (m_ioctlHandle != INVALID_HANDLE_VALUE) {
|
||
CloseHandle(m_ioctlHandle);
|
||
m_ioctlHandle = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
if (m_terminateEvent != INVALID_HANDLE_VALUE) {
|
||
CloseHandle(m_terminateEvent);
|
||
m_terminateEvent = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
// delete CS only if threads were teriminated properly
|
||
if (bTerminated) {
|
||
DeleteCriticalSection(&m_clientLock);
|
||
DeleteCriticalSection(&m_recallLock);
|
||
DeleteCriticalSection(&m_stateLock);
|
||
}
|
||
|
||
CWsbCollectable::FinalRelease();
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::FinalRelease"), OLESTR(""));
|
||
|
||
return(hr);
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::FindRecall(
|
||
IN GUID recallId,
|
||
OUT IFsaFilterRecall** pRecall
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
CFsaFilter:FindRecall().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::FindRecall"), OLESTR(""));
|
||
|
||
|
||
try {
|
||
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterRecallNTFS, NULL, CLSCTX_SERVER, IID_IFsaFilterRecallPriv, (void**) &pRecallPriv));
|
||
WsbAffirmHr(pRecallPriv->SetIdentifier(recallId));
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->Find(pRecallPriv, IID_IFsaFilterRecall, (void**) pRecall);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
WsbAffirmHr(hr);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::FindRecall"), OLESTR(""));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetAdminExemption(
|
||
OUT BOOL *pIsExempt
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::GetAdminExemption().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pIsExempt, E_POINTER);
|
||
*pIsExempt = m_exemptAdmin;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetClassID(
|
||
OUT CLSID* pClsid
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IPersist::GetClassID().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::GetClassID"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pClsid, E_POINTER);
|
||
*pClsid = CLSID_CFsaFilterNTFS;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::GetClassID"), OLESTR("hr = <%ls>, CLSID = <%ls>"), WsbHrAsString(hr), WsbGuidAsString(*pClsid));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetIdentifier(
|
||
OUT GUID* pId
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::GetIdentifier().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pId, E_POINTER);
|
||
|
||
*pId = m_id;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetLogicalName(
|
||
OUT OLECHAR** pName,
|
||
IN ULONG bufferSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::GetLogicalName().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pName, E_POINTER);
|
||
WsbAssert(m_pFsaServer != 0, E_POINTER);
|
||
|
||
// This has not been official defined, but for now the logical name will be the same name
|
||
// as the FSA, since we will only have one filter.
|
||
WsbAffirmHr(m_pFsaServer->GetLogicalName(pName, bufferSize));
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetMaxRecallBuffers(
|
||
OUT ULONG* pMaxBuffers
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::GetMaxRecallBuffers().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pMaxBuffers, E_POINTER);
|
||
*pMaxBuffers = m_maxRecallBuffers;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetMaxRecalls(
|
||
OUT ULONG* pMaxRecalls
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::GetMaxRecalls().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pMaxRecalls, E_POINTER);
|
||
*pMaxRecalls = m_maxRecalls;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetMinRecallInterval(
|
||
OUT ULONG* pMinInterval
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::GetMinRecallInterval().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pMinInterval, E_POINTER);
|
||
*pMinInterval = m_minRecallInterval;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetSizeMax(
|
||
OUT ULARGE_INTEGER* pSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IPersistStream::GetSizeMax().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IPersistStream> pPersistStream;
|
||
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::GetSizeMax"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pSize, E_POINTER);
|
||
|
||
// We are only storing the configuration information, NOT the collections.
|
||
pSize->QuadPart = WsbPersistSizeOf(GUID) + 3 * WsbPersistSizeOf(ULONG) + WsbPersistSizeOf(BOOL);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::GetSizeMax"), OLESTR("hr = <%ls>, Size = <%ls>"), WsbHrAsString(hr), WsbPtrToUliAsString(pSize));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::GetState(
|
||
OUT HSM_JOB_STATE* pState
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IPersistStream::GetState().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::GetState"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pState, E_POINTER);
|
||
|
||
*pState = m_state;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::GetState"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Init(
|
||
IN IFsaServer* pFsaServer
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilterPriv::Init().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != pFsaServer, E_POINTER);
|
||
|
||
// Store the parent FSA?
|
||
m_pFsaServer = pFsaServer;
|
||
|
||
m_isDirty = TRUE;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::IsEnabled(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::IsEnabled().
|
||
|
||
--*/
|
||
{
|
||
return(m_isEnabled ? S_OK : S_FALSE);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Load(
|
||
IN IStream* pStream
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IPersistStream::Load().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::Load"), OLESTR(""));
|
||
|
||
try {
|
||
WsbAssert(0 != pStream, E_POINTER);
|
||
|
||
// Do the easy stuff, but make sure that this order matches the order
|
||
// in the save method.
|
||
WsbAffirmHr(WsbLoadFromStream(pStream, &m_id));
|
||
WsbAffirmHr(WsbLoadFromStream(pStream, &m_maxRecalls));
|
||
WsbAffirmHr(WsbLoadFromStream(pStream, &m_minRecallInterval));
|
||
WsbAffirmHr(WsbLoadFromStream(pStream, &m_maxRecallBuffers));
|
||
WsbAffirmHr(WsbLoadFromStream(pStream, &m_isEnabled));
|
||
WsbAffirmHr(WsbLoadFromStream(pStream, &m_exemptAdmin));
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::Load"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Pause(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::Pause().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
HSM_JOB_STATE oldState;
|
||
RP_MSG tmp;
|
||
DWORD outSize = 0;
|
||
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::Pause"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
WsbAffirm((HSM_JOB_STATE_ACTIVE == m_state), E_UNEXPECTED);
|
||
|
||
oldState = m_state;
|
||
m_state = HSM_JOB_STATE_PAUSING;
|
||
|
||
try {
|
||
//
|
||
// Tell the kernel mode driver to stop accepting new recall requests.
|
||
//
|
||
if (m_ioctlHandle != INVALID_HANDLE_VALUE) {
|
||
tmp.inout.command = RP_SUSPEND_NEW_RECALLS;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL))
|
||
}
|
||
m_state = HSM_JOB_STATE_PAUSED;
|
||
|
||
} WsbCatchAndDo(hr, m_state = oldState;);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::Pause"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Resume(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::Resume().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
HSM_JOB_STATE oldState;
|
||
RP_MSG tmp;
|
||
DWORD outSize = 0;
|
||
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::Resume"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
WsbAffirm((HSM_JOB_STATE_PAUSED == m_state), E_UNEXPECTED);
|
||
|
||
oldState = m_state;
|
||
m_state = HSM_JOB_STATE_RESUMING;
|
||
|
||
try {
|
||
//
|
||
// Tell the kernel mode driver to start accepting recall requests.
|
||
//
|
||
if (m_ioctlHandle != INVALID_HANDLE_VALUE) {
|
||
tmp.inout.command = RP_ALLOW_NEW_RECALLS;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL))
|
||
}
|
||
|
||
m_state = HSM_JOB_STATE_ACTIVE;
|
||
|
||
} WsbCatchAndDo(hr, m_state = oldState;);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::Resume"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Save(
|
||
IN IStream* pStream,
|
||
IN BOOL clearDirty
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IPersistStream::Save().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IPersistStream> pPersistStream;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::Save"), OLESTR("clearDirty = <%ls>"), WsbBoolAsString(clearDirty));
|
||
|
||
try {
|
||
WsbAssert(0 != pStream, E_POINTER);
|
||
|
||
// Do the easy stuff, but make sure that this order matches the order
|
||
// in the load method.
|
||
WsbAffirmHr(WsbSaveToStream(pStream, m_id));
|
||
WsbAffirmHr(WsbSaveToStream(pStream, m_maxRecalls));
|
||
WsbAffirmHr(WsbSaveToStream(pStream, m_minRecallInterval));
|
||
WsbAffirmHr(WsbSaveToStream(pStream, m_maxRecallBuffers));
|
||
WsbAffirmHr(WsbSaveToStream(pStream, m_isEnabled));
|
||
WsbAffirmHr(WsbSaveToStream(pStream, m_exemptAdmin));
|
||
|
||
// If we got it saved and we were asked to clear the dirty bit, then
|
||
// do so now.
|
||
if (clearDirty) {
|
||
m_isDirty = FALSE;
|
||
}
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::Save"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SendCancel(
|
||
IN IFsaFilterRecallPriv *pRecallPriv
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilterPriv::SendCancel()
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
RP_MSG tmp;
|
||
DWORD outSize;
|
||
ULONG numEnt;
|
||
ULONGLONG driversId;
|
||
CComPtr<IFsaFilterRecall> pRecall;
|
||
|
||
//Get Drivers ID of failing recall
|
||
WsbAffirmHr(pRecallPriv->GetDriversRecallId(&driversId));
|
||
|
||
tmp.inout.command = RP_RECALL_COMPLETE;
|
||
tmp.inout.status = STATUS_CANCELLED;
|
||
tmp.msg.rRep.actionFlags = 0;
|
||
tmp.msg.rRep.filterId = driversId;
|
||
|
||
DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp, sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL);
|
||
|
||
//
|
||
// Remove it from our collection
|
||
//
|
||
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->RemoveAndRelease(pRecallPriv);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
WsbAffirmHr(hr);
|
||
|
||
hr = m_pRecalls->GetEntries(&numEnt);
|
||
WsbTrace(OLESTR("CFsaFilter::SendCancel: Recall queue has %u entries after delete\n"),
|
||
numEnt);
|
||
hr = S_OK;
|
||
|
||
|
||
|
||
return(hr);
|
||
}
|
||
|
||
#define FSA_MAX_XFER (1024 * 1024)
|
||
#define FSA_MIN_XFER (8 * 1024)
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SendComplete(
|
||
IN IFsaFilterRecallPriv *pRecallPriv,
|
||
IN HRESULT status
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilterPriv::SendComplete()
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
RP_MSG tmp;
|
||
BOOL code = FALSE;
|
||
ULONG numEnt;
|
||
BOOL didSend = FALSE;
|
||
DWORD ioSize;
|
||
CComPtr<IFsaFilterRecall> pRecall;
|
||
CWsbStringPtr pName;
|
||
|
||
|
||
|
||
try {
|
||
ioSize = sizeof(RP_MSG);
|
||
|
||
WsbAffirmHr(pRecallPriv->QueryInterface(IID_IFsaFilterRecall, (void**) &pRecall));
|
||
// Get path of file failing recall.
|
||
WsbAffirmHr(pRecall->GetPath((OLECHAR**) &pName, 0))
|
||
WsbAssertPointer(pName);
|
||
|
||
tmp.inout.command = RP_RECALL_COMPLETE;
|
||
if (status == S_OK)
|
||
tmp.inout.status = 0;
|
||
else {
|
||
// If the error indicates that Engine is not initialized yet -
|
||
// log an error with law severity (it is considered as a normal situation)
|
||
if (status != HSM_E_NOT_READY) {
|
||
WsbLogEvent(FSA_MESSAGE_RECALL_FAILED, 0, NULL, (OLECHAR*) WsbAbbreviatePath(pName, 120), WsbHrAsString(status), NULL);
|
||
} else {
|
||
WsbLogEvent(FSA_MESSAGE_RECALL_FAILED_NOT_READY, 0, NULL, (OLECHAR*) WsbAbbreviatePath(pName, 120), WsbHrAsString(status), NULL);
|
||
}
|
||
tmp.inout.status = TranslateHresultToNtStatus(status);
|
||
}
|
||
|
||
WsbAffirmHr(pRecall->GetRecallFlags(&tmp.msg.rRep.actionFlags));
|
||
|
||
WsbAffirmHr(pRecallPriv->GetDriversRecallId(&tmp.msg.rRep.filterId));
|
||
WsbTrace(OLESTR("CFsaFilter::SendComplete: id = %I64x status = %s\n"),
|
||
tmp.msg.rRep.filterId, WsbHrAsString(status));
|
||
|
||
WsbAffirmHr(pRecallPriv->LogComplete(status));
|
||
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp, ioSize,
|
||
&tmp, sizeof(RP_MSG), &ioSize, NULL));
|
||
|
||
didSend = TRUE;
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::SendComplete: Ioctl returned %u (%x)\n"), code, GetLastError());
|
||
|
||
//
|
||
// Remove it from our collection
|
||
//
|
||
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->RemoveAndRelease(pRecallPriv);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
WsbAffirmHr(hr);
|
||
|
||
hr = m_pRecalls->GetEntries(&numEnt);
|
||
WsbTrace(OLESTR("CFsaFilter::SendComplete: Recall queue has %u entries after delete\n"),
|
||
numEnt);
|
||
hr = S_OK;
|
||
|
||
} WsbCatchAndDo(hr,
|
||
RP_MSG aTmp;
|
||
|
||
//
|
||
// Log an event
|
||
//
|
||
WsbLogEvent(FSA_MESSAGE_RECALL_FAILED, 0, NULL, (OLECHAR*) WsbAbbreviatePath(pName, 120), WsbHrAsString(hr), NULL);
|
||
|
||
//
|
||
// If for some reason we did not tell the filter to complete the IO we
|
||
// try it here (with a error indication) so we have done everything possible to keep the
|
||
// application from hanging.
|
||
//
|
||
if (didSend == FALSE) {
|
||
WsbAffirmHr(pRecallPriv->GetDriversRecallId(&aTmp.msg.pRep.filterId));
|
||
aTmp.inout.command = RP_RECALL_COMPLETE;
|
||
aTmp.inout.status = STATUS_FILE_IS_OFFLINE;
|
||
aTmp.msg.rRep.actionFlags = 0;
|
||
|
||
DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &aTmp, sizeof(RP_MSG),
|
||
&aTmp, sizeof(RP_MSG), &ioSize, NULL);
|
||
//
|
||
// Remove it from our collection
|
||
//
|
||
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->RemoveAndRelease(pRecallPriv);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
}
|
||
|
||
|
||
);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SetAdminExemption(
|
||
IN BOOL isExempt
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::SetAdminExemption().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
m_exemptAdmin = isExempt; // TRUE == Admin is exempt from runaway recall check.
|
||
|
||
m_isDirty = TRUE;
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SetIdentifier(
|
||
IN GUID id
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilterPriv::SetIdentifier().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
m_id = id;
|
||
|
||
m_isDirty = TRUE;
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SetIsEnabled(
|
||
IN BOOL isEnabled
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::SetIsEnabled().
|
||
|
||
--*/
|
||
{
|
||
m_isEnabled = isEnabled;
|
||
|
||
return(S_OK);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SetMaxRecallBuffers(
|
||
IN ULONG maxBuffers
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::SetMaxRecallBuffers().
|
||
|
||
--*/
|
||
{
|
||
m_maxRecallBuffers = maxBuffers;
|
||
m_isDirty = TRUE;
|
||
|
||
return(S_OK);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SetMaxRecalls(
|
||
IN ULONG maxRecalls
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::SetMaxRecalls().
|
||
|
||
--*/
|
||
{
|
||
m_maxRecalls = maxRecalls;
|
||
m_isDirty = TRUE;
|
||
|
||
return(S_OK);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::SetMinRecallInterval(
|
||
IN ULONG minInterval
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::SetMinRecallInterval().
|
||
|
||
--*/
|
||
{
|
||
m_minRecallInterval = minInterval;
|
||
m_isDirty = TRUE;
|
||
|
||
return(S_OK);
|
||
}
|
||
|
||
HRESULT
|
||
CFsaFilter::StopIoctlThread(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
IFsaFilterPriv::StopIoctlThread()
|
||
|
||
This stops the IOCTL thread and cancels outstanding IO to the
|
||
kernel mode File System Filter.
|
||
|
||
--*/
|
||
|
||
{
|
||
HRESULT hr = S_OK;
|
||
RP_MSG tmp;
|
||
DWORD outSize;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::StopIoctlThread"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
//
|
||
// Signal the event to indicate to PipeThread that we are terminating
|
||
// It is important to do this first before setting the state to IDLE,
|
||
// to avoid deadlocks/race conditions
|
||
//
|
||
WsbAffirmStatus(SetEvent(m_terminateEvent));
|
||
//
|
||
// Set the filter state to idle
|
||
//
|
||
EnterCriticalSection(&m_stateLock);
|
||
m_state = HSM_JOB_STATE_IDLE;
|
||
|
||
//
|
||
// Cancel the IOCTLS in the kernel filter.
|
||
//
|
||
tmp.inout.command = RP_CANCEL_ALL_DEVICEIO;
|
||
BOOL bTmpStatus = DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL);
|
||
|
||
LeaveCriticalSection(&m_stateLock);
|
||
WsbAffirmStatus(bTmpStatus);
|
||
|
||
//
|
||
// Cancel any pending recalls
|
||
//
|
||
tmp.inout.command = RP_CANCEL_ALL_RECALLS;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL));
|
||
|
||
|
||
//
|
||
// Now tell the filter we are going away and it should fail all recall activity .
|
||
//
|
||
|
||
tmp.inout.command = RP_SUSPEND_NEW_RECALLS;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL));
|
||
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::StopIoctlThread"), OLESTR("Hr = %ls"), WsbHrAsString(hr));
|
||
|
||
return(0);
|
||
}
|
||
|
||
|
||
|
||
|
||
DWORD FsaIoctlThread(
|
||
void* pVoid
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr;
|
||
|
||
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||
hr = ((CFsaFilter*) pVoid)->IoctlThread();
|
||
CoUninitialize();
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
DWORD FsaPipeThread(
|
||
void* pVoid
|
||
)
|
||
|
||
/*++
|
||
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr;
|
||
|
||
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||
hr = ((CFsaFilter*) pVoid)->PipeThread();
|
||
CoUninitialize();
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Start(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IFsaFilter::Start().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
HSM_JOB_STATE oldState;
|
||
DWORD tid;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::Start"), OLESTR(""));
|
||
|
||
try {
|
||
//
|
||
// Create the event that is used to signal termination
|
||
//
|
||
WsbAffirmHandle((m_terminateEvent = CreateEvent(NULL,
|
||
TRUE,
|
||
FALSE,
|
||
NULL)));
|
||
|
||
WsbAffirm((HSM_JOB_STATE_IDLE == m_state) || (HSM_JOB_STATE_CANCELLED == m_state), E_UNEXPECTED);
|
||
|
||
oldState = m_state;
|
||
m_state = HSM_JOB_STATE_STARTING;
|
||
|
||
try {
|
||
|
||
// TBD - Do whatever it takes to start.
|
||
//
|
||
// This consists of starting a thread that will issue the IOCTL
|
||
// requests to the kernel mode filter. These IOCTL requests
|
||
// will complete when the kernel mode filter detects that a
|
||
// recall is needed.
|
||
//
|
||
|
||
WsbAffirm((m_pipeThread = CreateThread(0, 0, FsaPipeThread, (void*) this, 0, &tid)) != 0, HRESULT_FROM_WIN32(GetLastError()));
|
||
|
||
if (m_pipeThread == NULL) {
|
||
m_state = oldState;
|
||
WsbAssertHr(E_FAIL); // TBD What error to return here??
|
||
}
|
||
|
||
WsbAffirm((m_ioctlThread = CreateThread(0, 0, FsaIoctlThread, (void*) this, 0, &tid)) != 0, HRESULT_FROM_WIN32(GetLastError()));
|
||
|
||
if (m_ioctlThread == NULL) {
|
||
m_state = oldState;
|
||
WsbAssertHr(E_FAIL); // TBD What error to return here??
|
||
}
|
||
|
||
|
||
} WsbCatchAndDo(hr, m_state = oldState;);
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::Start"), OLESTR("hr = <%ls>"), WsbHrAsString(hr));
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::Test(
|
||
USHORT* passed,
|
||
USHORT* failed
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
IWsbTestable::Test().
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
try {
|
||
|
||
WsbAssert(0 != passed, E_POINTER);
|
||
WsbAssert(0 != failed, E_POINTER);
|
||
|
||
*passed = 0;
|
||
*failed = 0;
|
||
|
||
} WsbCatch(hr);
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CFsaFilter::TranslateHresultToNtStatus(
|
||
HRESULT hr
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
CFsaFilter::TranslateHresultToNtStatus().
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS ntStatus = 0;
|
||
DWORD w32Error;
|
||
|
||
switch (hr) {
|
||
case RMS_E_CARTRIDGE_BUSY:
|
||
ntStatus = STATUS_FILE_IS_OFFLINE;
|
||
break;
|
||
|
||
case RMS_E_DRIVE_BUSY:
|
||
case RMS_E_TIMEOUT:
|
||
case RMS_E_CARTRIDGE_UNAVAILABLE:
|
||
ntStatus = STATUS_FILE_IS_OFFLINE;
|
||
break;
|
||
|
||
case RMS_E_LIBRARY_UNAVAILABLE:
|
||
case RMS_E_NTMS_NOT_CONNECTED:
|
||
ntStatus = STATUS_FILE_IS_OFFLINE;
|
||
break;
|
||
|
||
case ERROR_IO_DEVICE:
|
||
ntStatus = STATUS_REMOTE_STORAGE_MEDIA_ERROR;
|
||
break;
|
||
|
||
case STATUS_END_OF_FILE:
|
||
ntStatus = hr;
|
||
break;
|
||
|
||
case E_FAIL:
|
||
ntStatus = STATUS_FILE_IS_OFFLINE;
|
||
break;
|
||
|
||
default:
|
||
if (hr & FACILITY_NT_BIT) {
|
||
//
|
||
// NT status code - return it unchanged (except for removing the facility bit)
|
||
//
|
||
ntStatus = hr & ~(FACILITY_NT_BIT);
|
||
} else if (hr & FACILITY_WIN32) {
|
||
w32Error = hr & 0x0000ffff;
|
||
//
|
||
// Now convert the win32 error to a NT status code.
|
||
//
|
||
// Since there does not seem to be any easy macro to do this we convert the most common ones
|
||
// and punt on the rest.
|
||
//
|
||
switch (w32Error) {
|
||
case ERROR_NOT_ENOUGH_MEMORY:
|
||
case ERROR_OUTOFMEMORY:
|
||
ntStatus = STATUS_NO_MEMORY;
|
||
break;
|
||
|
||
case ERROR_HANDLE_DISK_FULL:
|
||
case ERROR_DISK_FULL:
|
||
ntStatus = STATUS_DISK_FULL;
|
||
break;
|
||
|
||
default:
|
||
ntStatus = STATUS_FILE_IS_OFFLINE;
|
||
break;
|
||
}
|
||
} else {
|
||
ntStatus = STATUS_FILE_IS_OFFLINE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return(ntStatus);
|
||
}
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DoRecallAction(
|
||
PFSA_IOCTL_CONTROL pIoCmd
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoRecallAction()
|
||
|
||
Find the already created recall object and start the data transfer.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
CComPtr<IFsaFilterRecall> pRecall;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
CComPtr<IFsaResource> pRecallResource;
|
||
HRESULT hr = S_OK;
|
||
RP_MSG tmp;
|
||
DWORD outSize;
|
||
BOOL gotToInit = FALSE;
|
||
|
||
//
|
||
//
|
||
try {
|
||
//
|
||
// Get the Filter ID and find the recall object.
|
||
//
|
||
pRecall = NULL;
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterRecallNTFS,
|
||
NULL, CLSCTX_SERVER, IID_IFsaFilterRecallPriv,
|
||
(void**) &pRecallPriv));
|
||
|
||
WsbAffirmHr(pRecallPriv->SetDriversRecallId(pIoCmd->out.msg.rReq.filterId));
|
||
WsbAffirmHr(pRecallPriv->SetThreadId(pIoCmd->out.msg.rReq.threadId));
|
||
WsbAffirmHr(pRecallPriv->CompareBy(FSA_RECALL_COMPARE_ID));
|
||
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->Find(pRecallPriv, IID_IFsaFilterRecall, (void**) &pRecall);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
|
||
WsbAffirmHr(hr);
|
||
|
||
WsbAffirmHr(pRecall->CheckRecallLimit(m_minRecallInterval, m_maxRecalls, (BOOLEAN)( m_exemptAdmin ? TRUE : FALSE ) ) );
|
||
|
||
pRecallPriv = 0;
|
||
|
||
WsbAffirmHr(pRecall->QueryInterface(IID_IFsaFilterRecallPriv, (void**) &pRecallPriv));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallAction: Recall object Found - Calling StartRecall.\n"));
|
||
|
||
// Set marker for event logging. If we have failed before this point
|
||
// we want to issue an event. Init logs it's own events
|
||
//
|
||
gotToInit = TRUE;
|
||
WsbAffirmHr(pRecallPriv->StartRecall(pIoCmd->out.msg.rReq.offset,
|
||
pIoCmd->out.msg.rReq.length));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallAction: Recall Started.\n"));
|
||
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// Something failed while setting up the recall.
|
||
// Free any resources required and
|
||
// tell the kernel mode filter that the recall failed.
|
||
//
|
||
tmp.inout.status = TranslateHresultToNtStatus(hr);
|
||
tmp.inout.command = RP_RECALL_COMPLETE;
|
||
tmp.msg.rRep.actionFlags = 0;
|
||
tmp.msg.rRep.filterId = pIoCmd->out.msg.rReq.filterId;
|
||
DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp, sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL);
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallAction: Exception during recall processing.\n"));
|
||
|
||
if (FALSE == gotToInit) {
|
||
CWsbStringPtr pName;
|
||
HRESULT hrPath = S_FALSE;
|
||
//
|
||
// If we didn't complete init and the error was not due to the runaway recall limit, then we need to log an event here.
|
||
// Otherwise, an event is already sent.
|
||
//
|
||
// Get path of file failing recall.
|
||
if (pRecall != 0) {
|
||
hrPath = pRecall->GetPath((OLECHAR**) &pName, 0);
|
||
}
|
||
|
||
if (SUCCEEDED(hrPath) && ((WCHAR *)pName != NULL)) {
|
||
WsbLogEvent(FSA_MESSAGE_RECALL_FAILED, 0, NULL, (OLECHAR*) WsbAbbreviatePath(pName, 120), WsbHrAsString(hr), NULL);
|
||
} else {
|
||
WsbLogEvent(FSA_MESSAGE_RECALL_FAILED, 0, NULL, OLESTR("Path is NULL"), WsbHrAsString(hr), NULL);
|
||
}
|
||
|
||
//
|
||
// We also have to free the recall object
|
||
//
|
||
if (pRecall != NULL) {
|
||
EnterCriticalSection(&m_recallLock);
|
||
m_pRecalls->RemoveAndRelease(pRecall);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
}
|
||
}
|
||
|
||
);
|
||
//
|
||
// Cleanup any old client structures
|
||
// TBD This should be async and not in the recall path
|
||
//
|
||
CleanupClients();
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DoOpenAction(
|
||
PFSA_IOCTL_CONTROL pIoCmd
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoOpenAction()
|
||
|
||
Create an entry for the user opening the file and start the reall notification identification process.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
CComPtr<IFsaFilterClient> pClient, pFoundClient;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
CComPtr<IFsaResource> pRecallResource;
|
||
CComPtr<IFsaScanItem> pScanItem;
|
||
HRESULT hr = S_OK;
|
||
PRP_MSG aTmp = NULL;
|
||
RP_MSG tmp;
|
||
DWORD ioLen, outSize;
|
||
FSA_PLACEHOLDER placeHolder;
|
||
OLECHAR *pPath = NULL;
|
||
DWORD nameLen;
|
||
DWORD dNameLen;
|
||
CWsbStringPtr uName;
|
||
CWsbStringPtr dName;
|
||
CWsbStringPtr idPath;
|
||
WCHAR userName[USER_NAME_LEN];
|
||
WCHAR domainName[USER_NAME_LEN];
|
||
SID_NAME_USE nUse;
|
||
BOOL gotToInit = FALSE;
|
||
LONGLONG fileId;
|
||
BOOL status;
|
||
DWORD lErr;
|
||
|
||
//
|
||
// Got a recall request from the filter. Create the
|
||
// necessary objects. The recall is not actually started until the first read or write.
|
||
//
|
||
// See if a client object already exists for the
|
||
// authentication ID given by the filter driver.
|
||
// If it was found then check the runaway recall limit
|
||
// and fail the recall if the limit has been reached.
|
||
// If the client object does not exist then create it
|
||
// and start the identification process (this is done by
|
||
// the client object).
|
||
// Get the resource interface for the volume the file is on.
|
||
// Create the recall object and initialize it.
|
||
//
|
||
try {
|
||
ULONG numEnt;
|
||
|
||
//
|
||
// Get the file path from the filter.
|
||
// We get it in a separate call because the path and
|
||
// security information could be very large.
|
||
// Allocate the amount of space we will need and make the
|
||
// IOCTL call to get it.
|
||
//
|
||
ioLen = sizeof(RP_MSG) + pIoCmd->out.msg.oReq.userInfoLen +
|
||
pIoCmd->out.msg.oReq.nameLen;
|
||
|
||
WsbAffirmPointer((aTmp = (RP_MSG *) malloc(ioLen)));
|
||
|
||
aTmp->inout.command = RP_GET_RECALL_INFO;
|
||
aTmp->msg.riReq.filterId = pIoCmd->out.msg.oReq.filterId;
|
||
status = DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, aTmp,
|
||
ioLen,
|
||
aTmp, ioLen, &outSize, NULL);
|
||
|
||
if (!status) {
|
||
lErr = GetLastError();
|
||
if (lErr == ERROR_FILE_NOT_FOUND) {
|
||
//
|
||
// This is OK - the file must have been closed
|
||
// We can just bail out.
|
||
WsbThrow(S_OK);
|
||
} else {
|
||
//
|
||
// Anything else must be an error
|
||
//
|
||
WsbThrow(HRESULT_FROM_WIN32(lErr));
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// The path is UNICODE and in the format of
|
||
// \path\file.ext.
|
||
// or if open by ID the the ID is in the message
|
||
//
|
||
fileId = aTmp->msg.riReq.fileId;
|
||
|
||
pPath = (OLECHAR *) ((CHAR *) &aTmp->msg.riReq.userToken +
|
||
pIoCmd->out.msg.oReq.userInfoLen);
|
||
|
||
//
|
||
// Get the resource interface via the serial number
|
||
//
|
||
|
||
WsbAffirmHr(m_pFsaServer->FindResourceBySerial(pIoCmd->out.msg.oReq.serial, &pRecallResource));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Found the resource.\n"));
|
||
|
||
//if (pIoCmd->out.msg.oReq.options & FILE_OPEN_BY_FILE_ID) {
|
||
// if ((pIoCmd->out.msg.oReq.objIdHi != 0) || (pIoCmd->out.msg.oReq.objIdLo != 0)) {
|
||
// WsbAffirmHr(pRecallResource->FindObjectId(pIoCmd->out.msg.oReq.objIdHi, pIoCmd->out.msg.oReq.objIdLo, NULL, &pScanItem));
|
||
// } else {
|
||
// WsbAffirmHr(pRecallResource->FindFileId(pIoCmd->out.msg.oReq.fileId, NULL, &pScanItem));
|
||
// }
|
||
// WsbAffirmHr(pScanItem->GetPathAndName(NULL, &idPath, 0));
|
||
// pPath = idPath;
|
||
//}
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Recall request for <%ls>\n"),
|
||
WsbAbbreviatePath(pPath, 120));
|
||
|
||
pFoundClient = NULL;
|
||
//
|
||
// Recall notification is not done for no-recall operations - skip the client stuf
|
||
//
|
||
if (!(pIoCmd->out.msg.oReq.options & FILE_OPEN_NO_RECALL)) {
|
||
//
|
||
// Set up the client interface.
|
||
// If one has not been created for this authentication ID then
|
||
// create one now.
|
||
//
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Client ID is %x:%x.\n"),
|
||
pIoCmd->out.msg.oReq.userAuthentication.HighPart,
|
||
pIoCmd->out.msg.oReq.userAuthentication.LowPart);
|
||
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterClientNTFS, NULL, CLSCTX_SERVER, IID_IFsaFilterClient, (void**) &pClient));
|
||
WsbAffirmHr(pClient->SetAuthenticationId(pIoCmd->out.msg.oReq.userAuthentication.HighPart, pIoCmd->out.msg.oReq.userAuthentication.LowPart));
|
||
WsbAffirmHr(pClient->SetTokenSource(pIoCmd->out.msg.oReq.tokenSource));
|
||
WsbAffirmHr(pClient->CompareBy(FSA_FILTERCLIENT_COMPARE_ID));
|
||
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->Find(pClient, IID_IFsaFilterClient, (void**) &pFoundClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
|
||
if (hr == WSB_E_NOTFOUND) {
|
||
//
|
||
// Did not find an existing client structure -
|
||
// use the one we created for the find to initialize a new
|
||
// one. Add it to the collection in the filter object.
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Create new client object.\n"));
|
||
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->Add(pClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
WsbAffirmHr(hr);
|
||
pFoundClient = pClient;
|
||
|
||
//
|
||
// Get the username from the SID passed from the
|
||
// kernel mode filter.
|
||
//
|
||
try {
|
||
nameLen = USER_NAME_LEN;
|
||
dNameLen = USER_NAME_LEN;
|
||
WsbAffirmStatus((LookupAccountSidW(NULL,
|
||
(PSID) &aTmp->msg.riReq.userToken,
|
||
userName, &nameLen,
|
||
domainName, &dNameLen, &nUse)));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: User = %ls/%ls.\n"),
|
||
domainName, userName);
|
||
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// We do not consider it a fatal error if we cannot
|
||
// get the user name and domain.
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Failed to get the user name - %x.\n"),
|
||
GetLastError());
|
||
wcscpy(userName, L"");
|
||
wcscpy(domainName, L"");
|
||
);
|
||
|
||
WsbAffirmHr(pFoundClient->SetUserName(userName));
|
||
WsbAffirmHr(pFoundClient->SetDomainName(domainName));
|
||
} else {
|
||
//
|
||
// The find did not return WSB_E_NOTFOUND. Make sure it was not
|
||
// some other error.
|
||
//
|
||
WsbAffirmHr(hr);
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Found the client object.\n"));
|
||
WsbAffirmHr(pFoundClient->GetUserName((OLECHAR **) &uName, 0));
|
||
WsbAffirmHr(pFoundClient->GetDomainName((OLECHAR **) &dName, 0));
|
||
wcsncpy(userName, (WCHAR *) uName, USER_NAME_LEN);
|
||
wcsncpy(domainName, (WCHAR *) dName, USER_NAME_LEN);
|
||
}
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: User = %ls/%ls.\n"),
|
||
domainName, userName);
|
||
|
||
//
|
||
// Start the identification process if needed
|
||
//
|
||
// TBD This might be better done in an async fashion.
|
||
//
|
||
WsbAffirmHr(pFoundClient->StartIdentify());
|
||
WsbAffirmHr(pFoundClient->SetIsAdmin((BOOLEAN) pIoCmd->out.msg.oReq.isAdmin));
|
||
}
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Building recall request.\n"));
|
||
//
|
||
// Fill in the placeholder data the filter object will need
|
||
//
|
||
WsbAffirmHr(CopyRPDataToPlaceholder(&(pIoCmd->out.msg.oReq.eaData), &placeHolder));
|
||
|
||
//
|
||
// Now start the recall
|
||
//
|
||
//
|
||
// Create the recall interface and initialize it.
|
||
//
|
||
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterRecallNTFS,
|
||
NULL, CLSCTX_SERVER, IID_IFsaFilterRecallPriv,
|
||
(void**) &pRecallPriv));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Recall object created.\n"));
|
||
|
||
//
|
||
// Add it to the collection
|
||
//
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->Add(pRecallPriv);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
WsbAffirmHr(hr);
|
||
|
||
hr = m_pRecalls->GetEntries(&numEnt);
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Recall queue has %u entries. Calling Init.\n"),
|
||
numEnt);
|
||
|
||
//
|
||
// Set marker for event logging. If we have failed before this point
|
||
// we want to issue an event. Init logs it's own events
|
||
//
|
||
gotToInit = TRUE;
|
||
WsbAffirmHr(pRecallPriv->Init(pFoundClient,
|
||
pIoCmd->out.msg.oReq.filterId,
|
||
pRecallResource, pPath, fileId,
|
||
pIoCmd->out.msg.oReq.offset.QuadPart,
|
||
pIoCmd->out.msg.oReq.size.QuadPart,
|
||
pIoCmd->out.msg.oReq.options,
|
||
&placeHolder,
|
||
(IFsaFilterPriv*) this));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Init complete.\n"));
|
||
|
||
free(aTmp);
|
||
aTmp = NULL;
|
||
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// Something failed while setting up the recall.
|
||
// Free any resources required and
|
||
// tell the kernel mode filter that the recall failed.
|
||
//
|
||
if (hr != S_OK) {
|
||
tmp.inout.status = TranslateHresultToNtStatus(hr);
|
||
tmp.inout.command = RP_RECALL_COMPLETE;
|
||
tmp.msg.rRep.actionFlags = 0;
|
||
tmp.msg.rRep.filterId = pIoCmd->out.msg.oReq.filterId;
|
||
DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp, sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL);
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoOpenAction: Exception during recall processing.\n"));
|
||
}
|
||
|
||
// NOTE: IF RUNAWAY RECALL BEHAVIOR CHANGES TO TRUNCATE ON CLOSE, CHANGE
|
||
// FSA_MESSAGE_HIT_RECALL_LIMIT_ACCESSDENIED TO FSA_MESSAGE_HIT_RECALL_LIMIT_TRUNCATEONCLOSE.
|
||
|
||
if ((hr != S_OK) && (FALSE == gotToInit)) {
|
||
// If we didn't complete init, then we need to log an event here.
|
||
// Otherwise, an event is already sent.
|
||
if (hr == FSA_E_HIT_RECALL_LIMIT) {
|
||
WsbLogEvent(FSA_MESSAGE_HIT_RECALL_LIMIT_ACCESSDENIED, 0, NULL, userName, NULL);
|
||
} else {
|
||
WsbLogEvent(FSA_MESSAGE_RECALL_FAILED, 0, NULL, (OLECHAR*) WsbAbbreviatePath(pPath, 120), WsbHrAsString(hr), NULL);
|
||
}
|
||
}
|
||
|
||
if (NULL != aTmp)
|
||
free(aTmp);
|
||
aTmp = NULL;
|
||
);
|
||
//
|
||
// Cleanup any old client structures
|
||
// TBD This should be async and not in the recall path
|
||
//
|
||
CleanupClients();
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DoRecallWaitingAction(
|
||
PFSA_IOCTL_CONTROL pIoCmd
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoRecallWaitingAction()
|
||
|
||
Start the reall notification identification process: this is just another client
|
||
waiting on an already issued recall
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
CComPtr<IFsaFilterClient> pClient, pFoundClient;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
CComPtr<IFsaFilterRecall> pRecall;
|
||
CComPtr<IFsaResource> pRecallResource;
|
||
CComPtr<IFsaScanItem> pScanItem;
|
||
HRESULT hr = S_OK;
|
||
PRP_MSG aTmp = NULL;
|
||
DWORD ioLen, outSize;
|
||
OLECHAR *pPath = NULL;
|
||
DWORD nameLen;
|
||
DWORD dNameLen;
|
||
CWsbStringPtr uName;
|
||
CWsbStringPtr dName;
|
||
CWsbStringPtr idPath;
|
||
WCHAR userName[USER_NAME_LEN];
|
||
WCHAR domainName[USER_NAME_LEN];
|
||
SID_NAME_USE nUse;
|
||
LONGLONG fileId;
|
||
BOOL status;
|
||
DWORD lErr;
|
||
|
||
//
|
||
// Got a recall request from the filter. Create the
|
||
// necessary objects. The recall is not actually started until the first read or write.
|
||
//
|
||
// See if a client object already exists for the
|
||
// authentication ID given by the filter driver.
|
||
// If it was found then check the runaway recall limit
|
||
// and fail the recall if the limit has been reached.
|
||
// If the client object does not exist then create it
|
||
// and start the identification process (this is done by
|
||
// the client object).
|
||
// Get the resource interface for the volume the file is on.
|
||
// Create the recall object and initialize it.
|
||
//
|
||
try {
|
||
|
||
//
|
||
// Get the file path from the filter.
|
||
// We get it in a separate call because the path and
|
||
// security information could be very large.
|
||
// Allocate the amount of space we will need and make the
|
||
// IOCTL call to get it.
|
||
//
|
||
ioLen = sizeof(RP_MSG) + pIoCmd->out.msg.oReq.userInfoLen +
|
||
pIoCmd->out.msg.oReq.nameLen;
|
||
|
||
WsbAffirmPointer((aTmp = (RP_MSG *) malloc(ioLen)));
|
||
|
||
aTmp->inout.command = RP_GET_RECALL_INFO;
|
||
aTmp->msg.riReq.filterId = pIoCmd->out.msg.oReq.filterId;
|
||
status = DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, aTmp,
|
||
ioLen,
|
||
aTmp, ioLen, &outSize, NULL);
|
||
|
||
if (!status) {
|
||
lErr = GetLastError();
|
||
if (lErr == ERROR_FILE_NOT_FOUND) {
|
||
//
|
||
// This is OK - the file must have been closed
|
||
// We can just bail out.
|
||
WsbThrow(S_OK);
|
||
} else {
|
||
//
|
||
// Anything else must be an error
|
||
//
|
||
WsbThrow(HRESULT_FROM_WIN32(lErr));
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// The path is UNICODE and in the format of
|
||
// \path\file.ext.
|
||
// or if open by ID the the ID is in the message
|
||
//
|
||
fileId = aTmp->msg.riReq.fileId;
|
||
|
||
pPath = (OLECHAR *) ((CHAR *) &aTmp->msg.riReq.userToken +
|
||
pIoCmd->out.msg.oReq.userInfoLen);
|
||
|
||
//
|
||
// Get the resource interface via the serial number
|
||
//
|
||
|
||
WsbAffirmHr(m_pFsaServer->FindResourceBySerial(pIoCmd->out.msg.oReq.serial, &pRecallResource));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: Found the resource.\n"));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: Recall request for <%ls>\n"),
|
||
WsbAbbreviatePath(pPath, 120));
|
||
|
||
pFoundClient = NULL;
|
||
//
|
||
// Recall notification is not done for no-recall operations - skip the client stuf
|
||
//
|
||
if (!(pIoCmd->out.msg.oReq.options & FILE_OPEN_NO_RECALL)) {
|
||
//
|
||
// Set up the client interface.
|
||
// If one has not been created for this authentication ID then
|
||
// create one now.
|
||
//
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: Client ID is %x:%x.\n"),
|
||
pIoCmd->out.msg.oReq.userAuthentication.HighPart,
|
||
pIoCmd->out.msg.oReq.userAuthentication.LowPart);
|
||
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterClientNTFS, NULL, CLSCTX_SERVER, IID_IFsaFilterClient, (void**) &pClient));
|
||
WsbAffirmHr(pClient->SetAuthenticationId(pIoCmd->out.msg.oReq.userAuthentication.HighPart, pIoCmd->out.msg.oReq.userAuthentication.LowPart));
|
||
WsbAffirmHr(pClient->SetTokenSource(pIoCmd->out.msg.oReq.tokenSource));
|
||
WsbAffirmHr(pClient->CompareBy(FSA_FILTERCLIENT_COMPARE_ID));
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->Find(pClient, IID_IFsaFilterClient, (void**) &pFoundClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
if (hr == WSB_E_NOTFOUND) {
|
||
//
|
||
// Did not find an existing client structure -
|
||
// use the one we created for the find to initialize a new
|
||
// one. Add it to the collection in the filter object.
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: Create new client object.\n"));
|
||
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->Add(pClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
WsbAffirmHr(hr);
|
||
pFoundClient = pClient;
|
||
|
||
//
|
||
// Get the username from the SID passed from the
|
||
// kernel mode filter.
|
||
//
|
||
try {
|
||
nameLen = USER_NAME_LEN;
|
||
dNameLen = USER_NAME_LEN;
|
||
WsbAffirmStatus((LookupAccountSidW(NULL,
|
||
(PSID) &aTmp->msg.riReq.userToken,
|
||
userName, &nameLen,
|
||
domainName, &dNameLen, &nUse)));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: User = %ls/%ls.\n"),
|
||
domainName, userName);
|
||
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// We do not consider it a fatal error if we cannot
|
||
// get the user name and domain.
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: Failed to get the user name - %x.\n"),
|
||
GetLastError());
|
||
wcscpy(userName, L"");
|
||
wcscpy(domainName, L"");
|
||
);
|
||
|
||
WsbAffirmHr(pFoundClient->SetUserName(userName));
|
||
WsbAffirmHr(pFoundClient->SetDomainName(domainName));
|
||
} else {
|
||
//
|
||
// The find did not return WSB_E_NOTFOUND. Make sure it was not
|
||
// some other error.
|
||
//
|
||
WsbAffirmHr(hr);
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: Found the client object.\n"));
|
||
WsbAffirmHr(pFoundClient->GetUserName((OLECHAR **) &uName, 0));
|
||
WsbAffirmHr(pFoundClient->GetDomainName((OLECHAR **) &dName, 0));
|
||
wcsncpy(userName, (WCHAR *) uName, USER_NAME_LEN);
|
||
wcsncpy(domainName, (WCHAR *) dName, USER_NAME_LEN);
|
||
}
|
||
WsbTrace(OLESTR("CFsaFilter::DoRecallWaitingAction: User = %ls/%ls.\n"),
|
||
domainName, userName);
|
||
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterRecallNTFS,
|
||
NULL, CLSCTX_SERVER, IID_IFsaFilterRecallPriv,
|
||
(void**) &pRecallPriv));
|
||
WsbAffirmHr(pRecallPriv->SetDriversRecallId((pIoCmd->out.msg.oReq.filterId & 0xFFFFFFFF)));
|
||
WsbAffirmHr(pRecallPriv->CompareBy(FSA_RECALL_COMPARE_CONTEXT_ID));
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->Find(pRecallPriv, IID_IFsaFilterRecall, (void**) &pRecall);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
|
||
WsbAffirmHr(hr);
|
||
|
||
//
|
||
// Start the identification process if needed
|
||
//
|
||
// TBD This might be better done in an async fashion.
|
||
//
|
||
WsbAffirmHr(pFoundClient->StartIdentify());
|
||
WsbAffirmHr(pFoundClient->SetIsAdmin((BOOLEAN) pIoCmd->out.msg.oReq.isAdmin));
|
||
|
||
// Note: code for the notify itself is moved to AddClient method
|
||
hr = pRecall->AddClient(pFoundClient);
|
||
if (hr != S_OK) {
|
||
WsbTrace(OLESTR("CFsaFilterRecall::DoRecallWaitingAction: AddClient returned %ls.\n"),
|
||
WsbHrAsString(hr));
|
||
|
||
}
|
||
hr = S_OK;
|
||
}
|
||
free(aTmp);
|
||
aTmp = NULL;
|
||
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// Something failed while setting up the recall.
|
||
// Free any resources required and
|
||
// tell the kernel mode filter that the recall failed.
|
||
//
|
||
if (NULL != aTmp)
|
||
free(aTmp);
|
||
aTmp = NULL;
|
||
);
|
||
//
|
||
// Cleanup any old client structures
|
||
// TBD This should be async and not in the recall path
|
||
//
|
||
CleanupClients();
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DoNoRecallAction(
|
||
PFSA_IOCTL_CONTROL pIoCmd
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoNoRecallAction()
|
||
|
||
This is used for read without recall - the data is copied to memory and not
|
||
written to the file.
|
||
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
CComPtr<IFsaFilterClient> pClient, pFoundClient;
|
||
//CComPtr<IFsaFilterRecallPriv> pRecall;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
CComPtr<IFsaResource> pRecallResource;
|
||
CComPtr<IFsaScanItem> pScanItem;
|
||
HRESULT hr = S_OK;
|
||
PRP_MSG aTmp = NULL;
|
||
RP_MSG tmp;
|
||
DWORD ioLen, outSize;
|
||
FSA_PLACEHOLDER placeHolder;
|
||
OLECHAR *pPath;
|
||
CWsbStringPtr idPath;
|
||
CWsbStringPtr uName;
|
||
CWsbStringPtr dName;
|
||
WCHAR userName[USER_NAME_LEN];
|
||
WCHAR domainName[USER_NAME_LEN];
|
||
|
||
|
||
try {
|
||
|
||
//
|
||
// Get the file path from the filter.
|
||
// We get it in a separate call because the path and
|
||
// security information could be very large.
|
||
// Allocate the amount of space we will need and make the
|
||
// IOCTL call to get it.
|
||
//
|
||
ioLen = sizeof(RP_MSG) + pIoCmd->out.msg.oReq.userInfoLen +
|
||
pIoCmd->out.msg.oReq.nameLen;
|
||
|
||
WsbAffirmPointer((aTmp = (RP_MSG *) malloc(ioLen)));
|
||
|
||
aTmp->inout.command = RP_GET_RECALL_INFO;
|
||
aTmp->msg.riReq.filterId = pIoCmd->out.msg.oReq.filterId;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, aTmp,
|
||
ioLen,
|
||
aTmp, ioLen, &outSize, NULL));
|
||
|
||
|
||
//
|
||
// The path is UNICODE and in the format of
|
||
// \path\file.ext.
|
||
//
|
||
//
|
||
pPath = (OLECHAR *) ((CHAR *) &aTmp->msg.riReq.userToken +
|
||
pIoCmd->out.msg.oReq.userInfoLen);
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Recall (on read) request for %.80ls.\n"),
|
||
pPath);
|
||
|
||
//
|
||
// Get the resource interface via the serial number
|
||
//
|
||
|
||
WsbAffirmHr(m_pFsaServer->FindResourceBySerial(pIoCmd->out.msg.oReq.serial, &pRecallResource));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Found the resource.\n"));
|
||
|
||
if (pIoCmd->out.msg.oReq.options & FILE_OPEN_BY_FILE_ID) {
|
||
if ((pIoCmd->out.msg.oReq.objIdHi != 0) || (pIoCmd->out.msg.oReq.objIdLo != 0)) {
|
||
WsbAffirmHr(pRecallResource->FindObjectId(pIoCmd->out.msg.oReq.objIdHi, pIoCmd->out.msg.oReq.objIdLo, NULL, &pScanItem));
|
||
} else {
|
||
WsbAffirmHr(pRecallResource->FindFileId(pIoCmd->out.msg.oReq.fileId, NULL, &pScanItem));
|
||
}
|
||
WsbAffirmHr(pScanItem->GetPathAndName(NULL, &idPath, 0));
|
||
pPath = idPath;
|
||
}
|
||
|
||
//
|
||
// Set up the client interface.
|
||
// If one has not been created for this authentication ID then
|
||
// they are out of luck
|
||
//
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Client ID is %x:%x.\n"),
|
||
pIoCmd->out.msg.oReq.userAuthentication.HighPart,
|
||
pIoCmd->out.msg.oReq.userAuthentication.LowPart);
|
||
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterClientNTFS, NULL, CLSCTX_SERVER, IID_IFsaFilterClient, (void**) &pClient));
|
||
WsbAffirmHr(pClient->SetAuthenticationId(pIoCmd->out.msg.oReq.userAuthentication.HighPart, pIoCmd->out.msg.oReq.userAuthentication.LowPart));
|
||
WsbAffirmHr(pClient->SetTokenSource(pIoCmd->out.msg.oReq.tokenSource));
|
||
WsbAffirmHr(pClient->CompareBy(FSA_FILTERCLIENT_COMPARE_ID));
|
||
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->Find(pClient, IID_IFsaFilterClient, (void**) &pFoundClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
|
||
if (hr != WSB_E_NOTFOUND) {
|
||
//
|
||
// The find did not return WSB_E_NOTFOUND. Make sure it was not
|
||
// some other error.
|
||
//
|
||
WsbAffirmHr(hr);
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Found the client object.\n"));
|
||
WsbAffirmHr(pFoundClient->GetUserName((OLECHAR **) &uName, 0));
|
||
WsbAffirmHr(pFoundClient->GetDomainName((OLECHAR **) &dName, 0));
|
||
wcsncpy(userName, (WCHAR *) uName, USER_NAME_LEN);
|
||
wcsncpy(domainName, (WCHAR *) dName, USER_NAME_LEN);
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: User = %ls/%ls.\n"),
|
||
domainName, userName);
|
||
//
|
||
// Start the identification process if needed
|
||
//
|
||
// TBD This might be better done in an async fashion.
|
||
//
|
||
WsbAffirmHr(pFoundClient->StartIdentify());
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: User = UNKNOWN.\n"));
|
||
pFoundClient = 0;
|
||
}
|
||
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Building recall request.\n"));
|
||
//
|
||
// Fill in the placeholder data the filter object will need
|
||
//
|
||
if (RP_FILE_IS_TRUNCATED(pIoCmd->out.msg.oReq.eaData.data.bitFlags))
|
||
placeHolder.isTruncated = TRUE;
|
||
else
|
||
placeHolder.isTruncated = FALSE;
|
||
|
||
|
||
placeHolder.migrationTime.dwLowDateTime = pIoCmd->out.msg.oReq.eaData.data.migrationTime.LowPart;
|
||
placeHolder.migrationTime.dwHighDateTime = pIoCmd->out.msg.oReq.eaData.data.migrationTime.HighPart;
|
||
placeHolder.hsmId = pIoCmd->out.msg.oReq.eaData.data.hsmId;
|
||
placeHolder.bagId = pIoCmd->out.msg.oReq.eaData.data.bagId;
|
||
placeHolder.fileStart = pIoCmd->out.msg.oReq.eaData.data.fileStart.QuadPart;
|
||
placeHolder.fileSize = pIoCmd->out.msg.oReq.eaData.data.fileSize.QuadPart;
|
||
placeHolder.dataStart = pIoCmd->out.msg.oReq.eaData.data.dataStart.QuadPart;
|
||
placeHolder.dataSize = pIoCmd->out.msg.oReq.eaData.data.dataSize.QuadPart;
|
||
placeHolder.fileVersionId = pIoCmd->out.msg.oReq.eaData.data.fileVersionId.QuadPart;
|
||
placeHolder.verificationData = pIoCmd->out.msg.oReq.eaData.data.verificationData.QuadPart;
|
||
placeHolder.verificationType = pIoCmd->out.msg.oReq.eaData.data.verificationType;
|
||
placeHolder.recallCount = pIoCmd->out.msg.oReq.eaData.data.recallCount;
|
||
placeHolder.recallTime.dwLowDateTime = pIoCmd->out.msg.oReq.eaData.data.recallTime.LowPart;
|
||
placeHolder.recallTime.dwHighDateTime = pIoCmd->out.msg.oReq.eaData.data.recallTime.HighPart;
|
||
placeHolder.dataStreamStart = pIoCmd->out.msg.oReq.eaData.data.dataStreamStart.QuadPart;
|
||
placeHolder.dataStreamSize = pIoCmd->out.msg.oReq.eaData.data.dataStreamSize.QuadPart;
|
||
placeHolder.dataStream = pIoCmd->out.msg.oReq.eaData.data.dataStream;
|
||
|
||
//
|
||
// Now start the recall
|
||
//
|
||
//
|
||
// Create the recall interface and initialize it.
|
||
//
|
||
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterRecallNTFS,
|
||
NULL, CLSCTX_SERVER, IID_IFsaFilterRecallPriv,
|
||
(void**) &pRecallPriv));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Recall object created.\n"));
|
||
|
||
//
|
||
// Add it to the collection
|
||
//
|
||
|
||
//
|
||
// Just add & init
|
||
//
|
||
|
||
//WsbAffirmHr(pRecallPriv->QueryInterface(IID_IFsaFilterRecall, (void **)&pRecall));
|
||
EnterCriticalSection(&m_recallLock);
|
||
hr = m_pRecalls->Add(pRecallPriv);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
WsbAffirmHr(hr);
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Calling Init.\n"));
|
||
|
||
WsbAffirmHr(pRecallPriv->Init(pFoundClient,
|
||
pIoCmd->out.msg.oReq.filterId,
|
||
pRecallResource, pPath,
|
||
pIoCmd->out.msg.oReq.fileId,
|
||
pIoCmd->out.msg.oReq.offset.QuadPart,
|
||
pIoCmd->out.msg.oReq.size.QuadPart,
|
||
pIoCmd->out.msg.oReq.options,
|
||
&placeHolder,
|
||
(IFsaFilterPriv*) this));
|
||
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Init complete.\n"));
|
||
|
||
free(aTmp);
|
||
aTmp = NULL;
|
||
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// Something failed while setting up the recall.
|
||
// Free any resources required and
|
||
// tell the kernel mode filter that the recall failed.
|
||
//
|
||
tmp.inout.status = TranslateHresultToNtStatus(hr);
|
||
tmp.inout.command = RP_RECALL_COMPLETE;
|
||
tmp.msg.rRep.actionFlags = 0;
|
||
tmp.msg.rRep.filterId = pIoCmd->out.msg.oReq.filterId;
|
||
DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp, sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL);
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoNoRecallAction: Exception during recall processing.\n"));
|
||
|
||
if (NULL != aTmp)
|
||
free(aTmp);
|
||
aTmp = NULL;
|
||
);
|
||
|
||
|
||
return(hr);
|
||
}
|
||
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DoCloseAction(
|
||
PFSA_IOCTL_CONTROL pIoCmd
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoCloseAction()
|
||
|
||
Handles any action that must be done to log the fact that a premigrated files data
|
||
has changed.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
HRESULT hr = S_OK;
|
||
OLECHAR *pPath;
|
||
CComPtr<IFsaScanItem> pScanItem;
|
||
CComPtr<IFsaResource> pResource;
|
||
CWsbStringPtr idPath;
|
||
|
||
|
||
try {
|
||
//
|
||
WsbAffirmHr(m_pFsaServer->FindResourceBySerial(pIoCmd->out.msg.oReq.serial, &pResource));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoCloseAction: Found the resource.\n"));
|
||
|
||
//
|
||
// The path we give to the recall object does not include the
|
||
// device name.
|
||
//
|
||
WsbAffirmHr(pResource->FindFileId(pIoCmd->out.msg.oReq.fileId, NULL, &pScanItem));
|
||
WsbAffirmHr(pScanItem->GetPathAndName(NULL, &idPath, 0));
|
||
//
|
||
// We are done with this scan item
|
||
//
|
||
pScanItem = 0;
|
||
pPath = idPath;
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoCloseAction: Close action logging for %.80ls.\n"),
|
||
pPath);
|
||
|
||
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// Something failed while logging the close information.
|
||
// Free any resources required and
|
||
// tell the kernel mode filter that the close logging failed.
|
||
//
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoCloseAction: Exception during close processing.\n"));
|
||
);
|
||
|
||
return(hr);
|
||
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DoPreDeleteAction(
|
||
PFSA_IOCTL_CONTROL /*pIoCmd*/
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoPreDeleteAction()
|
||
|
||
Log the possible delete. Note that the file id is passed and not the name.
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoPreDeleteAction: Pre-Delete action.\n"));
|
||
|
||
return(hr);
|
||
|
||
}
|
||
|
||
HRESULT
|
||
CFsaFilter::DoPostDeleteAction(
|
||
PFSA_IOCTL_CONTROL /*pIoCmd*/
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoPostDeleteAction()
|
||
|
||
Log the completed delete.
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::DoPostDeleteAction: Post-Delete action.\n"));
|
||
|
||
|
||
return(hr);
|
||
|
||
}
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::DoCancelRecall(
|
||
ULONGLONG filterId
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
CFsaFilter::DoCancelRecall
|
||
|
||
Cancel the specified recall request.
|
||
|
||
--*/
|
||
{
|
||
HRESULT hr = S_OK;
|
||
CComPtr<IFsaFilterRecallPriv> pRecallPriv;
|
||
CComPtr<IFsaFilterRecall> pRecall;
|
||
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::DoCancelRecall"), OLESTR(""));
|
||
|
||
try {
|
||
ULONG numEnt;
|
||
|
||
|
||
if (S_OK == m_pRecalls->GetEntries(&numEnt)) {
|
||
WsbTrace(OLESTR("CFsaFilter::DoCancelRecall: Recall queue has %u entries before cancel\n"),
|
||
numEnt);
|
||
}
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterRecallNTFS, NULL, CLSCTX_SERVER, IID_IFsaFilterRecallPriv, (void**) &pRecallPriv));
|
||
WsbAffirmHr(pRecallPriv->SetDriversRecallId(filterId));
|
||
WsbAffirmHr(pRecallPriv->CompareBy(FSA_RECALL_COMPARE_ID));
|
||
|
||
|
||
// >>> ENTER CRITICAL SECTION
|
||
EnterCriticalSection(&m_recallLock);
|
||
try {
|
||
//
|
||
// Find the recall, and cancel.
|
||
//
|
||
WsbAffirmHr(m_pRecalls->Find(pRecallPriv, IID_IFsaFilterRecall, (void**) &pRecall));
|
||
pRecallPriv = NULL;
|
||
WsbAffirmHr(pRecall->QueryInterface(IID_IFsaFilterRecallPriv, (void**) &pRecallPriv));
|
||
WsbAffirmHr(pRecallPriv->CancelByDriver());
|
||
//
|
||
// Now remove the recall from our collection
|
||
//
|
||
WsbAffirmHr(m_pRecalls->RemoveAndRelease(pRecallPriv));
|
||
|
||
WsbAffirmHr(m_pRecalls->GetEntries(&numEnt));
|
||
WsbTrace(OLESTR("CFsaFilter::DoCancelRecall: Recall queue has %u entries after cancel\n"), numEnt);
|
||
|
||
} WsbCatch(hr);
|
||
LeaveCriticalSection(&m_recallLock);
|
||
WsbThrow(hr);
|
||
// <<< LEAVE CRITICAL SECTION
|
||
|
||
|
||
} WsbCatch(hr);
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::DoCancelRecall"), OLESTR("Hr = %ls"), WsbHrAsString(hr));
|
||
return(hr);
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::IoctlThread(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
IFsaFilterPriv::IoctlThread()
|
||
|
||
This is started as a thread and issues IOCTL requests to the
|
||
kernel mode File System Filter and waits for recall requests.
|
||
|
||
--*/
|
||
|
||
{
|
||
HRESULT hr = S_OK;
|
||
HANDLE port = NULL;
|
||
ULONG index;
|
||
RP_MSG tmp;
|
||
DWORD outSize;
|
||
OVERLAPPED *ovlp;
|
||
DWORD_PTR key;
|
||
DWORD lastError;
|
||
//PSID psidAdministrators;
|
||
ULONG numIoPending = 0;
|
||
OLECHAR ioctlDrive[128];
|
||
CWsbStringPtr pDrv;
|
||
BOOL code;
|
||
PFSA_IOCTL_CONTROL pIoCmd, pIo;
|
||
PFSA_IOCTL_CONTROL pIoList = NULL;
|
||
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
|
||
CComPtr<IFsaResource> pResource;
|
||
BOOL ioctlCancelled = FALSE;
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::IoctlThread"), OLESTR(""));
|
||
|
||
try {
|
||
|
||
//
|
||
// Set the ioctlDrive for the filter.
|
||
// The ioctlDrive needs to be any NTFS drive letter. By opening
|
||
// any NTFS drive we can issue IOCTL requests that the kernel mode filter
|
||
// will see. It does not matter if the drive chosen is managed or not.
|
||
// We just get the first resource interface in the list and get its drive
|
||
// letter to build the string.
|
||
//
|
||
swprintf(ioctlDrive, L"%s", RS_FILTER_SYM_LINK);
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: Drive = %ls\n"), ioctlDrive);
|
||
//
|
||
// Now issue several IOCTL requests to the filter driver to get
|
||
// recall requests. We must always keep at least one pending
|
||
// so the filter driver has somewhere to go when a migrated file is
|
||
// opened. We will issue the configured amount (m_maxRecallBuffers)
|
||
// and wait for completion of any of them via a completion port.
|
||
//
|
||
for (index = 0; index < m_maxRecallBuffers; index++) {
|
||
WCHAR nameString[MAX_PATH];
|
||
|
||
WsbAffirmPointer((pIoCmd = new FSA_IOCTL_CONTROL));
|
||
pIoCmd->next = pIoList;
|
||
pIoList = pIoCmd;
|
||
pIoCmd->overlap.hEvent = NULL;
|
||
pIoCmd->dHand = NULL;
|
||
|
||
//
|
||
// Create an event, a handle, and put it in the completion port.
|
||
//
|
||
swprintf(nameString, OLESTR("Ioctl Completion Port Event %d"), index);
|
||
WsbAffirmHandle(pIoCmd->overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, nameString));
|
||
WsbAffirmHandle(pIoCmd->dHand = CreateFile(ioctlDrive, GENERIC_READ | GENERIC_WRITE,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL));
|
||
|
||
WsbAffirmHandle(port = CreateIoCompletionPort(pIoCmd->dHand, port, (DWORD_PTR) pIoCmd, 1));
|
||
|
||
pIoCmd->in.inout.command = RP_GET_REQUEST;
|
||
pIoCmd->outSize = sizeof(RP_MSG);
|
||
//
|
||
// DeviceIoctlControl should always return ERROR_IO_PENDING
|
||
//
|
||
code = DeviceIoControl(pIoCmd->dHand, FSCTL_HSM_MSG, &pIoCmd->in,
|
||
sizeof(RP_MSG),
|
||
&pIoCmd->out, pIoCmd->outSize, &pIoCmd->outSize,
|
||
&pIoCmd->overlap);
|
||
|
||
lastError = GetLastError();
|
||
if ( (code == 0) && (lastError == ERROR_IO_PENDING)) {
|
||
// Life is good
|
||
numIoPending++;
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: DeviceIoControl returned %ls/%ls\n"),
|
||
WsbBoolAsString(code), WsbLongAsString(lastError));
|
||
}
|
||
}
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: %ls ioctls issued successfully.\n"), WsbLongAsString(numIoPending));
|
||
|
||
//
|
||
// Open the handle we will use for commands to the kernel filter
|
||
//
|
||
WsbAffirmHandle(m_ioctlHandle = CreateFile(ioctlDrive, GENERIC_READ | GENERIC_WRITE,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
||
FILE_ATTRIBUTE_NORMAL, NULL));
|
||
|
||
//
|
||
// Just in case we left the filter with outstanding recalls from a previous
|
||
// debug session or crash we tell it to cancel everything now.
|
||
//
|
||
|
||
tmp.inout.command = RP_CANCEL_ALL_RECALLS;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL))
|
||
|
||
|
||
//
|
||
// Now that we are ready to get recall requests we can tell the
|
||
// driver to start checking for recall activity.
|
||
//
|
||
|
||
tmp.inout.command = RP_ALLOW_NEW_RECALLS;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL))
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: Kernel level filter recalls enabled.\n"));
|
||
|
||
//
|
||
// We are now ready to rock and roll
|
||
//
|
||
m_state = HSM_JOB_STATE_ACTIVE;
|
||
|
||
//
|
||
// Now just sit around and wait for the IO requests to complete. When
|
||
// they do we are either quitting or a recall request was detected.
|
||
//
|
||
while (TRUE) {
|
||
//
|
||
// Wait for an IO request to complete.
|
||
//
|
||
if (! GetQueuedCompletionStatus(port, &outSize, &key, &ovlp, INFINITE)) {
|
||
DWORD dwErr = GetLastError();
|
||
if (ERROR_OPERATION_ABORTED == dwErr) {
|
||
numIoPending--;
|
||
ioctlCancelled = TRUE;
|
||
}
|
||
WsbAffirmHr(HRESULT_FROM_WIN32(dwErr));
|
||
} else {
|
||
numIoPending--;
|
||
}
|
||
|
||
pIoCmd = (FSA_IOCTL_CONTROL *) key;
|
||
WsbAffirm(NULL != pIoCmd, E_FAIL);
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: Filter event detected (%x:%x), Id = %I64x.\n"),
|
||
pIoCmd->out.inout.command,
|
||
pIoCmd->out.msg.oReq.action,
|
||
pIoCmd->out.msg.oReq.filterId);
|
||
switch (pIoCmd->out.inout.command) {
|
||
case RP_OPEN_FILE:
|
||
hr = DoOpenAction(pIoCmd);
|
||
break;
|
||
|
||
case RP_RECALL_WAITING:
|
||
hr = DoRecallWaitingAction(pIoCmd);
|
||
break;
|
||
|
||
//
|
||
// Must be a cancelled recall
|
||
//
|
||
case RP_CANCEL_RECALL:
|
||
DoCancelRecall(pIoCmd->out.msg.cReq.filterId);
|
||
break;
|
||
case RP_RUN_VALIDATE:
|
||
//
|
||
// A validate job is needed on a given volume (serial number is passed)
|
||
// No completion message is expected by the filter for this action.
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::Ioctlthread - Validate job for %x is needed\n"),
|
||
pIoCmd->out.msg.oReq.serial);
|
||
try {
|
||
SYSTEMTIME sysTime;
|
||
FILETIME curTime;
|
||
LARGE_INTEGER ctime;
|
||
|
||
GetLocalTime(&sysTime);
|
||
WsbAffirmStatus(SystemTimeToFileTime(&sysTime, &curTime));
|
||
ctime.LowPart = curTime.dwLowDateTime;
|
||
ctime.HighPart = curTime.dwHighDateTime;
|
||
ctime.QuadPart += WSB_FT_TICKS_PER_HOUR * 2;
|
||
curTime.dwLowDateTime = ctime.LowPart;
|
||
curTime.dwHighDateTime = ctime.HighPart;
|
||
WsbAffirmStatus(FileTimeToSystemTime(&curTime, &sysTime));
|
||
WsbAffirmHr(m_pFsaServer->FindResourceBySerial(pIoCmd->out.msg.oReq.serial, &pResource));
|
||
WsbAffirmHr(pResource->SetupValidateJob(sysTime));
|
||
} WsbCatchAndDo(hr,
|
||
//
|
||
// Log an event indicating that the validate job should be run manually
|
||
//
|
||
CWsbStringPtr tmpStr;
|
||
|
||
if (pResource != 0) {
|
||
hr = pResource->GetLogicalName(&tmpStr, 0);
|
||
if (hr != S_OK) {
|
||
tmpStr = L"<Unknown Volume>";
|
||
}
|
||
} else {
|
||
tmpStr = L"<Unknown Volume>";
|
||
}
|
||
WsbLogEvent(FSA_MESSAGE_AUTOVALIDATE_SCHEDULE_FAILED, 0, NULL, (OLECHAR *) tmpStr, NULL);
|
||
);
|
||
break;
|
||
case RP_RECALL_FILE:
|
||
hr = DoRecallAction(pIoCmd);
|
||
break;
|
||
|
||
case RP_START_NOTIFY:
|
||
|
||
break;
|
||
case RP_END_NOTIFY:
|
||
|
||
break;
|
||
case RP_CLOSE_FILE:
|
||
|
||
break;
|
||
default:
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: Unknown filter request - %u.\n"),
|
||
pIoCmd->out.inout.command);
|
||
break;
|
||
}
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: Issue new Ioctl.\n"));
|
||
//
|
||
// If object is still active, reset the event and issue another IOCTL
|
||
//
|
||
EnterCriticalSection(&m_stateLock);
|
||
if (m_state == HSM_JOB_STATE_ACTIVE) {
|
||
ResetEvent(pIoCmd->overlap.hEvent);
|
||
pIoCmd->in.inout.command = RP_GET_REQUEST;
|
||
pIoCmd->outSize = sizeof(RP_MSG);
|
||
code = DeviceIoControl(pIoCmd->dHand, FSCTL_HSM_MSG,
|
||
&pIoCmd->in,
|
||
sizeof(RP_MSG),
|
||
&pIoCmd->out, pIoCmd->outSize,
|
||
&pIoCmd->outSize, &pIoCmd->overlap);
|
||
lastError = GetLastError();
|
||
if ( (code == 0) && (lastError == ERROR_IO_PENDING)) {
|
||
// Life is good
|
||
numIoPending++;
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: DeviceIoControl returned %ls/%ls\n"),
|
||
WsbBoolAsString(code), WsbLongAsString(lastError));
|
||
}
|
||
} else {
|
||
//
|
||
// Get out of the while loop
|
||
//
|
||
hr = S_OK;
|
||
LeaveCriticalSection(&m_stateLock);
|
||
break;
|
||
}
|
||
LeaveCriticalSection(&m_stateLock);
|
||
|
||
} // End while active
|
||
|
||
//
|
||
// Now tell the filter we are going away and it should fail all recall activity .
|
||
//
|
||
tmp.inout.command = RP_SUSPEND_NEW_RECALLS;
|
||
WsbAffirmStatus(DeviceIoControl(m_ioctlHandle, FSCTL_HSM_DATA, &tmp,
|
||
sizeof(RP_MSG),
|
||
&tmp, sizeof(RP_MSG), &outSize, NULL))
|
||
|
||
} WsbCatch(hr);
|
||
|
||
//
|
||
// We need to wait for rest of Ioctls to be cancelled if we got out of the loop
|
||
// either because the object is not active or the first Ioctl was cancelled
|
||
// We cannot free Ioctl related data safely until all of them are done
|
||
//
|
||
if ((S_OK == hr) || ioctlCancelled) {
|
||
//
|
||
// Try to wait for the rest of the Ioctls to be cancelled
|
||
//
|
||
HRESULT freeHr;
|
||
|
||
hr = S_OK;
|
||
|
||
try {
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: Waiting for %lu more Ioctls to complete before freeing their resources\n"), numIoPending);
|
||
for (index = 0; index < numIoPending; index++) {
|
||
if (! GetQueuedCompletionStatus(port, &outSize, &key, &ovlp, 2000)) {
|
||
DWORD dwErr = GetLastError();
|
||
if (ERROR_OPERATION_ABORTED != dwErr) {
|
||
WsbAffirmHr(HRESULT_FROM_WIN32(dwErr));
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we got here, all the Ioctls were cancelled or completed as expected.
|
||
// It is safe to free their related resources
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: All of %lu Ioctls completed - free resources\n"), m_maxRecallBuffers);
|
||
pIoCmd = pIoList;
|
||
while (pIoCmd != NULL) {
|
||
if (pIoCmd->overlap.hEvent != NULL) {
|
||
CloseHandle(pIoCmd->overlap.hEvent);
|
||
}
|
||
if (pIoCmd->dHand != NULL) {
|
||
CloseHandle(pIoCmd->dHand);
|
||
}
|
||
pIo = pIoCmd;
|
||
pIoCmd = pIoCmd->next;
|
||
delete pIo;
|
||
}
|
||
pIoList = NULL;
|
||
WsbTrace(OLESTR("CFsaFilter::IoctlThread: Freed Ioctls resources successfully\n"));
|
||
|
||
} WsbCatchAndDo(freeHr,
|
||
WsbTraceAlways(L"CFsaResource::IoctlThread - Failed to free Ioctls, freeHr = %ls\n",
|
||
WsbHrAsString(freeHr));
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Free rest of allocated resources
|
||
// Note that if we couldn't wait for all the Ioctls to complete (or cancel)
|
||
// we better exit without freeing the Ioctl list
|
||
//
|
||
if (m_ioctlHandle != INVALID_HANDLE_VALUE) {
|
||
CloseHandle(m_ioctlHandle);
|
||
m_ioctlHandle = INVALID_HANDLE_VALUE;
|
||
}
|
||
if (port != NULL) {
|
||
CloseHandle(port);
|
||
}
|
||
|
||
if (m_state == HSM_JOB_STATE_ACTIVE) {
|
||
//
|
||
// There must have been an error of some kind
|
||
//
|
||
WsbLogEvent(FSA_MESSAGE_IOCTLTHREADFAILED, 0, NULL, WsbHrAsString(hr), NULL);
|
||
}
|
||
|
||
//
|
||
// Set the filter state to idle
|
||
//
|
||
m_state = HSM_JOB_STATE_IDLE;
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::IoctlThread"), OLESTR("Hr = %ls"), WsbHrAsString(hr));
|
||
|
||
return(0);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
HRESULT
|
||
CFsaFilter::PipeThread(
|
||
void
|
||
)
|
||
|
||
/*++
|
||
|
||
Implements:
|
||
|
||
|
||
IFsaFilterPriv::PipeThread()
|
||
|
||
This is started as a thread and creates the named pipe used by recall notification
|
||
clients to identify themselves. When an identification response is received we
|
||
impersonate the client, get the authentication token and find the client instance that
|
||
matches the token. If we find the client object we wet the machine name.
|
||
|
||
--*/
|
||
|
||
{
|
||
HRESULT hr = S_OK;
|
||
BOOL exitLoop, code, connected;
|
||
DWORD lastError, bytesRead, retCode;
|
||
WCHAR pName[100];
|
||
WSB_PIPE_MSG msg;
|
||
CComPtr<IFsaFilterClient> pClient;
|
||
HANDLE tHandle = INVALID_HANDLE_VALUE;
|
||
HANDLE handleArray[2];
|
||
DWORD retLen;
|
||
TOKEN_STATISTICS tStats;
|
||
SHORT result;
|
||
OVERLAPPED pOverlap;
|
||
WCHAR machineName[MAX_COMPUTERNAME_LENGTH + 1];
|
||
|
||
memset (&pOverlap, 0, sizeof(OVERLAPPED));
|
||
pOverlap.hEvent = INVALID_HANDLE_VALUE;
|
||
|
||
try {
|
||
|
||
WsbTraceIn(OLESTR("CFsaFilter::PipeThread"), OLESTR(""));
|
||
|
||
//
|
||
// Create a client instance to use for finding the client of interest.
|
||
//
|
||
WsbAffirmHr(CoCreateInstance(CLSID_CFsaFilterClientNTFS, NULL, CLSCTX_SERVER, IID_IFsaFilterClient, (void**) &pClient));
|
||
|
||
//
|
||
// Create a security scheme that allows anyone to write to the pipe on the client side,
|
||
// but preserves create-access (therefore creating-server-side privilege) to local system and admins.
|
||
//
|
||
PSID pAdminSID = NULL;
|
||
PSID pSystemSID = NULL;
|
||
PSID pWorldSID = NULL;
|
||
PSID pGuestSID = NULL;
|
||
PSID pAnonymousSID = NULL;
|
||
PACL pACL = NULL;
|
||
PSECURITY_DESCRIPTOR pSD = NULL;
|
||
#define FSA_PIPE_NUM_ACE 5
|
||
EXPLICIT_ACCESS ea[FSA_PIPE_NUM_ACE];
|
||
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
|
||
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
||
SECURITY_ATTRIBUTES sa;
|
||
|
||
memset(ea, 0, sizeof(EXPLICIT_ACCESS) * FSA_PIPE_NUM_ACE);
|
||
|
||
try {
|
||
// Create a SID for World
|
||
WsbAssertStatus( AllocateAndInitializeSid( &SIDAuthWorld, 1,
|
||
SECURITY_WORLD_RID,
|
||
0, 0, 0, 0, 0, 0, 0,
|
||
&pWorldSID) );
|
||
|
||
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
||
// The ACE allows the World limited access to the pipe
|
||
ea[0].grfAccessPermissions = (FILE_ALL_ACCESS & ~(FILE_CREATE_PIPE_INSTANCE | WRITE_OWNER | WRITE_DAC));
|
||
ea[0].grfAccessMode = SET_ACCESS;
|
||
ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||
ea[0].Trustee.pMultipleTrustee = NULL;
|
||
ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
||
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
||
ea[0].Trustee.ptstrName = (LPTSTR) pWorldSID;
|
||
|
||
// Create a SID for Guest
|
||
WsbAssertStatus( AllocateAndInitializeSid( &SIDAuthNT, 2,
|
||
SECURITY_BUILTIN_DOMAIN_RID,
|
||
DOMAIN_ALIAS_RID_GUESTS,
|
||
0, 0, 0, 0, 0, 0,
|
||
&pGuestSID) );
|
||
|
||
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
||
// The ACE allows the Guests limited access to the pipe
|
||
ea[1].grfAccessPermissions = (FILE_ALL_ACCESS & ~(FILE_CREATE_PIPE_INSTANCE | WRITE_OWNER | WRITE_DAC));
|
||
ea[1].grfAccessMode = SET_ACCESS;
|
||
ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||
ea[1].Trustee.pMultipleTrustee = NULL;
|
||
ea[1].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
||
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||
ea[1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
||
ea[1].Trustee.ptstrName = (LPTSTR) pGuestSID;
|
||
|
||
// Create a SID for Anonymous
|
||
WsbAssertStatus( AllocateAndInitializeSid( &SIDAuthNT, 1,
|
||
SECURITY_ANONYMOUS_LOGON_RID,
|
||
0, 0, 0, 0, 0, 0, 0,
|
||
&pAnonymousSID) );
|
||
|
||
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
||
// The ACE allows the Anonymous limited access to the pipe
|
||
ea[2].grfAccessPermissions = (FILE_ALL_ACCESS & ~(FILE_CREATE_PIPE_INSTANCE | WRITE_OWNER | WRITE_DAC));
|
||
ea[2].grfAccessMode = SET_ACCESS;
|
||
ea[2].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||
ea[2].Trustee.pMultipleTrustee = NULL;
|
||
ea[2].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
||
ea[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||
ea[2].Trustee.TrusteeType = TRUSTEE_IS_USER;
|
||
ea[2].Trustee.ptstrName = (LPTSTR) pAnonymousSID;
|
||
|
||
// Create a SID for the Administrators group.
|
||
WsbAssertStatus( AllocateAndInitializeSid( &SIDAuthNT, 2,
|
||
SECURITY_BUILTIN_DOMAIN_RID,
|
||
DOMAIN_ALIAS_RID_ADMINS,
|
||
0, 0, 0, 0, 0, 0,
|
||
&pAdminSID) );
|
||
|
||
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
||
// The ACE allows the Administrators group full access to the pipe
|
||
ea[3].grfAccessPermissions = FILE_ALL_ACCESS;
|
||
ea[3].grfAccessMode = SET_ACCESS;
|
||
ea[3].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||
ea[3].Trustee.pMultipleTrustee = NULL;
|
||
ea[3].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
||
ea[3].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||
ea[3].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
||
ea[3].Trustee.ptstrName = (LPTSTR) pAdminSID;
|
||
|
||
// Create a SID for the local system account
|
||
WsbAssertStatus( AllocateAndInitializeSid( &SIDAuthNT, 1,
|
||
SECURITY_LOCAL_SYSTEM_RID,
|
||
0, 0, 0, 0, 0, 0, 0,
|
||
&pSystemSID) );
|
||
|
||
// Initialize an EXPLICIT_ACCESS structure for an ACE.
|
||
// The ACE allows the local system full access to the pipe
|
||
ea[4].grfAccessPermissions = FILE_ALL_ACCESS;
|
||
ea[4].grfAccessMode = SET_ACCESS;
|
||
ea[4].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
|
||
ea[4].Trustee.pMultipleTrustee = NULL;
|
||
ea[4].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
|
||
ea[4].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||
ea[4].Trustee.TrusteeType = TRUSTEE_IS_USER;
|
||
ea[4].Trustee.ptstrName = (LPTSTR) pSystemSID;
|
||
|
||
// Create a new ACL that contains the new ACEs.
|
||
WsbAffirmNoError( SetEntriesInAcl(FSA_PIPE_NUM_ACE, ea, NULL, &pACL));
|
||
|
||
// Initialize a security descriptor.
|
||
pSD = (PSECURITY_DESCRIPTOR) WsbAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
|
||
WsbAffirmPointer(pSD);
|
||
WsbAffirmStatus(InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION));
|
||
|
||
// Add the ACL to the security descriptor.
|
||
WsbAffirmStatus(SetSecurityDescriptorDacl(
|
||
pSD,
|
||
TRUE, // fDaclPresent flag
|
||
pACL,
|
||
FALSE)); // not a default DACL
|
||
|
||
// Initialize a security attributes structure.
|
||
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
|
||
sa.lpSecurityDescriptor = pSD;
|
||
sa.bInheritHandle = FALSE;
|
||
|
||
//
|
||
// Create a local named pipe with
|
||
// the hsm pipe name
|
||
// '.' signifies local pipe.
|
||
|
||
swprintf(pName, L"\\\\.\\PIPE\\%s", WSB_PIPE_NAME);
|
||
|
||
WsbAffirmHandle((m_pipeHandle = CreateNamedPipeW(pName,
|
||
PIPE_ACCESS_DUPLEX // Duplex - NT5 for some reason needs this vs inbound only
|
||
| FILE_FLAG_OVERLAPPED // Use overlapped structure.
|
||
| FILE_FLAG_FIRST_PIPE_INSTANCE, // Make sure we are the creator of the pipe
|
||
PIPE_WAIT | // Wait on messages.
|
||
PIPE_TYPE_BYTE,
|
||
WSB_MAX_PIPES,
|
||
WSB_PIPE_BUFF_SIZE,
|
||
WSB_PIPE_BUFF_SIZE,
|
||
WSB_PIPE_TIME_OUT, // Specify time out.
|
||
&sa))); // Security attributes.
|
||
|
||
} WsbCatch(hr);
|
||
|
||
//
|
||
// Free security objects and verify hr of pipe creation
|
||
//
|
||
if (pAdminSID) {
|
||
FreeSid(pAdminSID);
|
||
}
|
||
if (pSystemSID) {
|
||
FreeSid(pSystemSID);
|
||
}
|
||
if (pWorldSID) {
|
||
FreeSid(pWorldSID);
|
||
}
|
||
if (pGuestSID) {
|
||
FreeSid(pGuestSID);
|
||
}
|
||
if (pAnonymousSID) {
|
||
FreeSid(pAnonymousSID);
|
||
}
|
||
if (pACL) {
|
||
LocalFree(pACL);
|
||
}
|
||
if (pSD) {
|
||
WsbFree(pSD);
|
||
}
|
||
|
||
WsbAffirmHr(hr);
|
||
|
||
|
||
//
|
||
// Create an event for overlapped i/o
|
||
//
|
||
WsbAffirmHandle((pOverlap.hEvent = CreateEvent(NULL,
|
||
FALSE,
|
||
FALSE,
|
||
NULL)));
|
||
|
||
//
|
||
// Initialize the handle array. The first element sbould be the terminate event,
|
||
// because we need to know if it signalled and always break out of the loop
|
||
// regardless of whether the overlapped i/o is done or not
|
||
//
|
||
handleArray[0] = m_terminateEvent;
|
||
handleArray[1] = pOverlap.hEvent;
|
||
|
||
exitLoop = FALSE;
|
||
while ((!exitLoop) && ((m_state == HSM_JOB_STATE_ACTIVE) || (m_state == HSM_JOB_STATE_STARTING))) {
|
||
connected = FALSE;
|
||
//
|
||
// Block until a client connects.
|
||
//
|
||
code = ConnectNamedPipe(m_pipeHandle, &pOverlap);
|
||
if (!code) {
|
||
lastError = GetLastError();
|
||
|
||
switch (lastError) {
|
||
// IO_PENDING, wait on the event
|
||
case ERROR_IO_PENDING: {
|
||
|
||
retCode = WaitForMultipleObjects(2,
|
||
handleArray,
|
||
FALSE,
|
||
INFINITE);
|
||
if (retCode == WAIT_OBJECT_0) {
|
||
//
|
||
// The termination event got signalled
|
||
//
|
||
exitLoop = TRUE;
|
||
continue;
|
||
} else if (retCode == (WAIT_OBJECT_0+1)) {
|
||
//
|
||
// A client connected
|
||
//
|
||
connected = TRUE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case ERROR_BROKEN_PIPE: {
|
||
//
|
||
// Pipe is broken, reconnect
|
||
//
|
||
break;
|
||
}
|
||
|
||
default: {
|
||
//
|
||
// Something else is wrong, just reconnect
|
||
//
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
connected = TRUE;
|
||
}
|
||
|
||
|
||
if (connected) {
|
||
//
|
||
// A client connected - get the identify message, identify them and continue waiting for
|
||
// pipe conections.
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::PipeThread: Client has connected.\n"));
|
||
|
||
pOverlap.Offset = 0;
|
||
pOverlap.OffsetHigh = 0;
|
||
code = ReadFile(m_pipeHandle, &msg, sizeof(WSB_PIPE_MSG), &bytesRead, &pOverlap);
|
||
if (!code) {
|
||
lastError = GetLastError();
|
||
}
|
||
else {
|
||
lastError = ERROR_IO_PENDING; // Read returned right away
|
||
}
|
||
|
||
switch (lastError) {
|
||
// IO_PENDING, wait on the event or timeout in 4 seconds
|
||
case ERROR_IO_PENDING:
|
||
if (!code) {
|
||
retCode = WaitForMultipleObjects(2,
|
||
handleArray,
|
||
FALSE,
|
||
(DWORD) 4000);
|
||
} else {
|
||
retCode = WAIT_OBJECT_0 + 1; // Read returned right away
|
||
}
|
||
if (retCode == (WAIT_OBJECT_0+1)) {
|
||
//
|
||
// Read some data. Do the identification
|
||
//
|
||
GetOverlappedResult(m_pipeHandle, &pOverlap, &bytesRead, FALSE);
|
||
if (bytesRead == sizeof(WSB_PIPE_MSG)) {
|
||
//
|
||
// Find the client instance that matches this user
|
||
//
|
||
code = ImpersonateNamedPipeClient(m_pipeHandle);
|
||
if (code) {
|
||
code = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY,
|
||
TRUE, &tHandle);
|
||
}
|
||
|
||
if (code) {
|
||
code = GetTokenInformation(tHandle, TokenStatistics, &tStats,
|
||
sizeof(TOKEN_STATISTICS), &retLen);
|
||
CloseHandle(tHandle);
|
||
if (code) {
|
||
//
|
||
// Get the passed in machine name in a local buffer and null terminated
|
||
//
|
||
wcsncpy(machineName, msg.msg.idrp.clientName, MAX_COMPUTERNAME_LENGTH);
|
||
machineName[MAX_COMPUTERNAME_LENGTH] = L'\0';
|
||
|
||
//
|
||
// First we need to clean up any old client objects that
|
||
// have the same machine name. It is assumed that there can
|
||
// only be one client per machine and a duplicate means that
|
||
// they must have re-connected with a different authentication
|
||
// token.
|
||
//
|
||
{
|
||
CComPtr<IFsaFilterClient> pFoundClient;
|
||
|
||
WsbAffirmHr(pClient->SetMachineName(machineName));
|
||
WsbAffirmHr(pClient->CompareBy(FSA_FILTERCLIENT_COMPARE_MACHINE));
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->Find(pClient, IID_IFsaFilterClient, (void**) &pFoundClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
if (hr == S_OK) {
|
||
//
|
||
// Found one with the same machine name - make sure the token
|
||
// does not match, just to be sure.
|
||
//
|
||
hr = pFoundClient->CompareToAuthenticationId(tStats.AuthenticationId.HighPart,
|
||
tStats.AuthenticationId.LowPart, &result);
|
||
|
||
if (hr != S_OK) {
|
||
//
|
||
// It did not match - remove and release this one from
|
||
// the collection.
|
||
//
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->RemoveAndRelease(pFoundClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
}
|
||
}
|
||
} // Let pFoundClient go out of scope
|
||
|
||
|
||
{
|
||
CComPtr<IFsaFilterClient> pFoundClient;
|
||
|
||
//
|
||
// Now set the machine name for this client if we can find
|
||
// it by authentication id.
|
||
//
|
||
WsbAffirmHr(pClient->SetAuthenticationId(tStats.AuthenticationId.HighPart,
|
||
tStats.AuthenticationId.LowPart));
|
||
WsbAffirmHr(pClient->CompareBy(FSA_FILTERCLIENT_COMPARE_ID));
|
||
|
||
WsbTrace(OLESTR("CFsaFilter::PipeThread: Finding client instance (%x:%x).\n"),
|
||
tStats.AuthenticationId.HighPart,
|
||
tStats.AuthenticationId.LowPart);
|
||
|
||
EnterCriticalSection(&m_clientLock);
|
||
hr = m_pClients->Find(pClient, IID_IFsaFilterClient, (void**) &pFoundClient);
|
||
LeaveCriticalSection(&m_clientLock);
|
||
if (hr == S_OK) {
|
||
//
|
||
// Got it - set the machine name
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::PipeThread: Identify client as %ws.\n"),
|
||
machineName);
|
||
|
||
pFoundClient->SetMachineName(machineName);
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::PipeThread: Failed to find the client instance (%ls).\n"),
|
||
WsbHrAsString(hr));
|
||
}
|
||
} // Let pFoundClient go out of scope
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::PipeThread: GetTokenInformation returned %u.\n"),
|
||
GetLastError());
|
||
}
|
||
} else {
|
||
WsbTrace(OLESTR("CFsaFilter::PipeThread: OpenThreadToken or ImpersonateNamedPipeClient returned %u.\n"),
|
||
GetLastError());
|
||
}
|
||
RevertToSelf();
|
||
DisconnectNamedPipe(m_pipeHandle);
|
||
} else {
|
||
//
|
||
// Bad data was read - blow them off
|
||
//
|
||
WsbTrace(OLESTR("CFsaFilter::PipeThread: Bad message size (%u)\n"),
|
||
bytesRead);
|
||
DisconnectNamedPipe(m_pipeHandle);
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// Timeout or error - cancel the read and disconnect the client
|
||
//
|
||
DisconnectNamedPipe(m_pipeHandle);
|
||
if (retCode == WAIT_OBJECT_0) {
|
||
//
|
||
// Termination event was signalled
|
||
//
|
||
exitLoop = TRUE;
|
||
continue;
|
||
}
|
||
}
|
||
break;
|
||
case ERROR_BROKEN_PIPE: {
|
||
// Pipe is broken.,
|
||
DisconnectNamedPipe(m_pipeHandle);
|
||
break;
|
||
}
|
||
|
||
default: {
|
||
// Something else is wrong.
|
||
DisconnectNamedPipe(m_pipeHandle);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
} // End while state
|
||
|
||
} WsbCatch(hr);
|
||
|
||
if (m_pipeHandle != INVALID_HANDLE_VALUE) {
|
||
CloseHandle(m_pipeHandle);
|
||
m_pipeHandle = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
if (pOverlap.hEvent != INVALID_HANDLE_VALUE) {
|
||
CloseHandle(pOverlap.hEvent);
|
||
}
|
||
|
||
WsbTraceOut(OLESTR("CFsaFilter::PipeThread"), OLESTR("Hr = %ls"), WsbHrAsString(hr));
|
||
|
||
return (hr);
|
||
}
|