sys: reimplement POSIX unlink

This commit is contained in:
Bill Zissimopoulos 2021-11-24 16:03:31 +00:00
parent 666561bfa1
commit 91211f6ccb
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
6 changed files with 352 additions and 175 deletions

View File

@ -1158,11 +1158,11 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
Result = FileSystem->Interface->SetDelete(FileSystem, Result = FileSystem->Interface->SetDelete(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetInformation), (PVOID)ValOfFileContext(Request->Req.SetInformation),
(PWSTR)Request->Buffer, (PWSTR)Request->Buffer,
Request->Req.SetInformation.Info.Disposition.Delete); 0 != (1/*DELETE*/ & Request->Req.SetInformation.Info.DispositionEx.Flags));
} }
else if (0 != FileSystem->Interface->CanDelete) else if (0 != FileSystem->Interface->CanDelete)
{ {
if (Request->Req.SetInformation.Info.Disposition.Delete) if (0 != (1/*DELETE*/ & Request->Req.SetInformation.Info.DispositionEx.Flags))
Result = FileSystem->Interface->CanDelete(FileSystem, Result = FileSystem->Interface->CanDelete(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetInformation), (PVOID)ValOfFileContext(Request->Req.SetInformation),
(PWSTR)Request->Buffer); (PWSTR)Request->Buffer);

View File

@ -89,7 +89,7 @@ static NTSTATUS FspFsvolCleanup(
FspFileNodeAcquireExclusive(FileNode, Main); FspFileNodeAcquireExclusive(FileNode, Main);
FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags); FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags);
Delete = (CleanupFlags & 1) && !FileNode->PosixDelete; Delete = CleanupFlags & 1;
SetAllocationSize = !!(CleanupFlags & 2); SetAllocationSize = !!(CleanupFlags & 2);
FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED); FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
@ -170,17 +170,17 @@ NTSTATUS FspFsvolCleanupComplete(
ASSERT(FileNode == FileDesc->FileNode); ASSERT(FileNode == FileDesc->FileNode);
/* send the appropriate notification; also invalidate dirinfo/etc. caches */ /* send the appropriate notification; also invalidate dirinfo/etc. caches */
if (FileNode->PosixDelete) if (Request->Req.Cleanup.Delete)
{
NotifyFilter = 0;
NotifyAction = 0;
}
else if (Request->Req.Cleanup.Delete)
{ {
NotifyFilter = FileNode->IsDirectory ? NotifyFilter = FileNode->IsDirectory ?
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME; FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;
NotifyAction = FILE_ACTION_REMOVED; NotifyAction = FILE_ACTION_REMOVED;
} }
else if (FileNode->PosixDelete)
{
NotifyFilter = 0;
NotifyAction = 0;
}
else else
{ {
/* send notification for any metadata changes */ /* send notification for any metadata changes */
@ -241,7 +241,7 @@ static VOID FspFsvolCleanupRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Co
FspFileNodeReleaseOwner(FileNode, Pgio, Request); FspFileNodeReleaseOwner(FileNode, Pgio, Request);
FspFileNodeCleanupComplete(FileNode, FileObject); FspFileNodeCleanupComplete(FileNode, FileObject, !!Request->Req.Cleanup.Delete);
if (!FileNode->IsDirectory) if (!FileNode->IsDirectory)
FspFileNodeOplockCheck(FileNode, Irp); FspFileNodeOplockCheck(FileNode, Irp);
SetFlag(FileObject->Flags, FO_CLEANUP_COMPLETE); SetFlag(FileObject->Flags, FO_CLEANUP_COMPLETE);

View File

@ -1492,7 +1492,7 @@ typedef struct
UINT64 UserContext2; UINT64 UserContext2;
UINT32 GrantedAccess; UINT32 GrantedAccess;
UINT32 UINT32
CaseSensitive:1, HasTraversePrivilege:1, DeleteOnClose:1, CaseSensitive:1, HasTraversePrivilege:1, DeleteOnClose:1, PosixDelete:1,
DidSetMetadata:1, DidSetMetadata:1,
DidSetFileAttributes:1, DidSetReparsePoint:1, DidSetSecurity:1, DidSetFileAttributes:1, DidSetReparsePoint:1, DidSetSecurity:1,
DidSetCreationTime:1, DidSetLastAccessTime:1, DidSetLastWriteTime:1, DidSetChangeTime:1, DidSetCreationTime:1, DidSetLastAccessTime:1, DidSetLastWriteTime:1, DidSetChangeTime:1,
@ -1561,8 +1561,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason); FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason);
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags); VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, BOOLEAN Delete);
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
PFILE_OBJECT FileObject, /* non-0 to remove share access */ PFILE_OBJECT FileObject, /* non-0 to remove share access */
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */ BOOLEAN HandleCleanup); /* TRUE to decrement handle count */

View File

@ -42,8 +42,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason); FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason);
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags); VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject); VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, BOOLEAN Delete);
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
PFILE_OBJECT FileObject, /* non-0 to remove share access */ PFILE_OBJECT FileObject, /* non-0 to remove share access */
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */ BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
@ -143,7 +142,6 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
#pragma alloc_text(PAGE, FspFileNodeCleanup) #pragma alloc_text(PAGE, FspFileNodeCleanup)
#pragma alloc_text(PAGE, FspFileNodeCleanupFlush) #pragma alloc_text(PAGE, FspFileNodeCleanupFlush)
#pragma alloc_text(PAGE, FspFileNodeCleanupComplete) #pragma alloc_text(PAGE, FspFileNodeCleanupComplete)
#pragma alloc_text(PAGE, FspFileNodePosixDelete)
#pragma alloc_text(PAGE, FspFileNodeClose) #pragma alloc_text(PAGE, FspFileNodeClose)
#pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache) #pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache)
#pragma alloc_text(PAGE, FspFileNodeOverwriteStreams) #pragma alloc_text(PAGE, FspFileNodeOverwriteStreams)
@ -792,7 +790,7 @@ VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject; PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2; FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
BOOLEAN DeletePending, SetAllocationSize, SingleHandle; BOOLEAN DeletePending, Delete, SetAllocationSize, SingleHandle;
FspFsvolDeviceLockContextTable(FsvolDeviceObject); FspFsvolDeviceLockContextTable(FsvolDeviceObject);
@ -807,7 +805,19 @@ VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject); FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
*PCleanupFlags = SingleHandle ? DeletePending | (SetAllocationSize << 1) : 0; Delete = FALSE;
if (!FileNode->PosixDelete)
{
if (FileDesc->PosixDelete)
{
FileNode->PosixDelete = TRUE;
Delete = TRUE;
}
else if (SingleHandle)
Delete = DeletePending;
}
*PCleanupFlags = SingleHandle ? Delete | (SetAllocationSize << 1) : Delete;
} }
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject) VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject)
@ -863,7 +873,7 @@ VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject)
} }
} }
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject) VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, BOOLEAN Delete)
{ {
/* /*
* Complete the cleanup of a FileNode. Remove its share access and * Complete the cleanup of a FileNode. Remove its share access and
@ -904,6 +914,52 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
IoRemoveShareAccess(FileObject, &FileNode->ShareAccess); IoRemoveShareAccess(FileObject, &FileNode->ShareAccess);
if (Delete)
{
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
&DeletedFromContextTable);
ASSERT(DeletedFromContextTable);
FileNode->OpenCount = 0;
/*
* We now have to deal with the scenario where there are cleaned up,
* but unclosed streams for this file still in the context table.
*/
if (FsvolDeviceExtension->VolumeParams.NamedStreams &&
0 == FileNode->MainFileNode)
{
BOOLEAN StreamDeletedFromContextTable;
USHORT FileNameLength = FileNode->FileName.Length;
GATHER_DESCENDANTS(&FileNode->FileName, FALSE,
if (DescendantFileNode->FileName.Length > FileNameLength &&
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)])
break;
ASSERT(FileNode != DescendantFileNode);
ASSERT(0 != DescendantFileNode->OpenCount);
);
for (
DescendantFileNodeIndex = 0;
DescendantFileNodeCount > DescendantFileNodeIndex;
DescendantFileNodeIndex++)
{
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &DescendantFileNode->FileName,
&StreamDeletedFromContextTable);
if (StreamDeletedFromContextTable)
{
DescendantFileNode->OpenCount = 0;
FspFileNodeDereference(DescendantFileNode);
}
}
SCATTER_DESCENDANTS(FALSE);
}
}
ASSERT(0 < FileNode->HandleCount); ASSERT(0 < FileNode->HandleCount);
if (0 == --FileNode->HandleCount) if (0 == --FileNode->HandleCount)
{ {
@ -912,54 +968,9 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
DeletePending = 0 != FileNode->DeletePending; DeletePending = 0 != FileNode->DeletePending;
MemoryBarrier(); MemoryBarrier();
if (DeletePending && !FileNode->PosixDelete) if (DeletePending)
{
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
&DeletedFromContextTable);
ASSERT(DeletedFromContextTable);
FileNode->OpenCount = 0;
FileNode->Header.FileSize.QuadPart = 0; FileNode->Header.FileSize.QuadPart = 0;
/*
* We now have to deal with the scenario where there are cleaned up,
* but unclosed streams for this file still in the context table.
*/
if (FsvolDeviceExtension->VolumeParams.NamedStreams &&
0 == FileNode->MainFileNode)
{
BOOLEAN StreamDeletedFromContextTable;
USHORT FileNameLength = FileNode->FileName.Length;
GATHER_DESCENDANTS(&FileNode->FileName, FALSE,
if (DescendantFileNode->FileName.Length > FileNameLength &&
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)])
break;
ASSERT(FileNode != DescendantFileNode);
ASSERT(0 != DescendantFileNode->OpenCount);
ASSERT(0 == DescendantFileNode->HandleCount);
);
for (
DescendantFileNodeIndex = 0;
DescendantFileNodeCount > DescendantFileNodeIndex;
DescendantFileNodeIndex++)
{
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &DescendantFileNode->FileName,
&StreamDeletedFromContextTable);
if (StreamDeletedFromContextTable)
{
DescendantFileNode->OpenCount = 0;
FspFileNodeDereference(DescendantFileNode);
}
}
SCATTER_DESCENDANTS(FALSE);
}
}
if (DeletePending || FileNode->TruncateOnClose) if (DeletePending || FileNode->TruncateOnClose)
{ {
UINT64 AllocationUnit = UINT64 AllocationUnit =
@ -1016,66 +1027,6 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
FspFileNodeDereference(FileNode); FspFileNodeDereference(FileNode);
} }
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject)
{
/*
* Perform a POSIX delete of a FileNode. This removes the FileNode from the Context table.
*
* The FileNode must be acquired exclusive (Main or Full) when calling this function.
*/
PAGED_CODE();
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
BOOLEAN DeletedFromContextTable = FALSE;
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
&DeletedFromContextTable);
ASSERT(DeletedFromContextTable);
FileNode->OpenCount = 0;
if (FsvolDeviceExtension->VolumeParams.NamedStreams &&
0 == FileNode->MainFileNode)
{
BOOLEAN StreamDeletedFromContextTable;
USHORT FileNameLength = FileNode->FileName.Length;
GATHER_DESCENDANTS(&FileNode->FileName, FALSE,
if (DescendantFileNode->FileName.Length > FileNameLength &&
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)])
break;
ASSERT(FileNode != DescendantFileNode);
ASSERT(0 != DescendantFileNode->OpenCount);
);
for (
DescendantFileNodeIndex = 0;
DescendantFileNodeCount > DescendantFileNodeIndex;
DescendantFileNodeIndex++)
{
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &DescendantFileNode->FileName,
&StreamDeletedFromContextTable);
if (StreamDeletedFromContextTable)
{
DescendantFileNode->OpenCount = 0;
FspFileNodeDereference(DescendantFileNode);
}
}
SCATTER_DESCENDANTS(FALSE);
}
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
FspFileNodeDereference(FileNode);
}
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode, VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
PFILE_OBJECT FileObject, /* non-0 to remove share access */ PFILE_OBJECT FileObject, /* non-0 to remove share access */
BOOLEAN HandleCleanup) /* TRUE to decrement handle count */ BOOLEAN HandleCleanup) /* TRUE to decrement handle count */

View File

@ -1460,7 +1460,7 @@ static NTSTATUS FspFsvolSetDispositionInformation(
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
DispositionFlags = !!((PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->DeleteFile; DispositionFlags = !!((PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->DeleteFile;
DispositionFlags |= FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK; DispositionFlags |= FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK;
// old-school delete always did image section check; see below // old-school delete does image section check
} }
else else
{ {
@ -1470,12 +1470,9 @@ static NTSTATUS FspFsvolSetDispositionInformation(
return STATUS_INVALID_PARAMETER; return STATUS_INVALID_PARAMETER;
DispositionFlags = ((PFILE_DISPOSITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer)->Flags; DispositionFlags = ((PFILE_DISPOSITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer)->Flags;
/* !!!: REVISIT: /* WinFsp does not support the FILE_DISPOSITION_ON_CLOSE flag */
* For now we cannot handle the FILE_DISPOSITION_ON_CLOSE flag,
* as we need to understand the semantics better.
*/
if (FlagOn(DispositionFlags, FILE_DISPOSITION_ON_CLOSE)) if (FlagOn(DispositionFlags, FILE_DISPOSITION_ON_CLOSE))
return STATUS_INVALID_PARAMETER; return STATUS_NOT_SUPPORTED;
} }
if (FileNode->IsRootDirectory) if (FileNode->IsRootDirectory)
@ -1485,6 +1482,12 @@ static NTSTATUS FspFsvolSetDispositionInformation(
retry: retry:
FspFileNodeAcquireExclusive(FileNode, Full); FspFileNodeAcquireExclusive(FileNode, Full);
if (FileNode->PosixDelete)
{
Result = STATUS_ACCESS_DENIED;
goto unlock_exit;
}
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE)) if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
{ {
/* /*
@ -1534,18 +1537,25 @@ retry:
goto unlock_exit; goto unlock_exit;
} }
/*
* The documentation states:
* A return value of STATUS_CANNOT_DELETE indicates that either the file is read-only,
* or there's an existing mapped view to the file. Specifying FILE_DISPOSITION_IGNORE_-
* READONLY_ATTRIBUTE avoids this return value due to the file being read-only, provided
* the caller has FILE_WRITE_ATTRIBUTES access to the file (the access that would be
* required to clear the read-only attribute).
*
* This appears to be incorrect with NTFS on Win10 and Win11. See:
* https://github.com/MicrosoftDocs/windows-driver-docs-ddi/issues/1216
*/
#if 0
if (FlagOn(DispositionFlags, FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE)) if (FlagOn(DispositionFlags, FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE))
{ {
/* if FileDesc does not have FILE_WRITE_ATTRIBUTE access, remove IGNORE_READONLY_ATTRIBUTE */ /* if FileDesc does not have FILE_WRITE_ATTRIBUTE access, remove IGNORE_READONLY_ATTRIBUTE */
if (!FlagOn(FileDesc->GrantedAccess, FILE_WRITE_ATTRIBUTES)) if (!FlagOn(FileDesc->GrantedAccess, FILE_WRITE_ATTRIBUTES))
DispositionFlags &= ~FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE; DispositionFlags &= ~FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
} }
} #endif
if (FileNode->PosixDelete)
{
Result = STATUS_SUCCESS;
goto unlock_exit;
} }
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE)) if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
@ -1587,50 +1597,29 @@ static NTSTATUS FspFsvolSetDispositionInformationSuccess(
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject; PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext; FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp); FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
UINT32 DispositionFlags = Request->Req.SetInformation.Info.DispositionEx.Flags; UINT32 DispositionFlags = Request->Req.SetInformation.Info.DispositionEx.Flags;
BOOLEAN DeleteFile = BooleanFlagOn(DispositionFlags, FILE_DISPOSITION_DELETE); BOOLEAN Delete = BooleanFlagOn(DispositionFlags, FILE_DISPOSITION_DELETE);
FileNode->DeletePending = DeleteFile; FileNode->DeletePending = Delete;
FileObject->DeletePending = DeleteFile; FileObject->DeletePending = Delete;
if (FlagOn(DispositionFlags, FILE_DISPOSITION_POSIX_SEMANTICS)) if (!Delete)
FileDesc->PosixDelete = FALSE;
else if (FlagOn(DispositionFlags, FILE_DISPOSITION_POSIX_SEMANTICS))
FileDesc->PosixDelete = TRUE;
/* fastfat does this, although it seems unnecessary */
#if 1
if (FileNode->IsDirectory && Delete)
{ {
ASSERT(DeleteFile); FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(IrpSp->DeviceObject);
FileNode->PosixDelete = TRUE; FspNotifyDeletePending(
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
if (FileNode->IsDirectory)
{
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(IrpSp->DeviceObject);
FspNotifyDeletePending(
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
}
/* send the appropriate notification; also invalidate dirinfo/etc. caches */
ULONG NotifyFilter, NotifyAction;
NotifyFilter = FileNode->IsDirectory ?
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;
NotifyAction = FILE_ACTION_REMOVED;
FspFileNodeNotifyChange(FileNode, NotifyFilter, NotifyAction, TRUE);
/* perform POSIX delete: remove file node from the context table */
FspFileNodePosixDelete(FileNode, FileObject);
}
else
{
/* fastfat does this, although it seems unnecessary */
#if 1
if (FileNode->IsDirectory && DeleteFile)
{
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(IrpSp->DeviceObject);
FspNotifyDeletePending(
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
}
#endif
} }
#endif
FspIopRequestContext(Request, RequestFileNode) = 0; FspIopRequestContext(Request, RequestFileNode) = 0;
FspFileNodeReleaseOwner(FileNode, Full, Request); FspFileNodeReleaseOwner(FileNode, Full, Request);

View File

@ -894,6 +894,242 @@ void delete_standby_test(void)
} }
} }
static void delete_ex_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileInfoTimeout)
{
BOOLEAN Success;
DWORD FileSystemFlags;
Success = GetVolumeInformationW(L"C:\\",
0, 0,
0, 0, &FileSystemFlags,
0, 0);
if (!Success || 0 == (FileSystemFlags & 0x400/*FILE_SUPPORTS_POSIX_UNLINK_RENAME*/))
/* skip this test if the system lacks FILE_SUPPORTS_POSIX_UNLINK_RENAME capability */
return;
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
NTSYSCALLAPI NTSTATUS NTAPI
NtSetInformationFile(
HANDLE FileHandle,
PIO_STATUS_BLOCK IoStatusBlock,
PVOID FileInformation,
ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass);
typedef struct
{
ULONG Flags;
} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
HANDLE Handle0, Handle1, Handle2, FindHandle;
WCHAR FilePath[MAX_PATH];
WIN32_FIND_DATAW FindData;
FILE_DISPOSITION_INFORMATION_EX DispositionInfo;
IO_STATUS_BLOCK IoStatus;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\",
VolPrefix ? L"" : L"\\\\?\\GLOBALROOT", VolPrefix ? VolPrefix : memfs_volumename(memfs));
Success = GetVolumeInformationW(FilePath,
0, 0,
0, 0, &FileSystemFlags,
0, 0);
ASSERT(Success);
if (0 != (FileSystemFlags & 0x400/*FILE_SUPPORTS_POSIX_UNLINK_RENAME*/))
{
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
/* POSIX Semantics / Ignore Readonly */
Handle0 = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
CREATE_NEW, FILE_ATTRIBUTE_READONLY, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle0);
Handle1 = CreateFileW(FilePath,
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle1);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */
IoStatus.Status = NtSetInformationFile(
Handle1, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(STATUS_CANNOT_DELETE == IoStatus.Status);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 0x13; /* DELETE | POSIX_SEMANTICS | IGNORE_READONLY_ATTRIBUTE */
IoStatus.Status = NtSetInformationFile(
Handle1, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(0 == IoStatus.Status);
FindHandle = FindFirstFileW(FilePath, &FindData);
ASSERT(INVALID_HANDLE_VALUE != FindHandle);
ASSERT(0 == mywcscmp(FindData.cFileName, 4, L"file", 4));
FindClose(FindHandle);
Handle2 = CreateFileW(FilePath,
0, 0, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE == Handle2);
ASSERT(ERROR_ACCESS_DENIED == GetLastError());
CloseHandle(Handle1);
FindHandle = FindFirstFileW(FilePath, &FindData);
ASSERT(INVALID_HANDLE_VALUE == FindHandle);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
Handle2 = CreateFileW(FilePath,
0, 0, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE == Handle2);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 0; /* DO_NOT_DELETE */
IoStatus.Status = NtSetInformationFile(
Handle0, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(STATUS_ACCESS_DENIED == IoStatus.Status);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 1; /* DELETE */
IoStatus.Status = NtSetInformationFile(
Handle0, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(STATUS_ACCESS_DENIED == IoStatus.Status);
CloseHandle(Handle0);
/* POSIX Semantics / Set/Reset */
Handle0 = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle0);
Handle1 = CreateFileW(FilePath,
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle1);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */
IoStatus.Status = NtSetInformationFile(
Handle1, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(STATUS_SUCCESS == IoStatus.Status);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 0; /* DO_NOT_DELETE */
IoStatus.Status = NtSetInformationFile(
Handle1, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(STATUS_SUCCESS == IoStatus.Status);
CloseHandle(Handle1);
FindHandle = FindFirstFileW(FilePath, &FindData);
ASSERT(INVALID_HANDLE_VALUE != FindHandle);
ASSERT(0 == mywcscmp(FindData.cFileName, 4, L"file", 4));
FindClose(FindHandle);
Handle1 = CreateFileW(FilePath,
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle1);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */
IoStatus.Status = NtSetInformationFile(
Handle1, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(STATUS_SUCCESS == IoStatus.Status);
CloseHandle(Handle1);
CloseHandle(Handle0);
#if 0
/* On Close */
Handle0 = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle0);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 8; /* DO_NOT_DELETE | ON_CLOSE */
IoStatus.Status = NtSetInformationFile(
Handle0, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(0 == IoStatus.Status);
CloseHandle(Handle0);
Handle0 = CreateFileW(FilePath,
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle0);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 9; /* DELETE | ON_CLOSE */;
IoStatus.Status = NtSetInformationFile(
Handle0, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(STATUS_NOT_SUPPORTED == IoStatus.Status);
memset(&DispositionInfo, 0, sizeof DispositionInfo);
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */;
IoStatus.Status = NtSetInformationFile(
Handle0, &IoStatus,
&DispositionInfo, sizeof DispositionInfo,
64/*FileDispositionInformationEx*/);
ASSERT(0 == IoStatus.Status);
CloseHandle(Handle0);
#endif
}
memfs_stop(memfs);
}
void delete_ex_test(void)
{
if (OptLegacyUnlinkRename)
return;
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH], DriveBuf[3];
GetTestDirectoryAndDrive(DirBuf, DriveBuf);
delete_ex_dotest(-1, DriveBuf, DirBuf, 0);
}
if (WinFspDiskTests)
{
delete_ex_dotest(MemfsDisk, 0, 0, 0);
delete_ex_dotest(MemfsDisk, 0, 0, 1000);
}
if (WinFspNetTests)
{
delete_ex_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 0);
delete_ex_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 1000);
}
}
static void rename_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout) static void rename_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{ {
void *memfs = memfs_start_ex(Flags, FileInfoTimeout); void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
@ -2225,6 +2461,8 @@ void info_tests(void)
if (!OptShareName) if (!OptShareName)
TEST(delete_mmap_test); TEST(delete_mmap_test);
TEST(delete_standby_test); TEST(delete_standby_test);
if (!OptLegacyUnlinkRename)
TEST(delete_ex_test);
TEST(rename_test); TEST(rename_test);
TEST(rename_backslash_test); TEST(rename_backslash_test);
TEST(rename_open_test); TEST(rename_open_test);