mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 01:12:58 -05:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
8ba8d31e50 | |||
c1ab78e8e1 | |||
ed404ee579 | |||
620ebd9e72 | |||
fc8d18e4de | |||
00219f29cf | |||
c4f994f8f6 | |||
91211f6ccb | |||
666561bfa1 | |||
4e94991221 | |||
826a514fe3 | |||
a9d90acd71 | |||
d72fe2ee33 | |||
e09042c028 | |||
6e13825dcc | |||
cc90b5dd80 | |||
4b6d9b70d7 | |||
1f68eb0f3d | |||
61c48ab417 | |||
d7e49dfb20 | |||
10c5fa6301 | |||
368855676a | |||
6fbe73ce4d | |||
e1763bcd10 | |||
378beb55eb | |||
14b212f9af | |||
cca8b32128 | |||
0b94e8bc6a | |||
ec3386c2b3 | |||
d67a917c6f | |||
2a86cd2c90 | |||
67c4011263 |
@ -1,7 +1,52 @@
|
||||
= Changelog
|
||||
|
||||
|
||||
v1.10B2 (2021.1 Beta2)::
|
||||
v1.10B4 (2022 Beta4)::
|
||||
+
|
||||
--
|
||||
Changes since v1.10B3:
|
||||
|
||||
* The Delete functionality introduced in release 2021.1 Beta3 (v1.10B3) has been reverted. For more information see this thread: https://groups.google.com/g/winfsp/c/1VYgTp1JpSI
|
||||
|
||||
Changes since v1.9:
|
||||
|
||||
* [NEW] Official Windows 11 support.
|
||||
|
||||
* [NEW] Support for `FileDispositionInformationEx` and `FileRenameInformationEx` has been added.
|
||||
|
||||
* [NEW] WinFsp now supports rebranding of the build products. This is primarily useful to commercial licensees. See https://github.com/billziss-gh/winfsp/wiki/WinFsp-Rebranding for more information.
|
||||
|
||||
* [NEW] WinFsp-FUSE has a new capability `FSP_FUSE_CAP_DELETE_ACCESS`. If this capability is specified WinFsp will call the FUSE `access` method with the WinFsp-specific flag `FSP_FUSE_DELETE_OK` to check whether the deletion should be allowed. This can be used to disallow deletion of files and directories that would normally be allowed under the FUSE/POSIX model.
|
||||
|
||||
* [NEW] WinFsp-FUSE has a new method for determining file access in the case where the user's primary SID (Windows Security Identifier) and group SID are the same (for example, this can happen when someone uses a Microsoft account as their primary login).
|
||||
** Previously when the user SID and group SID were the same WinFsp-FUSE looked at the UNIX permissions for the owner and the UNIX permissions for the group and used the MOST restrictive permissions, which often resulted in inadvertent "access denied" errors. (For example, if the owner permission was `rw-` and the group permission was `---` the result was `---` and therefore access denied).
|
||||
** Going forward this behavior will change. The user and group mode permissions will not be considered together even in the case where the user SID and group SID are the same. This will resolve the access denied errors.
|
||||
** However to preserve backward compatibility (there might be some file systems that rely on the old behavior) we will do so in stages. For release v1.10 (2021.1) there is a new registry setting under `HKLM\SOFTWARE\WinFsp` (or `HKLM\SOFTWARE\WOW6432Node\WinFsp` on a 64-bit system) called `DistinctPermsForSameOwnerGroup`, which if set to 1 will direct WinFsp-FUSE to use the new behavior. The default value is 0 which directs WinFsp-FUSE to use the old behavior. This default will change in a future release.
|
||||
|
||||
* [NEW] A new registry setting under `HKLM\SOFTWARE\WinFsp` (or `HKLM\SOFTWARE\WOW6432Node\WinFsp` on a 64-bit system) called `MountDoNotUseLauncher` has been introduced, which if set to 1 will disable the use of the Launcher during mounting. The default value is 0 which allows the use of the Launcher during mounting in those rare cases when necessary. (In general the Launcher is not necessary for mounting. However when running a file system in the Windows Service context (session 0) under an account that is not LocalSystem (e.g. `NT AUTHORITY\NETWORK SERVICE`), the Launcher is used to create global drives.)
|
||||
|
||||
* [NEW] A new sample memfs-fuse file system written in C++ has been added. This file system uses all FUSE2 features offered by WinFsp-FUSE, passes all file system tests (that can be passed by a FUSE2 file system) and will act as the reference WinFsp-FUSE file system going forward.
|
||||
|
||||
* [FIX] File share access when overwriting a file (e.g. when using `TRUNCATE_EXISTING`) is now done in a manner compatible with NTFS (previously there were cases when overwriting a file where behavior diverged from the NTFS one). (See GitHub issue #364.)
|
||||
|
||||
* [FIX] The FSD will now report a file's reparse tag in the `EaSize` field of `FILE_FULL_DIR_INFORMATION` and friends. This fixes problems such as `cmd.exe` not recognizing symlinks in a `dir` command. (See GitHub issue #380.)
|
||||
|
||||
* [FIX] Fixed a problem in the file system shutdown protocol which resolves an occasional access violation in the user mode file system process. Previously it was possible for a file system to crash when stopping itself using `FspFileSystemStopDispatcher`; this problem has been rectified. (See GitHub issue #369.)
|
||||
|
||||
* [FIX] Improved symlink support for FUSE file systems. Notably:
|
||||
|
||||
** FUSE file systems now support converting a directory to a symlink reparse point.
|
||||
|
||||
** The determination of whether a symlink is a file or directory (necessary because the Windows file system makes this distinction) is now possible for file systems that do not support slashdot (`/.`) queries. (A slashdot query is one like `getattr("/PATH/.")`; such queries are not normally expected to be resolved by FUSE file systems.)
|
||||
|
||||
* [FIX] Fixed a problem in the FUSE layer where in some rare circumstances the `release` operation could be called with an uninitialized `path` argument. (See GitHub issue billziss-gh/cgofuse#58 and commit f51bdef53427d1bba688fb6c768792fdc22ffc7b).
|
||||
|
||||
* [FIX] Fixed a potential problem when launching file system instances using the Launcher. (See GitHub issue #356.)
|
||||
|
||||
* [FIX] The `winfsp.h` header no longer defines `FILE_FULL_EA_INFORMATION` when compiled under mingw. This is because the mingw tool chain already includes a definition of this type. (GitHub PR #371. Thanks @lemourin.)
|
||||
--
|
||||
|
||||
v1.10B3 (2021.1 Beta3)::
|
||||
|
||||
Changes since v1.9:
|
||||
|
||||
@ -35,6 +80,28 @@ Changes since v1.9:
|
||||
* [FIX] The `winfsp.h` header no longer defines `FILE_FULL_EA_INFORMATION` when compiled under mingw. This is because the mingw tool chain already includes a definition of this type. (GitHub PR #371. Thanks @lemourin.)
|
||||
|
||||
|
||||
v1.10B2 (2021.1 Beta2)::
|
||||
|
||||
Changes since v1.9:
|
||||
|
||||
* [NEW] WinFsp now supports rebranding of the build products. This is primarily useful to commercial licensees. See https://github.com/billziss-gh/winfsp/wiki/WinFsp-Rebranding for more information.
|
||||
|
||||
* [NEW] WinFsp-FUSE has a new method for determining file access in the case where the user's primary SID (Windows Security Identifier) and group SID are the same (for example, this can happen when someone uses a Microsoft account as their primary login).
|
||||
** Previously when the user SID and group SID were the same WinFsp-FUSE looked at the UNIX permissions for the owner and the UNIX permissions for the group and used the MOST restrictive permissions, which often resulted in inadvertent "access denied" errors. (For example, if the owner permission was `rw-` and the group permission was `---` the result was `---` and therefore access denied).
|
||||
** Going forward this behavior will change. The user and group mode permissions will not be considered together even in the case where the user SID and group SID are the same. This will resolve the access denied errors.
|
||||
** However to preserve backward compatibility (there might be some file systems that rely on the old behavior) we will do so in stages. For release v1.10 (2021.1) there is a new registry setting under `HKLM\SOFTWARE\WinFsp` (or `HKLM\SOFTWARE\WOW6432Node\WinFsp` on a 64-bit system) called `DistinctPermsForSameOwnerGroup`, which if set to 1 will direct WinFsp-FUSE to use the new behavior. The default value is 0 which directs WinFsp-FUSE to use the old behavior. This default will change in a future release.
|
||||
|
||||
* [NEW] A new registry setting under `HKLM\SOFTWARE\WinFsp` (or `HKLM\SOFTWARE\WOW6432Node\WinFsp` on a 64-bit system) called `MountDoNotUseLauncher` has been introduced, which if set to 1 will disable the use of the Launcher during mounting. The default value is 0 which allows the use of the Launcher during mounting in those rare cases when necessary. (In general the Launcher is not necessary for mounting. However when running a file system in the Windows Service context (session 0) under an account that is not LocalSystem (e.g. `NT AUTHORITY\NETWORK SERVICE`), the Launcher is used to create global drives.)
|
||||
|
||||
* [FIX] File share access when overwriting a file (e.g. when using `TRUNCATE_EXISTING`) is now done in a manner compatible with NTFS (previously there were cases when overwriting a file where behavior diverged from the NTFS one). (See GitHub issue #364.)
|
||||
|
||||
* [FIX] Fixed a problem in the file system shutdown protocol which resolves an occasional access violation in the user mode file system process. Previously it was possible for a file system to crash when stopping itself using `FspFileSystemStopDispatcher`; this problem has been rectified. (See GitHub issue #369.)
|
||||
|
||||
* [FIX] Fixed a problem in the FUSE layer where in some rare circumstances the `release` operation could be called with an uninitialized `path` argument. (See GitHub issue billziss-gh/cgofuse#58 and commit f51bdef53427d1bba688fb6c768792fdc22ffc7b).
|
||||
|
||||
* [FIX] The `winfsp.h` header no longer defines `FILE_FULL_EA_INFORMATION` when compiled under mingw. This is because the mingw tool chain already includes a definition of this type. (GitHub PR #371. Thanks @lemourin.)
|
||||
|
||||
|
||||
v1.10B1 (2021.1 Beta1)::
|
||||
|
||||
Changes since v1.9:
|
||||
|
@ -117,6 +117,7 @@ WinFsp is designed to run on Windows 7 and above. It has been tested on the foll
|
||||
* Windows Server 2012
|
||||
* Windows 10 Pro
|
||||
* Windows Server 2016
|
||||
* Windows 11 Pro
|
||||
|
||||
## How to Help
|
||||
|
||||
|
@ -399,6 +399,29 @@
|
||||
<File Name="memfs-main.c" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="SMPDIR.memfs_fuse" Name="memfs-fuse">
|
||||
<Component Id="C.memfs_fuse.cpp">
|
||||
<File Name="memfs-fuse.cpp" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.memfs_fuse.compat.h">
|
||||
<File Id="F.memfs_fuse.compat.h" Name="compat.h" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.memfs_fuse.sln">
|
||||
<File Name="memfs-fuse.sln" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.memfs_fuse.vcxproj">
|
||||
<File Name="memfs-fuse.vcxproj" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.memfs_fuse.vcxproj.filters">
|
||||
<File Name="memfs-fuse.vcxproj.filters" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.memfs_fuse.Makefile">
|
||||
<File Id="F.memfs_fuse.Makefile" Name="Makefile" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.memfs_fuse.README.md">
|
||||
<File Id="F.memfsx_fuse.README.md" Name="README.md" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="SMPDIR.memfs_fuse3" Name="memfs-fuse3">
|
||||
<Component Id="C.memfs_fuse3.cpp">
|
||||
<File Name="memfs-fuse3.cpp" KeyPath="yes" />
|
||||
@ -662,6 +685,13 @@
|
||||
<ComponentRef Id="C.memfs.h" />
|
||||
<ComponentRef Id="C.memfs.cpp" />
|
||||
<ComponentRef Id="C.memfs_main.c" />
|
||||
<ComponentRef Id="C.memfs_fuse.cpp" />
|
||||
<ComponentRef Id="C.memfs_fuse.compat.h" />
|
||||
<ComponentRef Id="C.memfs_fuse.sln" />
|
||||
<ComponentRef Id="C.memfs_fuse.vcxproj" />
|
||||
<ComponentRef Id="C.memfs_fuse.vcxproj.filters" />
|
||||
<ComponentRef Id="C.memfs_fuse.Makefile" />
|
||||
<ComponentRef Id="C.memfs_fuse.README.md" />
|
||||
<ComponentRef Id="C.memfs_fuse3.cpp" />
|
||||
<ComponentRef Id="C.memfs_fuse3.compat.h" />
|
||||
<ComponentRef Id="C.memfs_fuse3.sln" />
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
<MyCanonicalVersion>1.10</MyCanonicalVersion>
|
||||
|
||||
<MyProductVersion>2021.1 Beta3</MyProductVersion>
|
||||
<MyProductVersion>2022 Beta4</MyProductVersion>
|
||||
<MyProductStage>Beta</MyProductStage>
|
||||
|
||||
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
||||
|
@ -83,8 +83,6 @@ STATUS\_SUCCESS or error code.
|
||||
|
||||
**Discussion**
|
||||
|
||||
(NOTE: use of this function is not recommended; use Delete instead.)
|
||||
|
||||
This function tests whether a file or directory can be safely deleted. This function does
|
||||
not need to perform access checks, but may performs tasks such as check for empty
|
||||
directories, etc.
|
||||
@ -102,7 +100,6 @@ most file systems need only implement the CanDelete operation.
|
||||
|
||||
- Cleanup
|
||||
- SetDelete
|
||||
- Delete
|
||||
|
||||
|
||||
</blockquote>
|
||||
@ -132,9 +129,6 @@ VOID ( *Cleanup)(
|
||||
|
||||
**Discussion**
|
||||
|
||||
(NOTE: use of this function with the FspCleanupDelete flag is not recommended;
|
||||
use Delete instead.)
|
||||
|
||||
When CreateFile is used to open or create a file the kernel creates a kernel mode file
|
||||
object (type FILE\_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
||||
be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
||||
@ -154,7 +148,11 @@ a file or directory in Windows is a three-stage process where the file is first
|
||||
tested to see if the delete can proceed and if the answer is positive the file is then
|
||||
deleted during Cleanup.
|
||||
|
||||
When this flag is set, this is the last outstanding cleanup for this particular file node.
|
||||
If the file system supports POSIX unlink (FSP\_FSCTL\_VOLUME\_PARAMS ::
|
||||
SupportsPosixUnlinkRename), then a Cleanup / FspCleanupDelete operation may arrive while
|
||||
there are other open file handles for this particular file node. If the file system does not
|
||||
support POSIX unlink, then a Cleanup / FspCleanupDelete operation will always be the last
|
||||
outstanding cleanup for this particular file node.
|
||||
|
||||
|
||||
- FspCleanupSetAllocationSize -
|
||||
@ -185,7 +183,6 @@ the file was modified/deleted.
|
||||
- Close
|
||||
- CanDelete
|
||||
- SetDelete
|
||||
- Delete
|
||||
|
||||
|
||||
</blockquote>
|
||||
@ -375,90 +372,6 @@ may contain extended attributes or a reparse point.
|
||||
NOTE: If both Create and CreateEx are defined, CreateEx takes precedence.
|
||||
|
||||
|
||||
</blockquote>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<b>Delete</b> - Set the file delete flag or delete a file.
|
||||
</summary>
|
||||
<blockquote>
|
||||
<br/>
|
||||
|
||||
```c
|
||||
NTSTATUS ( *Delete)(
|
||||
FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext,
|
||||
PWSTR FileName,
|
||||
ULONG Flags);
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
- _FileSystem_ \- The file system on which this request is posted.
|
||||
- _FileContext_ \- The file context of the file or directory to set the delete flag for.
|
||||
- _FileName_ \- The name of the file or directory to set the delete flag for.
|
||||
- _Flags_ \- File disposition flags
|
||||
|
||||
**Return Value**
|
||||
|
||||
STATUS\_SUCCESS or error code.
|
||||
|
||||
**Discussion**
|
||||
|
||||
This function replaces CanDelete, SetDelete and uses of Cleanup with the FspCleanupDelete flag
|
||||
and is recommended for use in all new code.
|
||||
|
||||
Due to the complexity of file deletion in the Windows file system this function is used
|
||||
in many scenarios. Its usage is controlled by the Flags parameter:
|
||||
|
||||
- FILE\_DISPOSITION\_DO\_NOT\_DELETE: Unmark the file for deletion.
|
||||
Do **NOT** delete the file either now or at Cleanup time.
|
||||
|
||||
|
||||
- FILE\_DISPOSITION\_DELETE: Mark the file for deletion,
|
||||
but do **NOT** delete the file. The file will be deleted at Cleanup time
|
||||
(via a call to Delete with Flags = -1).
|
||||
This function does not need to perform access checks, but may
|
||||
performs tasks such as check for empty directories, etc.
|
||||
|
||||
|
||||
- FILE\_DISPOSITION\_DELETE | FILE\_DISPOSITION\_POSIX\_SEMANTICS: Delete the file**NOW** using POSIX semantics. Open user mode handles to the file remain valid.
|
||||
This case will be received only when FSP\_FSCTL\_VOLUME\_PARAMS :: SupportsPosixUnlinkRename is set.
|
||||
|
||||
|
||||
- -1: Delete the file **NOW** using regular Windows semantics.
|
||||
Called during Cleanup with no open user mode handles remaining.
|
||||
If a file system implements Delete, Cleanup should **NOT** be used for deletion anymore.
|
||||
|
||||
|
||||
|
||||
This function gets called in all file deletion scenarios:
|
||||
|
||||
- When the DeleteFile or RemoveDirectory API's are used.
|
||||
|
||||
|
||||
- When the SetInformationByHandle API with FileDispositionInfo or FileDispositionInfoEx is used.
|
||||
|
||||
|
||||
- When a file is opened using FILE\_DELETE\_ON\_CLOSE.
|
||||
|
||||
|
||||
- Etc.
|
||||
|
||||
|
||||
|
||||
NOTE: Delete takes precedence over CanDelete, SetDelete and Cleanup with the FspCleanupDelete flag.
|
||||
This means that if Delete is defined, CanDelete and SetDelete will never be called and
|
||||
Cleanup will never be called with the FspCleanupDelete flag.
|
||||
|
||||
**See Also**
|
||||
|
||||
- Cleanup
|
||||
- CanDelete
|
||||
- SetDelete
|
||||
|
||||
|
||||
</blockquote>
|
||||
</details>
|
||||
|
||||
@ -1202,8 +1115,6 @@ STATUS\_SUCCESS or error code.
|
||||
|
||||
**Discussion**
|
||||
|
||||
(NOTE: use of this function is not recommended; use Delete instead.)
|
||||
|
||||
This function sets a flag to indicates whether the FSD file should delete a file
|
||||
when it is closed. This function does not need to perform access checks, but may
|
||||
performs tasks such as check for empty directories, etc.
|
||||
@ -1221,7 +1132,6 @@ most file systems need only implement the CanDelete operation.
|
||||
|
||||
- Cleanup
|
||||
- CanDelete
|
||||
- Delete
|
||||
|
||||
|
||||
</blockquote>
|
||||
|
@ -1,3 +1,5 @@
|
||||
*INVALID: The information presented in this document is invalid and should not be used.*
|
||||
|
||||
= WinFsp Delete Redesign
|
||||
|
||||
WinFsp has had its Delete functionality redesigned in release 2021.1 Beta3. This redesign unifies all Windows file deletion semantics under a single file system operation that also supports the new POSIX semantics introduced in Windows 10 Redstone 1. The new Delete design is recommended for new file systems, however existing file systems will continue to work without any changes.
|
||||
@ -32,7 +34,7 @@ With this knowledge we can now examine what happens in the `DeleteFileW` / `Remo
|
||||
- Open the file using `NtOpenFile`: The kernel creates an https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irp-mj-create[`IRP_MJ_CREATE`] IRP, places inside it a newly created https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_object[`FILE_OBJECT`] and forwards it to the FSD. If opening the file succeeds, the kernel will also create a `HANDLE` that is used to refer to this `FILE_OBJECT`; if opening the file fails, this `FILE_OBJECT` will be destroyed.
|
||||
- Set the "disposition" flag on the file handle using `NtSetInformationFile` with `FileDispositionInformation`: The kernel creates an https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irp-mj-set-information[`IRP_MJ_SET_INFORMATION`] IRP and passes the `FileDispositionInformation` information in it. The FSD performs some checks (e.g. if a directory is empty) and if they succeed it marks the file for deletion.
|
||||
- Close the file using `NtClose`: The kernel creates an https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irp-mj-cleanup[`IRP_MJ_CLEANUP`] IRP, which denotes that all ``HANDLE``s that refer to a `FILE_OBJECT` are closed. (It is possible to have multiple ``HANDLE``s to the same `FILE_OBJECT` by using an API such as https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle[`DuplicateHandle`].) The FSD checks and if this is the last `FILE_OBJECT` cleaned up for the file and if the file is marked for deletion, it deletes the file. Traditionally there was no way to return an error from `IRP_MJ_CLEANUP`.
|
||||
- Notice that while the file is closed from the perspective of user mode, it is not closed from the perspective of kernel mode. The kernel and the FSD maintain both a handle count and a reference count for the `FILE_OBJECT`. When the handle count goes to `0` then an `IRP_MJ_CLEANUP` IRP is issued (see above). When the reference count goes to `0` then a different https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/irp-mj-close[`IRP_MJ_CLOSE`] IRP is issued to the FSD. This signifies to the FSD that the `FILE_OBJECT` is going away and the file is fully closed (including from the kernel perspective). There is no way to return an error from `IRP_MJ_CLOSE`.
|
||||
- Notice that while the file is closed from the perspective of user mode, it is not closed from the perspective of kernel mode. The kernel and the FSD maintain both a handle count and a reference count for the `FILE_OBJECT`. When the handle count goes to `0` then an `IRP_MJ_CLEANUP` IRP is issued (see above). When the reference count goes to `0` then a different https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/irp-mj-close[`IRP_MJ_CLOSE`] IRP is issued to the FSD. This signifies to the FSD that the `FILE_OBJECT` is going away and the file is fully closed (including from the kernel perspective). There is no way to return an error from `IRP_MJ_CLOSE`.
|
||||
|
||||
The situation is similar in the `FILE_DELETE_ON_CLOSE` scenario, with the important difference that the FSD marks the file for deletion immediately upon receiving the `IRP_MJ_CREATE` IRP and that it never receives the `IRP_MJ_SET_INFORMATION` IRP. As before the actual deletion happens in `IRP_MJ_CLEANUP` and only when the last `HANDLE` to the file is closed.
|
||||
|
@ -197,7 +197,8 @@ enum
|
||||
/* user-mode flags */\
|
||||
UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */\
|
||||
UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */\
|
||||
UINT32 UmReservedFlags:6;\
|
||||
UINT32 UmNoReparsePointsDirCheck:1; /* user mode: no dir option check for reparse points */\
|
||||
UINT32 UmReservedFlags:5;\
|
||||
/* additional kernel-mode flags */\
|
||||
UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\
|
||||
UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\
|
||||
|
@ -47,19 +47,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The FILE_DISPOSITION_* definitions appear to be missing from the user mode headers.
|
||||
*/
|
||||
#if !defined(FILE_DISPOSITION_DELETE)
|
||||
#define FILE_DISPOSITION_DO_NOT_DELETE 0x00000000
|
||||
#define FILE_DISPOSITION_DELETE 0x00000001
|
||||
#define FILE_DISPOSITION_POSIX_SEMANTICS 0x00000002
|
||||
/* remaining flags are not needed for user mode file systems but included for completeness */
|
||||
#define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x00000004
|
||||
#define FILE_DISPOSITION_ON_CLOSE 0x00000008
|
||||
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x00000010
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The REPARSE_DATA_BUFFER definitions appear to be missing from the user mode headers.
|
||||
*/
|
||||
@ -361,9 +348,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
/**
|
||||
* Cleanup a file.
|
||||
*
|
||||
* (NOTE: use of this function with the FspCleanupDelete flag is not recommended;
|
||||
* use Delete instead.)
|
||||
*
|
||||
* When CreateFile is used to open or create a file the kernel creates a kernel mode file
|
||||
* object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
||||
* be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
||||
@ -383,7 +367,11 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
* tested to see if the delete can proceed and if the answer is positive the file is then
|
||||
* deleted during Cleanup.
|
||||
*
|
||||
* When this flag is set, this is the last outstanding cleanup for this particular file node.
|
||||
* If the file system supports POSIX unlink (FSP_FSCTL_VOLUME_PARAMS ::
|
||||
* SupportsPosixUnlinkRename), then a Cleanup / FspCleanupDelete operation may arrive while
|
||||
* there are other open file handles for this particular file node. If the file system does not
|
||||
* support POSIX unlink, then a Cleanup / FspCleanupDelete operation will always be the last
|
||||
* outstanding cleanup for this particular file node.
|
||||
* </li>
|
||||
* <li>FspCleanupSetAllocationSize -
|
||||
* The NTFS and FAT file systems reset a file's allocation size when they receive the last
|
||||
@ -418,7 +406,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
* Close
|
||||
* CanDelete
|
||||
* SetDelete
|
||||
* Delete
|
||||
*/
|
||||
VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, PWSTR FileName, ULONG Flags);
|
||||
@ -592,8 +579,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
/**
|
||||
* Determine whether a file or directory can be deleted.
|
||||
*
|
||||
* (NOTE: use of this function is not recommended; use Delete instead.)
|
||||
*
|
||||
* This function tests whether a file or directory can be safely deleted. This function does
|
||||
* not need to perform access checks, but may performs tasks such as check for empty
|
||||
* directories, etc.
|
||||
@ -618,7 +603,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
* @see
|
||||
* Cleanup
|
||||
* SetDelete
|
||||
* Delete
|
||||
*/
|
||||
NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, PWSTR FileName);
|
||||
@ -900,8 +884,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
/**
|
||||
* Set the file delete flag.
|
||||
*
|
||||
* (NOTE: use of this function is not recommended; use Delete instead.)
|
||||
*
|
||||
* This function sets a flag to indicates whether the FSD file should delete a file
|
||||
* when it is closed. This function does not need to perform access checks, but may
|
||||
* performs tasks such as check for empty directories, etc.
|
||||
@ -930,7 +912,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
* @see
|
||||
* Cleanup
|
||||
* CanDelete
|
||||
* Delete
|
||||
*/
|
||||
NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile);
|
||||
@ -1063,59 +1044,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
||||
PVOID FileContext,
|
||||
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
|
||||
FSP_FSCTL_FILE_INFO *FileInfo);
|
||||
/**
|
||||
* Set the file delete flag or delete a file.
|
||||
*
|
||||
* This function replaces CanDelete, SetDelete and uses of Cleanup with the FspCleanupDelete flag
|
||||
* and is recommended for use in all new code.
|
||||
*
|
||||
* Due to the complexity of file deletion in the Windows file system this function is used
|
||||
* in many scenarios. Its usage is controlled by the Flags parameter:
|
||||
* <ul>
|
||||
* <li>FILE_DISPOSITION_DO_NOT_DELETE: Unmark the file for deletion.
|
||||
* Do <b>NOT</b> delete the file either now or at Cleanup time.</li>
|
||||
* <li>FILE_DISPOSITION_DELETE: Mark the file for deletion,
|
||||
* but do <b>NOT</b> delete the file. The file will be deleted at Cleanup time
|
||||
* (via a call to Delete with Flags = -1).
|
||||
* This function does not need to perform access checks, but may
|
||||
* performs tasks such as check for empty directories, etc.</li>
|
||||
* <li>FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS: Delete the file
|
||||
* <b>NOW</b> using POSIX semantics. Open user mode handles to the file remain valid.
|
||||
* This case will be received only when FSP_FSCTL_VOLUME_PARAMS :: SupportsPosixUnlinkRename is set.</li>
|
||||
* <li>-1: Delete the file <b>NOW</b> using regular Windows semantics.
|
||||
* Called during Cleanup with no open user mode handles remaining.
|
||||
* If a file system implements Delete, Cleanup should <b>NOT</b> be used for deletion anymore.</li>
|
||||
* </ul>
|
||||
*
|
||||
* This function gets called in all file deletion scenarios:
|
||||
* <ul>
|
||||
* <li>When the DeleteFile or RemoveDirectory API's are used.</li>
|
||||
* <li>When the SetInformationByHandle API with FileDispositionInfo or FileDispositionInfoEx is used.</li>
|
||||
* <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);
|
||||
|
||||
NTSTATUS (*Obsolete0)(VOID);
|
||||
|
||||
/*
|
||||
* This ensures that this interface will always contain 64 function pointers.
|
||||
@ -1143,7 +1073,13 @@ typedef struct _FSP_FILE_SYSTEM
|
||||
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy;
|
||||
SRWLOCK OpGuardLock;
|
||||
BOOLEAN UmFileContextIsUserContext2, UmFileContextIsFullContext;
|
||||
UINT16 UmNoReparsePointsDirCheck:1;
|
||||
UINT16 UmReservedFlags:15;
|
||||
} FSP_FILE_SYSTEM;
|
||||
FSP_FSCTL_STATIC_ASSERT(
|
||||
(4 == sizeof(PVOID) && 660 == sizeof(FSP_FILE_SYSTEM)) ||
|
||||
(8 == sizeof(PVOID) && 792 == sizeof(FSP_FILE_SYSTEM)),
|
||||
"sizeof(FSP_FILE_SYSTEM) must be exactly 660 in 32-bit and 792 in 64-bit.");
|
||||
typedef struct _FSP_FILE_SYSTEM_OPERATION_CONTEXT
|
||||
{
|
||||
FSP_FSCTL_TRANSACT_REQ *Request;
|
||||
|
@ -161,6 +161,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath,
|
||||
|
||||
FileSystem->UmFileContextIsUserContext2 = !!VolumeParams->UmFileContextIsUserContext2;
|
||||
FileSystem->UmFileContextIsFullContext = !!VolumeParams->UmFileContextIsFullContext;
|
||||
FileSystem->UmNoReparsePointsDirCheck = VolumeParams->UmNoReparsePointsDirCheck;
|
||||
|
||||
*PFileSystem = FileSystem;
|
||||
|
||||
|
@ -57,9 +57,7 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
|
||||
Request->Req.Cleanup.Delete) ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(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)))) ||
|
||||
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||
0 == Request->Req.FlushBuffers.UserContext &&
|
||||
@ -71,8 +69,7 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
|
||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||
64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
|
||||
{
|
||||
@ -101,9 +98,7 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
|
||||
Request->Req.Cleanup.Delete) ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(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)))) ||
|
||||
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||
0 == Request->Req.FlushBuffers.UserContext &&
|
||||
@ -115,8 +110,7 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
|
||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||
64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
|
||||
{
|
||||
@ -991,37 +985,16 @@ FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_API NTSTATUS FspFileSystemOpCleanup(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
|
||||
{
|
||||
ULONG CleanupFlags =
|
||||
if (0 != FileSystem->Interface->Cleanup)
|
||||
FileSystem->Interface->Cleanup(FileSystem,
|
||||
(PVOID)ValOfFileContext(Request->Req.Cleanup),
|
||||
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
|
||||
(0 != Request->Req.Cleanup.Delete ? FspCleanupDelete : 0) |
|
||||
(0 != Request->Req.Cleanup.SetAllocationSize ? FspCleanupSetAllocationSize : 0) |
|
||||
(0 != Request->Req.Cleanup.SetArchiveBit ? FspCleanupSetArchiveBit : 0) |
|
||||
(0 != Request->Req.Cleanup.SetLastAccessTime ? FspCleanupSetLastAccessTime : 0) |
|
||||
(0 != Request->Req.Cleanup.SetLastWriteTime ? FspCleanupSetLastWriteTime : 0) |
|
||||
(0 != Request->Req.Cleanup.SetChangeTime ? FspCleanupSetChangeTime : 0);
|
||||
|
||||
if (Request->Req.Cleanup.Delete && 0 != FileSystem->Interface->Delete)
|
||||
{
|
||||
NTSTATUS Result = FileSystem->Interface->Delete(FileSystem,
|
||||
(PVOID)ValOfFileContext(Request->Req.Cleanup),
|
||||
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
|
||||
(ULONG)-1);
|
||||
/*
|
||||
* If Delete returns STATUS_NOT_IMPLEMENTED it means that it is unable
|
||||
* to handle file deletion during the Cleanup phase. In this case we
|
||||
* will handle file deletion in Cleanup with the FspCleanupDelete flag.
|
||||
*
|
||||
* This is necessary for file systems like the .NET layer where Delete
|
||||
* may be implemented but unable to handle all file deletion cases.
|
||||
*/
|
||||
if (STATUS_NOT_IMPLEMENTED != Result)
|
||||
CleanupFlags &= ~FspCleanupDelete;
|
||||
}
|
||||
|
||||
if (0 != FileSystem->Interface->Cleanup)
|
||||
FileSystem->Interface->Cleanup(FileSystem,
|
||||
(PVOID)ValOfFileContext(Request->Req.Cleanup),
|
||||
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
|
||||
CleanupFlags);
|
||||
(0 != Request->Req.Cleanup.SetChangeTime ? FspCleanupSetChangeTime : 0));
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@ -1180,26 +1153,16 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
|
||||
break;
|
||||
}
|
||||
}
|
||||
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)
|
||||
if (0 != FileSystem->Interface->SetDelete)
|
||||
{
|
||||
Result = FileSystem->Interface->SetDelete(FileSystem,
|
||||
(PVOID)ValOfFileContext(Request->Req.SetInformation),
|
||||
(PWSTR)Request->Buffer,
|
||||
Request->Req.SetInformation.Info.Disposition.Delete);
|
||||
0 != (1/*DELETE*/ & Request->Req.SetInformation.Info.DispositionEx.Flags));
|
||||
}
|
||||
else if (0 != FileSystem->Interface->CanDelete)
|
||||
{
|
||||
if (Request->Req.SetInformation.Info.Disposition.Delete)
|
||||
if (0 != (1/*DELETE*/ & Request->Req.SetInformation.Info.DispositionEx.Flags))
|
||||
Result = FileSystem->Interface->CanDelete(FileSystem,
|
||||
(PVOID)ValOfFileContext(Request->Req.SetInformation),
|
||||
(PWSTR)Request->Buffer);
|
||||
|
@ -642,6 +642,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
opt_data.VolumeParams.RejectIrpPriorToTransact0 = TRUE;
|
||||
#endif
|
||||
opt_data.VolumeParams.UmFileContextIsUserContext2 = TRUE;
|
||||
opt_data.VolumeParams.UmNoReparsePointsDirCheck = TRUE;
|
||||
if (L'\0' == opt_data.VolumeParams.FileSystemName[0])
|
||||
memcpy(opt_data.VolumeParams.FileSystemName, L"FUSE", 5 * sizeof(WCHAR));
|
||||
|
||||
|
@ -21,6 +21,13 @@
|
||||
|
||||
#include <dll/fuse/library.h>
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetReparsePointByName(
|
||||
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
||||
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize);
|
||||
static NTSTATUS fsp_fuse_intf_SetEaEntry(
|
||||
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
||||
PFILE_FULL_EA_INFORMATION SingleEa);
|
||||
|
||||
static inline
|
||||
VOID fsp_fuse_op_enter_lock(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
|
||||
@ -35,9 +42,7 @@ VOID fsp_fuse_op_enter_lock(FSP_FILE_SYSTEM *FileSystem,
|
||||
Request->Req.Cleanup.Delete) ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(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)))) ||
|
||||
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||
0 == Request->Req.FlushBuffers.UserContext &&
|
||||
@ -52,8 +57,7 @@ VOID fsp_fuse_op_enter_lock(FSP_FILE_SYSTEM *FileSystem,
|
||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||
64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
|
||||
/* FSCTL_GET_REPARSE_POINT may access namespace */
|
||||
@ -84,9 +88,7 @@ VOID fsp_fuse_op_leave_unlock(FSP_FILE_SYSTEM *FileSystem,
|
||||
Request->Req.Cleanup.Delete) ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(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)))) ||
|
||||
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||
0 == Request->Req.FlushBuffers.UserContext &&
|
||||
@ -101,8 +103,7 @@ VOID fsp_fuse_op_leave_unlock(FSP_FILE_SYSTEM *FileSystem,
|
||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||
64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass)) ||
|
||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
|
||||
/* FSCTL_GET_REPARSE_POINT may access namespace */
|
||||
@ -304,6 +305,9 @@ static BOOLEAN fsp_fuse_intf_CheckSymlinkDirectory(FSP_FILE_SYSTEM *FileSystem,
|
||||
const char *PosixPath)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
|
||||
if (FSP_FUSE_HAS_SLASHDOT(f))
|
||||
{
|
||||
char *PosixDotPath = 0;
|
||||
size_t Length;
|
||||
struct fuse_stat_ex stbuf;
|
||||
@ -332,6 +336,81 @@ static BOOLEAN fsp_fuse_intf_CheckSymlinkDirectory(FSP_FILE_SYSTEM *FileSystem,
|
||||
|
||||
return Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
PWSTR WindowsPath = 0, P;
|
||||
char *PosixResolvedPath = 0;
|
||||
UINT32 ReparsePointIndex;
|
||||
UINT32 ResolveFileAttributes[2] = { FILE_ATTRIBUTE_REPARSE_POINT, -1 };
|
||||
IO_STATUS_BLOCK IoStatus;
|
||||
union
|
||||
{
|
||||
REPARSE_DATA_BUFFER V;
|
||||
UINT8 B[FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) +
|
||||
FSP_FSCTL_TRANSACT_PATH_SIZEMAX + sizeof(WCHAR)/* add space for term-0 */];
|
||||
} ReparseDataBuf;
|
||||
SIZE_T ReparseDataSize;
|
||||
struct fuse_stat_ex stbuf;
|
||||
int err;
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = FspPosixMapPosixToWindowsPath(PosixPath, &WindowsPath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
ReparsePointIndex = 0;
|
||||
for (P = WindowsPath; '\0' != *P; P++)
|
||||
if (L'\\' == *P)
|
||||
ReparsePointIndex = (UINT32)(P + 1 - WindowsPath);
|
||||
|
||||
ReparseDataSize = sizeof ReparseDataBuf - sizeof(WCHAR)/* leave space for term-0 */;
|
||||
Result = FspFileSystemResolveReparsePoints(FileSystem,
|
||||
fsp_fuse_intf_GetReparsePointByName, ResolveFileAttributes,
|
||||
WindowsPath, ReparsePointIndex, TRUE,
|
||||
&IoStatus, &ReparseDataBuf,
|
||||
&ReparseDataSize);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
if (IO_REPARSE_TAG_SYMLINK != ReparseDataBuf.V.ReparseTag)
|
||||
{
|
||||
Result = STATUS_UNSUCCESSFUL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (-1 != ResolveFileAttributes[1])
|
||||
{
|
||||
Result = (FILE_ATTRIBUTE_DIRECTORY & ResolveFileAttributes[1]) ?
|
||||
STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
P = (PWSTR)(ReparseDataBuf.V.SymbolicLinkReparseBuffer.PathBuffer +
|
||||
ReparseDataBuf.V.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR));
|
||||
P[ReparseDataBuf.V.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)] = L'\0';
|
||||
|
||||
Result = FspPosixMapWindowsToPosixPath(P, &PosixResolvedPath);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
memset(&stbuf, 0, sizeof stbuf);
|
||||
if (0 != f->ops.getattr)
|
||||
err = f->ops.getattr(PosixResolvedPath, (void *)&stbuf);
|
||||
else
|
||||
err = -ENOSYS_(f->env);
|
||||
|
||||
Result = 0 == err && 0040000 == (stbuf.st_mode & 0170000) ?
|
||||
STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
|
||||
|
||||
exit:
|
||||
if (0 != PosixResolvedPath)
|
||||
FspPosixDeletePath(PosixResolvedPath);
|
||||
if (0 != WindowsPath)
|
||||
FspPosixDeletePath(WindowsPath);
|
||||
|
||||
return NT_SUCCESS(Result);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t fsp_fuse_intf_MapFileAttributesToFlags(UINT32 FileAttributes)
|
||||
{
|
||||
@ -367,10 +446,11 @@ static inline UINT32 fsp_fuse_intf_MapFlagsToFileAttributes(uint32_t flags)
|
||||
|
||||
#define FUSE_FILE_INFO(IsDirectory, fi) ((IsDirectory) ? 0 : (fi))
|
||||
#define fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, fi, PUid, PGid, PMode, FileInfo)\
|
||||
fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, PUid, PGid, PMode, 0, FileInfo)
|
||||
fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, PUid, PGid, PMode, 0, TRUE, FileInfo)
|
||||
static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem,
|
||||
const char *PosixPath, struct fuse_file_info *fi, const void *stbufp,
|
||||
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode, PUINT32 PDev,
|
||||
BOOLEAN CheckSymlinkDirectory,
|
||||
FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
@ -429,7 +509,7 @@ static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem,
|
||||
{
|
||||
FileInfo->FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
FileInfo->ReparseTag = IO_REPARSE_TAG_SYMLINK;
|
||||
if (fsp_fuse_intf_CheckSymlinkDirectory(FileSystem, PosixPath))
|
||||
if (CheckSymlinkDirectory && fsp_fuse_intf_CheckSymlinkDirectory(FileSystem, PosixPath))
|
||||
FileInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
break;
|
||||
}
|
||||
@ -572,7 +652,7 @@ exit:
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
const char *PosixPath, struct fuse_file_info *fi,
|
||||
PVOID Buffer, PSIZE_T PSize)
|
||||
PVOID Buffer, PSIZE_T PSize, PUINT32 PResolveFileAttributes)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
UINT32 Uid, Gid, Mode, Dev;
|
||||
@ -582,14 +662,29 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
SIZE_T Size;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (0 != PResolveFileAttributes && FILE_ATTRIBUTE_REPARSE_POINT == PResolveFileAttributes[0])
|
||||
{
|
||||
Mode = 0120000;
|
||||
memset(&FileInfo, 0, sizeof FileInfo);
|
||||
FileInfo.FileAttributes = PResolveFileAttributes[0];
|
||||
FileInfo.ReparseTag = IO_REPARSE_TAG_SYMLINK;
|
||||
PResolveFileAttributes[0] = 0;
|
||||
goto skip_getattr;
|
||||
}
|
||||
|
||||
Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0,
|
||||
&Uid, &Gid, &Mode, &Dev, &FileInfo);
|
||||
&Uid, &Gid, &Mode, &Dev, FALSE, &FileInfo);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return Result;
|
||||
|
||||
if (0 == (FILE_ATTRIBUTE_REPARSE_POINT & FileInfo.FileAttributes))
|
||||
{
|
||||
if (0 != PResolveFileAttributes)
|
||||
PResolveFileAttributes[1] = FileInfo.FileAttributes;
|
||||
return STATUS_NOT_A_REPARSE_POINT;
|
||||
}
|
||||
|
||||
skip_getattr:
|
||||
if (0 == Buffer)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
@ -688,13 +783,6 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetReparsePointByName(
|
||||
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
||||
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize);
|
||||
static NTSTATUS fsp_fuse_intf_SetEaEntry(
|
||||
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
||||
PFILE_FULL_EA_INFORMATION SingleEa);
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
||||
{
|
||||
@ -1031,7 +1119,12 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem,
|
||||
break;
|
||||
}
|
||||
|
||||
if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
{
|
||||
fi.fh = -1;
|
||||
Result = STATUS_SUCCESS;
|
||||
}
|
||||
else if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
if (0 != f->ops.opendir)
|
||||
{
|
||||
@ -1044,11 +1137,6 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem,
|
||||
Result = STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
else if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
{
|
||||
fi.fh = -1;
|
||||
Result = STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
@ -1181,6 +1269,50 @@ static NTSTATUS fsp_fuse_intf_Overwrite(FSP_FILE_SYSTEM *FileSystem,
|
||||
&Uid, &Gid, &Mode, FileInfo);
|
||||
}
|
||||
|
||||
static VOID fsp_fuse_intf_Cleanup(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileDesc, PWSTR FileName, ULONG Flags)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
struct fsp_fuse_file_desc *filedesc = FileDesc;
|
||||
|
||||
/*
|
||||
* In Windows a DeleteFile/RemoveDirectory is the sequence of the following:
|
||||
* Create(FILE_OPEN)
|
||||
* SetInformation(Disposition)
|
||||
* Cleanup
|
||||
* Close
|
||||
*
|
||||
* The FSD maintains a count of how many handles are currently open for a file. When the
|
||||
* last handle is closed *and* the disposition flag is set the FSD sends us a Cleanup with
|
||||
* the Delete flag set.
|
||||
*
|
||||
* Notice that when we receive a Cleanup with Delete set there can be no open handles other
|
||||
* than ours. [Even if there is a concurrent Open of this file, the FSD will fail it with
|
||||
* STATUS_DELETE_PENDING.] This means that we do not need to worry about the hard_remove
|
||||
* FUSE option and can safely remove the file at this time.
|
||||
*
|
||||
*
|
||||
* NOTE:
|
||||
*
|
||||
* Since WinFsp 2022 Beta4 (v1.10B4) it is possible to handle handles open other than ours
|
||||
* because of the new POSIX unlink semantics. Although we still do not provide the hard_remove
|
||||
* option, file systems that would need the hard_remove option can instead use the
|
||||
* LegacyUnlinkRename option to opt out of the POSIX unlink semantics.
|
||||
*/
|
||||
|
||||
if (Flags & FspCleanupDelete)
|
||||
if (filedesc->IsDirectory && !filedesc->IsReparsePoint)
|
||||
{
|
||||
if (0 != f->ops.rmdir)
|
||||
f->ops.rmdir(filedesc->PosixPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != f->ops.unlink)
|
||||
f->ops.unlink(filedesc->PosixPath);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID fsp_fuse_intf_Close(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileDesc)
|
||||
{
|
||||
@ -1192,15 +1324,15 @@ static VOID fsp_fuse_intf_Close(FSP_FILE_SYSTEM *FileSystem,
|
||||
fi.flags = filedesc->OpenFlags;
|
||||
fi.fh = filedesc->FileHandle;
|
||||
|
||||
if (filedesc->IsDirectory)
|
||||
if (filedesc->IsReparsePoint)
|
||||
{
|
||||
/* reparse points are not opened, nothing to do! */
|
||||
}
|
||||
else if (filedesc->IsDirectory)
|
||||
{
|
||||
if (0 != f->ops.releasedir)
|
||||
f->ops.releasedir(filedesc->PosixPath, &fi);
|
||||
}
|
||||
else if (filedesc->IsReparsePoint)
|
||||
{
|
||||
/* reparse points are not opened, nothing to do! */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != f->ops.flush)
|
||||
@ -1332,7 +1464,9 @@ static NTSTATUS fsp_fuse_intf_Flush(FSP_FILE_SYSTEM *FileSystem,
|
||||
fi.fh = filedesc->FileHandle;
|
||||
|
||||
Result = STATUS_SUCCESS; /* just say success, if fs does not support fsync */
|
||||
if (filedesc->IsDirectory)
|
||||
if (filedesc->IsReparsePoint)
|
||||
Result = STATUS_ACCESS_DENIED;
|
||||
else if (filedesc->IsDirectory)
|
||||
{
|
||||
if (0 != f->ops.fsyncdir)
|
||||
{
|
||||
@ -1340,8 +1474,6 @@ static NTSTATUS fsp_fuse_intf_Flush(FSP_FILE_SYSTEM *FileSystem,
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
}
|
||||
}
|
||||
else if (filedesc->IsReparsePoint)
|
||||
Result = STATUS_ACCESS_DENIED;
|
||||
else
|
||||
{
|
||||
if (0 != f->ops.fsync)
|
||||
@ -1563,6 +1695,19 @@ static NTSTATUS fsp_fuse_intf_CanDelete(FSP_FILE_SYSTEM *FileSystem,
|
||||
struct fuse_dirhandle dh;
|
||||
int err;
|
||||
|
||||
if (0 != (f->conn_want & FSP_FUSE_CAP_DELETE_ACCESS) && 0 != f->ops.access)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
err = f->ops.access(filedesc->PosixPath, FSP_FUSE_DELETE_OK);
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
if (!NT_SUCCESS(Result) && STATUS_INVALID_DEVICE_REQUEST != Result)
|
||||
{
|
||||
if (STATUS_ACCESS_DENIED == Result)
|
||||
Result = STATUS_CANNOT_DELETE;
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
if (filedesc->IsDirectory && !filedesc->IsReparsePoint)
|
||||
{
|
||||
/* check that directory is empty! */
|
||||
@ -1593,72 +1738,6 @@ static NTSTATUS fsp_fuse_intf_CanDelete(FSP_FILE_SYSTEM *FileSystem,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_Delete(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileDesc, PWSTR FileName, ULONG Flags)
|
||||
{
|
||||
struct fuse *f = FileSystem->UserContext;
|
||||
struct fsp_fuse_file_desc *filedesc = FileDesc;
|
||||
int err;
|
||||
NTSTATUS Result;
|
||||
|
||||
switch (Flags)
|
||||
{
|
||||
case FILE_DISPOSITION_DO_NOT_DELETE:
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case FILE_DISPOSITION_DELETE:
|
||||
Result = STATUS_SUCCESS;
|
||||
if (0 != (f->conn_want & FSP_FUSE_CAP_DELETE_ACCESS) && 0 != f->ops.access)
|
||||
{
|
||||
err = f->ops.access(filedesc->PosixPath, FSP_FUSE_DELETE_OK);
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
if (STATUS_INVALID_DEVICE_REQUEST == Result)
|
||||
Result = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (NT_SUCCESS(Result))
|
||||
Result = fsp_fuse_intf_CanDelete(FileSystem, FileDesc, FileName);
|
||||
|
||||
/* when doing unlink/rmdir convert EPERM/EACCES to STATUS_CANNOT_DELETE */
|
||||
if (STATUS_ACCESS_DENIED == Result)
|
||||
Result = STATUS_CANNOT_DELETE;
|
||||
|
||||
return Result;
|
||||
|
||||
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
|
||||
case -1:
|
||||
if (filedesc->IsDirectory && !filedesc->IsReparsePoint)
|
||||
{
|
||||
if (0 != f->ops.rmdir)
|
||||
{
|
||||
err = f->ops.rmdir(filedesc->PosixPath);
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
}
|
||||
else
|
||||
Result = STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != f->ops.unlink)
|
||||
{
|
||||
err = f->ops.unlink(filedesc->PosixPath);
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
}
|
||||
else
|
||||
Result = STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
/* when doing unlink/rmdir convert EPERM/EACCES to STATUS_CANNOT_DELETE */
|
||||
if (STATUS_ACCESS_DENIED == Result)
|
||||
Result = STATUS_CANNOT_DELETE;
|
||||
|
||||
return Result;
|
||||
|
||||
default:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_Rename(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileDesc,
|
||||
PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists)
|
||||
@ -1854,7 +1933,7 @@ int fsp_fuse_intf_AddDirInfo(void *buf, const char *name,
|
||||
NTSTATUS Result0;
|
||||
|
||||
Result0 = fsp_fuse_intf_GetFileInfoFunnel(dh->FileSystem, name, 0, stbuf,
|
||||
&Uid, &Gid, &Mode, 0, &DirInfo->FileInfo);
|
||||
&Uid, &Gid, &Mode, 0, TRUE, &DirInfo->FileInfo);
|
||||
if (NT_SUCCESS(Result0))
|
||||
DirInfo->Padding[0] = 1; /* HACK: remember that the FileInfo is valid */
|
||||
}
|
||||
@ -1976,6 +2055,9 @@ static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
|
||||
int err;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (!filedesc->IsDirectory || filedesc->IsReparsePoint)
|
||||
return STATUS_ACCESS_DENIED;
|
||||
|
||||
if (FspFileSystemAcquireDirectoryBuffer(&filedesc->DirBuffer, 0 == Marker, &Result))
|
||||
{
|
||||
memset(&dh, 0, sizeof dh);
|
||||
@ -2032,6 +2114,9 @@ static NTSTATUS fsp_fuse_intf_GetDirInfoByName(FSP_FILE_SYSTEM *FileSystem,
|
||||
UINT32 Uid, Gid, Mode;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (!filedesc->IsDirectory || filedesc->IsReparsePoint)
|
||||
return STATUS_ACCESS_DENIED;
|
||||
|
||||
Result = FspPosixMapWindowsToPosixPath(FileName, &PosixName);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
@ -2098,7 +2183,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointByName(
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
Result = fsp_fuse_intf_GetReparsePointEx(FileSystem, PosixPath, 0, Buffer, PSize);
|
||||
Result = fsp_fuse_intf_GetReparsePointEx(FileSystem, PosixPath, 0, Buffer, PSize, Context);
|
||||
|
||||
exit:
|
||||
if (0 != PosixPath)
|
||||
@ -2120,7 +2205,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
|
||||
return fsp_fuse_intf_GetReparsePointEx(FileSystem, filedesc->PosixPath,
|
||||
FUSE_FILE_INFO(filedesc->IsDirectory, &fi),
|
||||
Buffer, PSize);
|
||||
Buffer, PSize, 0);
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
@ -2159,23 +2244,25 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
* Note that this will also result in a change of the inode number for the reparse point!
|
||||
*/
|
||||
|
||||
if (0 == f->ops.rename || 0 == f->ops.unlink)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
ReparseData = (PREPARSE_DATA_BUFFER)Buffer;
|
||||
|
||||
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag || (
|
||||
IO_REPARSE_TAG_NFS == ReparseData->ReparseTag &&
|
||||
NFS_SPECFILE_LNK == *(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer))
|
||||
{
|
||||
if (0 == f->ops.symlink)
|
||||
if (filedesc->IsDirectory && 0 == f->ops.rmdir)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
if (0 == f->ops.symlink || 0 == f->ops.rename || 0 == f->ops.unlink)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
IsSymlink = TRUE;
|
||||
}
|
||||
else if (IO_REPARSE_TAG_NFS == ReparseData->ReparseTag)
|
||||
{
|
||||
if (0 == f->ops.mknod)
|
||||
/* FUSE cannot make a directory into an NFS reparse point */
|
||||
if (filedesc->IsDirectory)
|
||||
return STATUS_ACCESS_DENIED;
|
||||
if (0 == f->ops.mknod || 0 == f->ops.rename || 0 == f->ops.unlink)
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
|
||||
IsSymlink = FALSE;
|
||||
@ -2183,10 +2270,6 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
else
|
||||
return STATUS_IO_REPARSE_TAG_MISMATCH;
|
||||
|
||||
/* FUSE cannot make a directory into a reparse point */
|
||||
if (filedesc->IsDirectory)
|
||||
return STATUS_ACCESS_DENIED;
|
||||
|
||||
memset(&fi, 0, sizeof fi);
|
||||
fi.flags = filedesc->OpenFlags;
|
||||
fi.fh = filedesc->FileHandle;
|
||||
@ -2297,17 +2380,33 @@ static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
|
||||
}
|
||||
}
|
||||
|
||||
if (filedesc->IsDirectory)
|
||||
{
|
||||
err = f->ops.rmdir(filedesc->PosixPath);
|
||||
if (0 == err)
|
||||
err = f->ops.rename(PosixHiddenPath, filedesc->PosixPath);
|
||||
}
|
||||
else
|
||||
err = f->ops.rename(PosixHiddenPath, filedesc->PosixPath);
|
||||
if (0 != err)
|
||||
{
|
||||
/* on failure unlink "hidden" symlink */
|
||||
f->ops.unlink(PosixHiddenPath);
|
||||
|
||||
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (filedesc->IsDirectory)
|
||||
{
|
||||
if (0 != f->ops.releasedir)
|
||||
f->ops.releasedir(filedesc->PosixPath, &fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (0 != f->ops.release)
|
||||
f->ops.release(filedesc->PosixPath, &fi);
|
||||
}
|
||||
filedesc->IsReparsePoint = TRUE;
|
||||
filedesc->FileHandle = -1;
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
@ -2433,14 +2532,18 @@ static NTSTATUS fsp_fuse_intf_SetEaEntry(
|
||||
int err;
|
||||
|
||||
if (0 != SingleEa->EaValueLength)
|
||||
err = f->ops.setxattr(PosixPath,
|
||||
SingleEa->EaName, SingleEa->EaName + SingleEa->EaNameLength + 1, SingleEa->EaValueLength, 0);
|
||||
else
|
||||
err = f->ops.removexattr(PosixPath,
|
||||
SingleEa->EaName);
|
||||
|
||||
{
|
||||
err = f->ops.setxattr(PosixPath, SingleEa->EaName,
|
||||
SingleEa->EaName + SingleEa->EaNameLength + 1, SingleEa->EaValueLength, 0);
|
||||
return fsp_fuse_ntstatus_from_errno(f->env, err);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = f->ops.removexattr(PosixPath, SingleEa->EaName);
|
||||
/* ignore errors */
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS fsp_fuse_intf_SetEa(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileDesc,
|
||||
@ -2478,7 +2581,7 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
|
||||
0,
|
||||
fsp_fuse_intf_Open,
|
||||
0,
|
||||
0,
|
||||
fsp_fuse_intf_Cleanup,
|
||||
fsp_fuse_intf_Close,
|
||||
fsp_fuse_intf_Read,
|
||||
fsp_fuse_intf_Write,
|
||||
@ -2486,7 +2589,7 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
|
||||
fsp_fuse_intf_GetFileInfo,
|
||||
fsp_fuse_intf_SetBasicInfo,
|
||||
fsp_fuse_intf_SetFileSize,
|
||||
0,
|
||||
fsp_fuse_intf_CanDelete,
|
||||
fsp_fuse_intf_Rename,
|
||||
fsp_fuse_intf_GetSecurity,
|
||||
fsp_fuse_intf_SetSecurity,
|
||||
@ -2503,7 +2606,6 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
|
||||
fsp_fuse_intf_Overwrite,
|
||||
fsp_fuse_intf_GetEa,
|
||||
fsp_fuse_intf_SetEa,
|
||||
fsp_fuse_intf_Delete,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -160,6 +160,29 @@ static NTSTATUS fsp_fuse_loop_start(struct fuse *f)
|
||||
/* this should always fail with ENOSYS or EINVAL */
|
||||
err = f->ops.readlink("/", buf, sizeof buf);
|
||||
f->has_symlinks = -ENOSYS_(f->env) != err;
|
||||
|
||||
if (f->has_symlinks)
|
||||
{
|
||||
/*
|
||||
* Determine if the file system supports "/." queries.
|
||||
*
|
||||
* Symlinks on Windows are differentiated as "file" symlinks or "directory" symlinks.
|
||||
* When we need to make the distinction we can follow one of two techniques:
|
||||
*
|
||||
* - Slashdot technique: We issue a getattr(path + "/.") and check the stat result.
|
||||
* In general this is not a getattr() query that FUSE file systems are expected
|
||||
* to handle. For this reason we issue a getattr("/.") below to determine
|
||||
* if the file system handles this kind of query against the root directory.
|
||||
*
|
||||
* - Resolve technique: If the file system cannot handle slashdot queries, we resolve
|
||||
* the path using readlink on each path component, then issue getattr on the resolved
|
||||
* path and check the stat result.
|
||||
*/
|
||||
struct fuse_stat_ex stbuf;
|
||||
memset(&stbuf, 0, sizeof stbuf);
|
||||
err = f->ops.getattr("/.", (void *)&stbuf);
|
||||
f->has_slashdot = 0 == err && 0040000 == (stbuf.st_mode & 0170000);
|
||||
}
|
||||
}
|
||||
if (0 != f->ops.listxattr && 0 != f->ops.getxattr &&
|
||||
0 != f->ops.setxattr && 0 != f->ops.removexattr)
|
||||
|
@ -34,6 +34,7 @@
|
||||
((struct fuse_context *)((PUINT8)(h) + sizeof(struct fsp_fuse_context_header)))
|
||||
|
||||
#define FSP_FUSE_HAS_SYMLINKS(f) ((f)->has_symlinks)
|
||||
#define FSP_FUSE_HAS_SLASHDOT(f) ((f)->has_slashdot)
|
||||
|
||||
#define ENOSYS_(env) ('C' == (env)->environment ? 88 : 40)
|
||||
|
||||
@ -61,7 +62,7 @@ struct fuse
|
||||
void *data;
|
||||
unsigned conn_want;
|
||||
BOOLEAN fsinit;
|
||||
BOOLEAN has_symlinks;
|
||||
BOOLEAN has_symlinks, has_slashdot;
|
||||
UINT32 DebugLog;
|
||||
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy;
|
||||
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||
|
@ -325,6 +325,13 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* We allow some file systems (notably FUSE) to open reparse points
|
||||
* regardless of the FILE_DIRECTORY_FILE / FILE_NON_DIRECTORY_FILE options.
|
||||
*/
|
||||
if (0 != (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && FileSystem->UmNoReparsePointsDirCheck)
|
||||
goto skip_reparse_dir_check;
|
||||
|
||||
if ((Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) &&
|
||||
0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
@ -337,6 +344,9 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
Result = STATUS_FILE_IS_A_DIRECTORY;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
skip_reparse_dir_check:
|
||||
;
|
||||
}
|
||||
|
||||
if (Request->Req.Create.UserMode)
|
||||
|
@ -57,14 +57,6 @@ namespace Fsp
|
||||
public const UInt32 CleanupSetLastWriteTime = 0x40;
|
||||
public const UInt32 CleanupSetChangeTime = 0x80;
|
||||
|
||||
/* Disposition */
|
||||
public const UInt32 FILE_DISPOSITION_DO_NOT_DELETE = 0x00000000;
|
||||
public const UInt32 FILE_DISPOSITION_DELETE = 0x00000001;
|
||||
public const UInt32 FILE_DISPOSITION_POSIX_SEMANTICS = 0x00000002;
|
||||
public const UInt32 FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK = 0x00000004;
|
||||
public const UInt32 FILE_DISPOSITION_ON_CLOSE = 0x00000008;
|
||||
public const UInt32 FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE = 0x00000010;
|
||||
|
||||
/* NTSTATUS */
|
||||
public const Int32 STATUS_SUCCESS = unchecked((Int32)0x00000000);
|
||||
public const Int32 STATUS_WAIT_1 = unchecked((Int32)0x00000001);
|
||||
|
@ -289,9 +289,6 @@ namespace Fsp
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// (NOTE: use of this function with the CleanupDelete flag is not recommended;
|
||||
/// use Delete instead.)
|
||||
/// </para><para>
|
||||
/// When CreateFile is used to open or create a file the kernel creates a kernel mode file
|
||||
/// object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
||||
/// be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
||||
@ -345,7 +342,6 @@ namespace Fsp
|
||||
/// </param>
|
||||
/// <seealso cref="CanDelete"/>
|
||||
/// <seealso cref="SetDelete"/>
|
||||
/// <seealso cref="Delete"/>
|
||||
/// <seealso cref="Close"/>
|
||||
public virtual void Cleanup(
|
||||
Object FileNode,
|
||||
@ -602,8 +598,6 @@ namespace Fsp
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// (NOTE: use of this function is not recommended; use Delete instead.)
|
||||
/// </para><para>
|
||||
/// This function tests whether a file or directory can be safely deleted. This function does
|
||||
/// not need to perform access checks, but may performs tasks such as check for empty
|
||||
/// directories, etc.
|
||||
@ -630,7 +624,6 @@ namespace Fsp
|
||||
/// <returns>STATUS_SUCCESS or error code.</returns>
|
||||
/// <seealso cref="Cleanup"/>
|
||||
/// <seealso cref="SetDelete"/>
|
||||
/// <seealso cref="Delete"/>
|
||||
public virtual Int32 CanDelete(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
@ -1043,8 +1036,6 @@ namespace Fsp
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// (NOTE: use of this function is not recommended; use Delete instead.)
|
||||
/// </para><para>
|
||||
/// This function sets a flag to indicates whether the FSD file should delete a file
|
||||
/// when it is closed. This function does not need to perform access checks, but may
|
||||
/// performs tasks such as check for empty directories, etc.
|
||||
@ -1076,7 +1067,6 @@ namespace Fsp
|
||||
/// <returns>STATUS_SUCCESS or error code.</returns>
|
||||
/// <seealso cref="Cleanup"/>
|
||||
/// <seealso cref="CanDelete"/>
|
||||
/// <seealso cref="Delete"/>
|
||||
public virtual Int32 SetDelete(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
@ -1198,85 +1188,6 @@ namespace Fsp
|
||||
{
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the file delete flag or deletes a file or directory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This function replaces CanDelete, SetDelete and uses of Cleanup with the CleanupDelete flag
|
||||
/// and is recommended for use in all new code.
|
||||
/// </para><para>
|
||||
/// 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:
|
||||
/// <list>
|
||||
/// <item>FILE_DISPOSITION_DO_NOT_DELETE: Unmark the file for deletion.
|
||||
/// Do <b>NOT</b> delete the file either now or at Cleanup time.</item>
|
||||
/// <item>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.</item>
|
||||
/// <item>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 SupportsPosixUnlinkRename is set.</item>
|
||||
/// <item>-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.</item>
|
||||
/// </list>
|
||||
/// </para><para>
|
||||
/// This function gets called in all file deletion scenarios:
|
||||
/// <list>
|
||||
/// <item>When the DeleteFile or RemoveDirectory API's are used.</item>
|
||||
/// <item>When the SetInformationByHandle API with FileDispositionInfo or FileDispositionInfoEx is used.</item>
|
||||
/// <item>When a file is opened using FILE_DELETE_ON_CLOSE.</item>
|
||||
/// <item>Etc.</item>
|
||||
/// </list>
|
||||
/// </para><para>
|
||||
/// NOTE: Delete takes precedence over CanDelete, SetDelete and Cleanup with the CleanupDelete flag.
|
||||
/// This means that if Delete is defined, CanDelete and SetDelete will never be called and
|
||||
/// Cleanup will never be called with the CleanupDelete flag.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="FileNode">
|
||||
/// The file node of the file or directory.
|
||||
/// </param>
|
||||
/// <param name="FileDesc">
|
||||
/// The file descriptor of the file or directory.
|
||||
/// </param>
|
||||
/// <param name="FileName">
|
||||
/// The name of the file or directory.
|
||||
/// </param>
|
||||
/// <param name="Flags">
|
||||
/// File disposition flags.
|
||||
/// </param>
|
||||
/// <returns>STATUS_SUCCESS or error code.</returns>
|
||||
/// <seealso cref="Cleanup"/>
|
||||
/// <seealso cref="CanDelete"/>
|
||||
/// <seealso cref="SetDelete"/>
|
||||
public virtual Int32 Delete(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
String FileName,
|
||||
UInt32 Flags)
|
||||
{
|
||||
switch (Flags)
|
||||
{
|
||||
case FILE_DISPOSITION_DO_NOT_DELETE:
|
||||
return SetDelete(FileNode, FileDesc, FileName, false);
|
||||
|
||||
case FILE_DISPOSITION_DELETE:
|
||||
return SetDelete(FileNode, FileDesc, FileName, true);
|
||||
|
||||
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
case ~(UInt32)0:
|
||||
return STATUS_NOT_IMPLEMENTED;
|
||||
|
||||
default:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
/* helpers */
|
||||
/// <summary>
|
||||
|
@ -1348,6 +1348,28 @@ namespace Fsp
|
||||
return ExceptionHandler(FileSystem, ex);
|
||||
}
|
||||
}
|
||||
private static Int32 SetDelete(
|
||||
IntPtr FileSystemPtr,
|
||||
ref FullContext FullContext,
|
||||
String FileName,
|
||||
Boolean DeleteFile)
|
||||
{
|
||||
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
|
||||
try
|
||||
{
|
||||
Object FileNode, FileDesc;
|
||||
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
|
||||
return FileSystem.SetDelete(
|
||||
FileNode,
|
||||
FileDesc,
|
||||
FileName,
|
||||
DeleteFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ExceptionHandler(FileSystem, ex);
|
||||
}
|
||||
}
|
||||
private static Int32 GetEa(
|
||||
IntPtr FileSystemPtr,
|
||||
ref FullContext FullContext,
|
||||
@ -1398,28 +1420,6 @@ namespace Fsp
|
||||
return ExceptionHandler(FileSystem, ex);
|
||||
}
|
||||
}
|
||||
private static Int32 Delete(
|
||||
IntPtr FileSystemPtr,
|
||||
ref FullContext FullContext,
|
||||
String FileName,
|
||||
UInt32 Flags)
|
||||
{
|
||||
FileSystemBase FileSystem = (FileSystemBase)Api.GetUserContext(FileSystemPtr);
|
||||
try
|
||||
{
|
||||
Object FileNode, FileDesc;
|
||||
Api.GetFullContext(ref FullContext, out FileNode, out FileDesc);
|
||||
return FileSystem.Delete(
|
||||
FileNode,
|
||||
FileDesc,
|
||||
FileName,
|
||||
Flags);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ExceptionHandler(FileSystem, ex);
|
||||
}
|
||||
}
|
||||
|
||||
static FileSystemHost()
|
||||
{
|
||||
@ -1448,9 +1448,9 @@ namespace Fsp
|
||||
_FileSystemInterface.GetStreamInfo = GetStreamInfo;
|
||||
_FileSystemInterface.GetDirInfoByName = GetDirInfoByName;
|
||||
_FileSystemInterface.Control = Control;
|
||||
_FileSystemInterface.SetDelete = SetDelete;
|
||||
_FileSystemInterface.GetEa = GetEa;
|
||||
_FileSystemInterface.SetEa = SetEa;
|
||||
_FileSystemInterface.Delete = Delete;
|
||||
|
||||
_FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size);
|
||||
/* Marshal.AllocHGlobal does not zero memory; we must do it ourselves! */
|
||||
|
@ -743,11 +743,7 @@ namespace Fsp.Interop
|
||||
UInt32 EaLength,
|
||||
out FileInfo FileInfo);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate Int32 Delete(
|
||||
IntPtr FileSystem,
|
||||
ref FullContext FullContext,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] String FileName,
|
||||
UInt32 Flags);
|
||||
internal delegate Int32 Obsolete0();
|
||||
}
|
||||
|
||||
internal static int Size = IntPtr.Size * 64;
|
||||
@ -783,8 +779,8 @@ namespace Fsp.Interop
|
||||
internal Proto.OverwriteEx OverwriteEx;
|
||||
internal Proto.GetEa GetEa;
|
||||
internal Proto.SetEa SetEa;
|
||||
internal Proto.Delete Delete;
|
||||
/* NTSTATUS (*Reserved[32])(); */
|
||||
internal Proto.Obsolete0 Obsolete0;
|
||||
/* NTSTATUS (*Reserved[33])(); */
|
||||
}
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
|
@ -89,7 +89,7 @@ static NTSTATUS FspFsvolCleanup(
|
||||
FspFileNodeAcquireExclusive(FileNode, Main);
|
||||
|
||||
FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags);
|
||||
Delete = (CleanupFlags & 1) && !FileNode->PosixDelete;
|
||||
Delete = CleanupFlags & 1;
|
||||
SetAllocationSize = !!(CleanupFlags & 2);
|
||||
FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
|
||||
|
||||
@ -170,17 +170,17 @@ NTSTATUS FspFsvolCleanupComplete(
|
||||
ASSERT(FileNode == FileDesc->FileNode);
|
||||
|
||||
/* send the appropriate notification; also invalidate dirinfo/etc. caches */
|
||||
if (FileNode->PosixDelete)
|
||||
{
|
||||
NotifyFilter = 0;
|
||||
NotifyAction = 0;
|
||||
}
|
||||
else if (Request->Req.Cleanup.Delete)
|
||||
if (Request->Req.Cleanup.Delete)
|
||||
{
|
||||
NotifyFilter = FileNode->IsDirectory ?
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
NotifyAction = FILE_ACTION_REMOVED;
|
||||
}
|
||||
else if (FileNode->PosixDelete)
|
||||
{
|
||||
NotifyFilter = 0;
|
||||
NotifyAction = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send notification for any metadata changes */
|
||||
@ -241,7 +241,7 @@ static VOID FspFsvolCleanupRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Co
|
||||
|
||||
FspFileNodeReleaseOwner(FileNode, Pgio, Request);
|
||||
|
||||
FspFileNodeCleanupComplete(FileNode, FileObject);
|
||||
FspFileNodeCleanupComplete(FileNode, FileObject, !!Request->Req.Cleanup.Delete);
|
||||
if (!FileNode->IsDirectory)
|
||||
FspFileNodeOplockCheck(FileNode, Irp);
|
||||
SetFlag(FileObject->Flags, FO_CLEANUP_COMPLETE);
|
||||
|
@ -1492,7 +1492,7 @@ typedef struct
|
||||
UINT64 UserContext2;
|
||||
UINT32 GrantedAccess;
|
||||
UINT32
|
||||
CaseSensitive:1, HasTraversePrivilege:1, DeleteOnClose:1,
|
||||
CaseSensitive:1, HasTraversePrivilege:1, DeleteOnClose:1, PosixDelete:1,
|
||||
DidSetMetadata:1,
|
||||
DidSetFileAttributes:1, DidSetReparsePoint:1, DidSetSecurity:1,
|
||||
DidSetCreationTime:1, DidSetLastAccessTime:1, DidSetLastWriteTime:1, DidSetChangeTime:1,
|
||||
@ -1561,8 +1561,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
|
||||
FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason);
|
||||
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
|
||||
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, BOOLEAN Delete);
|
||||
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
||||
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
||||
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
|
||||
|
107
src/sys/file.c
107
src/sys/file.c
@ -42,8 +42,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
|
||||
FSP_FILE_NODE **POpenedFileNode, PULONG PSharingViolationReason);
|
||||
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
|
||||
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, BOOLEAN Delete);
|
||||
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
||||
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
||||
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
|
||||
@ -143,7 +142,6 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
||||
#pragma alloc_text(PAGE, FspFileNodeCleanup)
|
||||
#pragma alloc_text(PAGE, FspFileNodeCleanupFlush)
|
||||
#pragma alloc_text(PAGE, FspFileNodeCleanupComplete)
|
||||
#pragma alloc_text(PAGE, FspFileNodePosixDelete)
|
||||
#pragma alloc_text(PAGE, FspFileNodeClose)
|
||||
#pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache)
|
||||
#pragma alloc_text(PAGE, FspFileNodeOverwriteStreams)
|
||||
@ -792,7 +790,7 @@ VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG
|
||||
|
||||
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
||||
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
||||
BOOLEAN DeletePending, SetAllocationSize, SingleHandle;
|
||||
BOOLEAN DeletePending, Delete, SetAllocationSize, SingleHandle;
|
||||
|
||||
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
||||
|
||||
@ -807,7 +805,19 @@ VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG
|
||||
|
||||
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
||||
|
||||
*PCleanupFlags = SingleHandle ? DeletePending | (SetAllocationSize << 1) : 0;
|
||||
Delete = FALSE;
|
||||
if (!FileNode->PosixDelete)
|
||||
{
|
||||
if (FileDesc->PosixDelete)
|
||||
{
|
||||
FileNode->PosixDelete = TRUE;
|
||||
Delete = TRUE;
|
||||
}
|
||||
else if (SingleHandle)
|
||||
Delete = DeletePending;
|
||||
}
|
||||
|
||||
*PCleanupFlags = SingleHandle ? Delete | (SetAllocationSize << 1) : Delete;
|
||||
}
|
||||
|
||||
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject)
|
||||
@ -863,7 +873,7 @@ 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, BOOLEAN Delete)
|
||||
{
|
||||
/*
|
||||
* Complete the cleanup of a FileNode. Remove its share access and
|
||||
@ -904,22 +914,13 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
|
||||
|
||||
IoRemoveShareAccess(FileObject, &FileNode->ShareAccess);
|
||||
|
||||
ASSERT(0 < FileNode->HandleCount);
|
||||
if (0 == --FileNode->HandleCount)
|
||||
{
|
||||
SingleHandle = TRUE;
|
||||
|
||||
DeletePending = 0 != FileNode->DeletePending;
|
||||
MemoryBarrier();
|
||||
|
||||
if (DeletePending && !FileNode->PosixDelete)
|
||||
if (Delete)
|
||||
{
|
||||
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
|
||||
&DeletedFromContextTable);
|
||||
ASSERT(DeletedFromContextTable);
|
||||
|
||||
FileNode->OpenCount = 0;
|
||||
FileNode->Header.FileSize.QuadPart = 0;
|
||||
|
||||
/*
|
||||
* We now have to deal with the scenario where there are cleaned up,
|
||||
@ -937,7 +938,6 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
|
||||
break;
|
||||
ASSERT(FileNode != DescendantFileNode);
|
||||
ASSERT(0 != DescendantFileNode->OpenCount);
|
||||
ASSERT(0 == DescendantFileNode->HandleCount);
|
||||
);
|
||||
|
||||
for (
|
||||
@ -960,6 +960,17 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(0 < FileNode->HandleCount);
|
||||
if (0 == --FileNode->HandleCount)
|
||||
{
|
||||
SingleHandle = TRUE;
|
||||
|
||||
DeletePending = 0 != FileNode->DeletePending;
|
||||
MemoryBarrier();
|
||||
|
||||
if (DeletePending)
|
||||
FileNode->Header.FileSize.QuadPart = 0;
|
||||
|
||||
if (DeletePending || FileNode->TruncateOnClose)
|
||||
{
|
||||
UINT64 AllocationUnit =
|
||||
@ -1016,66 +1027,6 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
|
||||
FspFileNodeDereference(FileNode);
|
||||
}
|
||||
|
||||
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject)
|
||||
{
|
||||
/*
|
||||
* Perform a POSIX delete of a FileNode. This removes the FileNode from the Context table.
|
||||
*
|
||||
* The FileNode must be acquired exclusive (Main or Full) when calling this function.
|
||||
*/
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
BOOLEAN DeletedFromContextTable = FALSE;
|
||||
|
||||
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
||||
|
||||
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
|
||||
&DeletedFromContextTable);
|
||||
ASSERT(DeletedFromContextTable);
|
||||
|
||||
FileNode->OpenCount = 0;
|
||||
|
||||
if (FsvolDeviceExtension->VolumeParams.NamedStreams &&
|
||||
0 == FileNode->MainFileNode)
|
||||
{
|
||||
BOOLEAN StreamDeletedFromContextTable;
|
||||
USHORT FileNameLength = FileNode->FileName.Length;
|
||||
|
||||
GATHER_DESCENDANTS(&FileNode->FileName, FALSE,
|
||||
if (DescendantFileNode->FileName.Length > FileNameLength &&
|
||||
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)])
|
||||
break;
|
||||
ASSERT(FileNode != DescendantFileNode);
|
||||
ASSERT(0 != DescendantFileNode->OpenCount);
|
||||
);
|
||||
|
||||
for (
|
||||
DescendantFileNodeIndex = 0;
|
||||
DescendantFileNodeCount > DescendantFileNodeIndex;
|
||||
DescendantFileNodeIndex++)
|
||||
{
|
||||
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
||||
|
||||
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &DescendantFileNode->FileName,
|
||||
&StreamDeletedFromContextTable);
|
||||
if (StreamDeletedFromContextTable)
|
||||
{
|
||||
DescendantFileNode->OpenCount = 0;
|
||||
FspFileNodeDereference(DescendantFileNode);
|
||||
}
|
||||
}
|
||||
|
||||
SCATTER_DESCENDANTS(FALSE);
|
||||
}
|
||||
|
||||
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
||||
|
||||
FspFileNodeDereference(FileNode);
|
||||
}
|
||||
|
||||
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
||||
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
||||
BOOLEAN HandleCleanup) /* TRUE to decrement handle count */
|
||||
@ -1391,7 +1342,7 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
|
||||
* "rename" resource exclusively, which disallows new Opens.
|
||||
*/
|
||||
|
||||
if (!PosixRename && !CheckingOldName)
|
||||
if (!CheckingOldName)
|
||||
{
|
||||
/* replaced file cannot be a directory or mapped as an image */
|
||||
for (
|
||||
|
@ -1460,7 +1460,7 @@ static NTSTATUS FspFsvolSetDispositionInformation(
|
||||
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
|
||||
// old-school delete does image section check
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1470,12 +1470,9 @@ static NTSTATUS FspFsvolSetDispositionInformation(
|
||||
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.
|
||||
*/
|
||||
/* WinFsp does not support the FILE_DISPOSITION_ON_CLOSE flag */
|
||||
if (FlagOn(DispositionFlags, FILE_DISPOSITION_ON_CLOSE))
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (FileNode->IsRootDirectory)
|
||||
@ -1485,6 +1482,12 @@ static NTSTATUS FspFsvolSetDispositionInformation(
|
||||
retry:
|
||||
FspFileNodeAcquireExclusive(FileNode, Full);
|
||||
|
||||
if (FileNode->PosixDelete)
|
||||
{
|
||||
Result = STATUS_FILE_DELETED;
|
||||
goto unlock_exit;
|
||||
}
|
||||
|
||||
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
|
||||
{
|
||||
/*
|
||||
@ -1513,29 +1516,46 @@ retry:
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto unlock_exit;
|
||||
|
||||
if (FlagOn(DispositionFlags, FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK))
|
||||
{
|
||||
/* make sure no process is mapping the file as an image */
|
||||
/*
|
||||
* Make sure no process is mapping the file as an image.
|
||||
*
|
||||
* NOTE:
|
||||
* Turns out that NTFS always does this test, even when
|
||||
* FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK has been specified.
|
||||
* If MmFlushImageSection fails (e.g. an actively running EXE) then
|
||||
* it only allows the deletion to go through when a secondary hard
|
||||
* link is being deleted.
|
||||
*
|
||||
* Since WinFsp does not support hard links, we will go ahead and
|
||||
* ignore the FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK flag and
|
||||
* always respect the result of MmFlushImageSection.
|
||||
*/
|
||||
Success = MmFlushImageSection(FileObject->SectionObjectPointer, MmFlushForDelete);
|
||||
if (!Success)
|
||||
{
|
||||
Result = STATUS_CANNOT_DELETE;
|
||||
goto unlock_exit;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The documentation states:
|
||||
* A return value of STATUS_CANNOT_DELETE indicates that either the file is read-only,
|
||||
* or there's an existing mapped view to the file. Specifying FILE_DISPOSITION_IGNORE_-
|
||||
* READONLY_ATTRIBUTE avoids this return value due to the file being read-only, provided
|
||||
* the caller has FILE_WRITE_ATTRIBUTES access to the file (the access that would be
|
||||
* required to clear the read-only attribute).
|
||||
*
|
||||
* This appears to be incorrect with NTFS on Win10 and Win11. See:
|
||||
* https://github.com/MicrosoftDocs/windows-driver-docs-ddi/issues/1216
|
||||
*/
|
||||
#if 0
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
|
||||
@ -1577,42 +1597,22 @@ static NTSTATUS FspFsvolSetDispositionInformationSuccess(
|
||||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
||||
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
||||
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
||||
UINT32 DispositionFlags = Request->Req.SetInformation.Info.DispositionEx.Flags;
|
||||
BOOLEAN DeleteFile = BooleanFlagOn(DispositionFlags, FILE_DISPOSITION_DELETE);
|
||||
BOOLEAN Delete = BooleanFlagOn(DispositionFlags, FILE_DISPOSITION_DELETE);
|
||||
|
||||
FileNode->DeletePending = DeleteFile;
|
||||
FileObject->DeletePending = DeleteFile;
|
||||
FileNode->DeletePending = Delete;
|
||||
FileObject->DeletePending = Delete;
|
||||
|
||||
if (FlagOn(DispositionFlags, FILE_DISPOSITION_POSIX_SEMANTICS))
|
||||
{
|
||||
ASSERT(DeleteFile);
|
||||
if (!Delete)
|
||||
FileDesc->PosixDelete = FALSE;
|
||||
else if (FlagOn(DispositionFlags, FILE_DISPOSITION_POSIX_SEMANTICS))
|
||||
FileDesc->PosixDelete = TRUE;
|
||||
|
||||
FileNode->PosixDelete = TRUE;
|
||||
|
||||
if (FileNode->IsDirectory)
|
||||
{
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||
FspFsvolDeviceExtension(IrpSp->DeviceObject);
|
||||
FspNotifyDeletePending(
|
||||
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
|
||||
}
|
||||
|
||||
/* send the appropriate notification; also invalidate dirinfo/etc. caches */
|
||||
ULONG NotifyFilter, NotifyAction;
|
||||
NotifyFilter = FileNode->IsDirectory ?
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
NotifyAction = FILE_ACTION_REMOVED;
|
||||
FspFileNodeNotifyChange(FileNode, NotifyFilter, NotifyAction, TRUE);
|
||||
|
||||
/* perform POSIX delete: remove file node from the context table */
|
||||
FspFileNodePosixDelete(FileNode, FileObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* fastfat does this, although it seems unnecessary */
|
||||
#if 1
|
||||
if (FileNode->IsDirectory && DeleteFile)
|
||||
if (FileNode->IsDirectory && Delete)
|
||||
{
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||
FspFsvolDeviceExtension(IrpSp->DeviceObject);
|
||||
@ -1620,7 +1620,6 @@ static NTSTATUS FspFsvolSetDispositionInformationSuccess(
|
||||
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
FspIopRequestContext(Request, RequestFileNode) = 0;
|
||||
FspFileNodeReleaseOwner(FileNode, Full, Request);
|
||||
@ -1692,6 +1691,12 @@ retry:
|
||||
FspFsvolDeviceFileRenameAcquireExclusive(FsvolDeviceObject);
|
||||
FspFileNodeAcquireExclusive(FileNode, Full);
|
||||
|
||||
if (FileNode->PosixDelete)
|
||||
{
|
||||
Result = STATUS_ACCESS_DENIED;
|
||||
goto unlock_exit;
|
||||
}
|
||||
|
||||
if (0 == Request)
|
||||
{
|
||||
if (0 != TargetFileNode)
|
||||
|
@ -43,7 +43,6 @@ set dfl_tests=^
|
||||
winfsp-tests-x64-external-share ^
|
||||
fsx-memfs-x64-disk ^
|
||||
fsx-memfs-x64-net ^
|
||||
fsx-memfs-x64-slowio ^
|
||||
standby-memfs-x64-disk ^
|
||||
standby-memfs-x64-net ^
|
||||
net-use-memfs-x64 ^
|
||||
@ -64,7 +63,6 @@ set dfl_tests=^
|
||||
winfsp-tests-x86-external-share ^
|
||||
fsx-memfs-x86-disk ^
|
||||
fsx-memfs-x86-net ^
|
||||
fsx-memfs-x86-slowio ^
|
||||
standby-memfs-x86-disk ^
|
||||
standby-memfs-x86-net ^
|
||||
net-use-memfs-x86 ^
|
||||
@ -75,13 +73,16 @@ set dfl_tests=^
|
||||
winfsp-tests-dotnet-external-share ^
|
||||
fsx-memfs-dotnet-disk ^
|
||||
fsx-memfs-dotnet-net ^
|
||||
fsx-memfs-dotnet-slowio ^
|
||||
winfstest-memfs-dotnet-disk ^
|
||||
winfstest-memfs-dotnet-net
|
||||
set opt_tests=^
|
||||
ifstest-memfs-x64-disk ^
|
||||
ifstest-memfs-x86-disk ^
|
||||
ifstest-memfs-dotnet-disk ^
|
||||
sample-memfs-fuse-x64 ^
|
||||
sample-fsx-memfs-fuse-x64 ^
|
||||
sample-memfs-fuse-x86 ^
|
||||
sample-fsx-memfs-fuse-x86 ^
|
||||
sample-memfs-fuse3-x64 ^
|
||||
sample-fsx-memfs-fuse3-x64 ^
|
||||
sample-memfs-fuse3-x86 ^
|
||||
@ -99,6 +100,9 @@ set opt_tests=^
|
||||
sample-passthrough-fuse3-x86 ^
|
||||
sample-fsx-passthrough-fuse3-x86 ^
|
||||
sample-passthrough-dotnet ^
|
||||
slowio-fsx-memfs-x64 ^
|
||||
slowio-fsx-memfs-x86 ^
|
||||
slowio-fsx-memfs-dotnet ^
|
||||
compat-v1.2-memfs-x64 ^
|
||||
compat-v1.2-memfs-x86 ^
|
||||
compat-v1.1-passthrough-fuse-x64 ^
|
||||
@ -135,24 +139,32 @@ for %%f in (%tests%) do (
|
||||
)
|
||||
|
||||
pushd %cd%
|
||||
set "begtime=!time: =0!"
|
||||
call :%%f
|
||||
set ERRORLEVEL_save=!ERRORLEVEL!
|
||||
set "endtime=!time: =0!"
|
||||
popd
|
||||
|
||||
if !ERRORLEVEL! neq 0 (
|
||||
REM see https://stackoverflow.com/a/9935540
|
||||
set "endtimecalc=!endtime:%time:~8,1%=%%100)*100+1!" & set "begtimecalc=!begtime:%time:~8,1%=%%100)*100+1!"
|
||||
set /A "duration=((((10!endtimecalc:%time:~2,1%=%%100)*60+1!%%100)-((((10!begtimecalc:%time:~2,1%=%%100)*60+1!%%100), duration-=(duration>>31)*24*60*60*100"
|
||||
set /A "duration=10*duration"
|
||||
|
||||
if !ERRORLEVEL_save! neq 0 (
|
||||
set /a testfail=testfail+1
|
||||
|
||||
echo === Failed %%f
|
||||
echo === Failed %%f ^(!duration! ms^)
|
||||
|
||||
if defined APPVEYOR (
|
||||
appveyor UpdateTest "%%f" -FileName None -Framework None -Outcome Failed -Duration 0
|
||||
appveyor UpdateTest "%%f" -FileName None -Framework None -Outcome Failed -Duration !duration!
|
||||
)
|
||||
) else (
|
||||
set /a testpass=testpass+1
|
||||
|
||||
echo === Passed %%f
|
||||
echo === Passed %%f ^(!duration! ms^)
|
||||
|
||||
if defined APPVEYOR (
|
||||
appveyor UpdateTest "%%f" -FileName None -Framework None -Outcome Passed -Duration 0
|
||||
appveyor UpdateTest "%%f" -FileName None -Framework None -Outcome Passed -Duration !duration!
|
||||
)
|
||||
)
|
||||
echo:
|
||||
@ -517,17 +529,17 @@ if !ERRORLEVEL! neq 0 goto fail
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:fsx-memfs-x64-slowio
|
||||
:slowio-fsx-memfs-x64
|
||||
call :__run_fsx_memfs_slowio_test memfs64-slowio memfs-x64
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:fsx-memfs-x86-slowio
|
||||
:slowio-fsx-memfs-x86
|
||||
call :__run_fsx_memfs_slowio_test memfs32-slowio memfs-x86
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:fsx-memfs-dotnet-slowio
|
||||
:slowio-fsx-memfs-dotnet
|
||||
call :__run_fsx_memfs_slowio_test memfs.net-slowio memfs-dotnet-msil
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
@ -703,15 +715,35 @@ for /F "delims=" %%l in ('call "%ProjRoot%\tools\ifstest.bat" %* /v ^| findstr /
|
||||
if not X!IfsTestFound!==XYES set IfsTestExit=1
|
||||
exit /b !IfsTestExit!
|
||||
|
||||
:sample-memfs-fuse-x64
|
||||
call :__run_sample_fuse_test memfs-fuse x64 memfs-fuse-x64 winfsp-tests-x64 "+*"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-memfs-fuse-x86
|
||||
call :__run_sample_fuse_test memfs-fuse x86 memfs-fuse-x86 winfsp-tests-x86 "+*"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-fsx-memfs-fuse-x64
|
||||
call :__run_sample_fsx_fuse_test memfs-fuse x64 memfs-fuse-x64 fsx
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-fsx-memfs-fuse-x86
|
||||
call :__run_sample_fsx_fuse_test memfs-fuse x86 memfs-fuse-x86 fsx
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-memfs-fuse3-x64
|
||||
call :__run_sample_fuse_test memfs-fuse3 x64 memfs-fuse3-x64 winfsp-tests-x64 ^
|
||||
"-create_fileattr_test -create_readonlydir_test -setfileinfo_test"
|
||||
"+* -create_fileattr_test -create_readonlydir_test -setfileinfo_test -delete_access_test"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-memfs-fuse3-x86
|
||||
call :__run_sample_fuse_test memfs-fuse3 x86 memfs-fuse3-x86 winfsp-tests-x86 ^
|
||||
"-create_fileattr_test -create_readonlydir_test -setfileinfo_test"
|
||||
"+* -create_fileattr_test -create_readonlydir_test -setfileinfo_test -delete_access_test"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
@ -762,12 +794,12 @@ if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-passthrough-fuse-x64
|
||||
call :__run_sample_fuse_test passthrough-fuse x64 passthrough-fuse-x64 winfsp-tests-x64
|
||||
call :__run_sample_fuse_oldtest passthrough-fuse x64 passthrough-fuse-x64 winfsp-tests-x64
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-passthrough-fuse-x86
|
||||
call :__run_sample_fuse_test passthrough-fuse x86 passthrough-fuse-x86 winfsp-tests-x86
|
||||
call :__run_sample_fuse_oldtest passthrough-fuse x86 passthrough-fuse-x86 winfsp-tests-x86
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
@ -782,13 +814,13 @@ if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-passthrough-fuse3-x64
|
||||
call :__run_sample_fuse_test passthrough-fuse3 x64 passthrough-fuse3-x64 winfsp-tests-x64 ^
|
||||
call :__run_sample_fuse_oldtest passthrough-fuse3 x64 passthrough-fuse3-x64 winfsp-tests-x64 ^
|
||||
"-create_fileattr_test -create_readonlydir_test -setfileinfo_test"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:sample-passthrough-fuse3-x86
|
||||
call :__run_sample_fuse_test passthrough-fuse3 x86 passthrough-fuse3-x86 winfsp-tests-x86 ^
|
||||
call :__run_sample_fuse_oldtest passthrough-fuse3 x86 passthrough-fuse3-x86 winfsp-tests-x86 ^
|
||||
"-create_fileattr_test -create_readonlydir_test -setfileinfo_test"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
@ -869,6 +901,38 @@ set RunSampleTestExit=0
|
||||
call %ProjRoot%\tools\build-sample %Configuration% %2 %1 "%TMP%\%1"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
mkdir "%TMP%\%1\test"
|
||||
call "%ProjRoot%\tools\fsreg" %1 "%TMP%\%1\build\%Configuration%\%3.exe" ^
|
||||
"-orellinks,uid=11,gid=65792 --VolumePrefix=%%%%1 %%%%2" "D:P(A;;RPWPLC;;;WD)"
|
||||
echo net use L: "\\%1\%TMP::=$%\%1\test"
|
||||
net use L: "\\%1\%TMP::=$%\%1\test"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
echo net use ^| findstr L:
|
||||
net use | findstr L:
|
||||
pushd >nul
|
||||
cd L: >nul 2>nul || (echo Unable to find drive L: >&2 & goto fail)
|
||||
L:
|
||||
REM Exclude reparse_symlink* tests as they do not pass on AppVeyor VS2015. They do pass locally.
|
||||
if defined APPVEYOR (
|
||||
"%ProjRoot%\build\VStudio\build\%Configuration%\%4.exe" ^
|
||||
--fuse-external --resilient --case-insensitive-cmp --share-prefix="\%1\%TMP::=$%\%1\test" %~5 ^
|
||||
-reparse_symlink*
|
||||
) else (
|
||||
"%ProjRoot%\build\VStudio\build\%Configuration%\%4.exe" ^
|
||||
--fuse-external --resilient --case-insensitive-cmp --share-prefix="\%1\%TMP::=$%\%1\test" %~5
|
||||
)
|
||||
if !ERRORLEVEL! neq 0 set RunSampleTestExit=1
|
||||
popd
|
||||
echo net use L: /delete
|
||||
net use L: /delete
|
||||
call "%ProjRoot%\tools\fsreg" -u %1
|
||||
rmdir /s/q "%TMP%\%1"
|
||||
exit /b !RunSampleTestExit!
|
||||
|
||||
:__run_sample_fuse_oldtest
|
||||
set RunSampleTestExit=0
|
||||
call %ProjRoot%\tools\build-sample %Configuration% %2 %1 "%TMP%\%1"
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
mkdir "%TMP%\%1\test"
|
||||
call "%ProjRoot%\tools\fsreg" %1 "%TMP%\%1\build\%Configuration%\%3.exe" ^
|
||||
"-ouid=11,gid=65792 --VolumePrefix=%%%%1 %%%%2" "D:P(A;;RPWPLC;;;WD)"
|
||||
echo net use L: "\\%1\%TMP::=$%\%1\test"
|
||||
|
@ -564,6 +564,19 @@ namespace memfs
|
||||
AllocationUnit * AllocationUnit;
|
||||
SetFileSizeInternal(FileNode, AllocationSize, true);
|
||||
}
|
||||
|
||||
if (0 != (Flags & CleanupDelete) && !FileNodeMap.HasChild(FileNode))
|
||||
{
|
||||
List<String> StreamFileNames = new List<String>(FileNodeMap.GetStreamFileNames(FileNode));
|
||||
foreach (String StreamFileName in StreamFileNames)
|
||||
{
|
||||
FileNode StreamNode = FileNodeMap.Get(StreamFileName);
|
||||
if (null == StreamNode)
|
||||
continue; /* should not happen */
|
||||
FileNodeMap.Remove(StreamNode);
|
||||
}
|
||||
FileNodeMap.Remove(FileNode);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close(
|
||||
@ -908,6 +921,19 @@ namespace memfs
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
public override Int32 CanDelete(
|
||||
Object FileNode0,
|
||||
Object FileDesc,
|
||||
String FileName)
|
||||
{
|
||||
FileNode FileNode = (FileNode)FileNode0;
|
||||
|
||||
if (FileNodeMap.HasChild(FileNode))
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
public override Int32 Rename(
|
||||
Object FileNode0,
|
||||
Object FileDesc,
|
||||
@ -1305,45 +1331,6 @@ namespace memfs
|
||||
FileNode.FileInfo.EaSize = FileNode.FileInfo.EaSize + EaSizePlus - EaSizeMinus;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 Delete(
|
||||
Object FileNode0,
|
||||
Object FileDesc,
|
||||
String FileName,
|
||||
UInt32 Flags)
|
||||
{
|
||||
FileNode FileNode = (FileNode)FileNode0;
|
||||
|
||||
switch (Flags)
|
||||
{
|
||||
case FILE_DISPOSITION_DO_NOT_DELETE:
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case FILE_DISPOSITION_DELETE:
|
||||
if (FileNodeMap.HasChild(FileNode))
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
|
||||
case ~(UInt32)0:
|
||||
if (FileNodeMap.HasChild(FileNode))
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
|
||||
List<String> StreamFileNames = new List<String>(FileNodeMap.GetStreamFileNames(FileNode));
|
||||
foreach (String StreamFileName in StreamFileNames)
|
||||
{
|
||||
FileNode StreamNode = FileNodeMap.Get(StreamFileName);
|
||||
if (null == StreamNode)
|
||||
continue; /* should not happen */
|
||||
FileNodeMap.Remove(StreamNode);
|
||||
}
|
||||
|
||||
FileNodeMap.Remove(FileNode);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
default:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
private FileNodeMap FileNodeMap;
|
||||
private UInt32 MaxFileNodes;
|
||||
|
10
tst/memfs-fuse/.gitignore
vendored
Normal file
10
tst/memfs-fuse/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
build
|
||||
*.ncb
|
||||
*.suo
|
||||
*.vcproj.*
|
||||
*.vcxproj.user
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
.vs
|
||||
*.exe
|
||||
*.install
|
18
tst/memfs-fuse/Makefile
Normal file
18
tst/memfs-fuse/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
usage:
|
||||
@echo "make cygfuse|winfsp-fuse" 1>&2
|
||||
@echo "" 1>&2
|
||||
@echo " cygfuse Link with CYGFUSE" 1>&2
|
||||
@echo " winfsp-fuse Link with WinFsp-FUSE" 1>&2
|
||||
@exit 2
|
||||
|
||||
cygfuse: memfs-cygfuse
|
||||
|
||||
winfsp-fuse: memfs-winfsp-fuse
|
||||
|
||||
memfs-cygfuse: memfs-fuse.cpp
|
||||
g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse --cflags --libs`
|
||||
|
||||
memfs-winfsp-fuse: export PKG_CONFIG_PATH=$(PWD)/winfsp.install/lib
|
||||
memfs-winfsp-fuse: memfs-fuse.cpp
|
||||
ln -nsf "`regtool --wow32 get '/HKLM/Software/WinFsp/InstallDir' | cygpath -au -f -`" winfsp.install
|
||||
g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse --cflags --libs`
|
7
tst/memfs-fuse/README.md
Normal file
7
tst/memfs-fuse/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
`Memfs-fuse` is an in-memory FUSE file system.
|
||||
|
||||
It can be built with the following tools:
|
||||
|
||||
- Using Visual Studio (`memfs-fuse.sln`).
|
||||
- Using Cygwin GCC and linking directly with the WinFsp DLL (`make winfsp-fuse`).
|
||||
- Using Cygwin GCC and linking to CYGFUSE (`make cygfuse`).
|
104
tst/memfs-fuse/compat.h
Normal file
104
tst/memfs-fuse/compat.h
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @file compat.h
|
||||
*
|
||||
* @copyright 2015-2021 Bill Zissimopoulos
|
||||
*/
|
||||
/*
|
||||
* This file is part of WinFsp.
|
||||
*
|
||||
* You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 3 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Licensees holding a valid commercial license may use this software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
#ifndef COMPAT_H_INCLUDED
|
||||
#define COMPAT_H_INCLUDED
|
||||
|
||||
#if defined(_WIN32) && defined(FSP_FUSE_SYM)
|
||||
#include <winfsp/winfsp.h>
|
||||
#undef fuse_main
|
||||
#define fuse_main(argc, argv, ops, data)\
|
||||
(FspLoad(0), fuse_main_real(argc, argv, ops, sizeof *(ops), data))
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(fuse_stat)
|
||||
|
||||
#define fuse_uid_t uid_t
|
||||
#define fuse_gid_t gid_t
|
||||
#define fuse_pid_t pid_t
|
||||
|
||||
#define fuse_dev_t dev_t
|
||||
#define fuse_mode_t mode_t
|
||||
#define fuse_nlink_t nlink_t
|
||||
#define fuse_off_t off_t
|
||||
|
||||
#define fuse_fsblkcnt_t fsblkcnt_t
|
||||
#define fuse_fsfilcnt_t fsfilcnt_t
|
||||
#define fuse_blksize_t blksize_t
|
||||
#define fuse_blkcnt_t blkcnt_t
|
||||
|
||||
#define fuse_timespec timespec
|
||||
|
||||
#define fuse_stat stat
|
||||
|
||||
#define fuse_statvfs statvfs
|
||||
|
||||
#define fuse_flock flock
|
||||
|
||||
#define fuse_iovec iovec
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(S_IFMT)
|
||||
#define S_IFMT 0170000
|
||||
#endif
|
||||
#if !defined(S_IFDIR)
|
||||
#define S_IFDIR 0040000
|
||||
#endif
|
||||
#if !defined(S_IFCHR)
|
||||
#define S_IFCHR 0020000
|
||||
#endif
|
||||
#if !defined(S_IFBLK)
|
||||
#define S_IFBLK 0060000
|
||||
#endif
|
||||
#if !defined(S_IFREG)
|
||||
#define S_IFREG 0100000
|
||||
#endif
|
||||
#if !defined(S_IFLNK)
|
||||
#define S_IFLNK 0120000
|
||||
#endif
|
||||
#if !defined(S_IFSOCK)
|
||||
#define S_IFSOCK 0140000
|
||||
#endif
|
||||
#if !defined(S_IFIFO)
|
||||
#define S_IFIFO 0010000
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define st_atim st_atimespec
|
||||
#define st_ctim st_ctimespec
|
||||
#define st_mtim st_mtimespec
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__)
|
||||
#include <sys/xattr.h>
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(_WIN32)
|
||||
#define XATTR_CREATE 1
|
||||
#define XATTR_REPLACE 2
|
||||
#endif
|
||||
|
||||
#if !defined(ENOATTR)
|
||||
#define ENOATTR ENODATA
|
||||
#elif !defined(ENODATA)
|
||||
#define ENODATA ENOATTR
|
||||
#endif
|
||||
|
||||
#endif
|
693
tst/memfs-fuse/memfs-fuse.cpp
Normal file
693
tst/memfs-fuse/memfs-fuse.cpp
Normal file
@ -0,0 +1,693 @@
|
||||
/**
|
||||
* @file memfs-fuse.c
|
||||
*
|
||||
* @copyright 2015-2021 Bill Zissimopoulos
|
||||
*/
|
||||
/*
|
||||
* This file is part of WinFsp.
|
||||
*
|
||||
* You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 3 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Licensees holding a valid commercial license may use this software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <fuse.h>
|
||||
#include "compat.h"
|
||||
|
||||
class memfs
|
||||
{
|
||||
public:
|
||||
memfs() : _ino(1), _root(std::make_shared<node_t>(_ino, S_IFDIR | 00777, 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
static fuse_operations ops =
|
||||
{
|
||||
getattr,
|
||||
0, // getdir
|
||||
readlink,
|
||||
mknod,
|
||||
mkdir,
|
||||
unlink,
|
||||
rmdir,
|
||||
symlink,
|
||||
rename,
|
||||
link,
|
||||
chmod,
|
||||
chown,
|
||||
truncate,
|
||||
0, // utime
|
||||
open,
|
||||
read,
|
||||
write,
|
||||
statfs,
|
||||
flush,
|
||||
release,
|
||||
0, // fsync
|
||||
setxattr,
|
||||
getxattr,
|
||||
listxattr,
|
||||
removexattr,
|
||||
opendir,
|
||||
readdir,
|
||||
releasedir,
|
||||
0, // fsyncdir
|
||||
init,
|
||||
0, // destroy
|
||||
0, // access
|
||||
0, // create
|
||||
ftruncate,
|
||||
fgetattr,
|
||||
0, // lock
|
||||
utimens,
|
||||
0, // bmap
|
||||
0, // flag_nullpath_ok
|
||||
0, // flag_nopath
|
||||
0, // flag_utime_omit_ok
|
||||
0, // flag_reserved
|
||||
0, // ioctl
|
||||
0, // poll
|
||||
0, // write_buf
|
||||
0, // read_buf
|
||||
0, // flock
|
||||
0, // fallocate
|
||||
0, // reserved00
|
||||
0, // reserved01
|
||||
0, // reserved02
|
||||
0, // statfs_x
|
||||
0, // setvolname
|
||||
0, // exchange
|
||||
0, // getxtimes
|
||||
0, // setbkuptime
|
||||
0, // setchgtime
|
||||
setcrtime,
|
||||
#if defined(FSP_FUSE_USE_STAT_EX)
|
||||
chflags,
|
||||
#else
|
||||
0, // chflags
|
||||
#endif
|
||||
0, // setattr_x
|
||||
0, // fsetattr_x
|
||||
};
|
||||
return fuse_main(argc, argv, &ops, this);
|
||||
}
|
||||
|
||||
private:
|
||||
struct node_t
|
||||
{
|
||||
node_t(fuse_ino_t ino, fuse_mode_t mode, fuse_uid_t uid, fuse_gid_t gid, fuse_dev_t dev = 0)
|
||||
: stat()
|
||||
{
|
||||
stat.st_ino = ino;
|
||||
stat.st_mode = mode;
|
||||
stat.st_nlink = 1;
|
||||
stat.st_uid = uid;
|
||||
stat.st_gid = gid;
|
||||
stat.st_rdev = dev;
|
||||
stat.st_atim = stat.st_mtim = stat.st_ctim = stat.st_birthtim = now();
|
||||
}
|
||||
|
||||
void resize(size_t size, bool capacity)
|
||||
{
|
||||
if (capacity)
|
||||
{
|
||||
const size_t unit = 64 * 1024;
|
||||
size_t newcap = (size + unit - 1) / unit * unit;
|
||||
size_t oldcap = data.capacity();
|
||||
if (newcap > oldcap)
|
||||
data.reserve(newcap);
|
||||
else if (newcap < oldcap)
|
||||
{
|
||||
data.resize(newcap);
|
||||
data.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
data.resize(size);
|
||||
stat.st_size = size;
|
||||
}
|
||||
|
||||
struct fuse_stat stat;
|
||||
std::vector<uint8_t> data;
|
||||
std::unordered_map<std::string, std::shared_ptr<node_t>> childmap;
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> xattrmap;
|
||||
};
|
||||
|
||||
static fuse_timespec now()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
auto now = system_clock::now();
|
||||
auto sec = floor<seconds>(now);
|
||||
auto nsec = floor<nanoseconds>(now) - floor<nanoseconds>(sec);
|
||||
return fuse_timespec
|
||||
{
|
||||
static_cast<decltype(fuse_timespec::tv_sec)>(sec.time_since_epoch().count()),
|
||||
/* std::chrono epoch is UNIX epoch in C++20 */
|
||||
static_cast<decltype(fuse_timespec::tv_nsec)>(nsec.count()),
|
||||
};
|
||||
}
|
||||
|
||||
static memfs *getself()
|
||||
{
|
||||
return static_cast<memfs *>(fuse_get_context()->private_data);
|
||||
}
|
||||
|
||||
static int getattr(const char *path, struct fuse_stat *stbuf)
|
||||
{
|
||||
return fgetattr(path, stbuf, nullptr);
|
||||
}
|
||||
|
||||
static int fgetattr(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path, fi);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
*stbuf = node->stat;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int readlink(const char *path, char *buf, size_t size)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (S_IFLNK != (node->stat.st_mode & S_IFMT))
|
||||
return EINVAL;
|
||||
size = (std::min)(size - 1, node->data.size());
|
||||
std::memcpy(buf, node->data.data(), size);
|
||||
buf[size] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mknod(const char *path, fuse_mode_t mode, fuse_dev_t dev)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->make_node(path, mode, dev);
|
||||
}
|
||||
|
||||
static int mkdir(const char *path, fuse_mode_t mode)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->make_node(path, S_IFDIR | (mode & 07777), 0);
|
||||
}
|
||||
|
||||
static int unlink(const char *path)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->remove_node(path, false);
|
||||
}
|
||||
|
||||
static int rmdir(const char *path)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->remove_node(path, true);
|
||||
}
|
||||
|
||||
static int symlink(const char *dstpath, const char *srcpath)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->make_node(srcpath, S_IFLNK | 00777, 0, dstpath);
|
||||
}
|
||||
|
||||
static int rename(const char *oldpath, const char *newpath)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto oldlookup = self->lookup_node(oldpath);
|
||||
auto oldprnt = std::get<0>(oldlookup);
|
||||
auto oldname = std::get<1>(oldlookup);
|
||||
auto oldnode = std::get<2>(oldlookup);
|
||||
if (!oldnode)
|
||||
return -ENOENT;
|
||||
auto newlookup = self->lookup_node(newpath);
|
||||
auto newprnt = std::get<0>(newlookup);
|
||||
auto newname = std::get<1>(newlookup);
|
||||
auto newnode = std::get<2>(newlookup);
|
||||
if (!newprnt)
|
||||
return -ENOENT;
|
||||
if (newname.empty())
|
||||
// guard against directory loop creation
|
||||
return -EINVAL;
|
||||
if (oldprnt == newprnt && oldname == newname)
|
||||
return 0;
|
||||
if (newnode)
|
||||
{
|
||||
if (int errc = self->remove_node(newpath, S_IFDIR == (oldnode->stat.st_mode & S_IFMT)))
|
||||
return errc;
|
||||
}
|
||||
oldprnt->childmap.erase(oldname);
|
||||
newprnt->childmap[newname] = oldnode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link(const char *oldpath, const char *newpath)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto oldlookup = self->lookup_node(oldpath);
|
||||
auto oldnode = std::get<2>(oldlookup);
|
||||
if (!oldnode)
|
||||
return -ENOENT;
|
||||
auto newlookup = self->lookup_node(newpath);
|
||||
auto newprnt = std::get<0>(newlookup);
|
||||
auto newname = std::get<1>(newlookup);
|
||||
auto newnode = std::get<2>(newlookup);
|
||||
if (!newprnt)
|
||||
return -ENOENT;
|
||||
if (newnode)
|
||||
return -EEXIST;
|
||||
oldnode->stat.st_nlink++;
|
||||
newprnt->childmap[newname] = oldnode;
|
||||
oldnode->stat.st_ctim = newprnt->stat.st_ctim = newprnt->stat.st_mtim = now();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chmod(const char *path, fuse_mode_t mode)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
node->stat.st_mode = (node->stat.st_mode & S_IFMT) | (mode & 07777);
|
||||
node->stat.st_ctim = now();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chown(const char *path, fuse_uid_t uid, fuse_gid_t gid)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (-1 != uid)
|
||||
node->stat.st_uid = uid;
|
||||
if (-1 != gid)
|
||||
node->stat.st_gid = gid;
|
||||
node->stat.st_ctim = now();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int truncate(const char *path, fuse_off_t size)
|
||||
{
|
||||
return ftruncate(path, size, nullptr);
|
||||
}
|
||||
|
||||
static int ftruncate(const char *path, fuse_off_t size,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path, fi);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (SIZE_MAX < size)
|
||||
return -EFBIG;
|
||||
node->resize(static_cast<size_t>(size), true);
|
||||
node->stat.st_ctim = node->stat.st_mtim = now();
|
||||
#if defined(FSP_FUSE_USE_STAT_EX)
|
||||
node->stat.st_flags |= UF_ARCHIVE;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->open_node(path, false, fi);
|
||||
}
|
||||
|
||||
static int read(const char *path, char *buf, size_t size, fuse_off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path, fi);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
fuse_off_t endoff = (std::min)(
|
||||
off + static_cast<fuse_off_t>(size), static_cast<fuse_off_t>(node->data.size()));
|
||||
if (off > endoff)
|
||||
return 0;
|
||||
std::memcpy(buf, node->data.data() + off, static_cast<int>(endoff - off));
|
||||
node->stat.st_atim = now();
|
||||
return static_cast<int>(endoff - off);
|
||||
}
|
||||
|
||||
static int write(const char *path, const char *buf, size_t size, fuse_off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path, fi);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
fuse_off_t endoff = off + static_cast<fuse_off_t>(size);
|
||||
if (SIZE_MAX < endoff)
|
||||
return -EFBIG;
|
||||
if (node->data.size() < endoff)
|
||||
node->resize(static_cast<size_t>(endoff), true);
|
||||
std::memcpy(node->data.data() + off, buf, static_cast<int>(endoff - off));
|
||||
node->stat.st_ctim = node->stat.st_mtim = now();
|
||||
#if defined(FSP_FUSE_USE_STAT_EX)
|
||||
node->stat.st_flags |= UF_ARCHIVE;
|
||||
#endif
|
||||
return static_cast<int>(endoff - off);
|
||||
}
|
||||
|
||||
static int statfs(const char *path, struct fuse_statvfs *stbuf)
|
||||
{
|
||||
std::memset(stbuf, 0, sizeof *stbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flush(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int release(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->close_node(fi);
|
||||
}
|
||||
|
||||
static int setxattr(const char *path, const char *name0, const char *value, size_t size,
|
||||
int flags)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (0 == std::strcmp("com.apple.ResourceFork", name0))
|
||||
return -ENOTSUP;
|
||||
std::string name = name0;
|
||||
if (XATTR_CREATE == flags)
|
||||
{
|
||||
if (node->xattrmap.end() != node->xattrmap.find(name))
|
||||
return -EEXIST;
|
||||
}
|
||||
else if (XATTR_REPLACE == flags)
|
||||
{
|
||||
if (node->xattrmap.end() == node->xattrmap.find(name))
|
||||
return -ENOATTR;
|
||||
}
|
||||
node->xattrmap[name].assign(value, value + size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int getxattr(const char *path, const char *name0, char *value, size_t size)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (0 == std::strcmp("com.apple.ResourceFork", name0))
|
||||
return -ENOTSUP;
|
||||
std::string name = name0;
|
||||
auto iter = node->xattrmap.find(name);
|
||||
if (node->xattrmap.end() == iter)
|
||||
return -ENOATTR;
|
||||
if (0 != size)
|
||||
{
|
||||
if (iter->second.size() > size)
|
||||
return -ERANGE;
|
||||
std::memcpy(value, iter->second.data(), iter->second.size());
|
||||
}
|
||||
return static_cast<int>(iter->second.size());
|
||||
}
|
||||
|
||||
static int listxattr(const char *path, char *namebuf, size_t size)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
size_t copysize = 0;
|
||||
for (auto elem : node->xattrmap)
|
||||
{
|
||||
size_t namesize = elem.first.size() + 1;
|
||||
if (0 != size)
|
||||
{
|
||||
if (copysize + namesize > size)
|
||||
return -ERANGE;
|
||||
std::memcpy(namebuf + copysize, elem.first.c_str(), namesize);
|
||||
copysize += namesize;
|
||||
}
|
||||
}
|
||||
return static_cast<int>(copysize);
|
||||
}
|
||||
|
||||
static int removexattr(const char *path, const char *name0)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (0 == std::strcmp("com.apple.ResourceFork", name0))
|
||||
return -ENOTSUP;
|
||||
std::string name = name0;
|
||||
return node->xattrmap.erase(name) ? 0 : -ENOATTR;
|
||||
}
|
||||
|
||||
static int opendir(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->open_node(path, true, fi);
|
||||
}
|
||||
|
||||
static int readdir(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path, fi);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
filler(buf, ".", &node->stat, 0);
|
||||
filler(buf, "..", nullptr, 0);
|
||||
for (auto elem : node->childmap)
|
||||
if (0 != filler(buf, elem.first.c_str(), &elem.second->stat, 0))
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int releasedir(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
return self->close_node(fi);
|
||||
}
|
||||
|
||||
static void *init(struct fuse_conn_info *conn)
|
||||
{
|
||||
#if defined(FSP_FUSE_CAP_READDIR_PLUS)
|
||||
conn->want |= (conn->capable & FSP_FUSE_CAP_READDIR_PLUS);
|
||||
#endif
|
||||
|
||||
#if defined(FSP_FUSE_USE_STAT_EX) && defined(FSP_FUSE_CAP_STAT_EX)
|
||||
conn->want |= (conn->capable & FSP_FUSE_CAP_STAT_EX);
|
||||
#endif
|
||||
|
||||
return getself();
|
||||
}
|
||||
|
||||
static int utimens(const char *path, const struct fuse_timespec tmsp[2])
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (tmsp)
|
||||
{
|
||||
node->stat.st_ctim = now();
|
||||
node->stat.st_atim = tmsp[0];
|
||||
node->stat.st_mtim = tmsp[1];
|
||||
}
|
||||
else
|
||||
node->stat.st_ctim = node->stat.st_atim = node->stat.st_mtim = now();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setcrtime(const char *path, const struct fuse_timespec *tmsp)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (tmsp)
|
||||
{
|
||||
node->stat.st_ctim = now();
|
||||
node->stat.st_birthtim = tmsp[0];
|
||||
}
|
||||
else
|
||||
node->stat.st_ctim = node->stat.st_birthtim = now();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(FSP_FUSE_USE_STAT_EX)
|
||||
static int chflags(const char *path, uint32_t flags)
|
||||
{
|
||||
auto self = getself();
|
||||
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||
auto node = self->get_node(path);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
node->stat.st_flags = flags;
|
||||
node->stat.st_ctim = now();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::tuple<std::shared_ptr<node_t>, std::string, std::shared_ptr<node_t>>
|
||||
lookup_node(const char *path, node_t *ancestor = nullptr)
|
||||
{
|
||||
auto prnt = _root;
|
||||
std::string name;
|
||||
auto node = prnt;
|
||||
for (const char *part = path, *p; *part; part = p + !!(*p))
|
||||
{
|
||||
for (p = part; *p && '/' != *p; p++)
|
||||
;
|
||||
if (part == p)
|
||||
continue;
|
||||
prnt = node;
|
||||
if (!node)
|
||||
break;
|
||||
name.assign(part, p);
|
||||
auto iter = node->childmap.find(name);
|
||||
node = node->childmap.end() != iter ? iter->second : nullptr;
|
||||
if (ancestor && node.get() == ancestor)
|
||||
{
|
||||
name.assign(""); // special case loop condition
|
||||
break;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(prnt, name, node);
|
||||
}
|
||||
|
||||
int make_node(const char *path, fuse_mode_t mode, fuse_dev_t dev, const char *data = nullptr)
|
||||
{
|
||||
auto lookup = lookup_node(path);
|
||||
auto prnt = std::get<0>(lookup);
|
||||
auto name = std::get<1>(lookup);
|
||||
auto node = std::get<2>(lookup);
|
||||
if (!prnt)
|
||||
return -ENOENT;
|
||||
if (node)
|
||||
return -EEXIST;
|
||||
fuse_context *context = fuse_get_context();
|
||||
node = std::make_shared<node_t>(++_ino, mode, context->uid, context->gid, dev);
|
||||
#if defined(FSP_FUSE_USE_STAT_EX)
|
||||
if (S_IFDIR != (mode & S_IFMT))
|
||||
node->stat.st_flags |= UF_ARCHIVE;
|
||||
#endif
|
||||
if (data)
|
||||
{
|
||||
node->resize(std::strlen(data), false);
|
||||
std::memcpy(node->data.data(), data, node->data.size());
|
||||
}
|
||||
prnt->childmap[name] = node;
|
||||
prnt->stat.st_ctim = prnt->stat.st_mtim = node->stat.st_ctim;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int remove_node(const char *path, bool dir)
|
||||
{
|
||||
auto lookup = lookup_node(path);
|
||||
auto prnt = std::get<0>(lookup);
|
||||
auto name = std::get<1>(lookup);
|
||||
auto node = std::get<2>(lookup);
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT))
|
||||
return -EISDIR;
|
||||
if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT))
|
||||
return -ENOTDIR;
|
||||
if (0 < node->childmap.size())
|
||||
return -ENOTEMPTY;
|
||||
node->stat.st_nlink--;
|
||||
prnt->childmap.erase(name);
|
||||
node->stat.st_ctim = prnt->stat.st_ctim = prnt->stat.st_mtim = now();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_node(const char *path, bool dir, struct fuse_file_info *fi)
|
||||
{
|
||||
auto node = std::get<2>(lookup_node(path));
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT))
|
||||
return -EISDIR;
|
||||
if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT))
|
||||
return -ENOTDIR;
|
||||
// A file descriptor is a raw pointer to a shared_ptr.
|
||||
// This has the effect of incrementing the shared_ptr
|
||||
// refcount, thus keeping an open node around even
|
||||
// if the node is unlinked.
|
||||
fi->fh = (uint64_t)(uintptr_t)new std::shared_ptr<node_t>(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_node(struct fuse_file_info *fi)
|
||||
{
|
||||
delete (std::shared_ptr<node_t> *)(uintptr_t)fi->fh;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<node_t> get_node(const char *path, struct fuse_file_info *fi = nullptr)
|
||||
{
|
||||
if (!fi)
|
||||
return std::get<2>(lookup_node(path));
|
||||
else
|
||||
return *(std::shared_ptr<node_t> *)(uintptr_t)fi->fh;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex _mutex;
|
||||
fuse_ino_t _ino;
|
||||
std::shared_ptr<node_t> _root;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return memfs().main(argc, argv);
|
||||
}
|
28
tst/memfs-fuse/memfs-fuse.sln
Normal file
28
tst/memfs-fuse/memfs-fuse.sln
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memfs-fuse", "memfs-fuse.vcxproj", "{CF538F42-C714-4653-B351-E72FD7B0B217}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.Build.0 = Debug|x64
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.ActiveCfg = Release|x64
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.Build.0 = Release|x64
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
189
tst/memfs-fuse/memfs-fuse.vcxproj
Normal file
189
tst/memfs-fuse/memfs-fuse.vcxproj
Normal file
@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{CF538F42-C714-4653-B351-E72FD7B0B217}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>memfsfuse</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>FSP_FUSE_USE_STAT_EX;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>FSP_FUSE_USE_STAT_EX;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>FSP_FUSE_USE_STAT_EX;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>FSP_FUSE_USE_STAT_EX;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="compat.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="memfs-fuse.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
19
tst/memfs-fuse/memfs-fuse.vcxproj.filters
Normal file
19
tst/memfs-fuse/memfs-fuse.vcxproj.filters
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="compat.h">
|
||||
<Filter>Source</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="memfs-fuse.cpp">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -88,13 +88,6 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH,
|
||||
#define MEMFS_REJECT_EARLY_IRP
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define the MEMFS_DELETE macro to include new Delete support
|
||||
* (instead of Cleanup/FspCleanupDelete). This is required to
|
||||
* properly support POSIX unlink/rename.
|
||||
*/
|
||||
#define MEMFS_DELETE
|
||||
|
||||
/*
|
||||
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
|
||||
* a check for the Write buffer to ensure that it is read-only.
|
||||
@ -972,8 +965,6 @@ void SlowioReadDirectoryThread(
|
||||
* FSP_FILE_SYSTEM_INTERFACE
|
||||
*/
|
||||
|
||||
static NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileNode0, PWSTR FileName, ULONG Flags);
|
||||
#if defined(MEMFS_REPARSE_POINTS)
|
||||
static NTSTATUS GetReparsePointByName(
|
||||
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
||||
@ -1356,9 +1347,7 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
|
||||
MEMFS_FILE_NODE *MainFileNode = FileNode;
|
||||
#endif
|
||||
|
||||
#if !defined(MEMFS_DELETE)
|
||||
assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */
|
||||
#endif
|
||||
|
||||
if (Flags & FspCleanupSetArchiveBit)
|
||||
{
|
||||
@ -1387,10 +1376,21 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
|
||||
SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE);
|
||||
}
|
||||
|
||||
#if !defined(MEMFS_DELETE)
|
||||
if (Flags & FspCleanupDelete)
|
||||
Delete(FileSystem, FileNode0, FileName, -1);
|
||||
if ((Flags & FspCleanupDelete) && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
||||
{
|
||||
#if defined(MEMFS_NAMED_STREAMS)
|
||||
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
|
||||
ULONG Index;
|
||||
|
||||
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
|
||||
MemfsFileNodeMapEnumerateFn, &Context);
|
||||
for (Index = 0; Context.Count > Index; Index++)
|
||||
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
|
||||
MemfsFileNodeMapEnumerateFree(&Context);
|
||||
#endif
|
||||
|
||||
MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID Close(FSP_FILE_SYSTEM *FileSystem,
|
||||
@ -1651,13 +1651,17 @@ static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#if !defined(MEMFS_DELETE)
|
||||
static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileNode0, PWSTR FileName)
|
||||
{
|
||||
return Delete(FileSystem, FileNode0, FileName, FILE_DISPOSITION_DELETE);
|
||||
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
|
||||
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
|
||||
|
||||
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileNode0,
|
||||
@ -2227,54 +2231,6 @@ static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
|
||||
}
|
||||
#endif
|
||||
|
||||
static NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileNode0, PWSTR FileName, ULONG Flags)
|
||||
{
|
||||
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
|
||||
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
|
||||
|
||||
switch (Flags)
|
||||
{
|
||||
case FILE_DISPOSITION_DO_NOT_DELETE:
|
||||
// set file disposition flag: do not delete file at Cleanup
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case FILE_DISPOSITION_DELETE:
|
||||
// set file disposition flag: delete file at Cleanup
|
||||
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
|
||||
// delete file now; open handles to file remain valid
|
||||
/* fallthrough */
|
||||
|
||||
case -1:
|
||||
// delete file now; called during Cleanup
|
||||
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||
else
|
||||
{
|
||||
#if defined(MEMFS_NAMED_STREAMS)
|
||||
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
|
||||
ULONG Index;
|
||||
|
||||
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
|
||||
MemfsFileNodeMapEnumerateFn, &Context);
|
||||
for (Index = 0; Context.Count > Index; Index++)
|
||||
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
|
||||
MemfsFileNodeMapEnumerateFree(&Context);
|
||||
#endif
|
||||
|
||||
MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
default:
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
|
||||
{
|
||||
GetVolumeInfo,
|
||||
@ -2299,11 +2255,7 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
|
||||
GetFileInfo,
|
||||
SetBasicInfo,
|
||||
SetFileSize,
|
||||
#if !defined(MEMFS_DELETE)
|
||||
CanDelete,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
Rename,
|
||||
GetSecurity,
|
||||
SetSecurity,
|
||||
@ -2347,11 +2299,6 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
|
||||
0,
|
||||
0,
|
||||
#endif
|
||||
#if defined(MEMFS_DELETE)
|
||||
Delete,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1370,9 +1370,10 @@ void create_tests(void)
|
||||
TEST(create_fileattr_test);
|
||||
TEST(create_readonlydir_test);
|
||||
TEST(create_related_test);
|
||||
if (!OptFuseExternal)
|
||||
TEST(create_allocation_test);
|
||||
TEST(create_sd_test);
|
||||
if (!OptNoTraverseToken && !OptShareName)
|
||||
if (!OptFuseExternal && !OptNoTraverseToken && !OptShareName)
|
||||
TEST(create_notraverse_test);
|
||||
TEST(create_backup_test);
|
||||
TEST(create_restore_test);
|
||||
|
@ -170,7 +170,11 @@ static NTSTATUS ea_check_ea_enumerate(
|
||||
|
||||
if (0 == strcmp(SingleEa->EaName, "BNAMETWO"))
|
||||
{
|
||||
if (!OptFuseExternal)
|
||||
{
|
||||
/* FUSE has no concept of FILE_NEED_EA */
|
||||
ASSERT(FILE_NEED_EA == SingleEa->Flags);
|
||||
}
|
||||
ASSERT(SingleEa->EaNameLength == (UCHAR)strlen("BNAMETWO"));
|
||||
ASSERT(SingleEa->EaValueLength == (UCHAR)strlen("second"));
|
||||
ASSERT(0 == memcmp(SingleEa->EaName + SingleEa->EaNameLength + 1, "second", SingleEa->EaValueLength));
|
||||
@ -720,12 +724,17 @@ static void ea_create_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
CloseHandle(FileHandle);
|
||||
|
||||
if (!OptFuseExternal)
|
||||
{
|
||||
/* FUSE has no concept of FILE_NEED_EA */
|
||||
|
||||
Result = NtCreateFile(&FileHandle,
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
|
||||
&LargeZero, FILE_ATTRIBUTE_NORMAL, 0,
|
||||
FILE_OPEN, FILE_NO_EA_KNOWLEDGE,
|
||||
0, 0);
|
||||
ASSERT(STATUS_ACCESS_DENIED == Result);
|
||||
}
|
||||
|
||||
Result = NtCreateFile(&FileHandle,
|
||||
FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE, &Obja, &Iosb,
|
||||
|
@ -200,6 +200,27 @@ static void exec_delete_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
ASSERT(!DeleteFileW(FilePath));
|
||||
ASSERT(ERROR_ACCESS_DENIED == GetLastError());
|
||||
|
||||
{
|
||||
MY_FILE_DISPOSITION_INFO_EX DispositionInfo;
|
||||
HANDLE Handle;
|
||||
BOOLEAN Success;
|
||||
Handle = CreateFileW(FilePath,
|
||||
DELETE, FILE_SHARE_DELETE, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
if (INVALID_HANDLE_VALUE != Handle)
|
||||
{
|
||||
DispositionInfo.Flags = 3/*FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS*/;
|
||||
Success = SetFileInformationByHandle(Handle,
|
||||
21/*FileDispositionInfoEx*/, &DispositionInfo, sizeof DispositionInfo);
|
||||
ASSERT(!Success);
|
||||
ASSERT(
|
||||
ERROR_INVALID_PARAMETER == GetLastError() ||
|
||||
ERROR_ACCESS_DENIED == GetLastError());
|
||||
Success = CloseHandle(Handle);
|
||||
ASSERT(Success);
|
||||
}
|
||||
}
|
||||
|
||||
WaitHelper(Process, 1000);
|
||||
|
||||
ASSERT(DeleteFileW(FilePath));
|
||||
|
@ -894,6 +894,242 @@ void delete_standby_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_ex_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
{
|
||||
BOOLEAN Success;
|
||||
DWORD FileSystemFlags;
|
||||
|
||||
Success = GetVolumeInformationW(L"C:\\",
|
||||
0, 0,
|
||||
0, 0, &FileSystemFlags,
|
||||
0, 0);
|
||||
if (!Success || 0 == (FileSystemFlags & 0x400/*FILE_SUPPORTS_POSIX_UNLINK_RENAME*/))
|
||||
/* skip this test if the system lacks FILE_SUPPORTS_POSIX_UNLINK_RENAME capability */
|
||||
return;
|
||||
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
|
||||
NTSYSCALLAPI NTSTATUS NTAPI
|
||||
NtSetInformationFile(
|
||||
HANDLE FileHandle,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID FileInformation,
|
||||
ULONG Length,
|
||||
FILE_INFORMATION_CLASS FileInformationClass);
|
||||
typedef struct
|
||||
{
|
||||
ULONG Flags;
|
||||
} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
|
||||
|
||||
HANDLE Handle0, Handle1, Handle2, FindHandle;
|
||||
WCHAR FilePath[MAX_PATH];
|
||||
WIN32_FIND_DATAW FindData;
|
||||
FILE_DISPOSITION_INFORMATION_EX DispositionInfo;
|
||||
IO_STATUS_BLOCK IoStatus;
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\",
|
||||
VolPrefix ? L"" : L"\\\\?\\GLOBALROOT", VolPrefix ? VolPrefix : memfs_volumename(memfs));
|
||||
|
||||
Success = GetVolumeInformationW(FilePath,
|
||||
0, 0,
|
||||
0, 0, &FileSystemFlags,
|
||||
0, 0);
|
||||
ASSERT(Success);
|
||||
if (0 != (FileSystemFlags & 0x400/*FILE_SUPPORTS_POSIX_UNLINK_RENAME*/))
|
||||
{
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
/* POSIX Semantics / Ignore Readonly */
|
||||
|
||||
Handle0 = CreateFileW(FilePath,
|
||||
GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_READONLY, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||
|
||||
Handle1 = CreateFileW(FilePath,
|
||||
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle1);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle1, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(STATUS_CANNOT_DELETE == IoStatus.Status);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 0x13; /* DELETE | POSIX_SEMANTICS | IGNORE_READONLY_ATTRIBUTE */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle1, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(0 == IoStatus.Status);
|
||||
|
||||
FindHandle = FindFirstFileW(FilePath, &FindData);
|
||||
ASSERT(INVALID_HANDLE_VALUE != FindHandle);
|
||||
ASSERT(0 == mywcscmp(FindData.cFileName, 4, L"file", 4));
|
||||
FindClose(FindHandle);
|
||||
|
||||
Handle2 = CreateFileW(FilePath,
|
||||
0, 0, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE == Handle2);
|
||||
ASSERT(ERROR_ACCESS_DENIED == GetLastError());
|
||||
|
||||
CloseHandle(Handle1);
|
||||
|
||||
FindHandle = FindFirstFileW(FilePath, &FindData);
|
||||
ASSERT(INVALID_HANDLE_VALUE == FindHandle);
|
||||
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
|
||||
|
||||
Handle2 = CreateFileW(FilePath,
|
||||
0, 0, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE == Handle2);
|
||||
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 0; /* DO_NOT_DELETE */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle0, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(STATUS_FILE_DELETED == IoStatus.Status);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 1; /* DELETE */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle0, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(STATUS_FILE_DELETED == IoStatus.Status);
|
||||
|
||||
CloseHandle(Handle0);
|
||||
|
||||
/* POSIX Semantics / Set/Reset */
|
||||
|
||||
Handle0 = CreateFileW(FilePath,
|
||||
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||
|
||||
Handle1 = CreateFileW(FilePath,
|
||||
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle1);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle1, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(STATUS_SUCCESS == IoStatus.Status);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 0; /* DO_NOT_DELETE */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle1, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(STATUS_SUCCESS == IoStatus.Status);
|
||||
|
||||
CloseHandle(Handle1);
|
||||
|
||||
FindHandle = FindFirstFileW(FilePath, &FindData);
|
||||
ASSERT(INVALID_HANDLE_VALUE != FindHandle);
|
||||
ASSERT(0 == mywcscmp(FindData.cFileName, 4, L"file", 4));
|
||||
FindClose(FindHandle);
|
||||
|
||||
Handle1 = CreateFileW(FilePath,
|
||||
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle1);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle1, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(STATUS_SUCCESS == IoStatus.Status);
|
||||
|
||||
CloseHandle(Handle1);
|
||||
|
||||
CloseHandle(Handle0);
|
||||
|
||||
#if 0
|
||||
/* On Close */
|
||||
|
||||
Handle0 = CreateFileW(FilePath,
|
||||
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 8; /* DO_NOT_DELETE | ON_CLOSE */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle0, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(0 == IoStatus.Status);
|
||||
|
||||
CloseHandle(Handle0);
|
||||
|
||||
Handle0 = CreateFileW(FilePath,
|
||||
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 9; /* DELETE | ON_CLOSE */;
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle0, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(STATUS_NOT_SUPPORTED == IoStatus.Status);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */;
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle0, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(0 == IoStatus.Status);
|
||||
|
||||
CloseHandle(Handle0);
|
||||
#endif
|
||||
}
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
void delete_ex_test(void)
|
||||
{
|
||||
if (OptLegacyUnlinkRename)
|
||||
return;
|
||||
|
||||
if (NtfsTests)
|
||||
{
|
||||
WCHAR DirBuf[MAX_PATH], DriveBuf[3];
|
||||
GetTestDirectoryAndDrive(DirBuf, DriveBuf);
|
||||
delete_ex_dotest(-1, DriveBuf, DirBuf, 0);
|
||||
}
|
||||
if (WinFspDiskTests)
|
||||
{
|
||||
delete_ex_dotest(MemfsDisk, 0, 0, 0);
|
||||
delete_ex_dotest(MemfsDisk, 0, 0, 1000);
|
||||
}
|
||||
if (WinFspNetTests)
|
||||
{
|
||||
delete_ex_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 0);
|
||||
delete_ex_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 1000);
|
||||
}
|
||||
}
|
||||
|
||||
static void rename_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
@ -1813,6 +2049,10 @@ static void rename_ex_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG F
|
||||
ULONG FileNameLength;
|
||||
WCHAR FileName[1];
|
||||
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
|
||||
typedef struct
|
||||
{
|
||||
ULONG Flags;
|
||||
} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
|
||||
|
||||
HANDLE Handle0, Handle1, Handle2;
|
||||
WCHAR File0Path[MAX_PATH];
|
||||
@ -1822,6 +2062,7 @@ static void rename_ex_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG F
|
||||
FILE_RENAME_INFORMATION I;
|
||||
UINT8 B[sizeof(FILE_RENAME_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
|
||||
} RenameInfo;
|
||||
FILE_DISPOSITION_INFORMATION_EX DispositionInfo;
|
||||
IO_STATUS_BLOCK IoStatus;
|
||||
|
||||
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\",
|
||||
@ -1840,6 +2081,8 @@ static void rename_ex_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG F
|
||||
StringCbPrintfW(File2Path, sizeof File2Path, L"%s%s\\file2",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
/* File Test */
|
||||
|
||||
Handle0 = CreateFileW(File0Path,
|
||||
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
@ -1891,6 +2134,16 @@ static void rename_ex_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG F
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||
|
||||
memset(&RenameInfo.I, 0, sizeof RenameInfo.I);
|
||||
RenameInfo.I.Flags = 1/*FILE_RENAME_REPLACE_IF_EXISTS*/;
|
||||
RenameInfo.I.FileNameLength = (ULONG)(wcslen(L"file0") * sizeof(WCHAR));
|
||||
memcpy(RenameInfo.I.FileName, L"file0", RenameInfo.I.FileNameLength);
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle2, &IoStatus,
|
||||
&RenameInfo.I, FIELD_OFFSET(FILE_RENAME_INFORMATION, FileName) + RenameInfo.I.FileNameLength,
|
||||
10/*FileRenameInformation*/);
|
||||
ASSERT(STATUS_ACCESS_DENIED == IoStatus.Status);
|
||||
|
||||
memset(&RenameInfo.I, 0, sizeof RenameInfo.I);
|
||||
RenameInfo.I.Flags = 3/*FILE_RENAME_REPLACE_IF_EXISTS|FILE_RENAME_POSIX_SEMANTICS*/;
|
||||
RenameInfo.I.FileNameLength = (ULONG)(wcslen(L"file0") * sizeof(WCHAR));
|
||||
@ -1929,6 +2182,40 @@ static void rename_ex_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG F
|
||||
|
||||
Success = DeleteFileW(File0Path);
|
||||
ASSERT(Success);
|
||||
|
||||
/* Deleted File Test */
|
||||
|
||||
Handle0 = CreateFileW(File0Path,
|
||||
GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||
|
||||
Handle1 = CreateFileW(File0Path,
|
||||
DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle1);
|
||||
|
||||
memset(&DispositionInfo, 0, sizeof DispositionInfo);
|
||||
DispositionInfo.Flags = 3; /* DELETE | POSIX_SEMANTICS */
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle1, &IoStatus,
|
||||
&DispositionInfo, sizeof DispositionInfo,
|
||||
64/*FileDispositionInformationEx*/);
|
||||
ASSERT(0 == IoStatus.Status);
|
||||
|
||||
CloseHandle(Handle1);
|
||||
|
||||
memset(&RenameInfo.I, 0, sizeof RenameInfo.I);
|
||||
RenameInfo.I.Flags = 2/*FILE_RENAME_POSIX_SEMANTICS*/;
|
||||
RenameInfo.I.FileNameLength = (ULONG)(wcslen(L"file2") * sizeof(WCHAR));
|
||||
memcpy(RenameInfo.I.FileName, L"file2", RenameInfo.I.FileNameLength);
|
||||
IoStatus.Status = NtSetInformationFile(
|
||||
Handle0, &IoStatus,
|
||||
&RenameInfo.I, FIELD_OFFSET(FILE_RENAME_INFORMATION, FileName) + RenameInfo.I.FileNameLength,
|
||||
65/*FileRenameInformationEx*/);
|
||||
ASSERT(STATUS_ACCESS_DENIED == IoStatus.Status);
|
||||
|
||||
CloseHandle(Handle0);
|
||||
}
|
||||
|
||||
memfs_stop(memfs);
|
||||
@ -2213,9 +2500,10 @@ void query_winfsp_test(void)
|
||||
|
||||
void info_tests(void)
|
||||
{
|
||||
if (!OptShareName)
|
||||
if (!OptFuseExternal && !OptShareName)
|
||||
TEST(getfileattr_test);
|
||||
TEST(getfileinfo_test);
|
||||
if (!OptFuseExternal)
|
||||
TEST(getfileinfo_name_test);
|
||||
TEST(setfileinfo_test);
|
||||
TEST(delete_test);
|
||||
@ -2224,6 +2512,8 @@ void info_tests(void)
|
||||
if (!OptShareName)
|
||||
TEST(delete_mmap_test);
|
||||
TEST(delete_standby_test);
|
||||
if (!OptLegacyUnlinkRename)
|
||||
TEST(delete_ex_test);
|
||||
TEST(rename_test);
|
||||
TEST(rename_backslash_test);
|
||||
TEST(rename_open_test);
|
||||
|
@ -192,6 +192,10 @@ static void reparse_nfs_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||
|
||||
if (!OptFuseExternal)
|
||||
{
|
||||
/* FUSE cannot delete reparse points */
|
||||
|
||||
ReparseDataBuf.D.ReparseDataLength = 0;
|
||||
|
||||
Success = DeviceIoControl(Handle, FSCTL_DELETE_REPARSE_POINT,
|
||||
@ -206,6 +210,7 @@ static void reparse_nfs_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
&Bytes, 0);
|
||||
ASSERT(!Success);
|
||||
ASSERT(ERROR_NOT_A_REPARSE_POINT == GetLastError());
|
||||
}
|
||||
|
||||
CloseHandle(Handle);
|
||||
|
||||
@ -241,7 +246,7 @@ static void reparse_symlink_dotest0(ULONG Flags, PWSTR Prefix,
|
||||
PUINT8 NameInfoBuf[sizeof(FILE_NAME_INFO) + MAX_PATH];
|
||||
PFILE_NAME_INFO PNameInfo = (PVOID)NameInfoBuf;
|
||||
|
||||
Success = CreateSymbolicLinkW(LinkPath, TargetPath, 0);
|
||||
Success = BestEffortCreateSymbolicLinkW(LinkPath, TargetPath, 0);
|
||||
if (Success)
|
||||
{
|
||||
Handle = CreateFileW(FilePath,
|
||||
@ -401,7 +406,7 @@ static BOOL my_symlink_fn(ULONG Flags, PWSTR Prefix, void *memfs, PWSTR LinkName
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s",
|
||||
FileName);
|
||||
|
||||
return CreateSymbolicLinkW(LinkPath,
|
||||
return BestEffortCreateSymbolicLinkW(LinkPath,
|
||||
-1 == Flags && L'\\' == FileName[0] ? FilePath + 6 : FilePath,
|
||||
SymlinkFlags);
|
||||
}
|
||||
@ -422,8 +427,58 @@ static BOOL my_namecheck_fn(ULONG Flags, PWSTR Prefix, void *memfs, PWSTR FileNa
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
if (INVALID_HANDLE_VALUE == Handle)
|
||||
{
|
||||
/*
|
||||
* Prior to Windows 11 it used to be the case that NTFS open with FILE_FLAG_BACKUP_SEMANTICS
|
||||
* did not care about SYMLINK/SYMLINKD difference!
|
||||
*
|
||||
* On Windows 11 this no longer appears to be true. In order to keep this test around, we perform
|
||||
* an alternative name check in this case.
|
||||
*/
|
||||
|
||||
if (-1 == Flags && (ERROR_ACCESS_DENIED == GetLastError() || ERROR_DIRECTORY == GetLastError()))
|
||||
; /* Windows 11: if NTFS and appropriate error then ignore */
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
Handle = CreateFileW(FilePath, FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
|
||||
if (INVALID_HANDLE_VALUE == Handle)
|
||||
return FALSE;
|
||||
|
||||
union
|
||||
{
|
||||
REPARSE_DATA_BUFFER D;
|
||||
UINT8 B[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
||||
} ReparseDataBuf;
|
||||
DWORD Bytes;
|
||||
BOOL Success;
|
||||
|
||||
Success = DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT,
|
||||
0, 0,
|
||||
&ReparseDataBuf, sizeof ReparseDataBuf,
|
||||
&Bytes, 0);
|
||||
if (Success)
|
||||
{
|
||||
Success = Success &&
|
||||
ReparseDataBuf.D.ReparseTag == IO_REPARSE_TAG_SYMLINK;
|
||||
Success = Success &&
|
||||
ReparseDataBuf.D.SymbolicLinkReparseBuffer.SubstituteNameLength ==
|
||||
wcslen(ExpectedPath + 6) * sizeof(WCHAR);
|
||||
Success = Success &&
|
||||
0 == mywcscmp(
|
||||
ExpectedPath + 6,
|
||||
-1,
|
||||
ReparseDataBuf.D.SymbolicLinkReparseBuffer.PathBuffer +
|
||||
ReparseDataBuf.D.SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
|
||||
ReparseDataBuf.D.SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR));
|
||||
}
|
||||
|
||||
CloseHandle(Handle);
|
||||
return Success;
|
||||
}
|
||||
|
||||
if (GetFileInformationByHandleEx(Handle, FileNameInfo, PNameInfo, sizeof NameInfoBuf))
|
||||
{
|
||||
if (OptSharePrefixLength)
|
||||
@ -500,7 +555,11 @@ static void reparse_symlink_relative_dotest(ULONG Flags, PWSTR Prefix, ULONG Fil
|
||||
my_failcheck(L"\\loop");
|
||||
ASSERT(ERROR_CANT_RESOLVE_FILENAME == GetLastError());
|
||||
|
||||
/* NTFS open with FILE_FLAG_BACKUP_SEMANTICS does not care about SYMLINK/SYMLINKD difference! */
|
||||
/*
|
||||
* NTFS open with FILE_FLAG_BACKUP_SEMANTICS does not care about SYMLINK/SYMLINKD difference!
|
||||
*
|
||||
* UPDATE: Appears to no longer be true on Windows 11!
|
||||
*/
|
||||
my_namecheck(L"\\lf", L"\\1");
|
||||
my_namecheck(L"\\ld", L"\\1\\1.1\\1.1.1");
|
||||
|
||||
@ -555,6 +614,7 @@ void reparse_symlink_relative_test(void)
|
||||
|
||||
void reparse_tests(void)
|
||||
{
|
||||
if (!OptFuseExternal)
|
||||
TEST(reparse_guid_test);
|
||||
TEST(reparse_nfs_test);
|
||||
TEST(reparse_symlink_test);
|
||||
|
@ -333,6 +333,7 @@ void security_stress_meta_test(void)
|
||||
void security_tests(void)
|
||||
{
|
||||
TEST(getsecurity_test);
|
||||
if (!OptFuseExternal)
|
||||
TEST(setsecurity_test);
|
||||
TEST_OPT(security_stress_meta_test);
|
||||
}
|
||||
|
@ -2287,6 +2287,9 @@ void stream_dirnotify_test(void)
|
||||
|
||||
void stream_tests(void)
|
||||
{
|
||||
if (OptFuseExternal)
|
||||
return;
|
||||
|
||||
TEST(stream_create_test);
|
||||
if (!OptOplock)
|
||||
TEST(stream_create_overwrite_test);
|
||||
|
@ -34,6 +34,7 @@ int WinFspDiskTests = 1;
|
||||
int WinFspNetTests = 1;
|
||||
|
||||
BOOLEAN OptExternal = FALSE;
|
||||
BOOLEAN OptFuseExternal = FALSE;
|
||||
BOOLEAN OptResilient = FALSE;
|
||||
BOOLEAN OptCaseInsensitiveCmp = FALSE;
|
||||
BOOLEAN OptCaseInsensitive = FALSE;
|
||||
@ -270,6 +271,15 @@ int main(int argc, char *argv[])
|
||||
WinFspNetTests = 0;
|
||||
rmarg(argv, argc, argi);
|
||||
}
|
||||
else if (0 == strcmp("--fuse-external", a))
|
||||
{
|
||||
OptExternal = TRUE;
|
||||
OptFuseExternal = TRUE;
|
||||
NtfsTests = 1;
|
||||
WinFspDiskTests = 0;
|
||||
WinFspNetTests = 0;
|
||||
rmarg(argv, argc, argi);
|
||||
}
|
||||
else if (0 == strcmp("--resilient", a))
|
||||
{
|
||||
OptResilient = TRUE;
|
||||
|
@ -132,10 +132,32 @@ BOOL WINAPI ResilientDeleteFileW(
|
||||
BOOL WINAPI ResilientRemoveDirectoryW(
|
||||
LPCWSTR lpPathName);
|
||||
|
||||
static inline
|
||||
BOOLEAN BestEffortCreateSymbolicLinkW(
|
||||
PWSTR SymlinkFileName,
|
||||
PWSTR TargetFileName,
|
||||
DWORD Flags)
|
||||
{
|
||||
BOOLEAN Success = CreateSymbolicLinkW(
|
||||
SymlinkFileName,
|
||||
TargetFileName,
|
||||
Flags | 2/*SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE*/);
|
||||
if (!Success && ERROR_INVALID_PARAMETER == GetLastError())
|
||||
Success = CreateSymbolicLinkW(
|
||||
SymlinkFileName,
|
||||
TargetFileName,
|
||||
Flags & ~2/*SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE*/);
|
||||
return Success;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BOOLEAN Disposition;
|
||||
} MY_FILE_DISPOSITION_INFO;
|
||||
typedef struct
|
||||
{
|
||||
ULONG Flags;
|
||||
} MY_FILE_DISPOSITION_INFO_EX;
|
||||
|
||||
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
|
||||
void *memfs_start(ULONG Flags);
|
||||
@ -154,6 +176,7 @@ extern int WinFspDiskTests;
|
||||
extern int WinFspNetTests;
|
||||
|
||||
extern BOOLEAN OptExternal;
|
||||
extern BOOLEAN OptFuseExternal;
|
||||
extern BOOLEAN OptResilient;
|
||||
extern BOOLEAN OptCaseInsensitiveCmp;
|
||||
extern BOOLEAN OptCaseInsensitive;
|
||||
|
@ -184,5 +184,8 @@ static void wsl_stat_test(void)
|
||||
|
||||
void wsl_tests(void)
|
||||
{
|
||||
if (OptFuseExternal)
|
||||
return;
|
||||
|
||||
TEST_OPT(wsl_stat_test);
|
||||
}
|
||||
|
Reference in New Issue
Block a user