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: build_script:
- appveyor AddMessage "Reboot complete" -Category Information - appveyor AddMessage "Reboot complete" -Category Information
# build cygfuse # build cygfuse
- C:\cygwin64\setup-x86_64.exe -qnNd -P cygport #- C:\cygwin64\setup-x86_64.exe -qnNd -P cygport
- C:\cygwin64\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist" #- C:\cygwin64\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
#- C:\cygwin\setup-x86.exe -qnNd -P cygport #- C:\cygwin\setup-x86.exe -qnNd -P cygport
#- C:\cygwin\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist" #- C:\cygwin\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
# build winfsp # build winfsp

View File

@ -204,7 +204,8 @@ enum
UINT32 WslFeatures:1; /* support features required for WSLinux */\ UINT32 WslFeatures:1; /* support features required for WSLinux */\
UINT32 DirectoryMarkerAsNextOffset:1; /* directory marker is next offset instead of last name */\ 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 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 Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\
WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)];
#define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\ #define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\
@ -413,6 +414,10 @@ typedef struct
UINT32 Delete:1; UINT32 Delete:1;
} Disposition; } Disposition;
struct struct
{
UINT32 Flags;
} DispositionEx;
struct
{ {
UINT64 FileSize; UINT64 FileSize;
} EndOfFile; } EndOfFile;

View File

@ -47,6 +47,19 @@
extern "C" { extern "C" {
#endif #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. * 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. * 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 * 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 * 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 * be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
@ -402,6 +418,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Close * Close
* CanDelete * CanDelete
* SetDelete * SetDelete
* Delete
*/ */
VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem, VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName, ULONG Flags); 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. * 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 * 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 * not need to perform access checks, but may performs tasks such as check for empty
* directories, etc. * directories, etc.
@ -599,6 +618,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @see * @see
* Cleanup * Cleanup
* SetDelete * SetDelete
* Delete
*/ */
NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem, NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName); PVOID FileContext, PWSTR FileName);
@ -880,6 +900,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
/** /**
* Set the file delete flag. * 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 * 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 * when it is closed. This function does not need to perform access checks, but may
* performs tasks such as check for empty directories, etc. * performs tasks such as check for empty directories, etc.
@ -908,6 +930,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @see * @see
* Cleanup * Cleanup
* CanDelete * CanDelete
* Delete
*/ */
NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem, NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem,
PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile); PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile);
@ -1040,12 +1063,65 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
PVOID FileContext, PVOID FileContext,
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
FSP_FSCTL_FILE_INFO *FileInfo); 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. * This ensures that this interface will always contain 64 function pointers.
* Please update when changing the interface as it is important for future compatibility. * Please update when changing the interface as it is important for future compatibility.
*/ */
NTSTATUS (*Reserved[33])(); NTSTATUS (*Reserved[32])();
} FSP_FILE_SYSTEM_INTERFACE; } FSP_FILE_SYSTEM_INTERFACE;
FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()), FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()),
"FSP_FILE_SYSTEM_INTERFACE must have 64 entries."); "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 && (FspFsctlTransactCleanupKind == Request->Kind &&
Request->Req.Cleanup.Delete) || Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind && (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 || FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind && (FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext && 0 == Request->Req.FlushBuffers.UserContext &&
@ -67,7 +70,9 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
else else
if (FspFsctlTransactCreateKind == Request->Kind || if (FspFsctlTransactCreateKind == Request->Kind ||
(FspFsctlTransactSetInformationKind == 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 || FspFsctlTransactQueryDirectoryKind == Request->Kind ||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind) FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
{ {
@ -95,7 +100,10 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
(FspFsctlTransactCleanupKind == Request->Kind && (FspFsctlTransactCleanupKind == Request->Kind &&
Request->Req.Cleanup.Delete) || Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind && (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 || FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind && (FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext && 0 == Request->Req.FlushBuffers.UserContext &&
@ -106,7 +114,9 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
else else
if (FspFsctlTransactCreateKind == Request->Kind || if (FspFsctlTransactCreateKind == Request->Kind ||
(FspFsctlTransactSetInformationKind == 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 || FspFsctlTransactQueryDirectoryKind == Request->Kind ||
FspFsctlTransactQueryVolumeInformationKind == 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_API NTSTATUS FspFileSystemOpCleanup(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) 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) if (0 != FileSystem->Interface->Cleanup)
FileSystem->Interface->Cleanup(FileSystem, FileSystem->Interface->Cleanup(FileSystem,
(PVOID)ValOfFileContext(Request->Req.Cleanup), (PVOID)ValOfFileContext(Request->Req.Cleanup),
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0, 0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
(0 != Request->Req.Cleanup.Delete ? FspCleanupDelete : 0) | CleanupFlags);
(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));
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
@ -1134,7 +1156,9 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
&FileInfo); &FileInfo);
break; break;
case 13/*FileDispositionInformation*/: 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, Result = FileSystem->Interface->GetFileInfo(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetInformation), &FileInfo); (PVOID)ValOfFileContext(Request->Req.SetInformation), &FileInfo);
@ -1144,7 +1168,17 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
break; 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, Result = FileSystem->Interface->SetDelete(FileSystem,
(PVOID)ValOfFileContext(Request->Req.SetInformation), (PVOID)ValOfFileContext(Request->Req.SetInformation),
@ -1162,6 +1196,7 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
} }
break; break;
case 10/*FileRenameInformation*/: case 10/*FileRenameInformation*/:
case 65/*FileRenameInformationEx*/:
if (0 != FileSystem->Interface->Rename) if (0 != FileSystem->Interface->Rename)
{ {
if (0 != Request->Req.SetInformation.Info.Rename.AccessToken) if (0 != Request->Req.SetInformation.Info.Rename.AccessToken)

View File

@ -82,21 +82,21 @@ static NTSTATUS FspFsvolCleanup(
FSP_FILE_DESC *FileDesc = FileObject->FsContext2; FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
FSP_FSCTL_TRANSACT_REQ *Request; FSP_FSCTL_TRANSACT_REQ *Request;
ULONG CleanupFlags; ULONG CleanupFlags;
BOOLEAN DeletePending, SetAllocationSize, FileModified; BOOLEAN Delete, SetAllocationSize, FileModified;
ASSERT(FileNode == FileDesc->FileNode); ASSERT(FileNode == FileDesc->FileNode);
FspFileNodeAcquireExclusive(FileNode, Main); FspFileNodeAcquireExclusive(FileNode, Main);
FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags); FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags);
DeletePending = CleanupFlags & 1; Delete = (CleanupFlags & 1) && !FileNode->PosixDelete;
SetAllocationSize = !!(CleanupFlags & 2); SetAllocationSize = !!(CleanupFlags & 2);
FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED); FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
/* if this is a directory inform the FSRTL Notify mechanism */ /* if this is a directory inform the FSRTL Notify mechanism */
if (FileNode->IsDirectory) if (FileNode->IsDirectory)
{ {
if (DeletePending) if (Delete)
FspNotifyDeletePending( FspNotifyDeletePending(
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode); FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
@ -108,12 +108,12 @@ static NTSTATUS FspFsvolCleanup(
FspFileNodeUnlockAll(FileNode, FileObject, IoGetRequestorProcess(Irp)); FspFileNodeUnlockAll(FileNode, FileObject, IoGetRequestorProcess(Irp));
/* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */ /* 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); FspFsvolCleanupRequestFini, &Request);
Request->Kind = FspFsctlTransactCleanupKind; Request->Kind = FspFsctlTransactCleanupKind;
Request->Req.Cleanup.UserContext = FileNode->UserContext; Request->Req.Cleanup.UserContext = FileNode->UserContext;
Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2; Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2;
Request->Req.Cleanup.Delete = DeletePending; Request->Req.Cleanup.Delete = Delete;
Request->Req.Cleanup.SetAllocationSize = SetAllocationSize; Request->Req.Cleanup.SetAllocationSize = SetAllocationSize;
Request->Req.Cleanup.SetArchiveBit = (FileModified || FileDesc->DidSetSecurity) && Request->Req.Cleanup.SetArchiveBit = (FileModified || FileDesc->DidSetSecurity) &&
!FileDesc->DidSetFileAttributes; !FileDesc->DidSetFileAttributes;
@ -170,7 +170,12 @@ 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 (Request->Req.Cleanup.Delete) if (FileNode->PosixDelete)
{
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;

View File

@ -1462,6 +1462,7 @@ typedef struct FSP_FILE_NODE
ULONG EaChangeNumber; ULONG EaChangeNumber;
ULONG EaChangeCount; ULONG EaChangeCount;
BOOLEAN TruncateOnClose; BOOLEAN TruncateOnClose;
BOOLEAN PosixDelete;
FILE_LOCK FileLock; FILE_LOCK FileLock;
#if (NTDDI_VERSION < NTDDI_WIN8) #if (NTDDI_VERSION < NTDDI_WIN8)
OPLOCK Oplock; 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 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);
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

@ -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 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);
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 */
@ -141,6 +142,7 @@ 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)
@ -909,7 +911,7 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
DeletePending = 0 != FileNode->DeletePending; DeletePending = 0 != FileNode->DeletePending;
MemoryBarrier(); MemoryBarrier();
if (DeletePending) if (DeletePending && !FileNode->PosixDelete)
{ {
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName, FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
&DeletedFromContextTable); &DeletedFromContextTable);
@ -1013,6 +1015,66 @@ 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

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

View File

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

View File

@ -32,6 +32,7 @@ set dfl_tests=^
winfsp-tests-x64 ^ winfsp-tests-x64 ^
winfsp-tests-x64-case-randomize ^ winfsp-tests-x64-case-randomize ^
winfsp-tests-x64-flushpurge ^ winfsp-tests-x64-flushpurge ^
winfsp-tests-x64-legacy-unlink-rename ^
winfsp-tests-x64-mountpoint-drive ^ winfsp-tests-x64-mountpoint-drive ^
winfsp-tests-x64-mountpoint-dir ^ winfsp-tests-x64-mountpoint-dir ^
winfsp-tests-x64-mountpoint-dir-case-sensitive ^ winfsp-tests-x64-mountpoint-dir-case-sensitive ^
@ -52,6 +53,7 @@ set dfl_tests=^
winfsp-tests-x86 ^ winfsp-tests-x86 ^
winfsp-tests-x86-case-randomize ^ winfsp-tests-x86-case-randomize ^
winfsp-tests-x86-flushpurge ^ winfsp-tests-x86-flushpurge ^
winfsp-tests-x86-legacy-unlink-rename ^
winfsp-tests-x86-mountpoint-drive ^ winfsp-tests-x86-mountpoint-drive ^
winfsp-tests-x86-mountpoint-dir ^ winfsp-tests-x86-mountpoint-dir ^
winfsp-tests-x86-mountpoint-dir-case-sensitive ^ 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 if !ERRORLEVEL! neq 0 goto fail
exit /b 0 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-drive
winfsp-tests-x64 --mountpoint=X: --resilient * +ea* winfsp-tests-x64 --mountpoint=X: --resilient * +ea*
if !ERRORLEVEL! neq 0 goto fail 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 if !ERRORLEVEL! neq 0 goto fail
exit /b 0 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-drive
winfsp-tests-x86 --mountpoint=X: --resilient * +ea* winfsp-tests-x86 --mountpoint=X: --resilient * +ea*
if !ERRORLEVEL! neq 0 goto fail 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 #define MEMFS_REJECT_EARLY_IRP
#endif #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 * 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. * a check for the Write buffer to ensure that it is read-only.
@ -965,6 +972,8 @@ void SlowioReadDirectoryThread(
* FSP_FILE_SYSTEM_INTERFACE * FSP_FILE_SYSTEM_INTERFACE
*/ */
static NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, PWSTR FileName, ULONG Flags);
#if defined(MEMFS_REPARSE_POINTS) #if defined(MEMFS_REPARSE_POINTS)
static NTSTATUS GetReparsePointByName( static NTSTATUS GetReparsePointByName(
FSP_FILE_SYSTEM *FileSystem, PVOID Context, FSP_FILE_SYSTEM *FileSystem, PVOID Context,
@ -1347,7 +1356,9 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
MEMFS_FILE_NODE *MainFileNode = FileNode; MEMFS_FILE_NODE *MainFileNode = FileNode;
#endif #endif
#if !defined(MEMFS_DELETE)
assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */ assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */
#endif
if (Flags & FspCleanupSetArchiveBit) if (Flags & FspCleanupSetArchiveBit)
{ {
@ -1376,21 +1387,10 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE);
} }
if ((Flags & FspCleanupDelete) && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) #if !defined(MEMFS_DELETE)
{ if (Flags & FspCleanupDelete)
#if defined(MEMFS_NAMED_STREAMS) Delete(FileSystem, FileNode0, FileName, -1);
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 #endif
MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
}
} }
static VOID Close(FSP_FILE_SYSTEM *FileSystem, static VOID Close(FSP_FILE_SYSTEM *FileSystem,
@ -1651,17 +1651,13 @@ static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
#if !defined(MEMFS_DELETE)
static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, PWSTR FileName) PVOID FileNode0, PWSTR FileName)
{ {
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; return Delete(FileSystem, FileNode0, FileName, FILE_DISPOSITION_DELETE);
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
return STATUS_DIRECTORY_NOT_EMPTY;
return STATUS_SUCCESS;
} }
#endif
static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem, static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode0, PVOID FileNode0,
@ -2231,6 +2227,54 @@ static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
} }
#endif #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 = static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
{ {
GetVolumeInfo, GetVolumeInfo,
@ -2255,7 +2299,11 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
GetFileInfo, GetFileInfo,
SetBasicInfo, SetBasicInfo,
SetFileSize, SetFileSize,
#if !defined(MEMFS_DELETE)
CanDelete, CanDelete,
#else
0,
#endif
Rename, Rename,
GetSecurity, GetSecurity,
SetSecurity, SetSecurity,
@ -2293,11 +2341,15 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
#if defined(MEMFS_EA) #if defined(MEMFS_EA)
Overwrite, Overwrite,
GetEa, GetEa,
SetEa SetEa,
#else #else
0, 0,
0, 0,
0, 0,
#endif
#if defined(MEMFS_DELETE)
Delete,
#else
0, 0,
#endif #endif
}; };
@ -2323,6 +2375,7 @@ NTSTATUS MemfsCreateFunnel(
FSP_FSCTL_VOLUME_PARAMS VolumeParams; FSP_FSCTL_VOLUME_PARAMS VolumeParams;
BOOLEAN CaseInsensitive = !!(Flags & MemfsCaseInsensitive); BOOLEAN CaseInsensitive = !!(Flags & MemfsCaseInsensitive);
BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & MemfsFlushAndPurgeOnCleanup); BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & MemfsFlushAndPurgeOnCleanup);
BOOLEAN SupportsPosixUnlinkRename = !(Flags & MemfsLegacyUnlinkRename);
PWSTR DevicePath = MemfsNet == (Flags & MemfsDeviceMask) ? PWSTR DevicePath = MemfsNet == (Flags & MemfsDeviceMask) ?
L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME; L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME;
UINT64 AllocationUnit; UINT64 AllocationUnit;
@ -2404,6 +2457,7 @@ NTSTATUS MemfsCreateFunnel(
#if defined(MEMFS_REJECT_EARLY_IRP) #if defined(MEMFS_REJECT_EARLY_IRP)
VolumeParams.RejectIrpPriorToTransact0 = 1; VolumeParams.RejectIrpPriorToTransact0 = 1;
#endif #endif
VolumeParams.SupportsPosixUnlinkRename = SupportsPosixUnlinkRename;
if (0 != VolumePrefix) if (0 != VolumePrefix)
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),

View File

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

View File

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

View File

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

View File

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