sys,dll,inc: implement new Delete design and POSIX semantics

This commit is contained in:
Bill Zissimopoulos 2021-10-19 15:23:15 +01:00
parent 3e66082f11
commit 19823d84de
No known key found for this signature in database
GPG Key ID: 3D4F95D52C7B3EA3
15 changed files with 424 additions and 74 deletions

View File

@ -42,8 +42,8 @@ install:
build_script:
- appveyor AddMessage "Reboot complete" -Category Information
# build cygfuse
- C:\cygwin64\setup-x86_64.exe -qnNd -P cygport
- C:\cygwin64\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
#- C:\cygwin64\setup-x86_64.exe -qnNd -P cygport
#- C:\cygwin64\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
#- C:\cygwin\setup-x86.exe -qnNd -P cygport
#- C:\cygwin\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
# build winfsp

View File

@ -204,7 +204,8 @@ enum
UINT32 WslFeatures:1; /* support features required for WSLinux */\
UINT32 DirectoryMarkerAsNextOffset:1; /* directory marker is next offset instead of last name */\
UINT32 RejectIrpPriorToTransact0:1; /* reject IRP's prior to FspFsctlTransact with 0 buffers */\
UINT32 KmReservedFlags:3;\
UINT32 SupportsPosixUnlinkRename:1; /* file system supports POSIX-style unlink and rename */\
UINT32 KmReservedFlags:2;\
WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\
WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)];
#define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\
@ -413,6 +414,10 @@ typedef struct
UINT32 Delete:1;
} Disposition;
struct
{
UINT32 Flags;
} DispositionEx;
struct
{
UINT64 FileSize;
} EndOfFile;

View File

@ -47,6 +47,19 @@
extern "C" {
#endif
/*
* The FILE_DISPOSITION_* definitions appear to be missing from the user mode headers.
*/
#if !defined(FILE_DISPOSITION_DELETE)
#define FILE_DISPOSITION_DO_NOT_DELETE 0x00000000
#define FILE_DISPOSITION_DELETE 0x00000001
#define FILE_DISPOSITION_POSIX_SEMANTICS 0x00000002
/* remaining flags are not needed for user mode file systems but included for completeness */
#define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x00000004
#define FILE_DISPOSITION_ON_CLOSE 0x00000008
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x00000010
#endif
/*
* The REPARSE_DATA_BUFFER definitions appear to be missing from the user mode headers.
*/
@ -348,6 +361,9 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
/**
* Cleanup a file.
*
* (NOTE: use of this function with the FspCleanupDelete flag is not recommended;
* use Delete instead.)
*
* When CreateFile is used to open or create a file the kernel creates a kernel mode file
* object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
* be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
@ -402,6 +418,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Close
* CanDelete
* SetDelete
* Delete
*/
VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName, ULONG Flags);
@ -575,6 +592,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
/**
* Determine whether a file or directory can be deleted.
*
* (NOTE: use of this function is not recommended; use Delete instead.)
*
* This function tests whether a file or directory can be safely deleted. This function does
* not need to perform access checks, but may performs tasks such as check for empty
* directories, etc.
@ -599,6 +618,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @see
* Cleanup
* SetDelete
* Delete
*/
NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName);
@ -880,6 +900,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
/**
* Set the file delete flag.
*
* (NOTE: use of this function is not recommended; use Delete instead.)
*
* This function sets a flag to indicates whether the FSD file should delete a file
* when it is closed. This function does not need to perform access checks, but may
* performs tasks such as check for empty directories, etc.
@ -908,6 +930,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @see
* Cleanup
* CanDelete
* Delete
*/
NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile);
@ -1040,12 +1063,65 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
PVOID FileContext,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
FSP_FSCTL_FILE_INFO *FileInfo);
/**
* Set the file delete flag or delete a file.
*
* This function replaces CanDelete, SetDelete and uses of Cleanup with the FspCleanupDelete flag
* and is recommended for use in all new code.
*
* Due to the complexity of file deletion in the Windows file system this function is used
* in many scenarios. Its usage is controlled by the Flags parameter:
* <ul>
* <li>FILE_DISPOSITION_DO_NOT_DELETE: Unmark the file for deletion.
* Do <b>NOT</b> delete the file either now or at Cleanup time.</li>
* <li>FILE_DISPOSITION_DELETE: Mark the file for deletion,
* but do <b>NOT</b> delete the file. The file will be deleted at Cleanup time
* (via a call to Delete with Flags = -1).
* This function does not need to perform access checks, but may
* performs tasks such as check for empty directories, etc.</li>
* <li>FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS: Delete the file
* <b>NOW</b> using POSIX semantics. Open user mode handles to the file remain valid.
* This case will be received only when FSP_FSCTL_VOLUME_PARAMS :: SupportsPosixUnlinkRename is set.</li>
* <li>-1: Delete the file <b>NOW</b> using regular Windows semantics.
* Called during Cleanup with no open user mode handles remaining.
* If a file system implements Delete, Cleanup should <b>NOT</b> be used for deletion anymore.</li>
* </ul>
*
* This function gets called in all file deletion scenarios:
* <ul>
* <li>When the DeleteFile or RemoveDirectory API's are used.</li>
* <li>When the SetInformationByHandle API with FileDispositionInfo or FileDispositionInfoEx is used.
* <li>When a file is opened using FILE_DELETE_ON_CLOSE.</li>
* <li>Etc.</li>
* </ul>
*
* NOTE: Delete takes precedence over CanDelete, SetDelete and Cleanup with the FspCleanupDelete flag.
* This means that if Delete is defined, CanDelete and SetDelete will never be called and
* Cleanup will never be called with the FspCleanupDelete flag.
*
* @param FileSystem
* The file system on which this request is posted.
* @param FileContext
* The file context of the file or directory to set the delete flag for.
* @param FileName
* The name of the file or directory to set the delete flag for.
* @param Flags
* File disposition flags
* @return
* STATUS_SUCCESS or error code.
* @see
* Cleanup
* CanDelete
* SetDelete
*/
NTSTATUS (*Delete)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName, ULONG Flags);
/*
* This ensures that this interface will always contain 64 function pointers.
* Please update when changing the interface as it is important for future compatibility.
*/
NTSTATUS (*Reserved[33])();
NTSTATUS (*Reserved[32])();
} FSP_FILE_SYSTEM_INTERFACE;
FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()),
"FSP_FILE_SYSTEM_INTERFACE must have 64 entries.");

View File

@ -56,7 +56,10 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
(FspFsctlTransactCleanupKind == Request->Kind &&
Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass ||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
3/*DELETE|POSIX_SEMANTICS*/ == (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext &&
@ -67,7 +70,9 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
else
if (FspFsctlTransactCreateKind == Request->Kind ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
{
@ -95,7 +100,10 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
(FspFsctlTransactCleanupKind == Request->Kind &&
Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass ||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
3/*DELETE|POSIX_SEMANTICS*/ == (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext &&
@ -106,7 +114,9 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
else
if (FspFsctlTransactCreateKind == Request->Kind ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
{
@ -978,16 +988,28 @@ FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem,
FSP_API NTSTATUS FspFileSystemOpCleanup(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
ULONG CleanupFlags =
(0 != Request->Req.Cleanup.Delete ? FspCleanupDelete : 0) |
(0 != Request->Req.Cleanup.SetAllocationSize ? FspCleanupSetAllocationSize : 0) |
(0 != Request->Req.Cleanup.SetArchiveBit ? FspCleanupSetArchiveBit : 0) |
(0 != Request->Req.Cleanup.SetLastAccessTime ? FspCleanupSetLastAccessTime : 0) |
(0 != Request->Req.Cleanup.SetLastWriteTime ? FspCleanupSetLastWriteTime : 0) |
(0 != Request->Req.Cleanup.SetChangeTime ? FspCleanupSetChangeTime : 0);
if (Request->Req.Cleanup.Delete && 0 != FileSystem->Interface->Delete)
{
FileSystem->Interface->Delete(FileSystem,
(PVOID)ValOfFileContext(Request->Req.Cleanup),
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
(ULONG)-1);
CleanupFlags &= ~FspCleanupDelete;
}
if (0 != FileSystem->Interface->Cleanup)
FileSystem->Interface->Cleanup(FileSystem,
(PVOID)ValOfFileContext(Request->Req.Cleanup),
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
(0 != Request->Req.Cleanup.Delete ? FspCleanupDelete : 0) |
(0 != Request->Req.Cleanup.SetAllocationSize ? FspCleanupSetAllocationSize : 0) |
(0 != Request->Req.Cleanup.SetArchiveBit ? FspCleanupSetArchiveBit : 0) |
(0 != Request->Req.Cleanup.SetLastAccessTime ? FspCleanupSetLastAccessTime : 0) |
(0 != Request->Req.Cleanup.SetLastWriteTime ? FspCleanupSetLastWriteTime : 0) |
(0 != Request->Req.Cleanup.SetChangeTime ? FspCleanupSetChangeTime : 0));
CleanupFlags);
return STATUS_SUCCESS;
}
@ -1134,7 +1156,9 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
&FileInfo);
break;
case 13/*FileDispositionInformation*/:
if (0 != FileSystem->Interface->GetFileInfo)
case 64/*FileDispositionInformationEx*/:
if (0 == (0x10/*IGNORE_READONLY_ATTRIBUTE*/ & Request->Req.SetInformation.Info.DispositionEx.Flags) &&
0 != FileSystem->Interface->GetFileInfo)
{
Result = FileSystem->Interface->GetFileInfo(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetInformation), &FileInfo);
@ -1144,7 +1168,17 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
break;
}
}
if (0 != FileSystem->Interface->SetDelete)
if (0 != FileSystem->Interface->Delete)
{
Result = FileSystem->Interface->Delete(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetInformation),
(PWSTR)Request->Buffer,
Request->Req.SetInformation.Info.DispositionEx.Flags & 3/*DELETE|POSIX_SEMANTICS*/);
}
else if (0 != (2/*POSIX_SEMANTICS*/ & Request->Req.SetInformation.Info.DispositionEx.Flags))
/* only FSP_FILE_SYSTEM_INTERFACE::Delete can do POSIX semantics; return error if unimplemented */
Result = STATUS_INVALID_PARAMETER;
else if (0 != FileSystem->Interface->SetDelete)
{
Result = FileSystem->Interface->SetDelete(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetInformation),
@ -1162,6 +1196,7 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
}
break;
case 10/*FileRenameInformation*/:
case 65/*FileRenameInformationEx*/:
if (0 != FileSystem->Interface->Rename)
{
if (0 != Request->Req.SetInformation.Info.Rename.AccessToken)

View File

@ -82,21 +82,21 @@ static NTSTATUS FspFsvolCleanup(
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
FSP_FSCTL_TRANSACT_REQ *Request;
ULONG CleanupFlags;
BOOLEAN DeletePending, SetAllocationSize, FileModified;
BOOLEAN Delete, SetAllocationSize, FileModified;
ASSERT(FileNode == FileDesc->FileNode);
FspFileNodeAcquireExclusive(FileNode, Main);
FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags);
DeletePending = CleanupFlags & 1;
Delete = (CleanupFlags & 1) && !FileNode->PosixDelete;
SetAllocationSize = !!(CleanupFlags & 2);
FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
/* if this is a directory inform the FSRTL Notify mechanism */
if (FileNode->IsDirectory)
{
if (DeletePending)
if (Delete)
FspNotifyDeletePending(
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
@ -108,12 +108,12 @@ static NTSTATUS FspFsvolCleanup(
FspFileNodeUnlockAll(FileNode, FileObject, IoGetRequestorProcess(Irp));
/* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */
FspIopCreateRequestMustSucceedEx(Irp, DeletePending ? &FileNode->FileName : 0, 0,
FspIopCreateRequestMustSucceedEx(Irp, Delete ? &FileNode->FileName : 0, 0,
FspFsvolCleanupRequestFini, &Request);
Request->Kind = FspFsctlTransactCleanupKind;
Request->Req.Cleanup.UserContext = FileNode->UserContext;
Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2;
Request->Req.Cleanup.Delete = DeletePending;
Request->Req.Cleanup.Delete = Delete;
Request->Req.Cleanup.SetAllocationSize = SetAllocationSize;
Request->Req.Cleanup.SetArchiveBit = (FileModified || FileDesc->DidSetSecurity) &&
!FileDesc->DidSetFileAttributes;
@ -170,7 +170,12 @@ NTSTATUS FspFsvolCleanupComplete(
ASSERT(FileNode == FileDesc->FileNode);
/* send the appropriate notification; also invalidate dirinfo/etc. caches */
if (Request->Req.Cleanup.Delete)
if (FileNode->PosixDelete)
{
NotifyFilter = 0;
NotifyAction = 0;
}
else if (Request->Req.Cleanup.Delete)
{
NotifyFilter = FileNode->IsDirectory ?
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;

View File

@ -1462,6 +1462,7 @@ typedef struct FSP_FILE_NODE
ULONG EaChangeNumber;
ULONG EaChangeCount;
BOOLEAN TruncateOnClose;
BOOLEAN PosixDelete;
FILE_LOCK FileLock;
#if (NTDDI_VERSION < NTDDI_WIN8)
OPLOCK Oplock;
@ -1561,6 +1562,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodePosixDelete(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 */

View File

@ -43,6 +43,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
VOID FspFileNodePosixDelete(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 */
@ -141,6 +142,7 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
#pragma alloc_text(PAGE, FspFileNodeCleanup)
#pragma alloc_text(PAGE, FspFileNodeCleanupFlush)
#pragma alloc_text(PAGE, FspFileNodeCleanupComplete)
#pragma alloc_text(PAGE, FspFileNodePosixDelete)
#pragma alloc_text(PAGE, FspFileNodeClose)
#pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache)
#pragma alloc_text(PAGE, FspFileNodeOverwriteStreams)
@ -909,7 +911,7 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
DeletePending = 0 != FileNode->DeletePending;
MemoryBarrier();
if (DeletePending)
if (DeletePending && !FileNode->PosixDelete)
{
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
&DeletedFromContextTable);
@ -1013,6 +1015,66 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
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,
PFILE_OBJECT FileObject, /* non-0 to remove share access */
BOOLEAN HandleCleanup) /* TRUE to decrement handle count */

View File

@ -1441,7 +1441,8 @@ static NTSTATUS FspFsvolSetDispositionInformation(
NTSTATUS Result;
PFILE_OBJECT FileObject = IrpSp->FileObject;
PFILE_DISPOSITION_INFORMATION Info = (PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
UINT32 DispositionFlags;
ULONG Length = IrpSp->Parameters.SetFile.Length;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
@ -1449,9 +1450,34 @@ static NTSTATUS FspFsvolSetDispositionInformation(
BOOLEAN Success;
ASSERT(FileNode == FileDesc->FileNode);
ASSERT(
FileDispositionInformation == FileInformationClass ||
FileDispositionInformationEx == FileInformationClass);
if (FileDispositionInformation == FileInformationClass)
{
if (sizeof(FILE_DISPOSITION_INFORMATION) > Length)
return STATUS_INVALID_PARAMETER;
DispositionFlags = !!((PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->DeleteFile;
DispositionFlags |= FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK;
// old-school delete always did image section check; see below
}
else
{
if (!FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.SupportsPosixUnlinkRename)
return STATUS_INVALID_PARAMETER;
if (sizeof(FILE_DISPOSITION_INFORMATION_EX) > Length)
return STATUS_INVALID_PARAMETER;
DispositionFlags = ((PFILE_DISPOSITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer)->Flags;
/* !!!: REVISIT:
* 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))
return STATUS_INVALID_PARAMETER;
}
if (sizeof(FILE_DISPOSITION_INFORMATION) > Length)
return STATUS_INVALID_PARAMETER;
if (FileNode->IsRootDirectory)
/* cannot delete root directory */
return STATUS_CANNOT_DELETE;
@ -1459,7 +1485,7 @@ static NTSTATUS FspFsvolSetDispositionInformation(
retry:
FspFileNodeAcquireExclusive(FileNode, Full);
if (Info->DeleteFile)
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
{
/*
* Perform oplock check.
@ -1487,15 +1513,40 @@ retry:
if (!NT_SUCCESS(Result))
goto unlock_exit;
/* make sure no process is mapping the file as an image */
Success = MmFlushImageSection(FileObject->SectionObjectPointer, MmFlushForDelete);
if (!Success)
if (FlagOn(DispositionFlags, FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK))
{
Result = STATUS_CANNOT_DELETE;
goto unlock_exit;
/* make sure no process is mapping the file as an image */
Success = MmFlushImageSection(FileObject->SectionObjectPointer, MmFlushForDelete);
if (!Success)
{
Result = STATUS_CANNOT_DELETE;
goto unlock_exit;
}
}
if (FlagOn(DispositionFlags, FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE))
{
/* if FileDesc does not have FILE_WRITE_ATTRIBUTE access, remove IGNORE_READONLY_ATTRIBUTE */
if (!FlagOn(FileDesc->GrantedAccess, FILE_WRITE_ATTRIBUTES))
DispositionFlags &= ~FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
}
}
if (FileNode->PosixDelete)
{
Result = STATUS_SUCCESS;
goto unlock_exit;
}
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
DispositionFlags &=
FILE_DISPOSITION_DO_NOT_DELETE |
FILE_DISPOSITION_DELETE |
FILE_DISPOSITION_POSIX_SEMANTICS |
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
else
DispositionFlags = FILE_DISPOSITION_DO_NOT_DELETE;
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, 0,
FspFsvolSetInformationRequestFini, &Request);
if (!NT_SUCCESS(Result))
@ -1504,8 +1555,8 @@ retry:
Request->Kind = FspFsctlTransactSetInformationKind;
Request->Req.SetInformation.UserContext = FileNode->UserContext;
Request->Req.SetInformation.UserContext2 = FileDesc->UserContext2;
Request->Req.SetInformation.FileInformationClass = FileDispositionInformation;
Request->Req.SetInformation.Info.Disposition.Delete = Info->DeleteFile;
Request->Req.SetInformation.FileInformationClass = FileInformationClass;
Request->Req.SetInformation.Info.DispositionEx.Flags = DispositionFlags;
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestFileNode) = FileNode;
@ -1525,23 +1576,51 @@ static NTSTATUS FspFsvolSetDispositionInformationSuccess(
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
PFILE_DISPOSITION_INFORMATION Info = (PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
UINT32 DispositionFlags = Request->Req.SetInformation.Info.DispositionEx.Flags;
BOOLEAN DeleteFile = BooleanFlagOn(DispositionFlags, FILE_DISPOSITION_DELETE);
FileNode->DeletePending = Info->DeleteFile;
FileObject->DeletePending = Info->DeleteFile;
FileNode->DeletePending = DeleteFile;
FileObject->DeletePending = DeleteFile;
/* fastfat does this, although it seems unnecessary */
#if 1
if (FileNode->IsDirectory && Info->DeleteFile)
if (FlagOn(DispositionFlags, FILE_DISPOSITION_POSIX_SEMANTICS))
{
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(IrpSp->DeviceObject);
FspNotifyDeletePending(
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
ASSERT(DeleteFile);
FileNode->PosixDelete = TRUE;
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;
FspFileNodeReleaseOwner(FileNode, Full, Request);
@ -1796,10 +1875,15 @@ static NTSTATUS FspFsvolSetInformation(
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
/* special case FileDispositionInformation/FileRenameInformation */
if (FileDispositionInformation == FileInformationClass)
switch (FileInformationClass)
{
case FileDispositionInformation:
case FileDispositionInformationEx:
return FspFsvolSetDispositionInformation(FsvolDeviceObject, Irp, IrpSp);
if (FileRenameInformation == FileInformationClass)
case FileRenameInformation:
//case FileRenameInformationEx:
return FspFsvolSetRenameInformation(FsvolDeviceObject, Irp, IrpSp);
}
NTSTATUS Result;
PFILE_OBJECT FileObject = IrpSp->FileObject;
@ -1997,10 +2081,15 @@ NTSTATUS FspFsvolSetInformationComplete(
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
/* special case FileDispositionInformation/FileRenameInformation */
if (FileDispositionInformation == FileInformationClass)
switch (FileInformationClass)
{
case FileDispositionInformation:
case FileDispositionInformationEx:
FSP_RETURN(Result = FspFsvolSetDispositionInformationSuccess(Irp, Response));
if (FileRenameInformation == FileInformationClass)
case FileRenameInformation:
//case FileRenameInformationEx:
FSP_RETURN(Result = FspFsvolSetRenameInformationSuccess(Irp, Response));
}
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;

View File

@ -101,7 +101,8 @@ static NTSTATUS FspFsvolQueryFsAttributeInformation(
(FsvolDeviceExtension->VolumeParams.NamedStreams ? FILE_NAMED_STREAMS : 0) |
//(FsvolDeviceExtension->VolumeParams.HardLinks ? FILE_SUPPORTS_HARD_LINKS : 0) |
(FsvolDeviceExtension->VolumeParams.ExtendedAttributes ? FILE_SUPPORTS_EXTENDED_ATTRIBUTES : 0) |
(FsvolDeviceExtension->VolumeParams.ReadOnlyVolume ? FILE_READ_ONLY_VOLUME : 0);
(FsvolDeviceExtension->VolumeParams.ReadOnlyVolume ? FILE_READ_ONLY_VOLUME : 0) |
(FsvolDeviceExtension->VolumeParams.SupportsPosixUnlinkRename ? FILE_SUPPORTS_POSIX_UNLINK_RENAME : 0);
Info->MaximumComponentNameLength = FsvolDeviceExtension->VolumeParams.MaxComponentLength;
RtlInitUnicodeString(&FileSystemName, FsvolDeviceExtension->VolumeParams.FileSystemName);

View File

@ -32,6 +32,7 @@ set dfl_tests=^
winfsp-tests-x64 ^
winfsp-tests-x64-case-randomize ^
winfsp-tests-x64-flushpurge ^
winfsp-tests-x64-legacy-unlink-rename ^
winfsp-tests-x64-mountpoint-drive ^
winfsp-tests-x64-mountpoint-dir ^
winfsp-tests-x64-mountpoint-dir-case-sensitive ^
@ -52,6 +53,7 @@ set dfl_tests=^
winfsp-tests-x86 ^
winfsp-tests-x86-case-randomize ^
winfsp-tests-x86-flushpurge ^
winfsp-tests-x86-legacy-unlink-rename ^
winfsp-tests-x86-mountpoint-drive ^
winfsp-tests-x86-mountpoint-dir ^
winfsp-tests-x86-mountpoint-dir-case-sensitive ^
@ -191,6 +193,11 @@ winfsp-tests-x64 --flush-and-purge-on-cleanup * +ea*
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x64-legacy-unlink-rename
winfsp-tests-x64 --legacy-unlink-rename * +ea*
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x64-mountpoint-drive
winfsp-tests-x64 --mountpoint=X: --resilient * +ea*
if !ERRORLEVEL! neq 0 goto fail
@ -236,6 +243,11 @@ winfsp-tests-x86 --flush-and-purge-on-cleanup * +ea*
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x86-legacy-unlink-rename
winfsp-tests-x86 --legacy-unlink-rename * +ea*
if !ERRORLEVEL! neq 0 goto fail
exit /b 0
:winfsp-tests-x86-mountpoint-drive
winfsp-tests-x86 --mountpoint=X: --resilient * +ea*
if !ERRORLEVEL! neq 0 goto fail

View File

@ -88,6 +88,13 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH,
#define MEMFS_REJECT_EARLY_IRP
#endif
/*
* Define the MEMFS_DELETE macro to include new Delete support
* (instead of Cleanup/FspCleanupDelete). This is required to
* properly support POSIX unlink/rename.
*/
#define MEMFS_DELETE
/*
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
* a check for the Write buffer to ensure that it is read-only.
@ -965,6 +972,8 @@ void SlowioReadDirectoryThread(
* FSP_FILE_SYSTEM_INTERFACE
*/
static NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, PWSTR FileName, ULONG Flags);
#if defined(MEMFS_REPARSE_POINTS)
static NTSTATUS GetReparsePointByName(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
@ -1347,7 +1356,9 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
MEMFS_FILE_NODE *MainFileNode = FileNode;
#endif
#if !defined(MEMFS_DELETE)
assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */
#endif
if (Flags & FspCleanupSetArchiveBit)
{
@ -1376,21 +1387,10 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE);
}
if ((Flags & FspCleanupDelete) && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
{
#if defined(MEMFS_NAMED_STREAMS)
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
ULONG Index;
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
MemfsFileNodeMapEnumerateFn, &Context);
for (Index = 0; Context.Count > Index; Index++)
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
MemfsFileNodeMapEnumerateFree(&Context);
#if !defined(MEMFS_DELETE)
if (Flags & FspCleanupDelete)
Delete(FileSystem, FileNode0, FileName, -1);
#endif
MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
}
}
static VOID Close(FSP_FILE_SYSTEM *FileSystem,
@ -1651,17 +1651,13 @@ static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS;
}
#if !defined(MEMFS_DELETE)
static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, PWSTR FileName)
{
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
return STATUS_DIRECTORY_NOT_EMPTY;
return STATUS_SUCCESS;
return Delete(FileSystem, FileNode0, FileName, FILE_DISPOSITION_DELETE);
}
#endif
static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0,
@ -2231,6 +2227,54 @@ static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
}
#endif
static NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, PWSTR FileName, ULONG Flags)
{
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
switch (Flags)
{
case FILE_DISPOSITION_DO_NOT_DELETE:
// set file disposition flag: do not delete file at Cleanup
return STATUS_SUCCESS;
case FILE_DISPOSITION_DELETE:
// set file disposition flag: delete file at Cleanup
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
return STATUS_DIRECTORY_NOT_EMPTY;
return STATUS_SUCCESS;
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
// delete file now; open handles to file remain valid
/* fallthrough */
case -1:
// delete file now; called during Cleanup
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
return STATUS_DIRECTORY_NOT_EMPTY;
else
{
#if defined(MEMFS_NAMED_STREAMS)
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
ULONG Index;
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
MemfsFileNodeMapEnumerateFn, &Context);
for (Index = 0; Context.Count > Index; Index++)
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
MemfsFileNodeMapEnumerateFree(&Context);
#endif
MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
return STATUS_SUCCESS;
}
default:
return STATUS_INVALID_PARAMETER;
}
}
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
{
GetVolumeInfo,
@ -2255,7 +2299,11 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
GetFileInfo,
SetBasicInfo,
SetFileSize,
#if !defined(MEMFS_DELETE)
CanDelete,
#else
0,
#endif
Rename,
GetSecurity,
SetSecurity,
@ -2293,11 +2341,15 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
#if defined(MEMFS_EA)
Overwrite,
GetEa,
SetEa
SetEa,
#else
0,
0,
0,
#endif
#if defined(MEMFS_DELETE)
Delete,
#else
0,
#endif
};
@ -2323,6 +2375,7 @@ NTSTATUS MemfsCreateFunnel(
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
BOOLEAN CaseInsensitive = !!(Flags & MemfsCaseInsensitive);
BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & MemfsFlushAndPurgeOnCleanup);
BOOLEAN SupportsPosixUnlinkRename = !(Flags & MemfsLegacyUnlinkRename);
PWSTR DevicePath = MemfsNet == (Flags & MemfsDeviceMask) ?
L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME;
UINT64 AllocationUnit;
@ -2404,6 +2457,7 @@ NTSTATUS MemfsCreateFunnel(
#if defined(MEMFS_REJECT_EARLY_IRP)
VolumeParams.RejectIrpPriorToTransact0 = 1;
#endif
VolumeParams.SupportsPosixUnlinkRename = SupportsPosixUnlinkRename;
if (0 != VolumePrefix)
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),

View File

@ -37,9 +37,10 @@ enum
MemfsDeviceMask = 0x0000000f,
MemfsCaseInsensitive = 0x80000000,
MemfsFlushAndPurgeOnCleanup = 0x40000000,
MemfsLegacyUnlinkRename = 0x20000000,
};
#define MemfsCreate(Flags, FileInfoTimeout, MaxFileNodes, MaxFileSize, VolumePrefix, RootSddl, PMemfs)\
#define MemfsCreate(Flags, FileInfoTimeout, MaxFileNodes, MaxFileSize, VolumePrefix, RootSddl, PMemfs)\
MemfsCreateFunnel(\
Flags,\
FileInfoTimeout,\

View File

@ -43,7 +43,8 @@ void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
Result = MemfsCreateFunnel(
Flags |
(OptCaseInsensitive ? MemfsCaseInsensitive : 0) |
(OptFlushAndPurgeOnCleanup ? MemfsFlushAndPurgeOnCleanup : 0),
(OptFlushAndPurgeOnCleanup ? MemfsFlushAndPurgeOnCleanup : 0) |
(OptLegacyUnlinkRename ? MemfsLegacyUnlinkRename : 0),
FileInfoTimeout,
1024,
1024 * 1024,

View File

@ -39,6 +39,7 @@ BOOLEAN OptCaseInsensitiveCmp = FALSE;
BOOLEAN OptCaseInsensitive = FALSE;
BOOLEAN OptCaseRandomize = FALSE;
BOOLEAN OptFlushAndPurgeOnCleanup = FALSE;
BOOLEAN OptLegacyUnlinkRename = FALSE;
BOOLEAN OptNotify = FALSE;
WCHAR OptOplock = 0;
WCHAR OptMountPointBuf[MAX_PATH], *OptMountPoint;
@ -296,6 +297,11 @@ int main(int argc, char *argv[])
OptFlushAndPurgeOnCleanup = TRUE;
rmarg(argv, argc, argi);
}
else if (0 == strcmp("--legacy-unlink-rename", a))
{
OptLegacyUnlinkRename = TRUE;
rmarg(argv, argc, argi);
}
else if (0 == strcmp("--notify", a))
{
OptNotify = TRUE;

View File

@ -159,6 +159,7 @@ extern BOOLEAN OptCaseInsensitiveCmp;
extern BOOLEAN OptCaseInsensitive;
extern BOOLEAN OptCaseRandomize;
extern BOOLEAN OptFlushAndPurgeOnCleanup;
extern BOOLEAN OptLegacyUnlinkRename;
extern BOOLEAN OptNotify;
extern WCHAR OptOplock;
extern WCHAR OptMountPointBuf[], *OptMountPoint;