winfsp/src/sys/flush.c
2016-04-25 14:29:05 -07:00

184 lines
5.7 KiB
C

/**
* @file sys/flush.c
*
* @copyright 2015-2016 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License version 3 as published by the
* Free Software Foundation.
*
* Licensees holding a valid commercial license may use this file in
* accordance with the commercial license agreement provided with the
* software.
*/
#include <sys/driver.h>
static NTSTATUS FspFsvolFlushBuffers(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_IOCMPL_DISPATCH FspFsvolFlushBuffersComplete;
static FSP_IOP_REQUEST_FINI FspFsvolFlushBuffersRequestFini;
FSP_DRIVER_DISPATCH FspFlushBuffers;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFsvolFlushBuffers)
#pragma alloc_text(PAGE, FspFsvolFlushBuffersComplete)
#pragma alloc_text(PAGE, FspFsvolFlushBuffersRequestFini)
#pragma alloc_text(PAGE, FspFlushBuffers)
#endif
enum
{
/* FlushBuffers */
RequestFileNode = 0,
RequestFlushResult = 1,
};
static NTSTATUS FspFsvolFlushBuffers(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
NTSTATUS Result, FlushResult;
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
FSP_FILE_NODE **FileNodes;
ULONG FileNodeCount, Index;
PIRP TopLevelIrp;
IO_STATUS_BLOCK IoStatus;
FSP_FSCTL_TRANSACT_REQ *Request;
/*
* A flush request on the volume (or the root directory according to FastFat)
* is a request to flush the whole volume.
*/
if (!FspFileNodeIsValid(FileNode) || FileNode->IsRootDirectory)
{
Result = FspFileNodeCopyList(FsvolDeviceObject, &FileNodes, &FileNodeCount);
if (!NT_SUCCESS(Result))
return Result;
/* reset the top-level IRP to avoid deadlock on the FileNodes' resources */
TopLevelIrp = IoGetTopLevelIrp();
IoSetTopLevelIrp(0);
/*
* Enumerate in reverse order so that files are flushed before containing directories.
* This would be useful if we ever started flushing directories, but since we do not
* it is not as important now.
*/
FlushResult = STATUS_SUCCESS;
for (Index = FileNodeCount - 1; FileNodeCount > Index; Index--)
if (!FileNodes[Index]->IsDirectory)
{
Result = FspCcFlushCache(&FileNodes[Index]->NonPaged->SectionObjectPointers,
0, 0, &IoStatus);
if (!NT_SUCCESS(Result) && NT_SUCCESS(FlushResult))
FlushResult = Result;
}
IoSetTopLevelIrp(TopLevelIrp);
FspFileNodeDeleteList(FileNodes, FileNodeCount);
Result = FspIopCreateRequest(Irp, 0, 0, &Request);
if (!NT_SUCCESS(Result))
return Result;
/* a NULL UserContext indicates to user-mode to flush the whole volume! */
Request->Kind = FspFsctlTransactFlushBuffersKind;
//Request->Req.FlushBuffers.UserContext = 0;
//Request->Req.FlushBuffers.UserContext2 = 0;
FspIopRequestContext(Request, RequestFileNode) = 0;
FspIopRequestContext(Request, RequestFlushResult) = (PVOID)(UINT_PTR)FlushResult;
return FSP_STATUS_IOQ_POST;
}
else
{
ASSERT(FileNode == FileDesc->FileNode);
/* cannot really flush directories but we will say we did it! */
if (FileNode->IsDirectory)
return STATUS_SUCCESS;
FspFileNodeAcquireExclusive(FileNode, Full);
Result = FspCcFlushCache(FileObject->SectionObjectPointer, 0, 0, &IoStatus);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Result = FspIopCreateRequestEx(Irp, 0, 0, FspFsvolFlushBuffersRequestFini, &Request);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Request->Kind = FspFsctlTransactFlushBuffersKind;
Request->Req.FlushBuffers.UserContext = FileNode->UserContext;
Request->Req.FlushBuffers.UserContext2 = FileDesc->UserContext2;
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestFileNode) = FileNode;
FspIopRequestContext(Request, RequestFlushResult) = 0;
return FSP_STATUS_IOQ_POST;
}
}
NTSTATUS FspFsvolFlushBuffersComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response)
{
FSP_ENTER_IOC(PAGED_CODE());
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
NTSTATUS FlushResult = (NTSTATUS)(UINT_PTR)FspIopRequestContext(Request, RequestFlushResult);
Irp->IoStatus.Information = 0;
if (!NT_SUCCESS(Response->IoStatus.Status))
Result = Response->IoStatus.Status;
else if (!NT_SUCCESS(FlushResult))
Result = FlushResult;
else
Result = STATUS_SUCCESS;
FSP_LEAVE_IOC("FileObject=%p",
IrpSp->FileObject);
}
static VOID FspFsvolFlushBuffersRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4])
{
PAGED_CODE();
FSP_FILE_NODE *FileNode = Context[RequestFileNode];
if (0 != FileNode)
FspFileNodeReleaseOwner(FileNode, Full, Request);
}
NTSTATUS FspFlushBuffers(
PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
FSP_ENTER_MJ(PAGED_CODE());
switch (FspDeviceExtension(DeviceObject)->Kind)
{
case FspFsvolDeviceExtensionKind:
FSP_RETURN(Result = FspFsvolFlushBuffers(DeviceObject, Irp, IrpSp));
default:
FSP_RETURN(Result = STATUS_INVALID_DEVICE_REQUEST);
}
FSP_LEAVE_MJ("FileObject=%p",
IrpSp->FileObject);
}