/* Editor Settings: expandtabs and use 4 spaces for indentation
* ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
* -*- mode: c, c-basic-offset: 4 -*- */
/*
* Copyright Likewise Software
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. You should have received a copy of the GNU General
* Public License along with this program. If not, see
* .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Copyright (C) Likewise Software. All rights reserved.
*
* Module Name:
*
* notify.c
*
* Abstract:
*
* Likewise Posix File System Driver (PVFS)
*
* Directory Change Notify Package
*
* Authors: Gerald Carter
*/
#include "pvfs.h"
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyAddFilter(
PPVFS_FCB pFcb,
PPVFS_IRP_CONTEXT pIrpContext,
PPVFS_CCB pCcb,
FILE_NOTIFY_CHANGE NotifyFilter,
BOOLEAN bWatchTree,
PULONG pMaxBufferSize
);
static
NTSTATUS
PvfsNotifyReportBufferedChanges(
PPVFS_CCB pCcb,
PPVFS_FCB pFcb,
PPVFS_IRP_CONTEXT pIrpContext
);
NTSTATUS
PvfsReadDirectoryChange(
PPVFS_IRP_CONTEXT pIrpContext
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PIRP pIrp = pIrpContext->pIrp;
IRP_ARGS_READ_DIRECTORY_CHANGE Args = pIrp->Args.ReadDirectoryChange;
PPVFS_CCB pCcb = NULL;
PULONG pMaxBufferSize = Args.MaxBufferSize;
/* Sanity checks */
ntError = PvfsAcquireCCB(pIrp->FileHandle, &pCcb);
BAIL_ON_NT_STATUS(ntError);
if (!PVFS_IS_DIR(pCcb))
{
ntError = STATUS_NOT_A_DIRECTORY;
BAIL_ON_NT_STATUS(ntError);
}
ntError = PvfsAccessCheckFileHandle(pCcb, FILE_LIST_DIRECTORY);
BAIL_ON_NT_STATUS(ntError);
BAIL_ON_INVALID_PTR(Args.Buffer, ntError);
BAIL_ON_ZERO_LENGTH(Args.Length, ntError);
/* If we have something in the buffer, return that immediately. Else
register a notification filter */
ntError = PvfsNotifyReportBufferedChanges(
pCcb,
pCcb->pFcb,
pIrpContext);
if (ntError == STATUS_NOT_FOUND)
{
PvfsIrpMarkPending(pIrpContext, PvfsQueueCancelIrp, pIrpContext);
ntError = PvfsNotifyAddFilter(
pCcb->pFcb,
pIrpContext,
pCcb,
Args.NotifyFilter,
Args.WatchTree,
pMaxBufferSize);
if (ntError == STATUS_SUCCESS)
{
pIrpContext->QueueType = PVFS_QUEUE_TYPE_NOTIFY;
if (!pIrpContext->pFcb)
{
pIrpContext->pFcb = PvfsReferenceFCB(pCcb->pFcb);
}
/* Allow the call to be cancelled while in the queue */
PvfsIrpContextClearFlag(pIrpContext, PVFS_IRP_CTX_FLAG_ACTIVE);
ntError = STATUS_PENDING;
goto cleanup;
}
}
BAIL_ON_NT_STATUS(ntError);
cleanup:
if (pCcb)
{
PvfsReleaseCCB(pCcb);
}
return ntError;
error:
if (PvfsIrpContextCheckFlag(pIrpContext, PVFS_IRP_CTX_FLAG_PENDED))
{
pIrpContext->pIrp->IoStatusBlock.Status = ntError;
PvfsAsyncIrpComplete(pIrpContext);
}
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
VOID
PvfsNotifyClearBufferedChanges(
PPVFS_NOTIFY_FILTER_BUFFER pBuffer
);
static
NTSTATUS
PvfsNotifyReportBufferedChanges(
PPVFS_CCB pCcb,
PPVFS_FCB pFcb,
PPVFS_IRP_CONTEXT pIrpContext
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
FILE_NOTIFY_CHANGE Filter = pIrpContext->pIrp->Args.ReadDirectoryChange.NotifyFilter;
PVOID pBuffer = pIrpContext->pIrp->Args.ReadDirectoryChange.Buffer;
ULONG Length = pIrpContext->pIrp->Args.ReadDirectoryChange.Length;
PLW_LIST_LINKS pFilterLink = NULL;
PPVFS_NOTIFY_FILTER_RECORD pFilter = NULL;
BOOLEAN bLocked = FALSE;
LWIO_LOCK_MUTEX(bLocked, &pFcb->mutexNotify);
/* See if we have any changes to report immediately */
for (pFilterLink = PvfsListTraverse(pFcb->pNotifyListBuffer, NULL);
pFilterLink;
pFilterLink = PvfsListTraverse(pFcb->pNotifyListBuffer, pFilterLink))
{
pFilter = LW_STRUCT_FROM_FIELD(
pFilterLink,
PVFS_NOTIFY_FILTER_RECORD,
NotifyList);
/* To return the buffered changes, we have to match the handle
and the filter */
if ((pFilter->NotifyFilter != Filter) || (pFilter->pCcb != pCcb))
{
continue;
}
/* We have a match to check to see if we have anything */
if ((pFilter->Buffer.Length == 0) ||
(pFilter->Buffer.Offset == 0))
{
ntError = STATUS_NOT_FOUND;
BAIL_ON_NT_STATUS(ntError);
}
/* We have changes....Do we have enough space? Have we already
overflowed the buffer? */
ntError = pFilter->Buffer.Status;
BAIL_ON_NT_STATUS(ntError);
if (pFilter->Buffer.Offset > Length)
{
PvfsNotifyClearBufferedChanges(&pFilter->Buffer);
ntError = STATUS_NOTIFY_ENUM_DIR;
BAIL_ON_NT_STATUS(ntError);
}
memcpy(pBuffer, pFilter->Buffer.pData, pFilter->Buffer.Offset);
pIrpContext->pIrp->IoStatusBlock.BytesTransferred = pFilter->Buffer.Offset;
PvfsNotifyClearBufferedChanges(&pFilter->Buffer);
}
if (pFilterLink == NULL)
{
ntError = STATUS_NOT_FOUND;
BAIL_ON_NT_STATUS(ntError);
}
cleanup:
LWIO_UNLOCK_MUTEX(bLocked, &pFcb->mutexNotify);
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
VOID
PvfsNotifyClearBufferedChanges(
PPVFS_NOTIFY_FILTER_BUFFER pBuffer
)
{
memset(pBuffer->pData, 0x0, pBuffer->Length);
pBuffer->Status = STATUS_SUCCESS;
pBuffer->Offset = 0;
pBuffer->pNotify = NULL;
}
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyAllocateChangeBuffer(
PPVFS_NOTIFY_FILTER_BUFFER pBuffer,
ULONG Length
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
ntError = PvfsAllocateMemory((PVOID*)&pBuffer->pData, Length);
BAIL_ON_NT_STATUS(ntError);
pBuffer->Length = Length;
pBuffer->Status = STATUS_SUCCESS;
pBuffer->Offset = 0;
pBuffer->pNotify = NULL;
cleanup:
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
VOID
PvfsNotifyFreeChangeBuffer(
PPVFS_NOTIFY_FILTER_BUFFER pBuffer
)
{
PVFS_FREE(&pBuffer->pData);
PVFS_ZERO_MEMORY(pBuffer);
}
/*****************************************************************************
****************************************************************************/
VOID
PvfsFreeNotifyRecord(
PPVFS_NOTIFY_FILTER_RECORD *ppNotifyRecord
)
{
PPVFS_NOTIFY_FILTER_RECORD pFilter = NULL;
if (ppNotifyRecord && *ppNotifyRecord)
{
pFilter = *ppNotifyRecord;
if (pFilter->pIrpContext)
{
PvfsReleaseIrpContext(&pFilter->pIrpContext);
}
PvfsNotifyFreeChangeBuffer(&pFilter->Buffer);
if (pFilter->pCcb)
{
PvfsReleaseCCB(pFilter->pCcb);
}
PVFS_FREE(ppNotifyRecord);
}
return;
}
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyAllocateFilter(
PPVFS_NOTIFY_FILTER_RECORD *ppNotifyRecord,
PPVFS_IRP_CONTEXT pIrpContext,
PPVFS_CCB pCcb,
FILE_NOTIFY_CHANGE NotifyFilter,
BOOLEAN bWatchTree
);
static
NTSTATUS
PvfsNotifyAddFilter(
PPVFS_FCB pFcb,
PPVFS_IRP_CONTEXT pIrpContext,
PPVFS_CCB pCcb,
FILE_NOTIFY_CHANGE NotifyFilter,
BOOLEAN bWatchTree,
PULONG pMaxBufferSize
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PPVFS_NOTIFY_FILTER_RECORD pFilter = NULL;
BOOLEAN bLocked = FALSE;
BAIL_ON_INVALID_PTR(pFcb, ntError);
ntError = PvfsNotifyAllocateFilter(
&pFilter,
pIrpContext,
pCcb,
NotifyFilter,
bWatchTree);
BAIL_ON_NT_STATUS(ntError);
/* Add a buffer log to this filter if specified. We'll move
the record to the buffer list after first processing the Irp */
if (pMaxBufferSize && (*pMaxBufferSize > 0))
{
ntError = PvfsNotifyAllocateChangeBuffer(
&pFilter->Buffer,
*pMaxBufferSize);
BAIL_ON_NT_STATUS(ntError);
}
LWIO_LOCK_MUTEX(bLocked, &pFcb->mutexNotify);
ntError = PvfsListAddTail(
pFcb->pNotifyListIrp,
&pFilter->NotifyList);
LWIO_UNLOCK_MUTEX(bLocked, &pFcb->mutexNotify);
BAIL_ON_NT_STATUS(ntError);
cleanup:
return ntError;
error:
if (pFilter)
{
PvfsFreeNotifyRecord(&pFilter);
}
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyAllocateFilter(
PPVFS_NOTIFY_FILTER_RECORD *ppNotifyRecord,
PPVFS_IRP_CONTEXT pIrpContext,
PPVFS_CCB pCcb,
FILE_NOTIFY_CHANGE NotifyFilter,
BOOLEAN bWatchTree
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PPVFS_NOTIFY_FILTER_RECORD pFilter = NULL;
ntError = PvfsAllocateMemory(
(PVOID*)&pFilter,
sizeof(PVFS_NOTIFY_FILTER_RECORD));
BAIL_ON_NT_STATUS(ntError);
pFilter->pIrpContext = PvfsReferenceIrpContext(pIrpContext);
pFilter->pCcb = PvfsReferenceCCB(pCcb);
pFilter->NotifyFilter = NotifyFilter;
pFilter->bWatchTree = bWatchTree;
*ppNotifyRecord = pFilter;
pFilter = NULL;
cleanup:
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static NTSTATUS
PvfsNotifyFullReport(
PVOID pContext
);
static VOID
PvfsNotifyFullReportCtxFree(
PPVFS_NOTIFY_REPORT_RECORD *ppContext
);
VOID
PvfsNotifyScheduleFullReport(
PPVFS_FCB pFcb,
FILE_NOTIFY_CHANGE Filter,
FILE_ACTION Action,
PCSTR pszFilename
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PPVFS_WORK_CONTEXT pWorkCtx = NULL;
PPVFS_NOTIFY_REPORT_RECORD pReport = NULL;
BAIL_ON_INVALID_PTR(pFcb, ntError);
ntError = PvfsAllocateMemory(
(PVOID*)&pReport,
sizeof(PVFS_NOTIFY_REPORT_RECORD));
BAIL_ON_NT_STATUS(ntError);
pReport->pFcb = PvfsReferenceFCB(pFcb);
pReport->Filter = Filter;
pReport->Action = Action;
ntError = LwRtlCStringDuplicate(&pReport->pszFilename, pszFilename);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsCreateWorkContext(
&pWorkCtx,
FALSE,
pReport,
(PPVFS_WORK_CONTEXT_CALLBACK)PvfsNotifyFullReport,
(PPVFS_WORK_CONTEXT_FREE_CTX)PvfsNotifyFullReportCtxFree);
BAIL_ON_NT_STATUS(ntError);
pReport = NULL;
ntError = PvfsAddWorkItem(gpPvfsInternalWorkQueue, (PVOID)pWorkCtx);
BAIL_ON_NT_STATUS(ntError);
cleanup:
return;
error:
PvfsNotifyFullReportCtxFree(&pReport);
PvfsFreeWorkContext(&pWorkCtx);
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
VOID
PvfsNotifyFullReportBuffer(
PPVFS_FCB pFcb,
PPVFS_NOTIFY_REPORT_RECORD pReport
);
static
VOID
PvfsNotifyFullReportIrp(
PPVFS_FCB pFcb,
PPVFS_NOTIFY_REPORT_RECORD pReport
);
static
NTSTATUS
PvfsNotifyFullReport(
PVOID pContext
)
{
NTSTATUS ntError = STATUS_SUCCESS;
PPVFS_NOTIFY_REPORT_RECORD pReport = (PPVFS_NOTIFY_REPORT_RECORD)pContext;
PPVFS_FCB pParentFcb = NULL;
BOOLEAN bLocked = FALSE;
PPVFS_FCB pCursor = NULL;
BAIL_ON_INVALID_PTR(pReport, ntError);
/* Simply walk up the ancestory and process the notify filter
record on top if there is a match */
pCursor = PvfsReferenceFCB(pReport->pFcb);
while ((pParentFcb = PvfsGetParentFCB(pCursor)) != NULL)
{
LWIO_LOCK_MUTEX(bLocked, &pParentFcb->mutexNotify);
/* Process buffers before Irp so we don't doublt report
a change on a pending Irp that has requested buffering a
change log (which shouldn't start until the existing Irp
has been completed). */
PvfsNotifyFullReportBuffer(pParentFcb, pReport);
PvfsNotifyFullReportIrp(pParentFcb, pReport);
LWIO_UNLOCK_MUTEX(bLocked, &pParentFcb->mutexNotify);
PvfsReleaseFCB(&pCursor);
pCursor = pParentFcb;
}
cleanup:
if (pCursor)
{
PvfsReleaseFCB(&pCursor);
}
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyReportBuffer(
PPVFS_NOTIFY_FILTER_BUFFER pFilterBuffer,
FILE_ACTION Action,
PCSTR pszFilename
);
static
VOID
PvfsNotifyFullReportBuffer(
PPVFS_FCB pFcb,
PPVFS_NOTIFY_REPORT_RECORD pReport
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PLW_LIST_LINKS pFilterLink = NULL;
PPVFS_NOTIFY_FILTER_RECORD pFilter = NULL;
for (pFilterLink = PvfsListTraverse(pFcb->pNotifyListBuffer, NULL);
pFilterLink;
pFilterLink = PvfsListTraverse(pFcb->pNotifyListBuffer, pFilterLink))
{
pFilter = LW_STRUCT_FROM_FIELD(
pFilterLink,
PVFS_NOTIFY_FILTER_RECORD,
NotifyList);
/* Match the filter and depth */
if ((pFilter->NotifyFilter & pReport->Filter) &&
((pFcb == pReport->pFcb->pParentFcb) || pFilter->bWatchTree))
{
ntError = PvfsNotifyReportBuffer(
&pFilter->Buffer,
pReport->Action,
pReport->pszFilename);
break;
}
}
return;
}
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyReportIrp(
PPVFS_IRP_CONTEXT pIrpContext,
FILE_ACTION Action,
PCSTR pszFilename
);
static
VOID
PvfsNotifyFullReportIrp(
PPVFS_FCB pFcb,
PPVFS_NOTIFY_REPORT_RECORD pReport
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PLW_LIST_LINKS pFilterLink = NULL;
PPVFS_NOTIFY_FILTER_RECORD pFilter = NULL;
BOOLEAN bActive = FALSE;
for (pFilterLink = PvfsListTraverse(pFcb->pNotifyListIrp, NULL);
pFilterLink;
pFilterLink = PvfsListTraverse(pFcb->pNotifyListIrp, pFilterLink))
{
pFilter = LW_STRUCT_FROM_FIELD(
pFilterLink,
PVFS_NOTIFY_FILTER_RECORD,
NotifyList);
/* Continue if we don't match the filter and depth */
if (!((pFilter->NotifyFilter & pReport->Filter) &&
((pFcb == pReport->pFcb->pParentFcb) || pFilter->bWatchTree)))
{
pFilter = NULL;
continue;
}
PvfsListRemoveItem(pFcb->pNotifyListIrp, pFilterLink);
PvfsQueueCancelIrpIfRequested(pFilter->pIrpContext);
bActive = PvfsIrpContextMarkIfNotSetFlag(
pFilter->pIrpContext,
PVFS_IRP_CTX_FLAG_CANCELLED,
PVFS_IRP_CTX_FLAG_ACTIVE);
if (!bActive)
{
PvfsFreeNotifyRecord(&pFilter);
continue;
}
ntError = PvfsNotifyReportIrp(
pFilter->pIrpContext,
pReport->Action,
pReport->pszFilename);
BAIL_ON_NT_STATUS(ntError);
/* If we have been asked to buffer changes, move the Fitler Record
to the buffer list */
if (pFilter->Buffer.Length > 0)
{
ntError = PvfsListAddTail(pFcb->pNotifyListBuffer, pFilterLink);
BAIL_ON_NT_STATUS(ntError);
pFilter = NULL;
}
/* We only process on matching IRP */
break;
}
cleanup:
if (pFilter)
{
PvfsFreeNotifyRecord(&pFilter);
}
return;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyReportIrp(
PPVFS_IRP_CONTEXT pIrpContext,
FILE_ACTION Action,
PCSTR pszFilename
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PVOID pBuffer = pIrpContext->pIrp->Args.ReadDirectoryChange.Buffer;
ULONG Length = pIrpContext->pIrp->Args.ReadDirectoryChange.Length;
PFILE_NOTIFY_INFORMATION pNotifyInfo = NULL;
LONG FilenameBytes = 0;
PWSTR pwszFilename = NULL;
ULONG BytesNeeded = 0;
ntError = LwRtlWC16StringAllocateFromCString(&pwszFilename, pszFilename);
BAIL_ON_NT_STATUS(ntError);
FilenameBytes = (LwRtlWC16StringNumChars(pwszFilename) + 1) * sizeof(WCHAR);
BytesNeeded = sizeof(*pNotifyInfo) + FilenameBytes;
if (Length < BytesNeeded)
{
memset(pBuffer, 0x0, Length);
ntError = STATUS_NOTIFY_ENUM_DIR;
BAIL_ON_NT_STATUS(ntError);
}
pNotifyInfo = (PFILE_NOTIFY_INFORMATION)pBuffer;
pNotifyInfo->NextEntryOffset = 0;
pNotifyInfo->Action = Action;
pNotifyInfo->FileNameLength = FilenameBytes;
memcpy(&pNotifyInfo->FileName, pwszFilename, FilenameBytes);
pIrpContext->pIrp->IoStatusBlock.BytesTransferred = BytesNeeded;
cleanup:
pIrpContext->pIrp->IoStatusBlock.Status = ntError;
PvfsAsyncIrpComplete(pIrpContext);
LwRtlWC16StringFree(&pwszFilename);
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
NTSTATUS
PvfsNotifyReportBuffer(
PPVFS_NOTIFY_FILTER_BUFFER pFilterBuffer,
FILE_ACTION Action,
PCSTR pszFilename
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PVOID pBuffer = pFilterBuffer->pData + pFilterBuffer->Offset;
ULONG Length = pFilterBuffer->Length - pFilterBuffer->Offset;
PFILE_NOTIFY_INFORMATION pNotifyInfo = NULL;
LONG FilenameBytes = 0;
PWSTR pwszFilename = NULL;
ULONG BytesNeeded = 0;
/* Don't bother if we have already overflowed the buffer */
BAIL_ON_NT_STATUS(pFilterBuffer->Status);
ntError = LwRtlWC16StringAllocateFromCString(&pwszFilename, pszFilename);
BAIL_ON_NT_STATUS(ntError);
FilenameBytes = (LwRtlWC16StringNumChars(pwszFilename) + 1) * sizeof(WCHAR);
BytesNeeded = sizeof(*pNotifyInfo) + FilenameBytes;
PVFS_ALIGN_MEMORY(BytesNeeded, 8);
if (Length < BytesNeeded)
{
ntError = pFilterBuffer->Status = STATUS_NOTIFY_ENUM_DIR;
BAIL_ON_NT_STATUS(ntError);
}
pNotifyInfo = (PFILE_NOTIFY_INFORMATION)pBuffer;
pNotifyInfo->NextEntryOffset = 0;
pNotifyInfo->Action = Action;
pNotifyInfo->FileNameLength = FilenameBytes;
memcpy(&pNotifyInfo->FileName, pwszFilename, FilenameBytes);
if (pFilterBuffer->pNotify)
{
ULONG NextEntry = PVFS_PTR_DIFF(pFilterBuffer->pNotify, pNotifyInfo);
pFilterBuffer->pNotify->NextEntryOffset = NextEntry;
}
pFilterBuffer->pNotify = pNotifyInfo;
pFilterBuffer->Offset += BytesNeeded;
cleanup:
LwRtlWC16StringFree(&pwszFilename);
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static
VOID
PvfsNotifyFullReportCtxFree(
PPVFS_NOTIFY_REPORT_RECORD *ppReport
)
{
PPVFS_NOTIFY_REPORT_RECORD pReport = NULL;
if (ppReport && *ppReport)
{
pReport = (PPVFS_NOTIFY_REPORT_RECORD)*ppReport;
if (pReport->pFcb)
{
PvfsReleaseFCB(&pReport->pFcb);
}
LwRtlCStringFree(&pReport->pszFilename);
PVFS_FREE(ppReport);
}
return;
}
/*****************************************************************************
****************************************************************************/
static NTSTATUS
PvfsNotifyCleanIrpList(
PVOID pContext
);
static VOID
PvfsNotifyCleanIrpListFree(
PVOID *ppContext
);
NTSTATUS
PvfsScheduleCancelNotify(
PPVFS_IRP_CONTEXT pIrpContext
)
{
NTSTATUS ntError = STATUS_UNSUCCESSFUL;
PPVFS_WORK_CONTEXT pWorkCtx = NULL;
PPVFS_IRP_CONTEXT pIrpCtx = NULL;
BAIL_ON_INVALID_PTR(pIrpContext->pFcb, ntError);
pIrpCtx = PvfsReferenceIrpContext(pIrpContext);
ntError = PvfsCreateWorkContext(
&pWorkCtx,
FALSE,
pIrpCtx,
(PPVFS_WORK_CONTEXT_CALLBACK)PvfsNotifyCleanIrpList,
(PPVFS_WORK_CONTEXT_FREE_CTX)PvfsNotifyCleanIrpListFree);
BAIL_ON_NT_STATUS(ntError);
ntError = PvfsAddWorkItem(gpPvfsInternalWorkQueue, (PVOID)pWorkCtx);
BAIL_ON_NT_STATUS(ntError);
pWorkCtx = NULL;
cleanup:
PvfsFreeWorkContext(&pWorkCtx);
return ntError;
error:
if (pIrpCtx)
{
PvfsReleaseIrpContext(&pIrpCtx);
}
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static NTSTATUS
PvfsNotifyCleanIrpList(
PVOID pContext
)
{
NTSTATUS ntError = STATUS_SUCCESS;
PPVFS_IRP_CONTEXT pIrpCtx = (PPVFS_IRP_CONTEXT)pContext;
PPVFS_FCB pFcb = PvfsReferenceFCB(pIrpCtx->pFcb);
BOOLEAN bFcbLocked = FALSE;
PPVFS_NOTIFY_FILTER_RECORD pFilter = NULL;
PLW_LIST_LINKS pFilterLink = NULL;
PLW_LIST_LINKS pNextLink = NULL;
BOOLEAN bFound = FALSE;
LWIO_LOCK_MUTEX(bFcbLocked, &pFcb->mutexNotify);
pFilterLink = PvfsListTraverse(pFcb->pNotifyListIrp, NULL);
if (pFilterLink == NULL)
{
ntError = STATUS_INTERNAL_ERROR;
BAIL_ON_NT_STATUS(ntError);
}
while (pFilterLink)
{
pFilter = LW_STRUCT_FROM_FIELD(
pFilterLink,
PVFS_NOTIFY_FILTER_RECORD,
NotifyList);
pNextLink = PvfsListTraverse(pFcb->pNotifyListIrp, pFilterLink);
if (pFilter->pIrpContext != pIrpCtx)
{
pFilterLink = pNextLink;
continue;
}
bFound = TRUE;
PvfsListRemoveItem(pFcb->pNotifyListIrp, pFilterLink);
pFilterLink = NULL;
pFilter->pIrpContext->pIrp->IoStatusBlock.Status = STATUS_CANCELLED;
PvfsAsyncIrpComplete(pFilter->pIrpContext);
PvfsFreeNotifyRecord(&pFilter);
/* Can only be one IrpContext match so we are done */
}
if (!bFound)
{
pIrpCtx->pIrp->IoStatusBlock.Status = STATUS_CANCELLED;
PvfsAsyncIrpComplete(pIrpCtx);
}
cleanup:
LWIO_UNLOCK_MUTEX(bFcbLocked, &pFcb->mutexNotify);
if (pFcb)
{
PvfsReleaseFCB(&pFcb);
}
if (pIrpCtx)
{
PvfsReleaseIrpContext(&pIrpCtx);
}
return ntError;
error:
goto cleanup;
}
/*****************************************************************************
****************************************************************************/
static VOID
PvfsNotifyCleanIrpListFree(
PVOID *ppContext
)
{
/* No op -- context released in PvfsNotifyCleanIrpList */
return;
}
/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/