winfsp/src/sys/file.c
2016-01-30 22:02:30 -08:00

491 lines
15 KiB
C

/**
* @file sys/file.c
*
* @copyright 2015 Bill Zissimopoulos
*/
#include <sys/driver.h>
NTSTATUS FspFileNodeCreate(PDEVICE_OBJECT DeviceObject,
ULONG ExtraSize, FSP_FILE_NODE **PFileNode);
VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode);
VOID FspFileNodeAcquireSharedF(FSP_FILE_NODE *FileNode, ULONG Flags);
BOOLEAN FspFileNodeTryAcquireSharedF(FSP_FILE_NODE *FileNode, ULONG Flags);
VOID FspFileNodeAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags);
BOOLEAN FspFileNodeTryAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags);
VOID FspFileNodeSetOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner);
VOID FspFileNodeReleaseF(FSP_FILE_NODE *FileNode, ULONG Flags);
VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner);
FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
UINT32 GrantedAccess, UINT32 ShareAccess, BOOLEAN DeleteOnClose, NTSTATUS *PResult);
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
PBOOLEAN PDeletePending);
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, const FSP_FSCTL_FILE_INFO *FileInfo);
NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc);
VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc);
VOID FspFileObjectSetSizes(PFILE_OBJECT FileObject,
UINT64 AllocationSize, UINT64 FileSize);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFileNodeCreate)
#pragma alloc_text(PAGE, FspFileNodeDelete)
#pragma alloc_text(PAGE, FspFileNodeAcquireSharedF)
#pragma alloc_text(PAGE, FspFileNodeTryAcquireSharedF)
#pragma alloc_text(PAGE, FspFileNodeAcquireExclusiveF)
#pragma alloc_text(PAGE, FspFileNodeTryAcquireExclusiveF)
#pragma alloc_text(PAGE, FspFileNodeSetOwnerF)
#pragma alloc_text(PAGE, FspFileNodeReleaseF)
#pragma alloc_text(PAGE, FspFileNodeReleaseOwnerF)
#pragma alloc_text(PAGE, FspFileNodeOpen)
#pragma alloc_text(PAGE, FspFileNodeClose)
#pragma alloc_text(PAGE, FspFileNodeGetFileInfo)
#pragma alloc_text(PAGE, FspFileNodeTryGetFileInfo)
#pragma alloc_text(PAGE, FspFileNodeSetFileInfo)
#pragma alloc_text(PAGE, FspFileDescCreate)
#pragma alloc_text(PAGE, FspFileDescDelete)
#pragma alloc_text(PAGE, FspFileObjectSetSizes)
#endif
NTSTATUS FspFileNodeCreate(PDEVICE_OBJECT DeviceObject,
ULONG ExtraSize, FSP_FILE_NODE **PFileNode)
{
PAGED_CODE();
*PFileNode = 0;
FSP_FILE_NODE_NONPAGED *NonPaged = FspAllocNonPaged(sizeof *NonPaged);
if (0 == NonPaged)
return STATUS_INSUFFICIENT_RESOURCES;
FSP_FILE_NODE *FileNode = FspAlloc(sizeof *FileNode + ExtraSize);
if (0 == FileNode)
{
FspFree(NonPaged);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(NonPaged, sizeof *NonPaged);
ExInitializeResourceLite(&NonPaged->Resource);
ExInitializeResourceLite(&NonPaged->PagingIoResource);
ExInitializeFastMutex(&NonPaged->HeaderFastMutex);
KeInitializeSpinLock(&NonPaged->InfoSpinLock);
RtlZeroMemory(FileNode, sizeof *FileNode + ExtraSize);
FileNode->Header.NodeTypeCode = FspFileNodeFileKind;
FileNode->Header.NodeByteSize = sizeof *FileNode;
FileNode->Header.IsFastIoPossible = FastIoIsQuestionable;
FileNode->Header.Resource = &NonPaged->Resource;
FileNode->Header.PagingIoResource = &NonPaged->PagingIoResource;
FileNode->Header.ValidDataLength.QuadPart = MAXLONGLONG;
/* disable ValidDataLength functionality */
FsRtlSetupAdvancedHeader(&FileNode->Header, &NonPaged->HeaderFastMutex);
FileNode->NonPaged = NonPaged;
FileNode->RefCount = 1;
FileNode->FsvolDeviceObject = DeviceObject;
FspDeviceReference(FileNode->FsvolDeviceObject);
RtlInitEmptyUnicodeString(&FileNode->FileName, FileNode->FileNameBuf, (USHORT)ExtraSize);
*PFileNode = FileNode;
return STATUS_SUCCESS;
}
VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode)
{
PAGED_CODE();
FsRtlTeardownPerStreamContexts(&FileNode->Header);
FspDeviceDereference(FileNode->FsvolDeviceObject);
ExDeleteResourceLite(&FileNode->NonPaged->PagingIoResource);
ExDeleteResourceLite(&FileNode->NonPaged->Resource);
FspFree(FileNode->NonPaged);
FspFree(FileNode);
}
VOID FspFileNodeAcquireSharedF(FSP_FILE_NODE *FileNode, ULONG Flags)
{
PAGED_CODE();
if (Flags & FspFileNodeAcquireMain)
ExAcquireResourceSharedLite(FileNode->Header.Resource, TRUE);
if (Flags & FspFileNodeAcquirePgio)
ExAcquireResourceSharedLite(FileNode->Header.PagingIoResource, TRUE);
}
BOOLEAN FspFileNodeTryAcquireSharedF(FSP_FILE_NODE *FileNode, ULONG Flags)
{
PAGED_CODE();
BOOLEAN Result = FALSE;
if (Flags & FspFileNodeAcquireMain)
{
Result = ExAcquireResourceSharedLite(FileNode->Header.Resource, FALSE);
if (!Result)
return FALSE;
}
if (Flags & FspFileNodeAcquirePgio)
{
Result = ExAcquireResourceSharedLite(FileNode->Header.PagingIoResource, FALSE);
if (!Result)
{
if (Flags & FspFileNodeAcquireMain)
ExReleaseResourceLite(FileNode->Header.Resource);
return FALSE;
}
}
return Result;
}
VOID FspFileNodeAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags)
{
PAGED_CODE();
if (Flags & FspFileNodeAcquireMain)
ExAcquireResourceExclusiveLite(FileNode->Header.Resource, TRUE);
if (Flags & FspFileNodeAcquirePgio)
ExAcquireResourceExclusiveLite(FileNode->Header.PagingIoResource, TRUE);
}
BOOLEAN FspFileNodeTryAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags)
{
PAGED_CODE();
BOOLEAN Result = FALSE;
if (Flags & FspFileNodeAcquireMain)
{
Result = ExAcquireResourceExclusiveLite(FileNode->Header.Resource, FALSE);
if (!Result)
return FALSE;
}
if (Flags & FspFileNodeAcquirePgio)
{
Result = ExAcquireResourceExclusiveLite(FileNode->Header.PagingIoResource, FALSE);
if (!Result)
{
if (Flags & FspFileNodeAcquireMain)
ExReleaseResourceLite(FileNode->Header.Resource);
return FALSE;
}
}
return Result;
}
VOID FspFileNodeSetOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner)
{
PAGED_CODE();
Owner = (PVOID)((UINT_PTR)Owner | 3);
if (Flags & FspFileNodeAcquireMain)
ExSetResourceOwnerPointer(FileNode->Header.Resource, Owner);
if (Flags & FspFileNodeAcquirePgio)
ExSetResourceOwnerPointer(FileNode->Header.PagingIoResource, Owner);
}
VOID FspFileNodeReleaseF(FSP_FILE_NODE *FileNode, ULONG Flags)
{
PAGED_CODE();
if (Flags & FspFileNodeAcquirePgio)
ExReleaseResourceLite(FileNode->Header.PagingIoResource);
if (Flags & FspFileNodeAcquireMain)
ExReleaseResourceLite(FileNode->Header.Resource);
}
VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner)
{
PAGED_CODE();
Owner = (PVOID)((UINT_PTR)Owner | 3);
if (Flags & FspFileNodeAcquirePgio)
{
if (ExIsResourceAcquiredLite(FileNode->Header.PagingIoResource))
ExReleaseResourceLite(FileNode->Header.PagingIoResource);
else
ExReleaseResourceForThreadLite(FileNode->Header.PagingIoResource, (ERESOURCE_THREAD)Owner);
}
if (Flags & FspFileNodeAcquireMain)
{
if (ExIsResourceAcquiredLite(FileNode->Header.Resource))
ExReleaseResourceLite(FileNode->Header.Resource);
else
ExReleaseResourceForThreadLite(FileNode->Header.Resource, (ERESOURCE_THREAD)Owner);
}
}
FSP_FILE_NODE *FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
UINT32 GrantedAccess, UINT32 ShareAccess, BOOLEAN DeleteOnClose, NTSTATUS *PResult)
{
/*
* Attempt to insert our FileNode into the volume device's generic table.
* If an FileNode with the same UserContext already exists, then use that
* FileNode instead.
*/
PAGED_CODE();
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
FSP_FILE_NODE *OpenedFileNode;
BOOLEAN Inserted;
NTSTATUS Result;
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
OpenedFileNode = FspFsvolDeviceInsertContext(FsvolDeviceObject,
FileNode->UserContext, FileNode, &FileNode->ElementStorage, &Inserted);
ASSERT(0 != OpenedFileNode);
if (Inserted)
{
/*
* The new FileNode was inserted into the Context table. Set its share access
* and reference and open it. There should be (at least) two references to this
* FileNode, one from our caller and one from the Context table.
*/
ASSERT(OpenedFileNode == FileNode);
IoSetShareAccess(GrantedAccess, ShareAccess, FileObject,
&OpenedFileNode->ShareAccess);
}
else
{
/*
* The new FileNode was NOT inserted into the Context table. Instead we are
* opening a prior FileNode that we found in the table.
*/
ASSERT(OpenedFileNode != FileNode);
if (OpenedFileNode->Flags.DeletePending)
{
Result = STATUS_DELETE_PENDING;
goto exit;
}
/*
* FastFat says to do the following on Vista and above.
*
* Quote:
* Do an extra test for writeable user sections if the user did not allow
* write sharing - this is neccessary since a section may exist with no handles
* open to the file its based against.
*/
if (!FlagOn(ShareAccess, FILE_SHARE_WRITE) &&
FlagOn(GrantedAccess,
FILE_EXECUTE | FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE) &&
MmDoesFileHaveUserWritableReferences(&OpenedFileNode->NonPaged->SectionObjectPointers))
{
Result = STATUS_SHARING_VIOLATION;
goto exit;
}
/* share access check */
Result = IoCheckShareAccess(GrantedAccess, ShareAccess, FileObject,
&OpenedFileNode->ShareAccess, TRUE);
exit:
if (!NT_SUCCESS(Result))
{
if (0 != PResult)
*PResult = Result;
OpenedFileNode = 0;
}
}
if (0 != OpenedFileNode)
{
FspFileNodeReference(OpenedFileNode);
OpenedFileNode->OpenCount++;
if (DeleteOnClose)
OpenedFileNode->Flags.DeleteOnClose = TRUE;
}
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
return OpenedFileNode;
}
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
PBOOLEAN PDeletePending)
{
/*
* Close the FileNode. If the OpenCount becomes zero remove it
* from the Context table.
*/
PAGED_CODE();
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
BOOLEAN Deleted = FALSE, DeletePending;
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
if (FileNode->Flags.DeleteOnClose)
FileNode->Flags.DeletePending = TRUE;
DeletePending = 0 != FileNode->Flags.DeletePending;
IoRemoveShareAccess(FileObject, &FileNode->ShareAccess);
if (0 == --FileNode->OpenCount)
FspFsvolDeviceDeleteContext(FsvolDeviceObject, FileNode->UserContext, &Deleted);
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
if (Deleted)
FspFileNodeDereference(FileNode);
if (0 != PDeletePending)
*PDeletePending = Deleted && DeletePending;
}
static VOID FspFileNodeGetFileInfoNp(FSP_FILE_NODE_NONPAGED *FileNodeNp, FSP_FSCTL_FILE_INFO *FileInfoNp)
{
// !PAGED_CODE();
KIRQL Irql;
KeAcquireSpinLock(&FileNodeNp->InfoSpinLock, &Irql);
FileInfoNp->FileAttributes = FileNodeNp->FileAttributes;
FileInfoNp->ReparseTag = FileNodeNp->ReparseTag;
FileInfoNp->CreationTime = FileNodeNp->CreationTime;
FileInfoNp->LastAccessTime = FileNodeNp->LastAccessTime;
FileInfoNp->LastWriteTime = FileNodeNp->LastWriteTime;
FileInfoNp->ChangeTime = FileNodeNp->ChangeTime;
KeReleaseSpinLock(&FileNodeNp->InfoSpinLock, Irql);
}
static BOOLEAN FspFileNodeTryGetFileInfoNp(FSP_FILE_NODE_NONPAGED *FileNodeNp,
FSP_FSCTL_FILE_INFO *FileInfoNp)
{
// !PAGED_CODE();
KIRQL Irql;
BOOLEAN Result;
KeAcquireSpinLock(&FileNodeNp->InfoSpinLock, &Irql);
if (0 < FileNodeNp->InfoExpirationTime && KeQueryInterruptTime() < FileNodeNp->InfoExpirationTime)
{
FileInfoNp->FileAttributes = FileNodeNp->FileAttributes;
FileInfoNp->ReparseTag = FileNodeNp->ReparseTag;
FileInfoNp->CreationTime = FileNodeNp->CreationTime;
FileInfoNp->LastAccessTime = FileNodeNp->LastAccessTime;
FileInfoNp->LastWriteTime = FileNodeNp->LastWriteTime;
FileInfoNp->ChangeTime = FileNodeNp->ChangeTime;
Result = TRUE;
}
else
Result = FALSE;
KeReleaseSpinLock(&FileNodeNp->InfoSpinLock, Irql);
return Result;
}
static VOID FspFileNodeSetFileInfoNp(FSP_FILE_NODE_NONPAGED *FileNodeNp,
const FSP_FSCTL_FILE_INFO *FileInfoNp, UINT64 FileInfoTimeout)
{
// !PAGED_CODE();
KIRQL Irql;
KeAcquireSpinLock(&FileNodeNp->InfoSpinLock, &Irql);
FileNodeNp->FileAttributes = FileInfoNp->FileAttributes;
FileNodeNp->ReparseTag = FileInfoNp->ReparseTag;
FileNodeNp->CreationTime = FileInfoNp->CreationTime;
FileNodeNp->LastAccessTime = FileInfoNp->LastAccessTime;
FileNodeNp->LastWriteTime = FileInfoNp->LastWriteTime;
FileNodeNp->ChangeTime = FileInfoNp->ChangeTime;
FileNodeNp->InfoExpirationTime = 0 != FileInfoTimeout ?
KeQueryInterruptTime() + FileInfoTimeout : 0;
KeReleaseSpinLock(&FileNodeNp->InfoSpinLock, Irql);
}
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo)
{
PAGED_CODE();
FSP_FILE_NODE_NONPAGED *FileNodeNp = FileNode->NonPaged;
FSP_FSCTL_FILE_INFO FileInfoNp;
FspFileNodeGetFileInfoNp(FileNodeNp, &FileInfoNp);
*FileInfo = FileInfoNp;
}
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo)
{
PAGED_CODE();
FSP_FILE_NODE_NONPAGED *FileNodeNp = FileNode->NonPaged;
FSP_FSCTL_FILE_INFO FileInfoNp;
if (!FspFileNodeTryGetFileInfoNp(FileNodeNp, &FileInfoNp))
return FALSE;
*FileInfo = FileInfoNp;
return TRUE;
}
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, const FSP_FSCTL_FILE_INFO *FileInfo)
{
PAGED_CODE();
UINT64 FileInfoTimeout = FspFsvolDeviceExtension(FileNode->FsvolDeviceObject)->
VolumeParams.FileInfoTimeout * 10000ULL;
FSP_FILE_NODE_NONPAGED *FileNodeNp = FileNode->NonPaged;
FSP_FSCTL_FILE_INFO FileInfoNp = *FileInfo;
FspFileNodeSetFileInfoNp(FileNodeNp, &FileInfoNp, FileInfoTimeout);
}
NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc)
{
PAGED_CODE();
*PFileDesc = FspAlloc(sizeof(FSP_FILE_DESC));
if (0 == *PFileDesc)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(*PFileDesc, sizeof(FSP_FILE_DESC));
return STATUS_SUCCESS;
}
VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc)
{
PAGED_CODE();
FspFree(FileDesc);
}
VOID FspFileObjectSetSizes(PFILE_OBJECT FileObject,
UINT64 AllocationSize, UINT64 FileSize)
{
PAGED_CODE();
FSP_FILE_NODE *FileNode = FileObject->FsContext;
ASSERT(ExIsResourceAcquiredExclusiveLite(FileNode->Header.PagingIoResource));
FileNode->Header.AllocationSize.QuadPart = AllocationSize;
FileNode->Header.FileSize.QuadPart = FileSize;
FileNode->CcStatus = FspCcSetFileSizes(
FileObject, (PCC_FILE_SIZES)&FileNode->Header.AllocationSize);
}