mirror of
https://github.com/winfsp/winfsp.git
synced 2025-04-22 16:33:02 -05:00
2143 lines
74 KiB
C
2143 lines
74 KiB
C
/**
|
|
* @file sys/file.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
|
|
* 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>
|
|
|
|
NTSTATUS FspFileNodeCopyList(PDEVICE_OBJECT DeviceObject,
|
|
FSP_FILE_NODE ***PFileNodes, PULONG PFileNodeCount);
|
|
VOID FspFileNodeDeleteList(FSP_FILE_NODE **FileNodes, ULONG FileNodeCount);
|
|
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, BOOLEAN Wait);
|
|
VOID FspFileNodeAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags);
|
|
BOOLEAN FspFileNodeTryAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags, BOOLEAN Wait);
|
|
VOID FspFileNodeConvertExclusiveToSharedF(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);
|
|
NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
|
|
UINT32 GrantedAccess, UINT32 ShareAccess,
|
|
FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason);
|
|
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
|
|
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
|
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
|
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
|
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
|
|
NTSTATUS FspFileNodeFlushAndPurgeCache(FSP_FILE_NODE *FileNode,
|
|
UINT64 FlushOffset64, ULONG FlushLength, BOOLEAN FlushAndPurge);
|
|
VOID FspFileNodeOverwriteStreams(FSP_FILE_NODE *FileNode);
|
|
NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
|
|
PDEVICE_OBJECT FsvolDeviceObject,
|
|
PIRP OplockIrp,
|
|
FSP_FILE_NODE *FileNode,
|
|
ULONG AcquireFlags,
|
|
PUNICODE_STRING StreamFileName);
|
|
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
|
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
|
PUNICODE_STRING FileName, BOOLEAN CheckingOldName);
|
|
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName);
|
|
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, PFILE_OBJECT CcFileObject,
|
|
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose);
|
|
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
|
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber);
|
|
BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize);
|
|
VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
|
BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
|
ULONG SecurityChangeNumber);
|
|
BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize);
|
|
VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
|
BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
|
ULONG DirInfoChangeNumber);
|
|
static VOID FspFileNodeInvalidateDirInfo(FSP_FILE_NODE *FileNode);
|
|
static VOID FspFileNodeInvalidateDirInfoByName(PDEVICE_OBJECT FsvolDeviceObject,
|
|
PUNICODE_STRING FileName);
|
|
VOID FspFileNodeInvalidateParentDirInfo(FSP_FILE_NODE *FileNode);
|
|
BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize);
|
|
VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
|
BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
|
ULONG StreamInfoChangeNumber);
|
|
VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode);
|
|
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
|
|
BOOLEAN InvalidateCaches);
|
|
NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp);
|
|
static NTSTATUS FspFileNodeCompleteLockIrp(PVOID Context, PIRP Irp);
|
|
NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc);
|
|
VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc);
|
|
NTSTATUS FspFileDescResetDirectoryPattern(FSP_FILE_DESC *FileDesc,
|
|
PUNICODE_STRING FileName, BOOLEAN Reset);
|
|
NTSTATUS FspMainFileOpen(
|
|
PDEVICE_OBJECT FsvolDeviceObject,
|
|
PDEVICE_OBJECT DeviceObjectHint,
|
|
PUNICODE_STRING MainFileName, BOOLEAN CaseSensitive,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
ULONG FileAttributes,
|
|
ULONG Disposition,
|
|
PHANDLE PMainFileHandle,
|
|
PFILE_OBJECT *PMainFileObject);
|
|
NTSTATUS FspMainFileClose(
|
|
HANDLE MainFileHandle,
|
|
PFILE_OBJECT MainFileObject);
|
|
VOID FspFileNodeOplockPrepare(PVOID Context, PIRP Irp);
|
|
VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, FspFileNodeCopyList)
|
|
#pragma alloc_text(PAGE, FspFileNodeDeleteList)
|
|
#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, FspFileNodeConvertExclusiveToSharedF)
|
|
#pragma alloc_text(PAGE, FspFileNodeSetOwnerF)
|
|
#pragma alloc_text(PAGE, FspFileNodeReleaseF)
|
|
#pragma alloc_text(PAGE, FspFileNodeReleaseOwnerF)
|
|
#pragma alloc_text(PAGE, FspFileNodeOpen)
|
|
#pragma alloc_text(PAGE, FspFileNodeCleanup)
|
|
#pragma alloc_text(PAGE, FspFileNodeCleanupComplete)
|
|
#pragma alloc_text(PAGE, FspFileNodeClose)
|
|
#pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache)
|
|
#pragma alloc_text(PAGE, FspFileNodeOverwriteStreams)
|
|
#pragma alloc_text(PAGE, FspFileNodeCheckBatchOplocksOnAllStreams)
|
|
#pragma alloc_text(PAGE, FspFileNodeRenameCheck)
|
|
#pragma alloc_text(PAGE, FspFileNodeRename)
|
|
#pragma alloc_text(PAGE, FspFileNodeGetFileInfo)
|
|
#pragma alloc_text(PAGE, FspFileNodeTryGetFileInfo)
|
|
#pragma alloc_text(PAGE, FspFileNodeSetFileInfo)
|
|
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfo)
|
|
#pragma alloc_text(PAGE, FspFileNodeReferenceSecurity)
|
|
#pragma alloc_text(PAGE, FspFileNodeSetSecurity)
|
|
#pragma alloc_text(PAGE, FspFileNodeTrySetSecurity)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeReferenceDirInfo)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeSetDirInfo)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeTrySetDirInfo)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeInvalidateDirInfo)
|
|
#pragma alloc_text(PAGE, FspFileNodeInvalidateDirInfoByName)
|
|
#pragma alloc_text(PAGE, FspFileNodeInvalidateParentDirInfo)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeReferenceStreamInfo)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo)
|
|
// !#pragma alloc_text(PAGE, FspFileNodeInvalidateStreamInfo)
|
|
#pragma alloc_text(PAGE, FspFileNodeNotifyChange)
|
|
#pragma alloc_text(PAGE, FspFileNodeProcessLockIrp)
|
|
#pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp)
|
|
#pragma alloc_text(PAGE, FspFileDescCreate)
|
|
#pragma alloc_text(PAGE, FspFileDescDelete)
|
|
#pragma alloc_text(PAGE, FspFileDescResetDirectoryPattern)
|
|
#pragma alloc_text(PAGE, FspMainFileOpen)
|
|
#pragma alloc_text(PAGE, FspMainFileClose)
|
|
#pragma alloc_text(PAGE, FspFileNodeOplockPrepare)
|
|
#pragma alloc_text(PAGE, FspFileNodeOplockComplete)
|
|
#endif
|
|
|
|
#define FSP_FILE_NODE_GET_FLAGS() \
|
|
PIRP Irp = IoGetTopLevelIrp(); \
|
|
BOOLEAN IrpValid = (PIRP)FSRTL_MAX_TOP_LEVEL_IRP_FLAG < Irp &&\
|
|
IO_TYPE_IRP == Irp->Type; \
|
|
if (IrpValid) \
|
|
Flags &= ~FspIrpTopFlags(Irp)
|
|
#define FSP_FILE_NODE_ASSERT_FLAGS_CLR()\
|
|
ASSERT(IrpValid ? (0 == (FspIrpFlags(Irp) & Flags)) : TRUE)
|
|
#define FSP_FILE_NODE_ASSERT_FLAGS_SET()\
|
|
ASSERT(IrpValid ? (Flags == (FspIrpFlags(Irp) & Flags)) : TRUE)
|
|
#define FSP_FILE_NODE_SET_FLAGS() \
|
|
if (IrpValid) \
|
|
FspIrpSetFlags(Irp, FspIrpFlags(Irp) | Flags)
|
|
#define FSP_FILE_NODE_CLR_FLAGS() \
|
|
if (IrpValid) \
|
|
FspIrpSetFlags(Irp, FspIrpFlags(Irp) & (~Flags & 3))
|
|
|
|
NTSTATUS FspFileNodeCopyList(PDEVICE_OBJECT DeviceObject,
|
|
FSP_FILE_NODE ***PFileNodes, PULONG PFileNodeCount)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS Result;
|
|
ULONG Index;
|
|
|
|
FspFsvolDeviceLockContextTable(DeviceObject);
|
|
Result = FspFsvolDeviceCopyContextByNameList(DeviceObject, PFileNodes, PFileNodeCount);
|
|
if (NT_SUCCESS(Result))
|
|
{
|
|
for (Index = 0; *PFileNodeCount > Index; Index++)
|
|
FspFileNodeReference((*PFileNodes)[Index]);
|
|
}
|
|
FspFsvolDeviceUnlockContextTable(DeviceObject);
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID FspFileNodeDeleteList(FSP_FILE_NODE **FileNodes, ULONG FileNodeCount)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ULONG Index;
|
|
|
|
for (Index = 0; FileNodeCount > Index; Index++)
|
|
FspFileNodeDereference(FileNodes[Index]);
|
|
|
|
FspFsvolDeviceDeleteContextByNameList(FileNodes, FileNodeCount);
|
|
}
|
|
|
|
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->NpInfoSpinLock);
|
|
|
|
RtlZeroMemory(FileNode, sizeof *FileNode + ExtraSize);
|
|
FileNode->Header.NodeTypeCode = FspFileNodeFileKind;
|
|
FileNode->Header.NodeByteSize = sizeof *FileNode;
|
|
FileNode->Header.IsFastIoPossible = FastIoIsNotPossible;
|
|
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);
|
|
|
|
FsRtlInitializeFileLock(&FileNode->FileLock, FspFileNodeCompleteLockIrp, 0);
|
|
FsRtlInitializeOplock(FspFileNodeAddrOfOplock(FileNode));
|
|
|
|
*PFileNode = FileNode;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
|
|
FsRtlUninitializeOplock(FspFileNodeAddrOfOplock(FileNode));
|
|
FsRtlUninitializeFileLock(&FileNode->FileLock);
|
|
|
|
FsRtlTeardownPerStreamContexts(&FileNode->Header);
|
|
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo);
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo);
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security);
|
|
|
|
FspDeviceDereference(FileNode->FsvolDeviceObject);
|
|
|
|
if (0 != FileNode->ExternalFileName)
|
|
FspFree(FileNode->ExternalFileName);
|
|
|
|
ExDeleteResourceLite(&FileNode->NonPaged->PagingIoResource);
|
|
ExDeleteResourceLite(&FileNode->NonPaged->Resource);
|
|
FspFree(FileNode->NonPaged);
|
|
|
|
FspFree(FileNode);
|
|
}
|
|
|
|
VOID FspFileNodeAcquireSharedF(FSP_FILE_NODE *FileNode, ULONG Flags)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
FSP_FILE_NODE_ASSERT_FLAGS_CLR();
|
|
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
ExAcquireResourceSharedLite(FileNode->Header.Resource, TRUE);
|
|
|
|
if (Flags & FspFileNodeAcquirePgio)
|
|
ExAcquireResourceSharedLite(FileNode->Header.PagingIoResource, TRUE);
|
|
|
|
FSP_FILE_NODE_SET_FLAGS();
|
|
}
|
|
|
|
BOOLEAN FspFileNodeTryAcquireSharedF(FSP_FILE_NODE *FileNode, ULONG Flags, BOOLEAN Wait)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
FSP_FILE_NODE_ASSERT_FLAGS_CLR();
|
|
|
|
BOOLEAN Result = TRUE;
|
|
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
{
|
|
Result = ExAcquireResourceSharedLite(FileNode->Header.Resource, Wait);
|
|
if (!Result)
|
|
return FALSE;
|
|
}
|
|
|
|
if (Flags & FspFileNodeAcquirePgio)
|
|
{
|
|
Result = ExAcquireResourceSharedLite(FileNode->Header.PagingIoResource, Wait);
|
|
if (!Result)
|
|
{
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
ExReleaseResourceLite(FileNode->Header.Resource);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (Result)
|
|
FSP_FILE_NODE_SET_FLAGS();
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID FspFileNodeAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
FSP_FILE_NODE_ASSERT_FLAGS_CLR();
|
|
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
ExAcquireResourceExclusiveLite(FileNode->Header.Resource, TRUE);
|
|
|
|
if (Flags & FspFileNodeAcquirePgio)
|
|
ExAcquireResourceExclusiveLite(FileNode->Header.PagingIoResource, TRUE);
|
|
|
|
FSP_FILE_NODE_SET_FLAGS();
|
|
}
|
|
|
|
BOOLEAN FspFileNodeTryAcquireExclusiveF(FSP_FILE_NODE *FileNode, ULONG Flags, BOOLEAN Wait)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
FSP_FILE_NODE_ASSERT_FLAGS_CLR();
|
|
|
|
BOOLEAN Result = TRUE;
|
|
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
{
|
|
Result = ExAcquireResourceExclusiveLite(FileNode->Header.Resource, Wait);
|
|
if (!Result)
|
|
return FALSE;
|
|
}
|
|
|
|
if (Flags & FspFileNodeAcquirePgio)
|
|
{
|
|
Result = ExAcquireResourceExclusiveLite(FileNode->Header.PagingIoResource, Wait);
|
|
if (!Result)
|
|
{
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
ExReleaseResourceLite(FileNode->Header.Resource);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (Result)
|
|
FSP_FILE_NODE_SET_FLAGS();
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID FspFileNodeConvertExclusiveToSharedF(FSP_FILE_NODE *FileNode, ULONG Flags)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
|
|
if (Flags & FspFileNodeAcquirePgio)
|
|
ExConvertExclusiveToSharedLite(FileNode->Header.PagingIoResource);
|
|
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
ExConvertExclusiveToSharedLite(FileNode->Header.Resource);
|
|
}
|
|
|
|
VOID FspFileNodeSetOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
|
|
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 (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
FSP_FILE_NODE_ASSERT_FLAGS_SET();
|
|
|
|
if (Flags & FspFileNodeAcquirePgio)
|
|
ExReleaseResourceLite(FileNode->Header.PagingIoResource);
|
|
|
|
if (Flags & FspFileNodeAcquireMain)
|
|
ExReleaseResourceLite(FileNode->Header.Resource);
|
|
|
|
FSP_FILE_NODE_CLR_FLAGS();
|
|
}
|
|
|
|
VOID FspFileNodeReleaseOwnerF(FSP_FILE_NODE *FileNode, ULONG Flags, PVOID Owner)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FILE_NODE_GET_FLAGS();
|
|
FSP_FILE_NODE_ASSERT_FLAGS_SET();
|
|
|
|
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_CLR_FLAGS();
|
|
}
|
|
|
|
NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
|
|
UINT32 GrantedAccess, UINT32 ShareAccess,
|
|
FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason)
|
|
{
|
|
/*
|
|
* 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.
|
|
*
|
|
* There is no FileNode that can be acquired when calling this function.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
FSP_FILE_NODE *OpenedFileNode = 0;
|
|
BOOLEAN Inserted, DeletePending;
|
|
NTSTATUS Result;
|
|
|
|
*PSharingViolationReason = FspFileNodeSharingViolationGeneral;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
/*
|
|
* If this is a named stream we must also check with our main file.
|
|
* Note that FileNode->MainFileNode and OpenedFileNode->MainFileNode
|
|
* will always be the same.
|
|
*/
|
|
if (0 != FileNode->MainFileNode)
|
|
{
|
|
DeletePending = 0 != FileNode->MainFileNode->DeletePending;
|
|
MemoryBarrier();
|
|
if (DeletePending)
|
|
{
|
|
Result = STATUS_DELETE_PENDING;
|
|
goto exit;
|
|
}
|
|
|
|
/*
|
|
* Sharing violations between main file and streams were determined
|
|
* through experimentation with NTFS. They may be wrong!
|
|
*/
|
|
if (0 < FileNode->MainFileNode->MainFileDenyDeleteCount)
|
|
{
|
|
if (!FlagOn(ShareAccess, FILE_SHARE_DELETE) &&
|
|
FlagOn(GrantedAccess,
|
|
FILE_EXECUTE | FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE))
|
|
{
|
|
OpenedFileNode = FileNode->MainFileNode;
|
|
*PSharingViolationReason = FspFileNodeSharingViolationMainFile;
|
|
Result = STATUS_SHARING_VIOLATION;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
OpenedFileNode = FspFsvolDeviceInsertContextByName(FsvolDeviceObject,
|
|
&FileNode->FileName, FileNode, &FileNode->ContextByNameElementStorage, &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);
|
|
ASSERT(OpenedFileNode->MainFileNode == FileNode->MainFileNode);
|
|
|
|
DeletePending = 0 != OpenedFileNode->DeletePending;
|
|
MemoryBarrier();
|
|
if (DeletePending)
|
|
{
|
|
Result = STATUS_DELETE_PENDING;
|
|
goto exit;
|
|
}
|
|
|
|
/*
|
|
* Sharing violations between main file and streams were determined
|
|
* through experimentation with NTFS. They may be wrong!
|
|
*/
|
|
if (0 < OpenedFileNode->StreamDenyDeleteCount)
|
|
{
|
|
/* we must be the main file! */
|
|
ASSERT(0 == OpenedFileNode->MainFileNode);
|
|
|
|
if (FlagOn(GrantedAccess, DELETE))
|
|
{
|
|
*PSharingViolationReason = FspFileNodeSharingViolationStream;
|
|
Result = STATUS_SHARING_VIOLATION;
|
|
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);
|
|
if (!NT_SUCCESS(Result))
|
|
goto exit;
|
|
}
|
|
|
|
/*
|
|
* No more failures allowed at this point!
|
|
* This is because we have potentially inserted a new FileNode into the Context table.
|
|
* We also updated OpenedFileNode->ShareAccess in IoSetShareAccess/IoCheckShareAccess.
|
|
*/
|
|
|
|
/*
|
|
* Sharing violations between main file and streams were determined
|
|
* through experimentation with NTFS. They may be wrong!
|
|
*/
|
|
if (0 == OpenedFileNode->MainFileNode)
|
|
{
|
|
if (FileObject->DeleteAccess)
|
|
OpenedFileNode->MainFileDenyDeleteCount++;
|
|
}
|
|
else
|
|
{
|
|
if ((FileObject->ReadAccess || FileObject->WriteAccess || FileObject->DeleteAccess) &&
|
|
!FileObject->SharedDelete)
|
|
OpenedFileNode->MainFileNode->StreamDenyDeleteCount++;
|
|
}
|
|
|
|
Result = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
if (!NT_SUCCESS(Result) && STATUS_SHARING_VIOLATION != Result)
|
|
OpenedFileNode = 0;
|
|
|
|
if (0 != OpenedFileNode)
|
|
{
|
|
FspFileNodeReference(OpenedFileNode);
|
|
OpenedFileNode->OpenCount++;
|
|
OpenedFileNode->HandleCount++;
|
|
}
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
*POpenedFileNode = OpenedFileNode;
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags)
|
|
{
|
|
/*
|
|
* Determine whether a FileNode should be deleted. Note that when FileNode->DeletePending
|
|
* is set, the OpenCount/HandleCount cannot be increased because FspFileNodeOpen() will
|
|
* return STATUS_DELETE_PENDING.
|
|
*
|
|
* The FileNode must be acquired exclusive (Main) when calling this function.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
|
BOOLEAN DeletePending, SetAllocationSize, SingleHandle;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
if (FileDesc->DeleteOnClose)
|
|
FileNode->DeletePending = TRUE;
|
|
DeletePending = 0 != FileNode->DeletePending;
|
|
MemoryBarrier();
|
|
|
|
SetAllocationSize = !DeletePending && FileNode->TruncateOnClose;
|
|
|
|
SingleHandle = 1 == FileNode->HandleCount;
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
*PCleanupFlags = SingleHandle ? DeletePending | (SetAllocationSize << 1) : 0;
|
|
}
|
|
|
|
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject)
|
|
{
|
|
/*
|
|
* Complete the cleanup of a FileNode. Remove its share access and
|
|
* finalize its cache.
|
|
*
|
|
* NOTE: If the FileNode is not being deleted (!FileNode->DeletePending)
|
|
* the FileNode REMAINS in the Context table until Close time!
|
|
* This is so that if there are mapped views or write behind's pending
|
|
* when a file gets reopened the FileNode will be correctly reused.
|
|
*
|
|
* The FileNode must be acquired exclusive (Main) when calling this function.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
LARGE_INTEGER TruncateSize, *PTruncateSize = 0;
|
|
BOOLEAN DeletePending;
|
|
BOOLEAN DeletedFromContextTable = FALSE;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
/*
|
|
* Sharing violations between main file and streams were determined
|
|
* through experimentation with NTFS. They may be wrong!
|
|
*/
|
|
if (0 == FileNode->MainFileNode)
|
|
{
|
|
if (FileObject->DeleteAccess)
|
|
FileNode->MainFileDenyDeleteCount--;
|
|
}
|
|
else
|
|
{
|
|
if ((FileObject->ReadAccess || FileObject->WriteAccess || FileObject->DeleteAccess) &&
|
|
!FileObject->SharedDelete)
|
|
FileNode->MainFileNode->StreamDenyDeleteCount--;
|
|
}
|
|
|
|
IoRemoveShareAccess(FileObject, &FileNode->ShareAccess);
|
|
|
|
if (0 == --FileNode->HandleCount)
|
|
{
|
|
DeletePending = 0 != FileNode->DeletePending;
|
|
MemoryBarrier();
|
|
|
|
if (DeletePending)
|
|
{
|
|
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
|
|
&DeletedFromContextTable);
|
|
ASSERT(DeletedFromContextTable);
|
|
|
|
FileNode->OpenCount = 0;
|
|
FileNode->Header.FileSize.QuadPart = 0;
|
|
}
|
|
|
|
if (DeletePending || FileNode->TruncateOnClose)
|
|
{
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FsvolDeviceObject);
|
|
UINT64 AllocationUnit =
|
|
FsvolDeviceExtension->VolumeParams.SectorSize *
|
|
FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit;
|
|
|
|
/*
|
|
* Even when the FileInfo is expired, this is the best guess for a file size
|
|
* without asking the user-mode file system.
|
|
*/
|
|
TruncateSize = FileNode->Header.FileSize;
|
|
PTruncateSize = &TruncateSize;
|
|
|
|
FileNode->Header.AllocationSize.QuadPart = (TruncateSize.QuadPart + AllocationUnit - 1)
|
|
/ AllocationUnit * AllocationUnit;
|
|
}
|
|
|
|
FileNode->TruncateOnClose = FALSE;
|
|
}
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
CcUninitializeCacheMap(FileObject, PTruncateSize, 0);
|
|
|
|
if (DeletedFromContextTable)
|
|
FspFileNodeDereference(FileNode);
|
|
}
|
|
|
|
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
|
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
|
BOOLEAN HandleCleanup) /* TRUE to decrement handle count */
|
|
{
|
|
/*
|
|
* Close the FileNode. If the OpenCount becomes zero remove it
|
|
* from the Context table.
|
|
*
|
|
* The FileNode may or may not be acquired when calling this function.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
BOOLEAN DeletedFromContextTable = FALSE;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
if (0 != FileObject)
|
|
{
|
|
/*
|
|
* Sharing violations between main file and streams were determined
|
|
* through experimentation with NTFS. They may be wrong!
|
|
*/
|
|
if (0 == FileNode->MainFileNode)
|
|
{
|
|
if (FileObject->DeleteAccess)
|
|
FileNode->MainFileDenyDeleteCount--;
|
|
}
|
|
else
|
|
{
|
|
if ((FileObject->ReadAccess || FileObject->WriteAccess || FileObject->DeleteAccess) &&
|
|
!FileObject->SharedDelete)
|
|
FileNode->MainFileNode->StreamDenyDeleteCount--;
|
|
}
|
|
|
|
IoRemoveShareAccess(FileObject, &FileNode->ShareAccess);
|
|
}
|
|
|
|
if (HandleCleanup)
|
|
FileNode->HandleCount--;
|
|
|
|
if (0 < FileNode->OpenCount && 0 == --FileNode->OpenCount)
|
|
{
|
|
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
|
|
&DeletedFromContextTable);
|
|
ASSERT(DeletedFromContextTable);
|
|
}
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
if (DeletedFromContextTable)
|
|
FspFileNodeDereference(FileNode);
|
|
}
|
|
|
|
NTSTATUS FspFileNodeFlushAndPurgeCache(FSP_FILE_NODE *FileNode,
|
|
UINT64 FlushOffset64, ULONG FlushLength, BOOLEAN FlushAndPurge)
|
|
{
|
|
/*
|
|
* The FileNode must be acquired exclusive (Full) when calling this function.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
LARGE_INTEGER FlushOffset;
|
|
PLARGE_INTEGER PFlushOffset = &FlushOffset;
|
|
FSP_FSCTL_FILE_INFO FileInfo;
|
|
IO_STATUS_BLOCK IoStatus = { STATUS_SUCCESS };
|
|
|
|
FlushOffset.QuadPart = FlushOffset64;
|
|
if (FILE_WRITE_TO_END_OF_FILE == FlushOffset.LowPart && -1L == FlushOffset.HighPart)
|
|
{
|
|
if (FspFileNodeTryGetFileInfo(FileNode, &FileInfo))
|
|
FlushOffset.QuadPart = FileInfo.FileSize;
|
|
else
|
|
PFlushOffset = 0; /* we don't know how big the file is, so flush it all! */
|
|
}
|
|
|
|
/* it is unclear if the Cc* calls below can raise or not; wrap them just in case */
|
|
try
|
|
{
|
|
if (0 != FspMvCcCoherencyFlushAndPurgeCache)
|
|
{
|
|
/* if we are on Win7+ use CcCoherencyFlushAndPurgeCache */
|
|
FspMvCcCoherencyFlushAndPurgeCache(
|
|
&FileNode->NonPaged->SectionObjectPointers, PFlushOffset, FlushLength, &IoStatus,
|
|
FlushAndPurge ? 0 : CC_FLUSH_AND_PURGE_NO_PURGE);
|
|
|
|
if (STATUS_CACHE_PAGE_LOCKED == IoStatus.Status)
|
|
IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
/* do it the old-fashioned way; non-cached and mmap'ed I/O are non-coherent */
|
|
CcFlushCache(&FileNode->NonPaged->SectionObjectPointers, PFlushOffset, FlushLength, &IoStatus);
|
|
if (NT_SUCCESS(IoStatus.Status))
|
|
{
|
|
if (FlushAndPurge)
|
|
CcPurgeCacheSection(&FileNode->NonPaged->SectionObjectPointers, PFlushOffset, FlushLength, FALSE);
|
|
|
|
IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
IoStatus.Status = GetExceptionCode();
|
|
}
|
|
|
|
return IoStatus.Status;
|
|
}
|
|
|
|
#define GATHER_DESCENDANTS(FILENAME, REFERENCE, ...)\
|
|
FSP_FILE_NODE *DescendantFileNode;\
|
|
FSP_FILE_NODE *DescendantFileNodeArray[16], **DescendantFileNodes;\
|
|
ULONG DescendantFileNodeCount, DescendantFileNodeIndex;\
|
|
FSP_DEVICE_CONTEXT_BY_NAME_TABLE_RESTART_KEY RestartKey;\
|
|
DescendantFileNodes = DescendantFileNodeArray;\
|
|
DescendantFileNodeCount = 0;\
|
|
memset(&RestartKey, 0, sizeof RestartKey);\
|
|
for (;;) \
|
|
{ \
|
|
DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject,\
|
|
FILENAME, FALSE, &RestartKey);\
|
|
if (0 == DescendantFileNode) \
|
|
break; \
|
|
ASSERT(0 == ((UINT_PTR)DescendantFileNode & 7));\
|
|
__VA_ARGS__; \
|
|
if (REFERENCE) \
|
|
FspFileNodeReference((PVOID)((UINT_PTR)DescendantFileNode & ~7));\
|
|
if (ARRAYSIZE(DescendantFileNodeArray) > DescendantFileNodeCount)\
|
|
DescendantFileNodes[DescendantFileNodeCount] = DescendantFileNode;\
|
|
DescendantFileNodeCount++; \
|
|
} \
|
|
if (ARRAYSIZE(DescendantFileNodeArray) < DescendantFileNodeCount ||\
|
|
DEBUGTEST_EX(0 != DescendantFileNodeCount, 10, FALSE)) \
|
|
{ \
|
|
DescendantFileNodes = FspAllocMustSucceed(DescendantFileNodeCount * sizeof(FSP_FILE_NODE *));\
|
|
DescendantFileNodeIndex = 0; \
|
|
memset(&RestartKey, 0, sizeof RestartKey);\
|
|
for (;;) \
|
|
{ \
|
|
DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject,\
|
|
FILENAME, FALSE, &RestartKey);\
|
|
if (0 == DescendantFileNode)\
|
|
break; \
|
|
ASSERT(0 == ((UINT_PTR)DescendantFileNode & 7));\
|
|
__VA_ARGS__; \
|
|
DescendantFileNodes[DescendantFileNodeIndex] = DescendantFileNode;\
|
|
DescendantFileNodeIndex++; \
|
|
ASSERT(DescendantFileNodeCount >= DescendantFileNodeIndex);\
|
|
} \
|
|
ASSERT(DescendantFileNodeCount == DescendantFileNodeIndex);\
|
|
} \
|
|
((VOID)0)
|
|
#define SCATTER_DESCENDANTS(REFERENCE) \
|
|
if (REFERENCE) \
|
|
{ \
|
|
for ( \
|
|
DescendantFileNodeIndex = 0;\
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;\
|
|
DescendantFileNodeIndex++) \
|
|
{ \
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];\
|
|
FspFileNodeDereference((PVOID)((UINT_PTR)DescendantFileNode & ~7));\
|
|
} \
|
|
} \
|
|
if (DescendantFileNodeArray != DescendantFileNodes)\
|
|
FspFree(DescendantFileNodes); \
|
|
((VOID)0)
|
|
|
|
VOID FspFileNodeOverwriteStreams(FSP_FILE_NODE *FileNode)
|
|
{
|
|
/*
|
|
* Called during Create processing. The device rename resource has been acquired shared.
|
|
* No concurrent renames are allowed.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(0 == FileNode->MainFileNode);
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
USHORT FileNameLength = FileNode->FileName.Length;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
GATHER_DESCENDANTS(&FileNode->FileName, FALSE,
|
|
if (DescendantFileNode->FileName.Length > FileNameLength &&
|
|
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)])
|
|
break;
|
|
if (FileNode == DescendantFileNode || 0 >= DescendantFileNode->HandleCount)
|
|
continue;
|
|
);
|
|
|
|
/* mark any open named streams as DeletePending */
|
|
for (
|
|
DescendantFileNodeIndex = 0;
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
|
DescendantFileNodeIndex++)
|
|
{
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
|
DescendantFileNode->DeletePending = TRUE;
|
|
}
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
SCATTER_DESCENDANTS(FALSE);
|
|
}
|
|
|
|
NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
|
|
PDEVICE_OBJECT FsvolDeviceObject,
|
|
PIRP OplockIrp,
|
|
FSP_FILE_NODE *FileNode,
|
|
ULONG AcquireFlags,
|
|
PUNICODE_STRING StreamFileName)
|
|
{
|
|
/*
|
|
* Called during Create processing. The device rename resource has been acquired shared.
|
|
* No concurrent renames are allowed.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(0 == FileNode->MainFileNode);
|
|
|
|
USHORT FileNameLength = FileNode->FileName.Length;
|
|
BOOLEAN CaseInsensitive = !FspFsvolDeviceExtension(FsvolDeviceObject)->
|
|
VolumeParams.CaseSensitiveSearch;
|
|
ULONG IsBatchOplock, IsHandleOplock;
|
|
NTSTATUS Result;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
GATHER_DESCENDANTS(&FileNode->FileName, TRUE,
|
|
if (DescendantFileNode->FileName.Length > FileNameLength &&
|
|
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)])
|
|
break;
|
|
if (0 >= DescendantFileNode->HandleCount)
|
|
continue;
|
|
if (0 != StreamFileName)
|
|
{
|
|
if (DescendantFileNode != FileNode &&
|
|
0 != FspFileNameCompare(&DescendantFileNode->FileName, StreamFileName,
|
|
CaseInsensitive, 0))
|
|
continue;
|
|
});
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
/* break any Batch or Handle oplocks on descendants */
|
|
Result = STATUS_SUCCESS;
|
|
for (
|
|
DescendantFileNodeIndex = 0;
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
|
DescendantFileNodeIndex++)
|
|
{
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
|
|
|
if (FspFileNodeOplockIsBatch(DescendantFileNode))
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockCheckEx(DescendantFileNode, OplockIrp,
|
|
OPLOCK_FLAG_COMPLETE_IF_OPLOCKED);
|
|
if (STATUS_SUCCESS == Result0)
|
|
OplockIrp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY;
|
|
else
|
|
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result0)
|
|
{
|
|
OplockIrp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY;
|
|
DescendantFileNodes[DescendantFileNodeIndex] =
|
|
(PVOID)((UINT_PTR)DescendantFileNode | 2);
|
|
}
|
|
else
|
|
Result = STATUS_SHARING_VIOLATION;
|
|
}
|
|
else
|
|
if (FspFileNodeOplockIsHandle(DescendantFileNode))
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockBreakHandle(DescendantFileNode, OplockIrp,
|
|
OPLOCK_FLAG_COMPLETE_IF_OPLOCKED);
|
|
if (STATUS_SUCCESS == Result0)
|
|
;
|
|
else
|
|
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result0)
|
|
DescendantFileNodes[DescendantFileNodeIndex] =
|
|
(PVOID)((UINT_PTR)DescendantFileNode | 4);
|
|
else
|
|
Result = STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
|
|
/* release the FileNode so that we can safely wait without deadlocks */
|
|
FspFileNodeReleaseF(FileNode, AcquireFlags);
|
|
|
|
/* wait for oplock breaks to finish */
|
|
Result = STATUS_SUCCESS;
|
|
for (
|
|
DescendantFileNodeIndex = 0;
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
|
DescendantFileNodeIndex++)
|
|
{
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
|
IsBatchOplock = (UINT_PTR)DescendantFileNode & 2;
|
|
IsHandleOplock = (UINT_PTR)DescendantFileNode & 4;
|
|
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~7);
|
|
|
|
if (IsBatchOplock)
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockCheck(DescendantFileNode, OplockIrp);
|
|
ASSERT(STATUS_OPLOCK_BREAK_IN_PROGRESS != Result0);
|
|
if (STATUS_SUCCESS != Result0)
|
|
Result = STATUS_SHARING_VIOLATION;
|
|
}
|
|
else
|
|
if (IsHandleOplock)
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockBreakHandle(DescendantFileNode, OplockIrp, 0);
|
|
ASSERT(STATUS_OPLOCK_BREAK_IN_PROGRESS != Result0);
|
|
if (STATUS_SUCCESS != Result0)
|
|
Result = STATUS_SHARING_VIOLATION;
|
|
}
|
|
}
|
|
|
|
SCATTER_DESCENDANTS(TRUE);
|
|
|
|
return Result;
|
|
}
|
|
|
|
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
|
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
|
PUNICODE_STRING FileName, BOOLEAN CheckingOldName)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS Result;
|
|
ULONG HasHandles, IsBatchOplock, IsHandleOplock;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
if (CheckingOldName && !FileNode->IsDirectory && 1 == FileNode->HandleCount)
|
|
{
|
|
/* quick exit */
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
GATHER_DESCENDANTS(FileName, TRUE,
|
|
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode |
|
|
(0 < DescendantFileNode->HandleCount)));
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
if (0 == DescendantFileNodeCount)
|
|
{
|
|
Result = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
/*
|
|
* At this point all descendant FileNode's are enumerated and referenced.
|
|
* There can be no new FileNode's because Rename has acquired the device's
|
|
* "rename" resource exclusively, which disallows new Opens.
|
|
*/
|
|
|
|
if (!CheckingOldName)
|
|
{
|
|
/* replaced file cannot be a directory or mapped as an image */
|
|
for (
|
|
DescendantFileNodeIndex = 0;
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
|
DescendantFileNodeIndex++)
|
|
{
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
|
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~7);
|
|
|
|
/*
|
|
* Windows file systems do not allow the replaced file to be a directory.
|
|
* However POSIX allows this (when the directory is empty).
|
|
*
|
|
* For this reason we will allow the case where the replaced file is a directory
|
|
* (without any open files within it). The user mode file system can always fail
|
|
* such requests if it wants.
|
|
*/
|
|
|
|
if ((DescendantFileNode->FileName.Length > FileName->Length &&
|
|
L'\\' == DescendantFileNode->FileName.Buffer[FileName->Length / sizeof(WCHAR)]) ||
|
|
(0 != DescendantFileNode->NonPaged->SectionObjectPointers.ImageSectionObject &&
|
|
!MmFlushImageSection(&DescendantFileNode->NonPaged->SectionObjectPointers,
|
|
MmFlushForDelete)))
|
|
{
|
|
/* release the FileNode in case of failure! */
|
|
FspFileNodeReleaseF(FileNode, AcquireFlags);
|
|
|
|
Result = STATUS_ACCESS_DENIED;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* break any Batch or Handle oplocks on descendants */
|
|
Result = STATUS_SUCCESS;
|
|
for (
|
|
DescendantFileNodeIndex = 0;
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
|
DescendantFileNodeIndex++)
|
|
{
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
|
HasHandles = (UINT_PTR)DescendantFileNode & 1;
|
|
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~7);
|
|
|
|
if (!HasHandles)
|
|
continue;
|
|
|
|
if (FspFileNodeOplockIsBatch(DescendantFileNode))
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockCheckEx(DescendantFileNode, OplockIrp,
|
|
OPLOCK_FLAG_COMPLETE_IF_OPLOCKED);
|
|
if (STATUS_SUCCESS == Result0)
|
|
Result = NT_SUCCESS(Result) ? STATUS_OPLOCK_BREAK_IN_PROGRESS : Result;
|
|
else
|
|
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result0)
|
|
{
|
|
Result = NT_SUCCESS(Result) ? STATUS_OPLOCK_BREAK_IN_PROGRESS : Result;
|
|
DescendantFileNodes[DescendantFileNodeIndex] =
|
|
(PVOID)((UINT_PTR)DescendantFileNode | 2);
|
|
}
|
|
else
|
|
Result = STATUS_ACCESS_DENIED;
|
|
}
|
|
else
|
|
if (FspFileNodeOplockIsHandle(DescendantFileNode))
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockBreakHandle(DescendantFileNode, OplockIrp,
|
|
OPLOCK_FLAG_COMPLETE_IF_OPLOCKED);
|
|
if (STATUS_SUCCESS == Result0)
|
|
Result = NT_SUCCESS(Result) ? STATUS_OPLOCK_BREAK_IN_PROGRESS : Result;
|
|
else
|
|
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result0)
|
|
{
|
|
Result = NT_SUCCESS(Result) ? STATUS_OPLOCK_BREAK_IN_PROGRESS : Result;
|
|
DescendantFileNodes[DescendantFileNodeIndex] =
|
|
(PVOID)((UINT_PTR)DescendantFileNode | 2);
|
|
}
|
|
else
|
|
Result = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result || !NT_SUCCESS(Result))
|
|
{
|
|
/* release the FileNode so that we can safely wait without deadlocks */
|
|
FspFileNodeReleaseF(FileNode, AcquireFlags);
|
|
|
|
/* wait for oplock breaks to finish */
|
|
for (
|
|
DescendantFileNodeIndex = 0;
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
|
DescendantFileNodeIndex++)
|
|
{
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
|
IsBatchOplock = (UINT_PTR)DescendantFileNode & 2;
|
|
IsHandleOplock = (UINT_PTR)DescendantFileNode & 4;
|
|
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~7);
|
|
|
|
if (IsBatchOplock)
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockCheck(DescendantFileNode, OplockIrp);
|
|
ASSERT(STATUS_OPLOCK_BREAK_IN_PROGRESS != Result0);
|
|
if (STATUS_SUCCESS != Result0)
|
|
Result = STATUS_ACCESS_DENIED;
|
|
}
|
|
else
|
|
if (IsHandleOplock)
|
|
{
|
|
NTSTATUS Result0 = FspFileNodeOplockBreakHandle(DescendantFileNode, OplockIrp, 0);
|
|
ASSERT(STATUS_OPLOCK_BREAK_IN_PROGRESS != Result0);
|
|
if (STATUS_SUCCESS != Result0)
|
|
Result = STATUS_ACCESS_DENIED;
|
|
}
|
|
}
|
|
|
|
goto exit;
|
|
}
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
/* recheck whether there are still files with open handles */
|
|
memset(&RestartKey, 0, sizeof RestartKey);
|
|
for (;;)
|
|
{
|
|
DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject,
|
|
FileName, FALSE, &RestartKey);
|
|
if (0 == DescendantFileNode)
|
|
break;
|
|
|
|
/* if this is the FileNode being renamed then HandleCount must be 1, else 0 */
|
|
if ((DescendantFileNode == FileNode) < DescendantFileNode->HandleCount)
|
|
{
|
|
/* release the FileNode in case of failure! */
|
|
FspFileNodeReleaseF(FileNode, AcquireFlags);
|
|
|
|
Result = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
exit:
|
|
SCATTER_DESCENDANTS(TRUE);
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName)
|
|
{
|
|
/*
|
|
* FspFileNodeRename may block, because it attempts to acquire the Main
|
|
* resource of descendant file nodes. FspFileNodeRename is called at the
|
|
* completion path of IRP_MJ_SET_INFORMATION[Rename] and an IRP completion
|
|
* path should not block, with the notable exception of Rename.
|
|
*
|
|
* The original reason that Rename completion is allowed to block was that
|
|
* it was observed that IoCompleteRequest of a Rename could sometimes
|
|
* trigger a recursive call into the FSD (likely due to a filter). WinFsp
|
|
* was modified to accommodate this by allowing this recursive call to
|
|
* proceed on a different thread.
|
|
*
|
|
* Since WinFsp can already accommodate blocking on Rename completions,
|
|
* it is safe to acquire the Main resource of descendant file nodes.
|
|
*
|
|
* Note also that there can only be one rename at a time because of the
|
|
* device's FileRenameResource.
|
|
*/
|
|
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
BOOLEAN Deleted, Inserted, AcquireForeign;
|
|
FSP_FILE_NODE *InsertedFileNode;
|
|
USHORT FileNameLength;
|
|
PWSTR ExternalFileName;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
|
|
GATHER_DESCENDANTS(&FileNode->FileName, FALSE, {});
|
|
|
|
FileNameLength = FileNode->FileName.Length;
|
|
for (
|
|
DescendantFileNodeIndex = 0;
|
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
|
DescendantFileNodeIndex++)
|
|
{
|
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
|
ASSERT(DescendantFileNode->FileName.Length >= FileNameLength);
|
|
|
|
AcquireForeign = DescendantFileNode->FileName.Length > FileNameLength &&
|
|
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)];
|
|
if (AcquireForeign)
|
|
FspFileNodeAcquireExclusiveForeign(DescendantFileNode);
|
|
|
|
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &DescendantFileNode->FileName, &Deleted);
|
|
ASSERT(Deleted);
|
|
|
|
ExternalFileName = DescendantFileNode->ExternalFileName;
|
|
|
|
DescendantFileNode->FileName.MaximumLength =
|
|
DescendantFileNode->FileName.Length - FileNameLength + NewFileName->Length;
|
|
DescendantFileNode->ExternalFileName = FspAllocMustSucceed(
|
|
DescendantFileNode->FileName.MaximumLength);
|
|
|
|
RtlCopyMemory((PUINT8)DescendantFileNode->ExternalFileName + NewFileName->Length,
|
|
(PUINT8)DescendantFileNode->FileName.Buffer + FileNameLength,
|
|
DescendantFileNode->FileName.Length - FileNameLength);
|
|
RtlCopyMemory(DescendantFileNode->ExternalFileName,
|
|
NewFileName->Buffer,
|
|
NewFileName->Length);
|
|
|
|
DescendantFileNode->FileName.Length = DescendantFileNode->FileName.MaximumLength;
|
|
DescendantFileNode->FileName.Buffer = DescendantFileNode->ExternalFileName;
|
|
|
|
if (0 != ExternalFileName)
|
|
FspFree(ExternalFileName);
|
|
|
|
InsertedFileNode = FspFsvolDeviceInsertContextByName(
|
|
FsvolDeviceObject, &DescendantFileNode->FileName, DescendantFileNode,
|
|
&DescendantFileNode->ContextByNameElementStorage, &Inserted);
|
|
if (!Inserted)
|
|
{
|
|
/*
|
|
* Handle files that have been Cleanup'ed but not Close'd.
|
|
* For example, this can happen when the user has mapped and closed a file
|
|
* or immediately after breaking a Batch oplock.
|
|
*/
|
|
|
|
ASSERT(FspFileNodeIsValid(InsertedFileNode));
|
|
ASSERT(DescendantFileNode != InsertedFileNode);
|
|
ASSERT(0 == InsertedFileNode->HandleCount);
|
|
ASSERT(0 != InsertedFileNode->OpenCount);
|
|
|
|
InsertedFileNode->OpenCount = 0;
|
|
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &InsertedFileNode->FileName, &Deleted);
|
|
ASSERT(Deleted);
|
|
|
|
FspFileNodeDereference(InsertedFileNode);
|
|
|
|
FspFsvolDeviceInsertContextByName(
|
|
FsvolDeviceObject, &DescendantFileNode->FileName, DescendantFileNode,
|
|
&DescendantFileNode->ContextByNameElementStorage, &Inserted);
|
|
ASSERT(Inserted);
|
|
}
|
|
|
|
if (AcquireForeign)
|
|
FspFileNodeReleaseForeign(DescendantFileNode);
|
|
}
|
|
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
SCATTER_DESCENDANTS(FALSE);
|
|
}
|
|
|
|
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FileInfo->AllocationSize = FileNode->Header.AllocationSize.QuadPart;
|
|
FileInfo->FileSize = FileNode->Header.FileSize.QuadPart;
|
|
|
|
UINT32 FileAttributesMask = ~(UINT32)0;
|
|
if (0 != FileNode->MainFileNode)
|
|
{
|
|
FileAttributesMask = ~(UINT32)FILE_ATTRIBUTE_DIRECTORY;
|
|
FileNode = FileNode->MainFileNode;
|
|
}
|
|
|
|
FileInfo->FileAttributes = FileNode->FileAttributes & FileAttributesMask;
|
|
FileInfo->ReparseTag = FileNode->ReparseTag;
|
|
FileInfo->CreationTime = FileNode->CreationTime;
|
|
FileInfo->LastAccessTime = FileNode->LastAccessTime;
|
|
FileInfo->LastWriteTime = FileNode->LastWriteTime;
|
|
FileInfo->ChangeTime = FileNode->ChangeTime;
|
|
}
|
|
|
|
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
UINT64 CurrentTime = KeQueryInterruptTime();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
{
|
|
/* if this is a stream the main file basic info must have not expired as well! */
|
|
if (!FspExpirationTimeValidEx(FileNode->MainFileNode->BasicInfoExpirationTime, CurrentTime))
|
|
return FALSE;
|
|
}
|
|
|
|
if (!FspExpirationTimeValidEx(FileNode->FileInfoExpirationTime, CurrentTime))
|
|
return FALSE;
|
|
|
|
FspFileNodeGetFileInfo(FileNode, FileInfo);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID FspFileNodeSetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
|
const FSP_FSCTL_FILE_INFO *FileInfo, BOOLEAN TruncateOnClose)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
UINT64 AllocationSize = FileInfo->AllocationSize > FileInfo->FileSize ?
|
|
FileInfo->AllocationSize : FileInfo->FileSize;
|
|
UINT64 AllocationUnit;
|
|
|
|
AllocationUnit = FsvolDeviceExtension->VolumeParams.SectorSize *
|
|
FsvolDeviceExtension->VolumeParams.SectorsPerAllocationUnit;
|
|
AllocationSize = (AllocationSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit;
|
|
|
|
if (TruncateOnClose)
|
|
{
|
|
if ((UINT64)FileNode->Header.AllocationSize.QuadPart != AllocationSize ||
|
|
(UINT64)FileNode->Header.FileSize.QuadPart != FileInfo->FileSize)
|
|
FileNode->TruncateOnClose = TRUE;
|
|
|
|
FileNode->Header.AllocationSize.QuadPart = AllocationSize;
|
|
FileNode->Header.FileSize.QuadPart = FileInfo->FileSize;
|
|
}
|
|
else
|
|
{
|
|
FileNode->Header.AllocationSize.QuadPart = AllocationSize;
|
|
FileNode->Header.FileSize.QuadPart = FileInfo->FileSize;
|
|
}
|
|
|
|
FileNode->FileInfoExpirationTime = FileNode->BasicInfoExpirationTime =
|
|
FspExpirationTimeFromMillis(FsvolDeviceExtension->VolumeParams.FileInfoTimeout);
|
|
FileNode->FileInfoChangeNumber++;
|
|
|
|
FSP_FILE_NODE *MainFileNode = FileNode;
|
|
UINT32 FileAttributesMask = ~(UINT32)0;
|
|
if (0 != FileNode->MainFileNode)
|
|
{
|
|
FileAttributesMask = ~(UINT32)FILE_ATTRIBUTE_DIRECTORY;
|
|
MainFileNode = FileNode->MainFileNode;
|
|
|
|
MainFileNode->BasicInfoExpirationTime = FileNode->BasicInfoExpirationTime;
|
|
MainFileNode->FileInfoChangeNumber++;
|
|
}
|
|
|
|
MainFileNode->FileAttributes =
|
|
(MainFileNode->FileAttributes & ~FileAttributesMask) |
|
|
(FileInfo->FileAttributes & FileAttributesMask);
|
|
MainFileNode->ReparseTag = FileInfo->ReparseTag;
|
|
MainFileNode->CreationTime = FileInfo->CreationTime;
|
|
MainFileNode->LastAccessTime = FileInfo->LastAccessTime;
|
|
MainFileNode->LastWriteTime = FileInfo->LastWriteTime;
|
|
MainFileNode->ChangeTime = FileInfo->ChangeTime;
|
|
|
|
if (0 != CcFileObject)
|
|
{
|
|
NTSTATUS Result = FspCcSetFileSizes(
|
|
CcFileObject, (PCC_FILE_SIZES)&FileNode->Header.AllocationSize);
|
|
if (!NT_SUCCESS(Result))
|
|
{
|
|
/*
|
|
* CcSetFileSizes failed. This is a hard case to handle, because it is
|
|
* usually late in IRP processing. So we have the following strategy.
|
|
*
|
|
* Our goal is to completely stop all caching for this FileNode. The idea
|
|
* is that if some I/O arrives later for this FileNode, CcInitializeCacheMap
|
|
* will be executed (and possibly fail safely there). In fact we may decide
|
|
* later to make such CcInitializeCacheMap failures benign (by not using the
|
|
* cache when we cannot).
|
|
*
|
|
* In order to completely stop caching for the FileNode we do the following:
|
|
*
|
|
* - We flush the cache using CcFlushCache.
|
|
* - We purge the cache and uninitialize all PrivateCacheMap's using
|
|
* CcPurgeCacheSection with UninitializeCacheMaps==TRUE.
|
|
* - If the SharedCacheMap is still around, we perform an additional
|
|
* CcUninitializeCacheMap with an UninitializeEvent. At this point
|
|
* CcUninitializeCacheMap should delete the SharedCacheMap and
|
|
* signal the UninitializeEvent.
|
|
*
|
|
* One potential gotcha is whether there is any possibility for another
|
|
* system component to delay deletion of the SharedCacheMap and signaling
|
|
* of the UninitializeEvent. This could result in a deadlock, because we
|
|
* are already holding the FileNode exclusive and waiting for the
|
|
* UninitializeEvent. But the thread that would signal our event would have
|
|
* to first acquire our FileNode. Classic deadlock.
|
|
*
|
|
* I believe (but cannot prove) that this deadlock cannot happen. The reason
|
|
* is that we have flushed and purged the cache and we have closed all
|
|
* PrivateCacheMap's using this SharedCacheMap. There should be no reason for
|
|
* any system component to keep the SharedCacheMap around (famous last words).
|
|
*/
|
|
|
|
IO_STATUS_BLOCK IoStatus;
|
|
CACHE_UNINITIALIZE_EVENT UninitializeEvent;
|
|
|
|
FspCcFlushCache(CcFileObject->SectionObjectPointer, 0, 0, &IoStatus);
|
|
CcPurgeCacheSection(CcFileObject->SectionObjectPointer, 0, 0, TRUE);
|
|
if (0 != CcFileObject->SectionObjectPointer->SharedCacheMap)
|
|
{
|
|
UninitializeEvent.Next = 0;
|
|
KeInitializeEvent(&UninitializeEvent.Event, NotificationEvent, FALSE);
|
|
BOOLEAN CacheStopped = CcUninitializeCacheMap(CcFileObject, 0, &UninitializeEvent);
|
|
(VOID)CacheStopped; ASSERT(CacheStopped);
|
|
KeWaitForSingleObject(&UninitializeEvent.Event, Executive, KernelMode, FALSE, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN FspFileNodeTrySetFileInfo(FSP_FILE_NODE *FileNode, PFILE_OBJECT CcFileObject,
|
|
const FSP_FSCTL_FILE_INFO *FileInfo, ULONG InfoChangeNumber)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (FspFileNodeFileInfoChangeNumber(FileNode) != InfoChangeNumber)
|
|
return FALSE;
|
|
|
|
FspFileNodeSetFileInfo(FileNode, CcFileObject, FileInfo, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
|
|
return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->SecurityCache,
|
|
FileNode->Security, PBuffer, PSize);
|
|
}
|
|
|
|
VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security);
|
|
FileNode->Security = 0 != Buffer ?
|
|
FspMetaCacheAddItem(FsvolDeviceExtension->SecurityCache, Buffer, Size) : 0;
|
|
FileNode->SecurityChangeNumber++;
|
|
}
|
|
|
|
BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
|
ULONG SecurityChangeNumber)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (FspFileNodeSecurityChangeNumber(FileNode) != SecurityChangeNumber)
|
|
return FALSE;
|
|
|
|
FspFileNodeSetSecurity(FileNode, Buffer, Size);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
|
UINT64 DirInfo;
|
|
|
|
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
|
DirInfo = NonPaged->DirInfo;
|
|
|
|
return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->DirInfoCache,
|
|
DirInfo, PBuffer, PSize);
|
|
}
|
|
|
|
VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
|
KIRQL Irql;
|
|
UINT64 DirInfo;
|
|
|
|
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
|
DirInfo = NonPaged->DirInfo;
|
|
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, DirInfo);
|
|
DirInfo = 0 != Buffer ?
|
|
FspMetaCacheAddItem(FsvolDeviceExtension->DirInfoCache, Buffer, Size) : 0;
|
|
FileNode->DirInfoChangeNumber++;
|
|
|
|
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateDirInfo */
|
|
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
|
NonPaged->DirInfo = DirInfo;
|
|
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
|
}
|
|
|
|
BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
|
ULONG DirInfoChangeNumber)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
if (FspFileNodeDirInfoChangeNumber(FileNode) != DirInfoChangeNumber)
|
|
return FALSE;
|
|
|
|
FspFileNodeSetDirInfo(FileNode, Buffer, Size);
|
|
return TRUE;
|
|
}
|
|
|
|
static VOID FspFileNodeInvalidateDirInfo(FSP_FILE_NODE *FileNode)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
|
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
|
KIRQL Irql;
|
|
UINT64 DirInfo;
|
|
|
|
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetDirInfo */
|
|
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
|
DirInfo = NonPaged->DirInfo;
|
|
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
|
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, DirInfo);
|
|
}
|
|
|
|
static VOID FspFileNodeInvalidateDirInfoByName(PDEVICE_OBJECT FsvolDeviceObject,
|
|
PUNICODE_STRING FileName)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FILE_NODE *FileNode;
|
|
|
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
|
FileNode = FspFsvolDeviceLookupContextByName(FsvolDeviceObject, FileName);
|
|
if (0 != FileNode)
|
|
FspFileNodeReference(FileNode);
|
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
|
|
|
if (0 != FileNode)
|
|
{
|
|
FspFileNodeInvalidateDirInfo(FileNode);
|
|
FspFileNodeDereference(FileNode);
|
|
}
|
|
}
|
|
|
|
VOID FspFileNodeInvalidateParentDirInfo(FSP_FILE_NODE *FileNode)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (sizeof(WCHAR) == FileNode->FileName.Length && L'\\' == FileNode->FileName.Buffer[0])
|
|
return; /* root does not have a parent */
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
UNICODE_STRING Parent, Suffix;
|
|
|
|
FspFileNameSuffix(&FileNode->FileName, &Parent, &Suffix);
|
|
FspFileNodeInvalidateDirInfoByName(FsvolDeviceObject, &Parent);
|
|
}
|
|
|
|
BOOLEAN FspFileNodeReferenceStreamInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
|
UINT64 StreamInfo;
|
|
|
|
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
|
StreamInfo = NonPaged->StreamInfo;
|
|
|
|
return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->StreamInfoCache,
|
|
StreamInfo, PBuffer, PSize);
|
|
}
|
|
|
|
VOID FspFileNodeSetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
|
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
|
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
|
KIRQL Irql;
|
|
UINT64 StreamInfo;
|
|
|
|
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
|
StreamInfo = NonPaged->StreamInfo;
|
|
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, StreamInfo);
|
|
StreamInfo = 0 != Buffer ?
|
|
FspMetaCacheAddItem(FsvolDeviceExtension->StreamInfoCache, Buffer, Size) : 0;
|
|
FileNode->StreamInfoChangeNumber++;
|
|
|
|
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateStreamInfo */
|
|
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
|
NonPaged->StreamInfo = StreamInfo;
|
|
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
|
}
|
|
|
|
BOOLEAN FspFileNodeTrySetStreamInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
|
ULONG StreamInfoChangeNumber)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
if (FspFileNodeStreamInfoChangeNumber(FileNode) != StreamInfoChangeNumber)
|
|
return FALSE;
|
|
|
|
FspFileNodeSetStreamInfo(FileNode, Buffer, Size);
|
|
return TRUE;
|
|
}
|
|
|
|
VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode)
|
|
{
|
|
// !PAGED_CODE();
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
FileNode = FileNode->MainFileNode;
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
|
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
|
KIRQL Irql;
|
|
UINT64 StreamInfo;
|
|
|
|
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetStreamInfo */
|
|
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
|
StreamInfo = NonPaged->StreamInfo;
|
|
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
|
|
|
FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, StreamInfo);
|
|
}
|
|
|
|
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
|
|
BOOLEAN InvalidateCaches)
|
|
{
|
|
/* FileNode must be acquired (exclusive or shared) Main */
|
|
|
|
PAGED_CODE();
|
|
|
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
|
UNICODE_STRING Parent, Suffix;
|
|
|
|
if (0 != FileNode->MainFileNode)
|
|
{
|
|
if (FlagOn(Filter, FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME))
|
|
SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_NAME);
|
|
if (FlagOn(Filter, FILE_NOTIFY_CHANGE_SIZE))
|
|
SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_SIZE);
|
|
/* ???: what about FILE_NOTIFY_CHANGE_STREAM_WRITE */
|
|
ClearFlag(Filter, ~(FILE_NOTIFY_CHANGE_STREAM_NAME | FILE_NOTIFY_CHANGE_STREAM_SIZE));
|
|
|
|
switch (Action)
|
|
{
|
|
case FILE_ACTION_ADDED:
|
|
Action = FILE_ACTION_ADDED_STREAM;
|
|
break;
|
|
case FILE_ACTION_REMOVED:
|
|
Action = FILE_ACTION_REMOVED_STREAM;
|
|
break;
|
|
case FILE_ACTION_MODIFIED:
|
|
Action = FILE_ACTION_MODIFIED_STREAM;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 != Filter)
|
|
{
|
|
FspFileNameSuffix(&FileNode->FileName, &Parent, &Suffix);
|
|
|
|
if (InvalidateCaches)
|
|
{
|
|
FspFsvolDeviceInvalidateVolumeInfo(FsvolDeviceObject);
|
|
if (0 == FileNode->MainFileNode)
|
|
{
|
|
if (sizeof(WCHAR) == FileNode->FileName.Length && L'\\' == FileNode->FileName.Buffer[0])
|
|
; /* root does not have a parent */
|
|
else
|
|
FspFileNodeInvalidateDirInfoByName(FsvolDeviceObject, &Parent);
|
|
}
|
|
else
|
|
FspFileNodeInvalidateStreamInfo(FileNode);
|
|
}
|
|
|
|
FspNotifyReportChange(
|
|
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList,
|
|
&FileNode->FileName,
|
|
(USHORT)((PUINT8)Suffix.Buffer - (PUINT8)FileNode->FileName.Buffer),
|
|
0, Filter, Action);
|
|
}
|
|
}
|
|
|
|
NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
try
|
|
{
|
|
FsRtlProcessFileLock(&FileNode->FileLock, Irp, FileNode);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Irp->IoStatus.Status = GetExceptionCode();
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
FspFileNodeCompleteLockIrp(FileNode, Irp);
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
static NTSTATUS FspFileNodeCompleteLockIrp(PVOID Context, PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS Result = Irp->IoStatus.Status;
|
|
|
|
DEBUGLOGIRP(Irp, Result);
|
|
|
|
FspIopCompleteIrp(Irp, Result);
|
|
|
|
return Result;
|
|
}
|
|
|
|
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();
|
|
|
|
FspMainFileClose(FileDesc->MainFileHandle, FileDesc->MainFileObject);
|
|
|
|
if (0 != FileDesc->DirectoryPattern.Buffer &&
|
|
FspFileDescDirectoryPatternMatchAll != FileDesc->DirectoryPattern.Buffer)
|
|
{
|
|
if (FileDesc->CaseSensitive)
|
|
FspFree(FileDesc->DirectoryPattern.Buffer);
|
|
else
|
|
RtlFreeUnicodeString(&FileDesc->DirectoryPattern);
|
|
}
|
|
|
|
FspFree(FileDesc);
|
|
}
|
|
|
|
NTSTATUS FspFileDescResetDirectoryPattern(FSP_FILE_DESC *FileDesc,
|
|
PUNICODE_STRING FileName, BOOLEAN Reset)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (Reset || 0 == FileDesc->DirectoryPattern.Buffer)
|
|
{
|
|
UNICODE_STRING DirectoryPattern;
|
|
|
|
if (0 == FileName || (sizeof(WCHAR) == FileName->Length && L'*' == FileName->Buffer[0]))
|
|
{
|
|
DirectoryPattern.Length = DirectoryPattern.MaximumLength = sizeof(WCHAR); /* L"*" */
|
|
DirectoryPattern.Buffer = FspFileDescDirectoryPatternMatchAll;
|
|
}
|
|
else
|
|
{
|
|
if (FileDesc->CaseSensitive)
|
|
{
|
|
DirectoryPattern.Length = DirectoryPattern.MaximumLength = FileName->Length;
|
|
DirectoryPattern.Buffer = FspAlloc(FileName->Length);
|
|
if (0 == DirectoryPattern.Buffer)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
RtlCopyMemory(DirectoryPattern.Buffer, FileName->Buffer, FileName->Length);
|
|
}
|
|
else
|
|
{
|
|
NTSTATUS Result = RtlUpcaseUnicodeString(&DirectoryPattern, FileName, TRUE);
|
|
if (!NT_SUCCESS(Result))
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
if (0 != FileDesc->DirectoryPattern.Buffer &&
|
|
FspFileDescDirectoryPatternMatchAll != FileDesc->DirectoryPattern.Buffer)
|
|
{
|
|
if (FileDesc->CaseSensitive)
|
|
FspFree(FileDesc->DirectoryPattern.Buffer);
|
|
else
|
|
RtlFreeUnicodeString(&FileDesc->DirectoryPattern);
|
|
}
|
|
|
|
FileDesc->DirectoryPattern = DirectoryPattern;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS FspMainFileOpen(
|
|
PDEVICE_OBJECT FsvolDeviceObject,
|
|
PDEVICE_OBJECT DeviceObjectHint,
|
|
PUNICODE_STRING MainFileName, BOOLEAN CaseSensitive,
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
ULONG FileAttributes,
|
|
ULONG Disposition,
|
|
PHANDLE PMainFileHandle,
|
|
PFILE_OBJECT *PMainFileObject)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
|
UNICODE_STRING FullFileName = { 0 };
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
IO_DRIVER_CREATE_CONTEXT DriverCreateContext = { 0 };
|
|
PVOID ExtraCreateParameter;
|
|
HANDLE MainFileHandle;
|
|
PFILE_OBJECT MainFileObject;
|
|
|
|
/* assert that the supplied name is actually a main file name */
|
|
ASSERT(FspFileNameIsValid(MainFileName,
|
|
FsvolDeviceExtension->VolumeParams.MaxComponentLength,
|
|
0, 0));
|
|
|
|
*PMainFileHandle = 0;
|
|
*PMainFileObject = 0;
|
|
|
|
switch (Disposition)
|
|
{
|
|
case FILE_CREATE:
|
|
case FILE_OPEN_IF:
|
|
case FILE_OVERWRITE_IF:
|
|
case FILE_SUPERSEDE:
|
|
Disposition = FILE_OPEN_IF;
|
|
break;
|
|
case FILE_OPEN:
|
|
case FILE_OVERWRITE:
|
|
Disposition = FILE_OPEN;
|
|
break;
|
|
default:
|
|
IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
FullFileName.Length = 0;
|
|
FullFileName.MaximumLength =
|
|
FsvolDeviceExtension->VolumeName.Length +
|
|
FsvolDeviceExtension->VolumePrefix.Length +
|
|
MainFileName->Length;
|
|
FullFileName.Buffer = FspAlloc(FullFileName.MaximumLength);
|
|
if (0 == FullFileName.Buffer)
|
|
{
|
|
IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
RtlAppendUnicodeStringToString(&FullFileName, &FsvolDeviceExtension->VolumeName);
|
|
RtlAppendUnicodeStringToString(&FullFileName, &FsvolDeviceExtension->VolumePrefix);
|
|
RtlAppendUnicodeStringToString(&FullFileName, MainFileName);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&FullFileName,
|
|
OBJ_KERNEL_HANDLE | OBJ_FORCE_ACCESS_CHECK | (CaseSensitive ? 0 : OBJ_CASE_INSENSITIVE),
|
|
0/*RootDirectory*/,
|
|
SecurityDescriptor);
|
|
|
|
/* do not use SiloContext field as it is only available on Win10 v1607 and higher */
|
|
DriverCreateContext.Size =
|
|
FIELD_OFFSET(IO_DRIVER_CREATE_CONTEXT, TxnParameters) +
|
|
sizeof(((PIO_DRIVER_CREATE_CONTEXT)0)->TxnParameters);
|
|
DriverCreateContext.DeviceObjectHint = 0 != FsvolDeviceExtension->FsvrtDeviceObject ?
|
|
FsvolDeviceObject : DeviceObjectHint;
|
|
|
|
IoStatus.Status = FsRtlAllocateExtraCreateParameterList(0,
|
|
&DriverCreateContext.ExtraCreateParameter);
|
|
if (!NT_SUCCESS(IoStatus.Status))
|
|
goto exit;
|
|
|
|
IoStatus.Status = FsRtlAllocateExtraCreateParameter(&FspMainFileOpenEcpGuid,
|
|
sizeof(PVOID),
|
|
0/*Flags*/,
|
|
0/*CleanupCallback*/,
|
|
FSP_ALLOC_INTERNAL_TAG,
|
|
&ExtraCreateParameter);
|
|
if (!NT_SUCCESS(IoStatus.Status))
|
|
goto exit;
|
|
|
|
IoStatus.Status = FsRtlInsertExtraCreateParameter(DriverCreateContext.ExtraCreateParameter,
|
|
ExtraCreateParameter);
|
|
if (!NT_SUCCESS(IoStatus.Status))
|
|
{
|
|
FsRtlFreeExtraCreateParameter(ExtraCreateParameter);
|
|
goto exit;
|
|
}
|
|
|
|
IoStatus.Status = IoCreateFileEx(
|
|
&MainFileHandle,
|
|
FILE_READ_ATTRIBUTES,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
0/*AllocationSize*/,
|
|
FileAttributes,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
Disposition,
|
|
FILE_OPEN_REPARSE_POINT,
|
|
0/*EaBuffer*/,
|
|
0/*EaLength*/,
|
|
CreateFileTypeNone,
|
|
0/*InternalParameters*/,
|
|
IO_FORCE_ACCESS_CHECK,
|
|
&DriverCreateContext);
|
|
if (!NT_SUCCESS(IoStatus.Status))
|
|
goto exit;
|
|
|
|
IoStatus.Status = ObReferenceObjectByHandle(
|
|
MainFileHandle,
|
|
0/*DesiredAccess*/,
|
|
*IoFileObjectType,
|
|
KernelMode,
|
|
&MainFileObject,
|
|
0/*HandleInformation*/);
|
|
if (!NT_SUCCESS(IoStatus.Status))
|
|
{
|
|
ObCloseHandle(MainFileHandle, KernelMode);
|
|
goto exit;
|
|
}
|
|
|
|
*PMainFileHandle = MainFileHandle;
|
|
*PMainFileObject = MainFileObject;
|
|
|
|
IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
if (0 != DriverCreateContext.ExtraCreateParameter)
|
|
FsRtlFreeExtraCreateParameterList(DriverCreateContext.ExtraCreateParameter);
|
|
|
|
if (0 != FullFileName.Buffer)
|
|
FspFree(FullFileName.Buffer);
|
|
|
|
return IoStatus.Status;
|
|
}
|
|
|
|
NTSTATUS FspMainFileClose(
|
|
HANDLE MainFileHandle,
|
|
PFILE_OBJECT MainFileObject)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS Result = STATUS_SUCCESS;
|
|
|
|
if (0 != MainFileObject)
|
|
ObDereferenceObject(MainFileObject);
|
|
|
|
if (0 != MainFileHandle)
|
|
{
|
|
Result = ObCloseHandle(MainFileHandle, KernelMode);
|
|
if (!NT_SUCCESS(Result))
|
|
DEBUGLOG("ObCloseHandle() = %s", NtStatusSym(Result));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID FspFileNodeOplockPrepare(PVOID Context, PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
FSP_IOP_REQUEST_WORK *WorkRoutine = (FSP_IOP_REQUEST_WORK *)(UINT_PTR)
|
|
FspFileNodeReleaseForOplock(Context);
|
|
NTSTATUS Result;
|
|
|
|
FSP_FSCTL_STATIC_ASSERT(sizeof(PVOID) == sizeof(VOID (*)(VOID)),
|
|
"Data and code pointers must have same size!");
|
|
|
|
Result = FspWqCreateIrpWorkItem(Irp, WorkRoutine, 0);
|
|
if (!NT_SUCCESS(Result))
|
|
/*
|
|
* Only way to communicate failure is through ExRaiseStatus.
|
|
* We will catch it in FspCheckOplock, etc.
|
|
*/
|
|
ExRaiseStatus(Result);
|
|
}
|
|
|
|
VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (STATUS_SUCCESS == Irp->IoStatus.Status)
|
|
FspWqPostIrpWorkItem(Irp);
|
|
else
|
|
FspIopCompleteIrp(Irp, Irp->IoStatus.Status);
|
|
}
|
|
|
|
WCHAR FspFileDescDirectoryPatternMatchAll[] = L"*";
|
|
|
|
// {904862B4-EB3F-461E-ACB2-4DF2B3FC898B}
|
|
const GUID FspMainFileOpenEcpGuid =
|
|
{ 0x904862b4, 0xeb3f, 0x461e, { 0xac, 0xb2, 0x4d, 0xf2, 0xb3, 0xfc, 0x89, 0x8b } };
|