sys: cache FileDesc->DispositionStatus

DeleteFileW and RemoveDirectoryW in recent versions of Windows 10 have
been changed to perform a FileDispositionInformationEx with POSIX
semantics and if that fails to retry with FileDispositionInformation.
Unfortunately this is done even for legitimate error codes such as
STATUS_DIRECTORY_NOT_EMPTY.

This means that user mode file systems have to do unnecessary CanDelete
checks even when they support FileDispositionInformationEx. The extra
check incurs extra context switches, and in some cases it may also be
costly to compute (e.g. FUSE).

We optimize this away by storing the status of the last CanDelete check
in the FileDesc and then continue returning the same status code for
all checks for the same FileDesc.
This commit is contained in:
Bill Zissimopoulos 2021-12-04 12:36:12 +00:00
parent 87389f010b
commit c208e0ecbd
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
2 changed files with 73 additions and 2 deletions

View File

@ -1497,6 +1497,7 @@ typedef struct
DidSetFileAttributes:1, DidSetReparsePoint:1, DidSetSecurity:1,
DidSetCreationTime:1, DidSetLastAccessTime:1, DidSetLastWriteTime:1, DidSetChangeTime:1,
DirectoryHasSuchFile:1;
NTSTATUS DispositionStatus;
UNICODE_STRING DirectoryPattern;
UNICODE_STRING DirectoryMarker;
UINT64 DirInfo;

View File

@ -83,6 +83,8 @@ static NTSTATUS FspFsvolSetDispositionInformation(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolSetDispositionInformationSuccess(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response);
static NTSTATUS FspFsvolSetDispositionInformationFailure(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response);
static NTSTATUS FspFsvolSetRenameInformation(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolSetRenameInformationSuccess(
@ -125,6 +127,7 @@ FAST_IO_QUERY_OPEN FspFastIoQueryOpen;
#pragma alloc_text(PAGE, FspFsvolSetPositionInformation)
#pragma alloc_text(PAGE, FspFsvolSetDispositionInformation)
#pragma alloc_text(PAGE, FspFsvolSetDispositionInformationSuccess)
#pragma alloc_text(PAGE, FspFsvolSetDispositionInformationFailure)
#pragma alloc_text(PAGE, FspFsvolSetRenameInformation)
#pragma alloc_text(PAGE, FspFsvolSetRenameInformationSuccess)
#pragma alloc_text(PAGE, FspFsvolSetInformation)
@ -1567,6 +1570,27 @@ retry:
else
DispositionFlags = FILE_DISPOSITION_DO_NOT_DELETE;
/*
* DeleteFileW and RemoveDirectoryW in recent versions of Windows 10 have been changed to
* perform a FileDispositionInformationEx with POSIX semantics and if that fails to retry
* with FileDispositionInformation. Unfortunately this is done even for legitimate error
* codes such as STATUS_DIRECTORY_NOT_EMPTY.
*
* This means that user mode file systems have to do unnecessary CanDelete checks even when
* they support FileDispositionInformationEx. The extra check incurs extra context switches,
* and in some cases it may also be costly to compute (e.g. FUSE).
*
* We optimize this away by storing the status of the last CanDelete check in the FileDesc
* and then continue returning the same status code for all checks for the same FileDesc.
*/
if (FILE_DISPOSITION_DELETE == (DispositionFlags & ~FILE_DISPOSITION_POSIX_SEMANTICS) &&
STATUS_SUCCESS != FileDesc->DispositionStatus)
{
Result = FileDesc->DispositionStatus;
goto unlock_exit;
}
FileDesc->DispositionStatus = STATUS_SUCCESS;
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, 0,
FspFsvolSetInformationRequestFini, &Request);
if (!NT_SUCCESS(Result))
@ -1629,6 +1653,44 @@ static NTSTATUS FspFsvolSetDispositionInformationSuccess(
return STATUS_SUCCESS;
}
static NTSTATUS FspFsvolSetDispositionInformationFailure(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response)
{
PAGED_CODE();
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
UINT32 DispositionFlags = Request->Req.SetInformation.Info.DispositionEx.Flags;
/*
* DeleteFileW and RemoveDirectoryW in recent versions of Windows 10 have been changed to
* perform a FileDispositionInformationEx with POSIX semantics and if that fails to retry
* with FileDispositionInformation. Unfortunately this is done even for legitimate error
* codes such as STATUS_DIRECTORY_NOT_EMPTY.
*
* This means that user mode file systems have to do unnecessary CanDelete checks even when
* they support FileDispositionInformationEx. The extra check incurs extra context switches,
* and in some cases it may also be costly to compute (e.g. FUSE).
*
* We optimize this away by storing the status of the last CanDelete check in the FileDesc
* and then continue returning the same status code for all checks for the same FileDesc.
*/
switch (Response->IoStatus.Status)
{
case STATUS_ACCESS_DENIED:
case STATUS_DIRECTORY_NOT_EMPTY:
case STATUS_CANNOT_DELETE:
if (FILE_DISPOSITION_DELETE == (DispositionFlags & ~FILE_DISPOSITION_POSIX_SEMANTICS))
FileDesc->DispositionStatus = Response->IoStatus.Status;
break;
}
Irp->IoStatus.Information = 0;
return Response->IoStatus.Status;
}
static NTSTATUS FspFsvolSetRenameInformation(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
@ -2095,15 +2157,23 @@ NTSTATUS FspFsvolSetInformationComplete(
{
FSP_ENTER_IOC(PAGED_CODE());
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
if (!NT_SUCCESS(Response->IoStatus.Status))
{
/* special case FileDispositionInformation */
switch (FileInformationClass)
{
case FileDispositionInformation:
case FileDispositionInformationEx:
FSP_RETURN(Result = FspFsvolSetDispositionInformationFailure(Irp, Response));
}
Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status;
FSP_RETURN();
}
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
/* special case FileDispositionInformation/FileRenameInformation */
switch (FileInformationClass)
{