diff --git a/appveyor.yml b/appveyor.yml
index 9fd8613c..f3353fa6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -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
diff --git a/inc/winfsp/fsctl.h b/inc/winfsp/fsctl.h
index 1ad8d124..86871fed 100644
--- a/inc/winfsp/fsctl.h
+++ b/inc/winfsp/fsctl.h
@@ -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;
diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h
index fb44f895..2440bcd6 100644
--- a/inc/winfsp/winfsp.h
+++ b/inc/winfsp/winfsp.h
@@ -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:
+ *
+ * - FILE_DISPOSITION_DO_NOT_DELETE: Unmark the file for deletion.
+ * Do NOT delete the file either now or at Cleanup time.
+ * - FILE_DISPOSITION_DELETE: Mark the file for deletion,
+ * but do NOT 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.
+ * - FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS: Delete the file
+ * NOW 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.
+ * - -1: Delete the file NOW using regular Windows semantics.
+ * Called during Cleanup with no open user mode handles remaining.
+ * If a file system implements Delete, Cleanup should NOT be used for deletion anymore.
+ *
+ *
+ * This function gets called in all file deletion scenarios:
+ *
+ * - When the DeleteFile or RemoveDirectory API's are used.
+ * - When the SetInformationByHandle API with FileDispositionInfo or FileDispositionInfoEx is used.
+ *
- When a file is opened using FILE_DELETE_ON_CLOSE.
+ * - Etc.
+ *
+ *
+ * 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.");
diff --git a/src/dll/fsop.c b/src/dll/fsop.c
index 536aa16d..aed2c385 100644
--- a/src/dll/fsop.c
+++ b/src/dll/fsop.c
@@ -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)
diff --git a/src/sys/cleanup.c b/src/sys/cleanup.c
index ea8ddc19..46c249dc 100644
--- a/src/sys/cleanup.c
+++ b/src/sys/cleanup.c
@@ -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;
diff --git a/src/sys/driver.h b/src/sys/driver.h
index 8bc9992c..81d1cbaa 100644
--- a/src/sys/driver.h
+++ b/src/sys/driver.h
@@ -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 */
diff --git a/src/sys/file.c b/src/sys/file.c
index 649f3772..82cc5662 100644
--- a/src/sys/file.c
+++ b/src/sys/file.c
@@ -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 */
diff --git a/src/sys/fileinfo.c b/src/sys/fileinfo.c
index d4d8c670..50e1e250 100644
--- a/src/sys/fileinfo.c
+++ b/src/sys/fileinfo.c
@@ -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;
diff --git a/src/sys/volinfo.c b/src/sys/volinfo.c
index 3c236854..970a652d 100644
--- a/src/sys/volinfo.c
+++ b/src/sys/volinfo.c
@@ -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);
diff --git a/tools/run-tests.bat b/tools/run-tests.bat
index 7a1ce3e5..2bdee1fd 100755
--- a/tools/run-tests.bat
+++ b/tools/run-tests.bat
@@ -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
diff --git a/tst/memfs/memfs.cpp b/tst/memfs/memfs.cpp
index bdd9e8aa..07c45fd2 100644
--- a/tst/memfs/memfs.cpp
+++ b/tst/memfs/memfs.cpp
@@ -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),
diff --git a/tst/memfs/memfs.h b/tst/memfs/memfs.h
index bc4bd1d1..d3e66334 100644
--- a/tst/memfs/memfs.h
+++ b/tst/memfs/memfs.h
@@ -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,\
diff --git a/tst/winfsp-tests/memfs-test.c b/tst/winfsp-tests/memfs-test.c
index 312e5b31..1fa64ea6 100644
--- a/tst/winfsp-tests/memfs-test.c
+++ b/tst/winfsp-tests/memfs-test.c
@@ -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,
diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c
index d9828c84..9fe244f4 100644
--- a/tst/winfsp-tests/winfsp-tests.c
+++ b/tst/winfsp-tests/winfsp-tests.c
@@ -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;
diff --git a/tst/winfsp-tests/winfsp-tests.h b/tst/winfsp-tests/winfsp-tests.h
index dbc52ec5..c3d41b89 100644
--- a/tst/winfsp-tests/winfsp-tests.h
+++ b/tst/winfsp-tests/winfsp-tests.h
@@ -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;