Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3667 lines
109 KiB

/*++
(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);
}