mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-02 17:02:57 -05:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
db319bc3c1 | |||
c04e3d9534 | |||
1912814d79 | |||
97c075e744 | |||
874a223bcc | |||
901a98e118 | |||
0ab4300738 |
8
.github/workflows/avm.yml
vendored
8
.github/workflows/avm.yml
vendored
@ -11,8 +11,8 @@ jobs:
|
||||
- uses: billziss-gh/avm@v1
|
||||
with:
|
||||
files: |
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.6/winfsp-1.6.20027.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.7/winfsp-1.7.20172.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.8/winfsp-1.8.20304.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.9/winfsp-1.9.21096.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.10/winfsp-1.10.22006.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.11/winfsp-1.11.22176.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.12/winfsp-1.12.22301.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v1.12.22339/winfsp-1.12.22339.msi
|
||||
https://github.com/winfsp/winfsp/releases/download/v2.0B2/winfsp-2.0.23033.msi
|
||||
|
33
Changelog.md
33
Changelog.md
@ -1,6 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## v2.0RC1 (2023 RC1)
|
||||
|
||||
This release is a major version change for WinFsp (from 1.x to 2.x). There are no backwards incompatible API changes in this release, but nevertheless enough things change that warrant a version change.
|
||||
|
||||
The major new feature of this release is that it allows uninstallation and reinstallation of WinFsp **without reboot**. Going forward installers named `winfsp-2.x.y.msi` can be uninstalled and reinstalled without reboot. Furthermore a later version `winfsp-2.x.y.msi` installer can be used to upgrade over an earlier version `winfsp-2.x.y.msi` installer. However note that a `winfsp-2.x.y.msi` installer cannot be used to upgrade over a legacy `winfsp-1.x.y.msi` installer; you will still need to uninstall the old `winfsp-1.x.y.msi` installer, potentially reboot and then install the new `winfsp-2.x.y.msi` installer.
|
||||
|
||||
Changes visible to file system developers are listed below:
|
||||
|
||||
- WinFsp executable files are now installed by default in the directory `C:\Program Files (x86)\WinFsp\SxS\sxs.<InstanceID>\bin`. The previous directory `C:\Program Files (x86)\WinFsp\bin` is now a junction that points to the above directory.
|
||||
|
||||
- The WinFsp driver name is no longer `winfsp`, but rather a name such as `winfsp+<InstanceID>`. This means that managing the driver using the `sc.exe` utility is no longer as easy.
|
||||
|
||||
- The `fsptool` utility has been updated with new commands `lsdrv`, `load`, `unload` and `ver`. The `lsdrv`, `load` and `unload` commands can be used to manage the driver from the command line. This is rarely necessary, but may be useful for troubleshooting purposes.
|
||||
|
||||
- Prior to this release the WinFsp driver would never unmount a file system volume unless the user mode file system requested the unmount. From this release onward it is possible for the WinFsp driver to unmount a file system volume, without a user mode file system request. This is to allow for the driver to be unloaded.
|
||||
|
||||
A new operation `DispatcherStopped` has been added to `FSP_FILE_SYSTEM_INTERFACE`, which is sent after the file system volume has been unmounted and the file system dispatcher has been stopped. This can happen because of a user mode file system request via `FspFileSystemStopDispatcher` or because of driver unload. The `DispatcherStopped` operation includes a `Normally` parameter, which is `TRUE` for normal file system shutdown via `FspFileSystemStopDispatcher` and `FALSE` otherwise.
|
||||
|
||||
Native file systems that use the `FspService` infrastructure can use the `FspFileSystemStopServiceIfNecessary` API to handle the `DispatcherStopped` operation (see the MEMFS and NTPTFS samples). FUSE file systems get this functionality for free. .NET file systems that use the `Service` class infrastructure also get this functionality for free.
|
||||
|
||||
- WinFsp now offers a .NET library that targets .NET Framework 3.5 (as before) and one that targets .NET Standard 2.0. This is due to work by @Noire001 in PR #451.
|
||||
|
||||
- There is now a winfsp.net nuget package at https://www.nuget.org/packages/winfsp.net
|
||||
|
||||
- FUSE now supports path components up to 255 characters long (previously it was 255 bytes). This is due to work by @zeho11 in PR #474.
|
||||
|
||||
- The FUSE passthrough file systems have been updated to support long paths. This is also due to work by @zeho11.
|
||||
|
||||
- In some rare circumstances WinFsp file systems could report duplicate directory entries. This problem has been fixed. (GitHub issue #475.)
|
||||
|
||||
- The WinFsp symbols directory has been removed. If you are looking for WinFsp symbols you can find them at https://github.com/winfsp/winfsp.sym
|
||||
|
||||
|
||||
## v2.0B2 (2023 Beta2)
|
||||
|
||||
This release is a major version change for WinFsp (from 1.x to 2.x). There are no backwards incompatible API changes in this release, but nevertheless enough things change that warrant a version change.
|
||||
|
@ -20,8 +20,8 @@
|
||||
|
||||
<MyCanonicalVersion>2.0</MyCanonicalVersion>
|
||||
|
||||
<MyProductVersion>2023 Beta2</MyProductVersion>
|
||||
<MyProductStage>Beta</MyProductStage>
|
||||
<MyProductVersion>2023 RC1</MyProductVersion>
|
||||
<MyProductStage>RC</MyProductStage>
|
||||
|
||||
<MyCrossCert>DigiCertGlobalG3CodeSigningECCSHA3842021CA1.cer</MyCrossCert>
|
||||
<MyCertIssuer>DigiCert</MyCertIssuer>
|
||||
|
@ -6,18 +6,17 @@
|
||||
|
||||
I am running Windows 7 and I am finding that the installed driver is not signed.::
|
||||
|
||||
Your Windows 7 OS is missing SHA-2 Code Signing Support. You need to install the following security advisory that will rectify the problem:
|
||||
https://technet.microsoft.com/en-us/library/security/3033929.aspx
|
||||
Your Windows 7 OS is missing SHA-2 Code Signing Support. Make sure it is fully updated.
|
||||
|
||||
|
||||
I cannot run a program located in a WinFsp drive as administrator. I cannot run `regedit.exe` from within a WinFsp drive.::
|
||||
|
||||
When running an executable as administrator, the Windows OS seems to require that the name of the file system that is housing the executable is "NTFS". For example, the MEMFS file system with the command line `memfs-x64.exe -i -F NTFS -m X:` works.
|
||||
|
||||
|
||||
Disconnecting (unmapping) a network drive does not work.::
|
||||
|
||||
You may have Dokany installed. Dokany installs its own Network Provider DLL that unfortunately interferes with the WinFsp handling of network drives. The solution is to change your system's Network Provider order and ensure that the WinFsp Network Provider runs before the Dokany one. Instructions on how to change the Network Provider order can be found in this http://blogs.interfacett.com/changing-the-network-provider-order-in-windows-10[article].
|
||||
|
||||
|
||||
Case-sensitive file systems do not work properly when mounted as a directory.::
|
||||
|
||||
This is fixed as of WinFsp 2018.2 B3.
|
||||
You may have Dokany installed. Dokany installs its own Network Provider DLL that unfortunately interferes with the WinFsp handling of network drives. The solution is to change your system's Network Provider order and ensure that the WinFsp Network Provider runs before the Dokany one. Instructions on how to change the Network Provider order can be found in this https://www.interfacett.com/blogs/changing-the-network-provider-order-in-windows-10/[article].
|
||||
|
||||
|
||||
Why is the DLL not installed in the Windows system directories?::
|
||||
@ -29,7 +28,7 @@ There are a few alternative methods to overcome this problem. WinFsp recommends
|
||||
|
||||
Does WinFsp provide debugging symbols?::
|
||||
|
||||
Public debugging symbols are already included in the installer. You need to install the "Developer" feature; the symbols can be found in the `sym` directory under the WinFsp installation directory.
|
||||
Debugging symbols can be found in the https://github.com/winfsp/winfsp.sym repository.
|
||||
|
||||
|
||||
Is there a maximum number of concurrent file systems?::
|
||||
@ -61,11 +60,6 @@ With this in mind here are the reasons for the current WinFsp-FUSE behavior:
|
||||
- Most importantly: inability to guarantee that the mount point will cease to exist if the file system crashes. WinFsp attempts to guarantee that all resources used by a file system will get cleaned up. This is certainly true for the kernel-mode FSD, but an attempt is made to do so also in user mode. For this reason, drive symbolic links are marked as temporary and (importantly for our discussion) mount directories are opened with `FILE_FLAG_DELETE_ON_CLOSE`. There is no way to guarantee the removal of a reparse point in the same way.
|
||||
|
||||
|
||||
WinFsp-FUSE does not have the ability to support multiple file systems from within the same process. Why?::
|
||||
|
||||
This is supported as of WinFsp 2018.2 B2.
|
||||
|
||||
|
||||
I have problems getting permissions to work properly in a WinFsp-FUSE file system. Can you help?::
|
||||
|
||||
The WinFsp-FUSE layer includes a built-in command line option that can help: `-o uid=-1`. This instructs the WinFsp-FUSE layer to present all file system files as if they are owned by the user that launched the file system.
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <dll/library.h>
|
||||
#include <dbt.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
static INIT_ONCE FspMountInitOnce = INIT_ONCE_STATIC_INIT;
|
||||
static NTSTATUS (NTAPI *FspNtOpenSymbolicLinkObject)(
|
||||
@ -350,6 +351,19 @@ exit:
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID FspMountNotifyShellDriveChange(VOID)
|
||||
{
|
||||
HRESULT HResult;
|
||||
LPITEMIDLIST Pidl;
|
||||
|
||||
HResult = SHGetKnownFolderIDList(&FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, 0, &Pidl);
|
||||
if (SUCCEEDED(HResult))
|
||||
{
|
||||
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, Pidl, 0);
|
||||
CoTaskMemFree(Pidl);
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS FspMountSet_Drive(PWSTR VolumeName, PWSTR MountPoint, PHANDLE PMountHandle)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
@ -568,7 +582,7 @@ static NTSTATUS FspMountRemove_Directory(HANDLE MountHandle)
|
||||
return CloseHandle(MountHandle) ? STATUS_SUCCESS : FspNtStatusFromWin32(GetLastError());
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc)
|
||||
NTSTATUS FspMountSet_Internal(FSP_MOUNT_DESC *Desc)
|
||||
{
|
||||
InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0);
|
||||
|
||||
@ -609,7 +623,7 @@ FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc)
|
||||
&Desc->MountHandle);
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc)
|
||||
NTSTATUS FspMountRemove_Internal(FSP_MOUNT_DESC *Desc)
|
||||
{
|
||||
InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0);
|
||||
|
||||
@ -623,3 +637,81 @@ FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc)
|
||||
else
|
||||
return FspMountRemove_Directory(Desc->MountHandle);
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
BOOLEAN IsDrive;
|
||||
|
||||
IsDrive =
|
||||
(L'*' == Desc->MountPoint[0] && ':' == Desc->MountPoint[1] && L'\0' == Desc->MountPoint[2]) ||
|
||||
FspPathIsMountmgrDrive(Desc->MountPoint) ||
|
||||
FspPathIsDrive(Desc->MountPoint);
|
||||
|
||||
#if defined(FSP_CFG_REJECT_EARLY_IRP)
|
||||
/*
|
||||
* In the original WinFsp design the FSD could accept incoming file system requests
|
||||
* immediately after the in-kernel file system instance was created. Such requests would
|
||||
* be queued in the internal FSD queues and only delivered to the user mode file system
|
||||
* when its dispatcher was started and actively receiving them.
|
||||
*
|
||||
* At the same time the original WinFsp API design was that a user mode file system first
|
||||
* calls FspFileSystemSetMountPoint to create the file system mount point and then calls
|
||||
* FspFileSystemStartDispatcher to start the dispatcher. This design made sense at the time:
|
||||
* creating a mount point involved the creation of a symbolic link of one kind or another,
|
||||
* which could fail for a number of reasons (e.g. drive already exists), so there was no
|
||||
* point to start the dispatcher if the mounting did not succeed. Compatibility with FUSE
|
||||
* and the Unix mounting protocol was another consideration.
|
||||
*
|
||||
* Unfortunately this API design has proved problematic. The problem is that with the
|
||||
* proliferation of ways to mount a file system in WinFsp more and more system components and
|
||||
* third party filters may attempt to access the mount point upon its creation and before the
|
||||
* file system dispatcher is ready. This can result in various consequences.
|
||||
*
|
||||
* In order to properly fix this problem we should probably mandate that a user mode file
|
||||
* system should start its dispatcher first and then create its mountpoint. This way the user
|
||||
* mode file system would be ready to handle any requests from system components or third
|
||||
* party filters during mount point creation. Unfortunately we cannot easily do so because
|
||||
* of backwards compatibility.
|
||||
*
|
||||
* This problem first appeared as an incompatibility with Avast AntiVirus (GitHub issue #221).
|
||||
* In order to avoid backwards incompatible API changes the "Transact0" work around was
|
||||
* devised: when the FSD first creates an in-kernel file system it remains inoperative and any
|
||||
* requests posted to it will fail with STATUS_CANCELLED, until it receives a Transact0
|
||||
* message (an FSP_FSCTL_TRANSACT message with 0 buffers). In order to enable this work around
|
||||
* a user mode file system would specify the RejectIrpPriorToTransact0 flag upon creation.
|
||||
*
|
||||
* Another instance of this problem appeared when support for directory mounting via the Mount
|
||||
* Manager was added: mounting would not complete unless the RejectIrpPriorToTransact0 flag
|
||||
* was set. At this point the RejectIrpPriorToTransact0 was hard coded to 1 in the FSD.
|
||||
*
|
||||
* However if we are creating a drive the Transact0 work around is unnecessary and perhaps
|
||||
* harmful. So in this case send a Transact0 message to the FSD to allow file system requests
|
||||
* to be queued rather than rejected with STATUS_CANCELLED.
|
||||
*/
|
||||
if (IsDrive)
|
||||
FspFsctlTransact(Desc->VolumeHandle, 0, 0, 0, 0, FALSE);
|
||||
#endif
|
||||
|
||||
Result = FspMountSet_Internal(Desc);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
BOOLEAN IsDrive;
|
||||
|
||||
IsDrive =
|
||||
FspPathIsMountmgrDrive(Desc->MountPoint) ||
|
||||
FspPathIsDrive(Desc->MountPoint);
|
||||
|
||||
Result = FspMountRemove_Internal(Desc);
|
||||
|
||||
if (NT_SUCCESS(Result) && IsDrive)
|
||||
/* send an extra notification to remove the "ghost" drive in the shell's navigation pane */
|
||||
FspMountNotifyShellDriveChange();
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
@ -205,6 +205,7 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
|
||||
ASSERT(sizeof(UINT64) == DirectoryMarker->Length);
|
||||
DirectoryMarkerFound = DirectoryNextOffset == *(PUINT64)DirectoryMarker->Buffer;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* CopyLength is the same as FileName.Length except on STATUS_BUFFER_OVERFLOW */
|
||||
|
@ -731,6 +731,7 @@ static NTSTATUS FspFsvolFileSystemControl(
|
||||
Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp);
|
||||
break;
|
||||
case FSP_FSCTL_QUERY_WINFSP:
|
||||
case FSCTL_IS_VOLUME_MOUNTED:
|
||||
Irp->IoStatus.Information = 0;
|
||||
Result = STATUS_SUCCESS;
|
||||
break;
|
||||
|
@ -68,6 +68,27 @@ function Get-ReleaseInfo ($Tag) {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-HwapiCredentials {
|
||||
$Credentials = (& "$ProjectRoot\tools\wincred.ps1" get "Hardware Dashboard API")
|
||||
if ($Credentials) {
|
||||
try { $Credentials = ConvertFrom-Json $Credentials[1] } catch {;}
|
||||
}
|
||||
if ($Credentials -and $Credentials.TenantId -and $Credentials.ClientId -and $Credentials.ClientId) {
|
||||
return $Credentials
|
||||
}
|
||||
}
|
||||
|
||||
function Start-Sdcm {
|
||||
Start-Job -ArgumentList $args -ScriptBlock {
|
||||
$env:SDCM_CREDS_TENANTID = $using:HwapiCredentials.TenantId
|
||||
$env:SDCM_CREDS_CLIENTID = $using:HwapiCredentials.ClientId
|
||||
$env:SDCM_CREDS_KEY = $using:HwapiCredentials.Key
|
||||
$env:SDCM_CREDS_URL = "https://manage.devcenter.microsoft.com"
|
||||
$env:SDCM_CREDS_URLPREFIX = "v2.0/my"
|
||||
& "$using:ProjectRoot\..\winfsp.sdcm\SurfaceDevCenterManager\bin\Debug\sdcm.exe" -creds envonly @args
|
||||
} | Receive-Job -Wait -AutoRemoveJob
|
||||
}
|
||||
|
||||
function Get-FileVersion ($FileName) {
|
||||
return [System.Diagnostics.FileVersionInfo]::GetVersionInfo($FileName).FileVersion
|
||||
}
|
||||
@ -96,6 +117,12 @@ function Check-Prerequisites {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# check scdm.exe
|
||||
if (!(Get-Command "$ProjectRoot\..\winfsp.sdcm\SurfaceDevCenterManager\bin\Debug\sdcm.exe" -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot find sdcm.exe"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# check gh.exe
|
||||
if (!(Get-Command "gh.exe" -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot find gh.exe"
|
||||
@ -137,6 +164,17 @@ function Check-Prerequisites {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# check hardware dashboard api credentials
|
||||
$script:HwapiCredentials = Get-HwapiCredentials
|
||||
if (!$script:HwapiCredentials) {
|
||||
Write-Stderr "error: cannot get Hardware Dashboard API credentials"
|
||||
Write-Stderr ' The expected format of the credentials is as follows:'
|
||||
Write-Stderr ' TargetName: Hardware Dashboard API'
|
||||
Write-Stderr ' UserName: Credentials'
|
||||
Write-Stderr ' Password: {"TenantId":"TENANTID","ClientId":"CLIENTID","Key":"KEY"}'
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($State -contains $Name) {
|
||||
if (!($State -contains "$Tag-$CommitCount-g$Commit")) {
|
||||
Write-Stderr "error: invalid state for tag $Tag"
|
||||
@ -149,6 +187,12 @@ function Check-Prerequisites {
|
||||
}
|
||||
|
||||
function Check-Assets {
|
||||
# check driver.cab
|
||||
if (!(Test-Path "$ProjectRoot\build\VStudio\build\Release\driver.cab" -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot find driver.cab"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# check winfsp.msi
|
||||
if (!(Test-Path "$ProjectRoot\build\VStudio\build\Release\winfsp*.msi" -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot find winfsp*.msi"
|
||||
@ -192,8 +236,215 @@ function Build-AssetsPhase1 {
|
||||
|
||||
Write-Stdout @"
|
||||
|
||||
Upload file driver.cab to Microsoft Partner Center for attestation signing.
|
||||
When the file has been signed, download and extract to ~\Downloads\drivers
|
||||
Assets have been built but are not properly signed.
|
||||
Signable assets are ready for submission to the hardware dashboard.
|
||||
|
||||
"@
|
||||
}
|
||||
}
|
||||
|
||||
function Submit-AssetsToHwapi {
|
||||
Task -ScriptBlock {
|
||||
Check-Assets
|
||||
|
||||
$MsiFile = Resolve-Path "$ProjectRoot\build\VStudio\build\Release\winfsp*.msi"
|
||||
$MsiName = Split-Path -Leaf $MsiFile
|
||||
if ($MsiName -match "winfsp-(.+)\.msi") {
|
||||
$Version = $matches[1]
|
||||
}
|
||||
if (!$Version) {
|
||||
Write-Stderr "error: cannot determine version for winfsp.msi"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$DocRequestedSignatures = @()
|
||||
$Documentation = Invoke-WebRequest -Uri "https://raw.githubusercontent.com/MicrosoftDocs/windows-driver-docs/staging/windows-driver-docs-pr/dashboard/get-product-data.md"
|
||||
$Documentation = $Documentation.Content
|
||||
$List = $false
|
||||
foreach ($Line in $Documentation -Split "`n") {
|
||||
if ($Line -match "^### List of Operating System Codes") {
|
||||
$List = $true
|
||||
} elseif ($Line -match "^###") {
|
||||
$List = $false
|
||||
} elseif ($List -and $Line -cmatch "\| *(WINDOWS_v100_[^| ]+) *\|") {
|
||||
$DocRequestedSignatures += $matches[1]
|
||||
}
|
||||
}
|
||||
if ($DocRequestedSignatures.length -lt 32) {
|
||||
Write-Stderr "error: cannot determine signatures to request"
|
||||
Write-Stderr ' Does the document at the URL below still contain a "List of Operating System Codes":'
|
||||
Write-Stderr " https://raw.githubusercontent.com/MicrosoftDocs/windows-driver-docs/staging/windows-driver-docs-pr/dashboard/get-product-data.md"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# start with the base signatures and add any new ones found in the docs
|
||||
$RequestedSignatures = @(
|
||||
"WINDOWS_v100_TH2_FULL"
|
||||
"WINDOWS_v100_X64_TH2_FULL"
|
||||
"WINDOWS_v100_RS1_FULL"
|
||||
"WINDOWS_v100_X64_RS1_FULL"
|
||||
"WINDOWS_v100_RS2_FULL"
|
||||
"WINDOWS_v100_X64_RS2_FULL"
|
||||
"WINDOWS_v100_RS3_FULL"
|
||||
"WINDOWS_v100_X64_RS3_FULL"
|
||||
"WINDOWS_v100_ARM64_RS3_FULL"
|
||||
"WINDOWS_v100_RS4_FULL"
|
||||
"WINDOWS_v100_X64_RS4_FULL"
|
||||
"WINDOWS_v100_ARM64_RS4_FULL"
|
||||
"WINDOWS_v100_RS5_FULL"
|
||||
"WINDOWS_v100_X64_RS5_FULL"
|
||||
"WINDOWS_v100_ARM64_RS5_FULL"
|
||||
"WINDOWS_v100_19H1_FULL"
|
||||
"WINDOWS_v100_X64_19H1_FULL"
|
||||
"WINDOWS_v100_ARM64_19H1_FULL"
|
||||
"WINDOWS_v100_VB_FULL"
|
||||
"WINDOWS_v100_X64_VB_FULL"
|
||||
"WINDOWS_v100_ARM64_VB_FULL"
|
||||
"WINDOWS_v100_X64_CO_FULL"
|
||||
"WINDOWS_v100_ARM64_CO_FULL"
|
||||
"WINDOWS_v100_X64_NI_FULL"
|
||||
"WINDOWS_v100_ARM64_NI_FULL"
|
||||
)
|
||||
foreach ($Signature in $DocRequestedSignatures) {
|
||||
if ($RequestedSignatures -contains $Signature) {
|
||||
continue
|
||||
}
|
||||
if ($Signature.Contains("_SERVER_")) {
|
||||
continue
|
||||
}
|
||||
if ($Signature -eq "WINDOWS_v100_TH1_FULL") {
|
||||
continue
|
||||
}
|
||||
if ($Signature -eq "WINDOWS_v100_X64_TH1_FULL") {
|
||||
continue
|
||||
}
|
||||
$RequestedSignatures += $Signature
|
||||
Write-Stdout "New doc signature: $Signature"
|
||||
}
|
||||
|
||||
$CreateProduct = @{
|
||||
createType = "product"
|
||||
createProduct = @{
|
||||
productName = "winfsp-$Version"
|
||||
testHarness = "attestation"
|
||||
deviceType = "internalExternal"
|
||||
requestedSignatures = $RequestedSignatures
|
||||
# deviceMetaDataIds = $null
|
||||
# firmwareVersion = "0"
|
||||
# isTestSign = $false
|
||||
# isFlightSign = $false
|
||||
# markettingNames = $null
|
||||
# selectedProductTypes = $null
|
||||
# additionalAttributes = $null
|
||||
}
|
||||
}
|
||||
$CreateSubmission = @{
|
||||
createType = "submission"
|
||||
createSubmission = @{
|
||||
name = "winfsp-$Version"
|
||||
type = "initial"
|
||||
}
|
||||
}
|
||||
New-Item "$ProjectRoot\build\VStudio\build\Release\hwapi" -Type Directory -ErrorAction SilentlyContinue >$null
|
||||
ConvertTo-Json $CreateProduct | Out-File -Encoding Ascii "$ProjectRoot\build\VStudio\build\Release\hwapi\CreateProduct.json"
|
||||
ConvertTo-Json $CreateSubmission | Out-File -Encoding Ascii "$ProjectRoot\build\VStudio\build\Release\hwapi\CreateSubmission.json"
|
||||
|
||||
Start-Sdcm -create "$ProjectRoot\build\VStudio\build\Release\hwapi\CreateProduct.json" | Tee-Object -Variable Output
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Stderr "error: cannot create product on hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
if (-not ([string]$Output -match "--- Product: (\d+)")) {
|
||||
Write-Stderr "error: cannot get product id from hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
$ProductId = $matches[1]
|
||||
|
||||
Start-Sdcm -create "$ProjectRoot\build\VStudio\build\Release\hwapi\CreateSubmission.json" -productid $ProductId | Tee-Object -Variable Output
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Stderr "error: cannot create submission on hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
if (-not ([string]$Output -match "---- Submission: (\d+)")) {
|
||||
Write-Stderr "error: cannot get submission id from hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
$SubmissionId = $matches[1]
|
||||
|
||||
Set-Content "$ProjectRoot\build\VStudio\build\Release\hwapi\ProductId" -Value $ProductId
|
||||
Set-Content "$ProjectRoot\build\VStudio\build\Release\hwapi\SubmissionId" -Value $SubmissionId
|
||||
|
||||
Write-Stdout @"
|
||||
|
||||
Product submission has been prepared on hardware dashboard.
|
||||
|
||||
"@
|
||||
}
|
||||
}
|
||||
|
||||
function Upload-AssetsToHwapi {
|
||||
Task -ScriptBlock {
|
||||
Check-Assets
|
||||
|
||||
$ProductId = Get-Content "$ProjectRoot\build\VStudio\build\Release\hwapi\ProductId"
|
||||
$SubmissionId = Get-Content "$ProjectRoot\build\VStudio\build\Release\hwapi\SubmissionId"
|
||||
|
||||
Start-Sdcm -upload "$ProjectRoot\build\VStudio\build\Release\driver.cab" -productid $ProductId -submissionid $SubmissionId
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Stderr "error: cannot upload driver.cab to hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Start-Sdcm -commit -productid $ProductId -submissionid $SubmissionId
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Stderr "error: cannot commit submission to hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Stdout @"
|
||||
|
||||
Signable assets have been uploaded to the hardware dashboard.
|
||||
|
||||
"@
|
||||
}
|
||||
}
|
||||
|
||||
function Download-AssetsFromHwapi {
|
||||
Task -ScriptBlock {
|
||||
Check-Assets
|
||||
|
||||
$ProductId = Get-Content "$ProjectRoot\build\VStudio\build\Release\hwapi\ProductId"
|
||||
$SubmissionId = Get-Content "$ProjectRoot\build\VStudio\build\Release\hwapi\SubmissionId"
|
||||
|
||||
Remove-Item -Force "$ProjectRoot\build\VStudio\build\Release\hwapi\Signed-$SubmissionId.zip" -ErrorAction SilentlyContinue
|
||||
Remove-Item -Recurse -Force "$ProjectRoot\build\VStudio\build\Release\hwapi\drivers" -ErrorAction SilentlyContinue
|
||||
|
||||
Start-Sdcm -download "$ProjectRoot\build\VStudio\build\Release\hwapi\Signed-$SubmissionId.zip" -productid $ProductId -submissionid $SubmissionId
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Stderr "error: cannot download signed drivers from hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (!(Test-Path "$ProjectRoot\build\VStudio\build\Release\hwapi\Signed-$SubmissionId.zip" -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot download signed drivers from hardware dashboard"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$ExpandError = ""
|
||||
Expand-Archive "$ProjectRoot\build\VStudio\build\Release\hwapi\Signed-$SubmissionId.zip" -DestinationPath "$ProjectRoot\build\VStudio\build\Release\hwapi" -ErrorVariable ExpandError
|
||||
if ($ExpandError) {
|
||||
Write-Stderr "error: cannot expand signed drivers archive"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (!(Test-Path "$ProjectRoot\build\VStudio\build\Release\hwapi\drivers" -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot expand signed drivers archive"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Stdout @"
|
||||
|
||||
Signable assets have been downloaded and can be used to complete the build.
|
||||
|
||||
"@
|
||||
}
|
||||
@ -204,16 +455,16 @@ function Build-AssetsPhase2 {
|
||||
Check-Assets
|
||||
|
||||
# check signed drivers folder
|
||||
if (!(Test-Path ~\Downloads\drivers -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot find ~\Downloads\drivers"
|
||||
if (!(Test-Path "$ProjectRoot\build\VStudio\build\Release\hwapi\drivers" -ErrorAction SilentlyContinue)) {
|
||||
Write-Stderr "error: cannot find hwapi\drivers"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$SignedPackage = Resolve-Path ~\Downloads\drivers
|
||||
$SignedPackage = Resolve-Path "$ProjectRoot\build\VStudio\build\Release\hwapi\drivers"
|
||||
|
||||
$VerX64 = Get-FileVersion "$ProjectRoot\build\VStudio\build\Release\winfsp-x64.sys"
|
||||
if ($VerX64 -ne (Get-FileVersion "$SignedPackage\x64\winfsp-x64.sys")) {
|
||||
Write-Stderr "error: incompatible versions in ~\Downloads\drivers"
|
||||
Write-Stderr "error: incompatible versions in hwapi\drivers"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@ -375,8 +626,12 @@ Check-Prerequisites
|
||||
|
||||
# Workflow tasks
|
||||
Build-AssetsPhase1
|
||||
Submit-AssetsToHwapi
|
||||
Upload-AssetsToHwapi
|
||||
Download-AssetsFromHwapi
|
||||
Build-AssetsPhase2
|
||||
Make-GitHubRelease
|
||||
Upload-Symbols
|
||||
Make-NugetRelease
|
||||
Make-ChocoRelease
|
||||
Write-Stdout "ALL COMPLETE"
|
||||
|
124
tools/wincred.ps1
Normal file
124
tools/wincred.ps1
Normal file
@ -0,0 +1,124 @@
|
||||
Start-Job -ArgumentList $args -ScriptBlock {
|
||||
param (
|
||||
[ValidateSet("get", "set", "del")]
|
||||
[string]$Command
|
||||
)
|
||||
|
||||
Add-Type -TypeDefinition @"
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
namespace Win32
|
||||
{
|
||||
public class Api
|
||||
{
|
||||
public static String[] CredRead(
|
||||
String TargetName)
|
||||
{
|
||||
String[] Result = null;
|
||||
IntPtr CredentialPtr;
|
||||
if (CredReadW(TargetName, 1/*CRED_TYPE_GENERIC*/, 0, out CredentialPtr))
|
||||
{
|
||||
CREDENTIALW Credential = Marshal.PtrToStructure<CREDENTIALW>(CredentialPtr);
|
||||
Result = new String[2]{
|
||||
Credential.UserName,
|
||||
Marshal.PtrToStringUni(
|
||||
Credential.CredentialBlob, (int)Credential.CredentialBlobSize / 2)
|
||||
};
|
||||
CredFree(CredentialPtr);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
public static Boolean CredWrite(
|
||||
String TargetName,
|
||||
String UserName,
|
||||
String Password)
|
||||
{
|
||||
CREDENTIALW Credential = new CREDENTIALW{
|
||||
Type = 1/*CRED_TYPE_GENERIC*/,
|
||||
Persist = 2/*CRED_PERSIST_LOCAL_MACHINE*/,
|
||||
TargetName = TargetName,
|
||||
UserName = UserName,
|
||||
CredentialBlobSize = (UInt32)Password.Length * 2,
|
||||
CredentialBlob = Marshal.StringToCoTaskMemUni(Password),
|
||||
};
|
||||
Boolean Result = CredWriteW(ref Credential, 0);
|
||||
Marshal.FreeCoTaskMem(Credential.CredentialBlob);
|
||||
return Result;
|
||||
}
|
||||
public static Boolean CredDelete(
|
||||
String TargetName)
|
||||
{
|
||||
return CredDeleteW(TargetName, 1/*CRED_TYPE_GENERIC*/, 0);
|
||||
}
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern Boolean CredReadW(
|
||||
String TargetName,
|
||||
UInt32 Type,
|
||||
UInt32 Flags,
|
||||
out IntPtr Credential);
|
||||
[DllImport("advapi32.dll")]
|
||||
private static extern void CredFree(
|
||||
IntPtr Buffer);
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern Boolean CredWriteW(
|
||||
ref CREDENTIALW Credential,
|
||||
UInt32 Flags);
|
||||
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
private static extern Boolean CredDeleteW(
|
||||
String TargetName,
|
||||
UInt32 Type,
|
||||
UInt32 Flags);
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct CREDENTIALW
|
||||
{
|
||||
public UInt32 Flags;
|
||||
public UInt32 Type;
|
||||
public String TargetName;
|
||||
public String Comment;
|
||||
public UInt64 LastWritten;
|
||||
public UInt32 CredentialBlobSize;
|
||||
public IntPtr CredentialBlob;
|
||||
public UInt32 Persist;
|
||||
public UInt32 AttributeCount;
|
||||
public IntPtr Attributes;
|
||||
public String TargetAlias;
|
||||
public String UserName;
|
||||
};
|
||||
};
|
||||
};
|
||||
"@
|
||||
|
||||
function Get-WindowsCredential {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$TargetName
|
||||
)
|
||||
return [Win32.Api]::CredRead($TargetName)
|
||||
}
|
||||
|
||||
function Set-WindowsCredential {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$TargetName,
|
||||
[Parameter(Mandatory)][string]$UserName,
|
||||
[Parameter(Mandatory)][string]$Password
|
||||
)
|
||||
return [Win32.Api]::CredWrite($TargetName, $UserName, $Password)
|
||||
}
|
||||
|
||||
function Delete-WindowsCredential {
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$TargetName
|
||||
)
|
||||
return [Win32.Api]::CredDelete($TargetName)
|
||||
}
|
||||
|
||||
$Function = @{
|
||||
"get" = "Get-WindowsCredential"
|
||||
"set" = "Set-WindowsCredential"
|
||||
"del" = "Delete-WindowsCredential"
|
||||
}[$Command]
|
||||
& $Function @args
|
||||
|
||||
} | Receive-Job -Wait -AutoRemoveJob
|
@ -38,6 +38,7 @@ enum
|
||||
MemfsCaseInsensitive = 0x80000000,
|
||||
MemfsFlushAndPurgeOnCleanup = 0x40000000,
|
||||
MemfsLegacyUnlinkRename = 0x20000000,
|
||||
MemfsNoSlowio = 0x10000000,
|
||||
};
|
||||
|
||||
#define MemfsCreate(Flags, FileInfoTimeout, MaxFileNodes, MaxFileSize, VolumePrefix, RootSddl, PMemfs)\
|
||||
|
@ -28,6 +28,52 @@
|
||||
|
||||
#include "winfsp-tests.h"
|
||||
|
||||
typedef struct _FILE_FULL_DIR_INFORMATION
|
||||
{
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
WCHAR FileName[1];
|
||||
} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;
|
||||
|
||||
typedef struct _FILE_DIRECTORY_INFORMATION
|
||||
{
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
NtQueryDirectoryFile (
|
||||
HANDLE FileHandle,
|
||||
HANDLE Event,
|
||||
PIO_APC_ROUTINE ApcRoutine,
|
||||
PVOID ApcContext,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID FileInformation,
|
||||
ULONG Length,
|
||||
FILE_INFORMATION_CLASS FileInformationClass,
|
||||
BOOLEAN ReturnSingleEntry,
|
||||
PUNICODE_STRING FileName,
|
||||
BOOLEAN RestartScan);
|
||||
|
||||
static void querydir_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout, ULONG SleepTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
@ -269,6 +315,212 @@ void querydir_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
static inline size_t hash_chars(const wchar_t *s, size_t length)
|
||||
{
|
||||
/* djb2: see http://www.cse.yorku.ca/~oz/hash.html */
|
||||
size_t h = 5381;
|
||||
for (const wchar_t *t = s + length; t > s; ++s)
|
||||
h = 33 * h + *s;
|
||||
return h;
|
||||
}
|
||||
static NTSTATUS querydir_nodup_dotest_thread_loop(void *FilePath)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
void *Buffer = 0;
|
||||
HANDLE Handle = INVALID_HANDLE_VALUE;
|
||||
IO_STATUS_BLOCK IoStatus;
|
||||
size_t hash[64], hash_count = 0;
|
||||
|
||||
Buffer = malloc(4096);
|
||||
if (0 == Buffer)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Handle = CreateFileW(
|
||||
FilePath,
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
0);
|
||||
if (INVALID_HANDLE_VALUE == Handle)
|
||||
{
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Result = NtQueryDirectoryFile(
|
||||
Handle,
|
||||
0, 0, 0,
|
||||
&IoStatus,
|
||||
Buffer,
|
||||
4096,
|
||||
2/*FileFullDirectoryInformation*/,
|
||||
FALSE,
|
||||
0,
|
||||
FALSE);
|
||||
if (STATUS_NO_MORE_FILES == Result)
|
||||
break;
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
for (FILE_FULL_DIR_INFORMATION *DirInfo = Buffer;;
|
||||
DirInfo = (PVOID)((PUINT8)DirInfo + DirInfo->NextEntryOffset))
|
||||
{
|
||||
size_t h = hash_chars(DirInfo->FileName, DirInfo->FileNameLength / sizeof(WCHAR));
|
||||
for (size_t i = 0; hash_count > i; i++)
|
||||
if (hash[i] == h)
|
||||
{
|
||||
WCHAR FileName[256];
|
||||
memcpy(FileName, DirInfo->FileName, DirInfo->FileNameLength);
|
||||
FileName[DirInfo->FileNameLength / sizeof(WCHAR)] = L'\0';
|
||||
FspDebugLog(__FUNCTION__ ": duplicate \"%S\"\n", FileName);
|
||||
Result = STATUS_UNSUCCESSFUL;
|
||||
goto exit;
|
||||
}
|
||||
if (sizeof hash / sizeof hash[0] > hash_count)
|
||||
hash[hash_count++] = h;
|
||||
if (0 == DirInfo->NextEntryOffset)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (INVALID_HANDLE_VALUE != Handle)
|
||||
CloseHandle(Handle);
|
||||
|
||||
free(Buffer);
|
||||
|
||||
return Result;
|
||||
}
|
||||
static unsigned __stdcall querydir_nodup_dotest_thread(void *FilePath)
|
||||
{
|
||||
FspDebugLog(__FUNCTION__ ": \"%S\"\n", FilePath);
|
||||
|
||||
NTSTATUS Result;
|
||||
|
||||
for (size_t i = 0; 5000 > i; i++)
|
||||
{
|
||||
Result = querydir_nodup_dotest_thread_loop(FilePath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
return Result;
|
||||
}
|
||||
|
||||
static void querydir_nodup_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
|
||||
static WCHAR *FileNames[] =
|
||||
{
|
||||
L"Extensions.cs",
|
||||
L"JsonSchema.cs",
|
||||
L"JsonSchemaBuilder.cs",
|
||||
L"JsonSchemaConstants.cs",
|
||||
L"JsonSchemaException.cs",
|
||||
L"JsonSchemaGenerator.cs",
|
||||
L"JsonSchemaModel.cs",
|
||||
L"JsonSchemaModelBuilder.cs",
|
||||
L"JsonSchemaNode.cs",
|
||||
L"JsonSchemaNodeCollection.cs",
|
||||
L"JsonSchemaResolver.cs",
|
||||
L"JsonSchemaType.cs",
|
||||
L"JsonSchemaWriter.cs",
|
||||
L"UndefinedSchemaIdHandling.cs",
|
||||
L"ValidationEventArgs.cs",
|
||||
L"ValidationEventHandler.cs",
|
||||
};
|
||||
HANDLE Handle;
|
||||
BOOL Success;
|
||||
WCHAR FilePath[MAX_PATH];
|
||||
HANDLE Threads[2];
|
||||
DWORD ExitCode;
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir0",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
Success = CreateDirectoryW(FilePath, 0);
|
||||
ASSERT(Success);
|
||||
|
||||
for (size_t i = 0; sizeof FileNames / sizeof FileNames[0] > i; i++)
|
||||
{
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir0\\%s",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), FileNames[i]);
|
||||
Handle = CreateFileW(FilePath, GENERIC_ALL, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||
Success = CloseHandle(Handle);
|
||||
ASSERT(Success);
|
||||
}
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir0",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
for (size_t i = 0; sizeof Threads / sizeof Threads[0] > i; i++)
|
||||
{
|
||||
Threads[i] = (HANDLE)_beginthreadex(0, 0, querydir_nodup_dotest_thread, FilePath, 0, 0);
|
||||
ASSERT(0 != Threads[i]);
|
||||
}
|
||||
for (size_t i = 0; sizeof Threads / sizeof Threads[0] > i; i++)
|
||||
{
|
||||
WaitForSingleObject(Threads[i], INFINITE);
|
||||
GetExitCodeThread(Threads[i], &ExitCode);
|
||||
CloseHandle(Threads[i]);
|
||||
ASSERT(0 == ExitCode);
|
||||
}
|
||||
|
||||
for (size_t i = 0; sizeof FileNames / sizeof FileNames[0] > i; i++)
|
||||
{
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir0\\%s",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), FileNames[i]);
|
||||
Success = DeleteFileW(FilePath);
|
||||
ASSERT(Success);
|
||||
}
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir0",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
Success = RemoveDirectoryW(FilePath);
|
||||
ASSERT(Success);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static void querydir_nodup_test(void)
|
||||
{
|
||||
/*
|
||||
* Test GitHub issue #475. Credit for the investigation and reproduction
|
||||
* of this issue goes to GitHub user @hach-que.
|
||||
*/
|
||||
|
||||
if (NtfsTests)
|
||||
{
|
||||
WCHAR DirBuf[MAX_PATH];
|
||||
GetTestDirectory(DirBuf);
|
||||
querydir_nodup_dotest(-1, DirBuf, 0);
|
||||
}
|
||||
if (WinFspDiskTests)
|
||||
{
|
||||
querydir_nodup_dotest(MemfsDisk | MemfsNoSlowio, 0, 0);
|
||||
querydir_nodup_dotest(MemfsDisk | MemfsNoSlowio, 0, 1000);
|
||||
}
|
||||
#if 0
|
||||
if (WinFspNetTests)
|
||||
{
|
||||
querydir_nodup_dotest(MemfsNet | MemfsNoSlowio, L"\\\\memfs\\share", 0);
|
||||
querydir_nodup_dotest(MemfsNet | MemfsNoSlowio, L"\\\\memfs\\share", 1000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void querydir_single_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout, ULONG SleepTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
@ -368,36 +620,6 @@ void querydir_expire_cache_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct _FILE_DIRECTORY_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
|
||||
|
||||
NTSTATUS
|
||||
NTAPI
|
||||
NtQueryDirectoryFile (
|
||||
_In_ HANDLE FileHandle,
|
||||
_In_opt_ HANDLE Event,
|
||||
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
|
||||
_In_opt_ PVOID ApcContext,
|
||||
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
|
||||
_Out_writes_bytes_(Length) PVOID FileInformation,
|
||||
_In_ ULONG Length,
|
||||
_In_ FILE_INFORMATION_CLASS FileInformationClass,
|
||||
_In_ BOOLEAN ReturnSingleEntry,
|
||||
_In_opt_ PUNICODE_STRING FileName,
|
||||
_In_ BOOLEAN RestartScan
|
||||
);
|
||||
|
||||
static void querydir_buffer_overflow_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout, ULONG SleepTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
@ -766,6 +988,7 @@ void dirnotify_test(void)
|
||||
void dirctl_tests(void)
|
||||
{
|
||||
TEST(querydir_test);
|
||||
TEST_OPT(querydir_nodup_test);
|
||||
if (!OptShareName)
|
||||
TEST_OPT(querydir_single_test);
|
||||
TEST(querydir_expire_cache_test);
|
||||
|
@ -48,9 +48,9 @@ void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
|
||||
FileInfoTimeout,
|
||||
1024,
|
||||
1024 * 1024,
|
||||
50, /*SlowioMaxDelay*/
|
||||
10, /*SlowioPercentDelay*/
|
||||
5, /*SlowioRarefyDelay*/
|
||||
(Flags & MemfsNoSlowio) ? 0 : 50, /*SlowioMaxDelay*/
|
||||
(Flags & MemfsNoSlowio) ? 0 : 10, /*SlowioPercentDelay*/
|
||||
(Flags & MemfsNoSlowio) ? 0 : 5, /*SlowioRarefyDelay*/
|
||||
0,
|
||||
MemfsNet == Flags ? L"\\memfs\\share" : 0,
|
||||
0,
|
||||
|
Reference in New Issue
Block a user