Compare commits

...

14 Commits

23 changed files with 306 additions and 67 deletions

View File

@ -1,6 +1,42 @@
= Changelog
v1.5B4 (2019.3 B4)::
Changes since v1.4:
* [GEN] WinFsp file systems can now be used by WSLinux. File systems must enable this support by setting the `FSP_FSCTL_VOLUME_PARAMS::WslFeatures` bit. Use the command `sudo mount -t drvfs x: /mnt/x` to mount.
* [GEN] Extended attribute support has been added for all WinFsp API's: native, .NET, FUSE2 and FUSE3.
* [GEN] Mount Manager support has been added and it works for current and new file systems:
** If the file system mountpoint is in the syntax `\\.\X:` then the Mount Manager is used.
** If the file system mountpoint is in the syntax `X:` then `DefineDosDeviceW` is used (i.e. same as today).
** If the file system mountpoint is in the syntax `X:\DIR` then a reparse point is used and the file system is mounted as a directory (i.e. same as today).
** Caveats:
*** It requires Administrator access. This is because opening the `\\.\MountPointManager` device requires Administrator access.
*** It currently works with drives (`\\.\X:`) but not directories (`\\.\X:\DIR`).
*** Mount Manager drives created by WinFsp are transient. WinFsp takes various steps to ensure that this is the case.
*** Mount Manager drives are global and are visible across Terminal Services sessions (they go into the `\GLOBAL??` portion of the NT namespace).
* [FSD] Support for kernel-mode file systems on top of WinFsp has been added. See `FspFsextProvider`. This is in preparation for WinFuse - FUSE for Windows and WSLinux.
* [FSD] FastIO support has been added. FastIO operations are enabled on cache-enabled file systems with the notable exception of `FastIoQueryOpen`, which allows opening files in kernel mode; this operation requires the file system to specify the `FSP_FSCTL_VOLUME_PARAMS::AllowOpenInKernelMode` flag.
* [FSD] Support for `FileFsSectorSizeInformation` and `IOCTL_STORAGE_QUERY_PROPERTY / StorageAccessAlignmentProperty` has been added.
* [DLL] The `FspFileSystemStartDispatcher` default number of threads (`ThreadCount==0`) has been changed. See commit 3902874ac93fe40685d9761f46a96358ba24f24c for more.
* [FUSE] FUSE has new `-o UserName=DOMAIN+USERNAME` and `-o GroupName=DOMAIN+GROUPNAME` options. These function like the `-o uid=UID` and `-o gid=GID` options, but accept Windows user and groups names.
* [FUSE] FUSE has new `-o dothidden` option that is used to add the Windows hidden file attribute to files that start with a dot.
* [FUSE] FUSE has new `-o create_file_umask=nnn` and `-o create_dir_umask=nnn` options that allow for more control than the `-o create_umask=nnn` option.
* [FUSE] FUSE has new `--ExactFileSystemName=FSNAME` option that removes the "FUSE-" prefix from the file system name. (Use with caution: see discussion in PR #251.) (Thanks @johntyner.)
* [.NET] The .NET API now supports asynchronous handling of `Read`, `Write` and `ReadDirectory`. (Thanks @dworkin.)
* [.NET] The .NET API now supports fine-grained timeouts (`VolumeInfoTimeout`, `DirInfoTimeout`, etc).
* [.NET] The .NET API has new method `FileSystemHost.MountEx` that adds a `ThreadCount` parameter.
* [LAUNCH] The Launcher can now rewrite path arguments passed to file systems during launching using "Path Transformation Language". See commit a73f1b95592617ac7484e16c2e642573a4d65644 for more.
* [MEMFS] A new memfs FUSE3 file system written in C++ has been added. See `tst/memfs-fuse3`.
* [AIRFS] John Oberschelp has done some fantastic work adding persistence to the airfs file system. (Thanks @JohnOberschelp.)
* [FIX] Fixes for very large (> 4GiB) files. (Thanks @dworkin.)
* [FIX] A fix for how FUSE handles the return value from `opendir`. (GitHub issue billziss-gh/sshfs-win#54)
* [FIX] A fix for an invalid UID to SID mapping on domains with a lot of users. (Thanks @sganis.)
* [FIX] A fix on the C++ layer. (Thanks @colatkinson.)
* Other fixes and improvements.
v1.5B3 (2019.3 B3)::
Changes since v1.4:

View File

@ -18,7 +18,7 @@
<MyCanonicalVersion>1.5</MyCanonicalVersion>
<MyProductVersion>2019.3 B3</MyProductVersion>
<MyProductVersion>2019.3 B5</MyProductVersion>
<MyProductStage>Beta</MyProductStage>
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>

View File

@ -96,7 +96,7 @@ FSP_FSCTL_STATIC_ASSERT(FSP_FSCTL_VOLUME_NAME_SIZEMAX <= 260 * sizeof(WCHAR),
/* marshalling */
#pragma warning(push)
#pragma warning(disable:4200) /* zero-sized array in struct/union */
#pragma warning(disable:4200 4201) /* zero-sized array in struct/union; nameless struct/union */
enum
{
FspFsctlTransactReservedKind = 0,
@ -175,7 +175,8 @@ enum
UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\
UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\
UINT32 WslFeatures:1; /* support features required for WSLinux */\
UINT32 KmReservedFlags:5;\
UINT32 DirectoryMarkerAsNextOffset:1; /* directory marker is next offset instead of last name */\
UINT32 KmReservedFlags:4;\
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\
@ -242,8 +243,12 @@ typedef struct
{
UINT16 Size;
FSP_FSCTL_FILE_INFO FileInfo;
union
{
UINT64 NextOffset;
UINT8 Padding[24];
/* make struct as big as FILE_ID_BOTH_DIR_INFORMATION; allows for in-place copying */
} DUMMYUNIONNAME;
WCHAR FileNameBuf[];
} FSP_FSCTL_DIR_INFO;
FSP_FSCTL_STATIC_ASSERT(104 == sizeof(FSP_FSCTL_DIR_INFO),

View File

@ -53,6 +53,9 @@ typedef struct
FSP_DDI_DEF(NTSTATUS, FspFsextProviderRegister,
FSP_FSEXT_PROVIDER *Provider)
FSP_DDI_DEF(NTSTATUS, FspFsextProviderTransact,
PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
FSP_FSCTL_TRANSACT_RSP *Response, FSP_FSCTL_TRANSACT_REQ **PRequest)
FSP_DDI_DEF(NTSTATUS, FspPosixMapUidToSid,
UINT32 Uid,

Binary file not shown.

Binary file not shown.

View File

@ -207,6 +207,11 @@ static inline int FspKuMultiByteToWideChar(
return ByteCount / sizeof(WCHAR);
}
static inline PGENERIC_MAPPING FspGetFileGenericMapping(VOID)
{
return IoGetFileObjectGenericMapping();
}
static inline void *MemAlloc(size_t Size)
{
return FspAlloc(Size);

View File

@ -725,6 +725,20 @@ lasterror:
goto exit;
}
static inline ACCESS_MASK FspPosixCanonicalizeAccessMask(ACCESS_MASK AccessMask)
{
PGENERIC_MAPPING Mapping = FspGetFileGenericMapping();
if (AccessMask & GENERIC_READ)
AccessMask |= Mapping->GenericRead;
if (AccessMask & GENERIC_WRITE)
AccessMask |= Mapping->GenericWrite;
if (AccessMask & GENERIC_EXECUTE)
AccessMask |= Mapping->GenericExecute;
if (AccessMask & GENERIC_ALL)
AccessMask |= Mapping->GenericAll;
return AccessMask & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
}
static inline UINT32 FspPosixMapAccessMaskToPermission(ACCESS_MASK AccessMask)
{
/* [PERMS]
@ -749,6 +763,14 @@ FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
{
FSP_KU_CODE;
BOOLEAN OwnerOptional = (UINT_PTR)PUid & 1;
PUid = (PVOID)((UINT_PTR)PUid & ~1);
UINT32 OrigUid = *PUid;
BOOLEAN GroupOptional = (UINT_PTR)PGid & 1;
PGid = (PVOID)((UINT_PTR)PGid & ~1);
UINT32 OrigGid = *PGid;
PSID OwnerSid = 0, GroupSid = 0;
BOOL Defaulted, DaclPresent;
PACL Acl = 0;
@ -757,6 +779,7 @@ FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
PSID AceSid;
DWORD AceAccessMask;
DWORD OwnerAllow, OwnerDeny, GroupAllow, GroupDeny, WorldAllow, WorldDeny;
UINT32 AceUid = 0;
UINT32 Uid, Gid, Mode;
NTSTATUS Result;
@ -771,13 +794,23 @@ FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
if (!GetSecurityDescriptorDacl(SecurityDescriptor, &DaclPresent, &Acl, &Defaulted))
goto lasterror;
if (0 == OwnerSid && OwnerOptional)
Uid = OrigUid;
else
{
Result = FspPosixMapSidToUid(OwnerSid, &Uid);
if (!NT_SUCCESS(Result))
goto exit;
}
if (0 == GroupSid && GroupOptional)
Gid = OrigGid;
else
{
Result = FspPosixMapSidToUid(GroupSid, &Gid);
if (!NT_SUCCESS(Result))
goto exit;
}
if (0 != Acl)
{
@ -810,6 +843,8 @@ FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
else
continue;
AceAccessMask = FspPosixCanonicalizeAccessMask(AceAccessMask);
/* [PERMS]
* If the ACE contains the Authenticated Users SID or the World SID then
* add the allowed or denied access right bits into the "owner", "group"
@ -840,6 +875,9 @@ FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
}
else
{
if (0 == OwnerSid || 0 == GroupSid)
FspPosixMapSidToUid(AceSid, &AceUid);
/* [PERMS]
* Note that if the file owner and file group SIDs are the same,
* then the access rights are saved in both the "owner" and "group"
@ -851,7 +889,7 @@ FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
* in the "group" collection as appropriate in the corresponding set of
* granted or denied rights (as described above).
*/
if (EqualSid(GroupSid, AceSid))
if (0 != GroupSid ? EqualSid(GroupSid, AceSid) : (Gid == AceUid))
{
if (ACCESS_ALLOWED_ACE_TYPE == Ace->AceType)
GroupAllow |= AceAccessMask & ~GroupDeny;
@ -864,7 +902,7 @@ FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
* in the "owner" collection as appropriate in the corresponding set of
* granted or denied rights (as described above).
*/
if (EqualSid(OwnerSid, AceSid))
if (0 != OwnerSid ? EqualSid(OwnerSid, AceSid) : (Uid == AceUid))
{
if (ACCESS_ALLOWED_ACE_TYPE == Ace->AceType)
OwnerAllow |= AceAccessMask & ~OwnerDeny;

View File

@ -72,7 +72,10 @@ NTSYSAPI VOID NTAPI RtlMoveMemory(VOID *Destination, CONST VOID *Source, DWORD L
#pragma function(memset)
#pragma function(memcpy)
#pragma warning(push)
#pragma warning(disable:4163) /* not available as an intrinsic function */
#pragma function(memmove)
#pragma warning(pop)
static inline
void *memset(void *dst, int val, size_t siz)
{

View File

@ -308,7 +308,7 @@ VOID FspPropagateTopFlags(PIRP Irp, PIRP TopLevelIrp)
{
DEBUGBREAK_EX(iorecu);
FspIrpSetTopFlags(Irp, FspIrpFlags(TopLevelIrp));
FspIrpSetTopFlags(Irp, FspIrpTopFlags(TopLevelIrp) | FspIrpFlags(TopLevelIrp));
}
}
}

View File

@ -340,6 +340,7 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject)
Result = Provider->DeviceInit(DeviceObject, &FsvolDeviceExtension->VolumeParams);
if (!NT_SUCCESS(Result))
return Result;
FsvolDeviceExtension->Provider = Provider;
FsvolDeviceExtension->InitDoneFsext = 1;
}
else
@ -525,10 +526,8 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject)
/* finalize any fsext provider */
if (FsvolDeviceExtension->InitDoneFsext)
{
FSP_FSEXT_PROVIDER *Provider = FspFsextProvider(
FsvolDeviceExtension->VolumeParams.FsextControlCode, 0);
if (0 != Provider)
Provider->DeviceFini(DeviceObject);
if (0 != FsvolDeviceExtension->Provider)
FsvolDeviceExtension->Provider->DeviceFini(DeviceObject);
}
}
@ -577,13 +576,8 @@ static VOID FspFsvolDeviceExpirationRoutine(PVOID Context)
FspMetaCacheInvalidateExpired(FsvolDeviceExtension->DirInfoCache, InterruptTime);
FspMetaCacheInvalidateExpired(FsvolDeviceExtension->StreamInfoCache, InterruptTime);
/* run any fsext provider expiration routine */
if (0 != FsvolDeviceExtension->VolumeParams.FsextControlCode)
{
FSP_FSEXT_PROVIDER *Provider = FspFsextProvider(
FsvolDeviceExtension->VolumeParams.FsextControlCode, 0);
if (0 != Provider)
Provider->DeviceExpirationRoutine(DeviceObject, InterruptTime);
}
if (0 != FsvolDeviceExtension->Provider)
FsvolDeviceExtension->Provider->DeviceExpirationRoutine(DeviceObject, InterruptTime);
FspIoqRemoveExpired(FsvolDeviceExtension->Ioq, InterruptTime);
KeAcquireSpinLock(&FsvolDeviceExtension->ExpirationLock, &Irql);

View File

@ -24,6 +24,7 @@
static NTSTATUS FspFsvolQueryDirectoryCopy(
PUNICODE_STRING DirectoryPattern, BOOLEAN CaseInsensitive,
PUNICODE_STRING DirectoryMarker, PUNICODE_STRING DirectoryMarkerOut,
PUINT64 DirectoryMarkerAsNextOffset,
FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry,
BOOLEAN ReturnEaSize,
FSP_FSCTL_DIR_INFO **PDirInfo, ULONG DirInfoSize,
@ -88,6 +89,7 @@ enum
static NTSTATUS FspFsvolQueryDirectoryCopy(
PUNICODE_STRING DirectoryPattern, BOOLEAN CaseInsensitive,
PUNICODE_STRING DirectoryMarker, PUNICODE_STRING DirectoryMarkerOut,
PUINT64 DirectoryMarkerAsNextOffset,
FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry,
BOOLEAN ReturnEaSize,
FSP_FSCTL_DIR_INFO **PDirInfo, ULONG DirInfoSize,
@ -130,6 +132,7 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
PVOID PrevDestBuf = 0;
ULONG BaseInfoLen, CopyLength;
UNICODE_STRING FileName;
UINT64 DirectoryNextOffset;
*PDestLen = 0;
@ -176,12 +179,21 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
FileName.MaximumLength = (USHORT)(DirInfoSize - sizeof(FSP_FSCTL_DIR_INFO));
FileName.Buffer = DirInfo->FileNameBuf;
DirectoryNextOffset = DirInfo->NextOffset;
if (0 != DirectoryMarker && 0 != DirectoryMarker->Buffer &&
!DirectoryMarkerFound)
{
if (0 == DirectoryMarkerAsNextOffset)
{
DirectoryMarkerFound = 0 == FspFileNameCompare(
&FileName, DirectoryMarker, CaseInsensitive, 0);
continue;
}
else
{
ASSERT(sizeof(UINT64) == DirectoryMarker->Length);
DirectoryMarkerFound = DirectoryNextOffset == *(PUINT64)DirectoryMarker->Buffer;
}
}
/* CopyLength is the same as FileName.Length except on STATUS_BUFFER_OVERFLOW */
@ -272,8 +284,17 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
break;
}
if (0 == DirectoryMarkerAsNextOffset)
{
DirectoryMarkerOut->Length = DirectoryMarkerOut->MaximumLength = FileName.Length;
DirectoryMarkerOut->Buffer = (PVOID)((PUINT8)DestBuf + BaseInfoLen);
}
else
{
DirectoryMarkerOut->Length = DirectoryMarkerOut->MaximumLength = sizeof(UINT64);
DirectoryMarkerOut->Buffer = (PVOID)DirectoryMarkerAsNextOffset;
*DirectoryMarkerAsNextOffset = DirectoryNextOffset;
}
DestBuf = (PVOID)((PUINT8)DestBuf +
FSP_FSCTL_ALIGN_UP(BaseInfoLen + CopyLength, sizeof(LONGLONG)));
@ -283,7 +304,16 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
Loop = FALSE;
}
else
{
if (0 == DirectoryMarkerAsNextOffset)
*DirectoryMarkerOut = FileName;
else
{
DirectoryMarkerOut->Length = DirectoryMarkerOut->MaximumLength = sizeof(UINT64);
DirectoryMarkerOut->Buffer = (PVOID)DirectoryMarkerAsNextOffset;
*DirectoryMarkerAsNextOffset = DirectoryNextOffset;
}
}
}
}
except (EXCEPTION_EXECUTE_HANDLER)
@ -323,9 +353,12 @@ static NTSTATUS FspFsvolQueryDirectoryCopyCache(
FileDesc->DirInfo = FileNode->NonPaged->DirInfo;
NTSTATUS Result;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
BOOLEAN CaseInsensitive = !FileDesc->CaseSensitive;
PUNICODE_STRING DirectoryPattern = &FileDesc->DirectoryPattern;
UNICODE_STRING DirectoryMarker = FileDesc->DirectoryMarker;
UINT64 DirectoryMarkerAsNextOffset = 0;
PUINT8 DirInfoBgn = (PUINT8)DirInfo;
PUINT8 DirInfoEnd = (PUINT8)DirInfo + DirInfoSize;
@ -334,8 +367,10 @@ static NTSTATUS FspFsvolQueryDirectoryCopyCache(
Result = FspFsvolQueryDirectoryCopy(DirectoryPattern, CaseInsensitive,
0 != FileDesc->DirInfoCacheHint ? 0 : &FileDesc->DirectoryMarker, &DirectoryMarker,
FsvolDeviceExtension->VolumeParams.DirectoryMarkerAsNextOffset ?
&DirectoryMarkerAsNextOffset : 0,
FileInformationClass, ReturnSingleEntry,
!!FspFsvolDeviceExtension(FileNode->FsvolDeviceObject)->VolumeParams.ExtendedAttributes,
!!FsvolDeviceExtension->VolumeParams.ExtendedAttributes,
&DirInfo, DirInfoSize,
DestBuf, PDestLen);
@ -366,10 +401,13 @@ static NTSTATUS FspFsvolQueryDirectoryCopyInPlace(
PAGED_CODE();
NTSTATUS Result;
FSP_FILE_NODE *FileNode = FileDesc->FileNode;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
BOOLEAN CaseInsensitive = !FileDesc->CaseSensitive;
PUNICODE_STRING DirectoryPattern = &FileDesc->DirectoryPattern;
UNICODE_STRING DirectoryMarker = FileDesc->DirectoryMarker;
FSP_FILE_NODE *FileNode = FileDesc->FileNode;
UINT64 DirectoryMarkerAsNextOffset = 0;
FSP_FSCTL_STATIC_ASSERT(
FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) >=
@ -378,8 +416,10 @@ static NTSTATUS FspFsvolQueryDirectoryCopyInPlace(
Result = FspFsvolQueryDirectoryCopy(DirectoryPattern, CaseInsensitive,
0, &DirectoryMarker,
FsvolDeviceExtension->VolumeParams.DirectoryMarkerAsNextOffset ?
&DirectoryMarkerAsNextOffset : 0,
FileInformationClass, ReturnSingleEntry,
!!FspFsvolDeviceExtension(FileNode->FsvolDeviceObject)->VolumeParams.ExtendedAttributes,
!!FsvolDeviceExtension->VolumeParams.ExtendedAttributes,
&DirInfo, DirInfoSize,
DestBuf, PDestLen);

View File

@ -1079,6 +1079,7 @@ typedef struct
PVPB SwapVpb;
FSP_DELAYED_WORK_ITEM DeleteVolumeDelayedWorkItem;
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
FSP_FSEXT_PROVIDER *Provider;
UNICODE_STRING VolumePrefix;
UNICODE_PREFIX_TABLE_ENTRY VolumePrefixEntry;
FSP_IOQ *Ioq;

View File

@ -1337,8 +1337,9 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
!MmFlushImageSection(&DescendantFileNode->NonPaged->SectionObjectPointers,
MmFlushForDelete)))
{
/* release the FileNode in case of failure! */
/* release the FileNode and rename lock in case of failure! */
FspFileNodeReleaseF(FileNode, AcquireFlags);
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
Result = STATUS_ACCESS_DENIED;
goto exit;
@ -1441,8 +1442,9 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result || !NT_SUCCESS(Result))
{
/* release the FileNode so that we can safely wait without deadlocks */
/* release the FileNode and rename lock so that we can safely wait without deadlocks */
FspFileNodeReleaseF(FileNode, AcquireFlags);
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
/* wait for oplock breaks to finish */
for (
@ -1488,8 +1490,9 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
if (DescendantFileNode != FileNode && 0 < DescendantFileNode->HandleCount)
{
/* release the FileNode in case of failure! */
/* release the FileNode and rename lock in case of failure! */
FspFileNodeReleaseF(FileNode, AcquireFlags);
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
Result = STATUS_ACCESS_DENIED;
break;

View File

@ -1576,8 +1576,8 @@ static NTSTATUS FspFsvolSetRenameInformation(
ASSERT(TargetFileNode->IsDirectory);
}
FspFsvolDeviceFileRenameAcquireExclusive(FsvolDeviceObject);
retry:
FspFsvolDeviceFileRenameAcquireExclusive(FsvolDeviceObject);
FspFileNodeAcquireExclusive(FileNode, Full);
if (0 == Request)
@ -1651,13 +1651,13 @@ retry:
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
FileNode, FspFileNodeAcquireFull,
&FileNode->FileName, TRUE);
/* FspFileNodeRenameCheck releases FileNode with STATUS_OPLOCK_BREAK_IN_PROGRESS or failure */
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
goto retry;
if (!NT_SUCCESS(Result))
{
Result = STATUS_ACCESS_DENIED;
goto rename_unlock_exit;
goto exit;
}
if (0 != FspFileNameCompare(&FileNode->FileName, &NewFileName, !FileDesc->CaseSensitive, 0))
@ -1665,13 +1665,13 @@ retry:
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
FileNode, FspFileNodeAcquireFull,
&NewFileName, FALSE);
/* FspFileNodeRenameCheck releases FileNode with STATUS_OPLOCK_BREAK_IN_PROGRESS or failure */
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
goto retry;
if (!NT_SUCCESS(Result))
{
Result = STATUS_ACCESS_DENIED;
goto rename_unlock_exit;
goto exit;
}
}
else
@ -1713,9 +1713,9 @@ retry:
unlock_exit:
FspFileNodeRelease(FileNode, Full);
rename_unlock_exit:
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
exit:
return Result;
}

View File

@ -28,7 +28,7 @@
* the data structure used to store providers (currently 2 parallel
* arrays) must be revisited.
*/
#define FSP_FSEXT_PROVIDER_COUNTMAX 4
#define FSP_FSEXT_PROVIDER_COUNTMAX 16
static KSPIN_LOCK FsextSpinLock = 0;
static UINT32 FsextControlCodes[FSP_FSEXT_PROVIDER_COUNTMAX];
@ -37,23 +37,9 @@ static FSP_FSEXT_PROVIDER *FsextProviders[FSP_FSEXT_PROVIDER_COUNTMAX];
static inline
FSP_FSEXT_PROVIDER *FspFsextProviderGet(UINT32 FsextControlCode)
{
#if 0
for (ULONG I = 0; FSP_FSEXT_PROVIDER_COUNTMAX > I; I++)
if (FsextControlCode == FsextControlCodes[I])
return FsextProviders[I];
#else
/* unroll by hand */
FSP_FSCTL_STATIC_ASSERT(4 == FSP_FSEXT_PROVIDER_COUNTMAX,
"unrolled loop expects FsextProviders to have 4 elements");
if (FsextControlCode == FsextControlCodes[0])
return FsextProviders[0];
if (FsextControlCode == FsextControlCodes[1])
return FsextProviders[1];
if (FsextControlCode == FsextControlCodes[2])
return FsextProviders[2];
if (FsextControlCode == FsextControlCodes[3])
return FsextProviders[3];
#endif
return 0;
}
@ -146,3 +132,56 @@ NTSTATUS FspFsextProviderRegister(FSP_FSEXT_PROVIDER *Provider)
return Result;
}
NTSTATUS FspFsextProviderTransact(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
FSP_FSCTL_TRANSACT_RSP *Response, FSP_FSCTL_TRANSACT_REQ **PRequest)
{
/*
* This function uses IoBuildDeviceIoControlRequest to build an IRP and then send it
* to the WinFsp driver. IoBuildDeviceIoControlRequest places the IRP in the IRP queue
* thus allowing it to be cancelled with CancelSynchronousIo.
*
* Special kernel APC's must be enabled:
* See https://www.osr.com/blog/2018/02/14/beware-iobuilddeviceiocontrolrequest/
*/
ASSERT(!KeAreAllApcsDisabled());
NTSTATUS Result;
IO_STATUS_BLOCK IoStatus;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
if (0 != PRequest)
*PRequest = 0;
if (0 == DeviceObject)
DeviceObject = IoGetRelatedDeviceObject(FileObject);
Irp = IoBuildDeviceIoControlRequest(FSP_FSCTL_TRANSACT_INTERNAL,
DeviceObject,
Response,
0 != Response ? Response->Size : 0,
PRequest,
0 != PRequest ? sizeof(PVOID) : 0,
FALSE,
0,
&IoStatus);
if (0 == Irp)
return STATUS_INSUFFICIENT_RESOURCES;
/*
* IoBuildDeviceIoControlRequest builds an IOCTL IRP without a FileObject.
* Patch it so that it is an FSCTL IRP with a FileObject. Mark it as
* IRP_SYNCHRONOUS_API so that CancelSynchronousIo can cancel it.
*/
Irp->Flags |= IRP_SYNCHRONOUS_API;
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
IrpSp->MinorFunction = IRP_MN_USER_FS_REQUEST;
IrpSp->FileObject = FileObject;
Result = IoCallDriver(DeviceObject, Irp);
ASSERT(STATUS_PENDING != Result);
return Result;
}

View File

@ -992,14 +992,13 @@ NTSTATUS FspVolumeTransactFsext(
if (!FspDeviceReference(FsvolDeviceObject))
return STATUS_CANCELLED;
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
if (IrpSp->Parameters.FileSystemControl.FsControlCode ==
FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.FsextControlCode)
FsvolDeviceExtension->VolumeParams.FsextControlCode)
{
FSP_FSEXT_PROVIDER *Provider = FspFsextProvider(
IrpSp->Parameters.FileSystemControl.FsControlCode, 0);
if (0 != Provider)
Result = Provider->DeviceTransact(FsvolDeviceObject, Irp);
if (0 != FsvolDeviceExtension->Provider)
Result = FsvolDeviceExtension->Provider->DeviceTransact(FsvolDeviceObject, Irp);
}
FspDeviceDereference(FsvolDeviceObject);

0
tools/build-sample.bat Normal file → Executable file
View File

View File

@ -83,11 +83,18 @@ for %%f in (build\%Configuration%\winfsp-*.msi) do (
if not %signfail%==0 echo SIGNING FAILED! The product has been successfully built, but not signed.
for %%f in (build\%Configuration%\winfsp-*.msi) do set Version=%%~nf
set Version=!Version:winfsp-=!
if X%SignedPackage%==X (
pushd build\%Configuration%
powershell -command "Compress-Archive -Path winfsp-tests-*.exe,..\..\..\..\License.txt,..\..\..\..\tst\winfsp-tests\README.md -DestinationPath winfsp-tests-!Version!.zip"
if errorlevel 1 goto fail
popd
)
where /q choco.exe
if %ERRORLEVEL% equ 0 (
for %%f in (build\%Configuration%\winfsp-*.msi) do set Version=%%~nf
set Version=!Version:winfsp-=!
copy ..\choco\* build\%Configuration%
copy ..\choco\LICENSE.TXT /B + ..\..\License.txt /B build\%Configuration%\LICENSE.txt /B
certutil -hashfile build\%Configuration%\winfsp-!Version!.msi SHA256 >>build\%Configuration%\VERIFICATION.txt

0
tools/fsreg.bat Normal file → Executable file
View File

0
tools/impdef.bat Normal file → Executable file
View File

View File

@ -0,0 +1,64 @@
## WINFSP-TESTS
Winfsp-tests is a file system test suite that is used to test WinFsp and the file systems that ship with it. It is not intended for use by end users. If you have downloaded winfsp-tests, you likely wanted to download the WinFsp installer instead (i.e. the file that is named `winfsp-VERSION.msi`).
**WINFSP-TESTS IS UNSUPPORTED SOFTWARE. IT MAY CRASH OR LOCK UP YOUR COMPUTER, CREATE ZOMBIE/UNKILLABLE PROCESSES OR CORRUPT YOUR DATA. DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING. DO NOT POST BUGS/ISSUES/QUESTIONS AGAINST WINFSP-TESTS UNLESS YOU ALSO POST THE FIX. YOU HAVE BEEN WARNED!**
## USAGE
Winfsp-tests has two different test modes: a default internal mode in which it runs its tests against an embedded copy of MEMFS and an external mode in which it runs its tests against the file system in the current directory.
Unless you are doing WinFsp development there should never be a need to run winfsp-tests in the default internal mode. However the external mode can be useful to test third party file systems that do not ship with WinFsp.
To run winfsp-tests in external mode, you must use the `--external` command line option. I also recommend using the `--resilient` command line option, to have winfsp-tests improve test flakiness by retrying failed operations.
```
> winfsp-tests-x64 --external --resilient
create_test............................ OK 0.00s
create_fileattr_test................... OK 0.00s
...
--- COMPLETE ---
```
Specific tests to be run may be specified. For example, to run all tests that start with `rename`:
```
> winfsp-tests-x64 --external --resilient rename*
rename_test............................ OK 0.01s
rename_open_test....................... OK 0.00s
...
--- COMPLETE ---
```
To exclude a test or tests use the `-` prefix. For example, to run all `create` tests, except the `create_fileattr_test`:
```
> winfsp-tests-x64 --external --resilient create* -create_fileattr_test
create_test............................ OK 0.05s
create_readonlydir_test................ OK 0.01s
...
--- COMPLETE ---
```
By default only regular tests are run. To include optional or long running tests use the `+` prefix. For example, to run all tests use `+*`; to run oplock tests use `+oplock*`:
```
> winfsp-tests-x64 --external --resilient +oplock*
oplock_level1_test..................... OK 1.26s
oplock_level2_test..................... OK 2.46s
...
--- COMPLETE ---
```
To list tests without running them use the `--list` option:
```
> winfsp-tests-x64 --external --resilient --list +oplock*
oplock_level1_test
oplock_level2_test
...
```
If a test fails the test suite stops immediately with an assertion failure. There is no additional explanation of the problem and you have to study the winfsp-tests source code to understand the failure and determine a fix for your file system. Additionally there may be garbage files remaining in the file system as winfsp-tests does not cleanup after itself.
**NOTE**: Some tests require Administrator privileges in order to run.

View File

@ -134,7 +134,9 @@ BOOL WINAPI ResilientRemoveDirectoryW(
else
{
for (ULONG MaxTries = DeleteMaxTries;
!Success && ERROR_SHARING_VIOLATION == GetLastError() && 0 != MaxTries;
!Success &&
(ERROR_SHARING_VIOLATION == GetLastError() || ERROR_DIR_NOT_EMPTY == GetLastError()) &&
0 != MaxTries;
MaxTries--)
{
Sleep(DeleteSleepTimeout);