sys: FspFileNodeRenameCheck

This commit is contained in:
Bill Zissimopoulos 2016-11-14 11:20:32 -08:00
parent cb6b10385b
commit 53e2f13e38

View File

@ -838,55 +838,64 @@ BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp,
{ {
PAGED_CODE(); PAGED_CODE();
/*
* Special rules for renaming open files:
* - A file cannot be renamed if it has any open handles,
* unless it is only open because of a batch opportunistic lock (oplock)
* and the batch oplock can be broken immediately.
* - A file cannot be renamed if a file with the same name exists
* and has open handles (except in the batch-oplock case described earlier).
* - A directory cannot be renamed if it or any of its subdirectories contains a file
* that has open handles (except in the batch-oplock case described earlier).
*/
FSP_FILE_NODE *DescendantFileNode; FSP_FILE_NODE *DescendantFileNode;
FSP_FILE_NODE *DescendantFileNodeArray[16], **DescendantFileNodes; FSP_FILE_NODE *DescendantFileNodeArray[16], **DescendantFileNodes;
ULONG DescendantFileNodeCount, DescendantFileNodeIndex; ULONG DescendantFileNodeCount, DescendantFileNodeIndex;
FSP_DEVICE_CONTEXT_BY_NAME_TABLE_RESTART_KEY RestartKey; FSP_DEVICE_CONTEXT_BY_NAME_TABLE_RESTART_KEY RestartKey;
BOOLEAN SubpathOnly = 0 != FileNode; BOOLEAN CheckingOldName = 0 != FileNode;
BOOLEAN HasOpenHandles;
BOOLEAN Success = TRUE; BOOLEAN Success = TRUE;
DescendantFileNodes = DescendantFileNodeArray;
DescendantFileNodeCount = 0;
/* if we are checking the existing file name, do a quick check here */
if (CheckingOldName)
{
/* if file has single open handle (also means no streams open) and not a directory, exit now */
if (1 == FileNode->HandleCount && !FileNode->IsDirectory)
{
ASSERT(Success);
goto unlock_exit;
}
/* Note: when CheckingOldName==TRUE, the old FileNode is not included in enumerations below */
}
FspFsvolDeviceLockContextTable(FsvolDeviceObject); FspFsvolDeviceLockContextTable(FsvolDeviceObject);
DescendantFileNodes = DescendantFileNodeArray; /* count descendant file nodes and try to gather them in a local array if possible */
if (0 != FileNode)
{
if (1 < FileNode->HandleCount)
{
Success = FALSE;
goto unlock_exit;
}
if (!FileNode->IsDirectory)
goto unlock_exit;
}
DescendantFileNodeCount = 0;
memset(&RestartKey, 0, sizeof RestartKey); memset(&RestartKey, 0, sizeof RestartKey);
for (;;) for (;;)
{ {
DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject, DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject,
FileName, SubpathOnly, &RestartKey); FileName, CheckingOldName, &RestartKey);
if (0 == DescendantFileNode) if (0 == DescendantFileNode)
break; break;
if (0 < DescendantFileNode->HandleCount) /* keep a reference to the FileNode in case it goes away in later processing */
{ FspFileNodeReference(DescendantFileNode);
if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(DescendantFileNode)) ||
FsRtlCurrentOplockH(FspFileNodeAddrOfOplock(DescendantFileNode))) HasOpenHandles = 0 < DescendantFileNode->HandleCount;
{
if (ARRAYSIZE(DescendantFileNodeArray) > DescendantFileNodeCount) if (ARRAYSIZE(DescendantFileNodeArray) > DescendantFileNodeCount)
DescendantFileNodes[DescendantFileNodeCount] = DescendantFileNode; DescendantFileNodes[DescendantFileNodeCount] =
(PVOID)((UINT_PTR)DescendantFileNode | HasOpenHandles);
DescendantFileNodeCount++; DescendantFileNodeCount++;
} }
else
{
Success = FALSE;
goto unlock_exit;
}
}
}
/* if the local array is out of space, gather descendant file nodes in the pool */
if (ARRAYSIZE(DescendantFileNodeArray) < DescendantFileNodeCount) if (ARRAYSIZE(DescendantFileNodeArray) < DescendantFileNodeCount)
{ {
DescendantFileNodes = FspAllocMustSucceed(DescendantFileNodeCount * sizeof(FSP_FILE_NODE *)); DescendantFileNodes = FspAllocMustSucceed(DescendantFileNodeCount * sizeof(FSP_FILE_NODE *));
@ -895,49 +904,69 @@ BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp,
for (;;) for (;;)
{ {
DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject, DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject,
FileName, SubpathOnly, &RestartKey); FileName, CheckingOldName, &RestartKey);
if (0 == DescendantFileNode) if (0 == DescendantFileNode)
break; break;
if (0 < DescendantFileNode->HandleCount) HasOpenHandles = 0 < DescendantFileNode->HandleCount;
{
if (FsRtlCurrentBatchOplock(FspFileNodeAddrOfOplock(DescendantFileNode)) || DescendantFileNodes[DescendantFileNodeIndex] =
FsRtlCurrentOplockH(FspFileNodeAddrOfOplock(DescendantFileNode))) (PVOID)((UINT_PTR)DescendantFileNode | HasOpenHandles);
{
DescendantFileNodes[DescendantFileNodeIndex] = DescendantFileNode;
DescendantFileNodeIndex++; DescendantFileNodeIndex++;
ASSERT(DescendantFileNodeCount >= DescendantFileNodeIndex); ASSERT(DescendantFileNodeCount >= DescendantFileNodeIndex);
} }
else
{
Success = FALSE;
goto unlock_exit;
}
}
}
ASSERT(DescendantFileNodeCount == DescendantFileNodeIndex); ASSERT(DescendantFileNodeCount == DescendantFileNodeIndex);
} }
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
/*
* 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)
{
/* make sure no processes are mapping any descendants as an image */
for ( for (
DescendantFileNodeIndex = 0; DescendantFileNodeIndex = 0;
DescendantFileNodeCount > DescendantFileNodeIndex; DescendantFileNodeCount > DescendantFileNodeIndex;
DescendantFileNodeIndex++) DescendantFileNodeIndex++)
{ {
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex]; DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~1);
Success = MmFlushImageSection(&DescendantFileNode->NonPaged->SectionObjectPointers,
MmFlushForDelete);
if (!Success)
goto unlock_exit;
}
}
/* break any batch oplocks on descendants */
for (
DescendantFileNodeIndex = 0;
DescendantFileNodeCount > DescendantFileNodeIndex;
DescendantFileNodeIndex++)
{
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
HasOpenHandles = (UINT_PTR)DescendantFileNode & 1;
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~1);
if (HasOpenHandles)
FspCheckOplock(FspFileNodeAddrOfOplock(DescendantFileNode), Irp, 0, 0, 0); FspCheckOplock(FspFileNodeAddrOfOplock(DescendantFileNode), Irp, 0, 0, 0);
} }
FspFsvolDeviceLockContextTable(FsvolDeviceObject); FspFsvolDeviceLockContextTable(FsvolDeviceObject);
/* recheck whether there are still files with open handles */
memset(&RestartKey, 0, sizeof RestartKey); memset(&RestartKey, 0, sizeof RestartKey);
for (;;) for (;;)
{ {
DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject, DescendantFileNode = FspFsvolDeviceEnumerateContextByName(FsvolDeviceObject,
FileName, SubpathOnly, &RestartKey); FileName, CheckingOldName, &RestartKey);
if (0 == DescendantFileNode) if (0 == DescendantFileNode)
break; break;
@ -948,16 +977,21 @@ BOOLEAN FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp,
} }
} }
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
if (DescendantFileNodeArray != DescendantFileNodes)
FspFree(DescendantFileNodes);
return Success;
unlock_exit: unlock_exit:
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
/* dereference all FileNode's referenced during initial enumeration */
for (
DescendantFileNodeIndex = 0;
DescendantFileNodeCount > DescendantFileNodeIndex;
DescendantFileNodeIndex++)
{
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~1);
FspFileNodeDereference(DescendantFileNode);
}
if (DescendantFileNodeArray != DescendantFileNodes) if (DescendantFileNodeArray != DescendantFileNodes)
FspFree(DescendantFileNodes); FspFree(DescendantFileNodes);