Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
90d67bc07d | |||
70643f40a0 | |||
366a9c562f | |||
d7a8b0d9fb | |||
52dd6f7478 | |||
73359d682b | |||
2a3f1a3990 | |||
e823103334 | |||
1208c6c652 | |||
b13b24e0b1 | |||
91aa0ac2d0 | |||
dcce0d44a7 | |||
af257d4bff | |||
9eaaefd154 | |||
bb3e92df6c | |||
76bfa395a8 | |||
81248f3899 | |||
a4d7aee6f5 | |||
19823d84de | |||
3e66082f11 | |||
490d021b22 | |||
2d41693f3c | |||
068270fa7f | |||
f51bdef534 | |||
63f91cc667 | |||
6e3e469fcb |
@ -1,6 +1,40 @@
|
|||||||
= Changelog
|
= Changelog
|
||||||
|
|
||||||
|
|
||||||
|
v1.10B2 (2021.1 Beta2)::
|
||||||
|
|
||||||
|
Changes since v1.9:
|
||||||
|
|
||||||
|
* [NEW] Official Windows 11 support.
|
||||||
|
|
||||||
|
* [NEW] 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. See https://github.com/billziss-gh/winfsp/wiki/WinFsp-Delete-Redesign for more information.
|
||||||
|
|
||||||
|
* [NEW] Support for `FileDispositionInformationEx` and `FileRenameInformationEx` has been added (see above).
|
||||||
|
|
||||||
|
* [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.)
|
||||||
|
|
||||||
|
* [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] 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.10B1 (2021.1 Beta1)::
|
v1.10B1 (2021.1 Beta1)::
|
||||||
|
|
||||||
Changes since v1.9:
|
Changes since v1.9:
|
||||||
|
@ -42,8 +42,8 @@ install:
|
|||||||
build_script:
|
build_script:
|
||||||
- appveyor AddMessage "Reboot complete" -Category Information
|
- appveyor AddMessage "Reboot complete" -Category Information
|
||||||
# build cygfuse
|
# build cygfuse
|
||||||
- C:\cygwin64\setup-x86_64.exe -qnNd -P cygport
|
#- C:\cygwin64\setup-x86_64.exe -qnNd -P cygport
|
||||||
- C:\cygwin64\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
|
#- C:\cygwin64\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
|
||||||
#- C:\cygwin\setup-x86.exe -qnNd -P cygport
|
#- C:\cygwin\setup-x86.exe -qnNd -P cygport
|
||||||
#- C:\cygwin\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
|
#- C:\cygwin\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
|
||||||
# build winfsp
|
# build winfsp
|
||||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 451 KiB After Width: | Height: | Size: 451 KiB |
Before Width: | Height: | Size: 451 KiB After Width: | Height: | Size: 451 KiB |
Before Width: | Height: | Size: 451 KiB After Width: | Height: | Size: 451 KiB |
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<MyCanonicalVersion>1.10</MyCanonicalVersion>
|
<MyCanonicalVersion>1.10</MyCanonicalVersion>
|
||||||
|
|
||||||
<MyProductVersion>2021.1 Beta1</MyProductVersion>
|
<MyProductVersion>2021.1 Beta3</MyProductVersion>
|
||||||
<MyProductStage>Beta</MyProductStage>
|
<MyProductStage>Beta</MyProductStage>
|
||||||
|
|
||||||
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
= Known File Systems and File System Libraries
|
= Known File Systems and File System Libraries
|
||||||
|
|
||||||
This document contains a list of known file systems and file system libraries that run on WinFsp. Please contact the WinFsp project to have your file system solution added to this list.
|
This document contains a list of known open-source file systems and file system libraries that run on WinFsp. Please contact the WinFsp project to have your file system solution added to this list.
|
||||||
|
|
||||||
== File Systems
|
== File Systems
|
||||||
|
|
||||||
@ -9,13 +9,16 @@ This document contains a list of known file systems and file system libraries th
|
|||||||
- https://github.com/lowleveldesign/fsmemfs[fsmemfs] - Memory File System written in F#
|
- https://github.com/lowleveldesign/fsmemfs[fsmemfs] - Memory File System written in F#
|
||||||
- https://github.com/ihaveamac/fuse-3ds[fuse-3ds] - FUSE Filesystem Python scripts for Nintendo 3DS files
|
- https://github.com/ihaveamac/fuse-3ds[fuse-3ds] - FUSE Filesystem Python scripts for Nintendo 3DS files
|
||||||
- https://github.com/sganis/golddrive[golddrive] - Windows ssh network drive
|
- https://github.com/sganis/golddrive[golddrive] - Windows ssh network drive
|
||||||
|
- https://github.com/billziss-gh/hubfs[hubfs] - File system for GitHub
|
||||||
- https://github.com/FrKaram/KS2.Drive[KS2.Drive] - Mount a webDAV/AOS server as a local drive
|
- https://github.com/FrKaram/KS2.Drive[KS2.Drive] - Mount a webDAV/AOS server as a local drive
|
||||||
- https://github.com/billziss-gh/nfs-win[nfs-win] - NFS for Windows
|
- https://github.com/billziss-gh/nfs-win[nfs-win] - NFS for Windows
|
||||||
|
- https://github.com/billziss-gh/objfs[objfs] - Object Storage File System
|
||||||
- https://github.com/ncw/rclone[rclone] - rsync for cloud storage
|
- https://github.com/ncw/rclone[rclone] - rsync for cloud storage
|
||||||
- https://github.com/hasse69/rar2fs[rar2fs] - FUSE file system for reading RAR archives
|
- https://github.com/hasse69/rar2fs[rar2fs] - FUSE file system for reading RAR archives
|
||||||
- https://github.com/billziss-gh/redditfs[redditfs] - ls -l /r/programming
|
- https://github.com/billziss-gh/redditfs[redditfs] - ls -l /r/programming
|
||||||
- https://github.com/netheril96/securefs[securefs] - Filesystem in userspace (FUSE) with transparent authenticated encryption
|
- https://github.com/netheril96/securefs[securefs] - Filesystem in userspace (FUSE) with transparent authenticated encryption
|
||||||
- https://github.com/billziss-gh/sshfs-win[sshfs-win] - SSHFS for Windows
|
- https://github.com/billziss-gh/sshfs-win[sshfs-win] - SSHFS for Windows
|
||||||
|
- https://github.com/printpagestopdf/WordpressDrive[WordpressDrive] - Windows Userspace Filesystem based on WinFsp that presents a Wordpress Site as a Windows Drive
|
||||||
- https://github.com/emoose/xbox-winfsp[xbox-winfsp] - Adds native support to Windows for the FATX, STFS & GDFX (aka XGD/XDVDFS) Xbox filesystems.
|
- https://github.com/emoose/xbox-winfsp[xbox-winfsp] - Adds native support to Windows for the FATX, STFS & GDFX (aka XGD/XDVDFS) Xbox filesystems.
|
||||||
- https://github.com/UtrechtUniversity/YodaDrive[YodaDrive] - Mount a Yoda drive as a local drive
|
- https://github.com/UtrechtUniversity/YodaDrive[YodaDrive] - Mount a Yoda drive as a local drive
|
||||||
|
|
||||||
|
@ -17,13 +17,14 @@ WinFsp supports the following NTFS features:
|
|||||||
- Opportunistic locks.
|
- Opportunistic locks.
|
||||||
- Open, create, close, delete, query named streams.
|
- Open, create, close, delete, query named streams.
|
||||||
- Reparse points with special support for symbolic links.
|
- Reparse points with special support for symbolic links.
|
||||||
|
- Extended attributes.
|
||||||
|
- Traditional Windows and POSIX semantics for delete and rename.
|
||||||
|
|
||||||
== Unsupported features
|
== Unsupported features
|
||||||
|
|
||||||
WinFsp does not support the following NTFS features:
|
WinFsp does not support the following NTFS features:
|
||||||
|
|
||||||
- Hard links. Rather rarely used on Windows. However it might be worthwhile to implement them for WinFsp.
|
- Hard links. Rather rarely used on Windows. However it might be worthwhile to implement them for WinFsp.
|
||||||
- Extended attributes. Although popular with POSIX file systems, they are severely hampered and rarely used on Windows. They are also not exposed via the Win32 API.
|
|
||||||
- Short file names. Short file names are a relic of the past. WinFsp made a conscious decision not to support them.
|
- Short file names. Short file names are a relic of the past. WinFsp made a conscious decision not to support them.
|
||||||
- Paging files. Providing paging file support via a user mode file system is impossible for a number of reasons.
|
- Paging files. Providing paging file support via a user mode file system is impossible for a number of reasons.
|
||||||
- Object ID's. Opening files by ID (+FILE_OPEN_BY_FILE_ID+) is not supported.
|
- Object ID's. Opening files by ID (+FILE_OPEN_BY_FILE_ID+) is not supported.
|
||||||
|
@ -397,11 +397,13 @@ typedef struct _FSP_LAUNCH_REG_RECORD {
|
|||||||
PWSTR RunAs;
|
PWSTR RunAs;
|
||||||
PWSTR Security;
|
PWSTR Security;
|
||||||
PWSTR AuthPackage;
|
PWSTR AuthPackage;
|
||||||
PVOID Reserved0[5];
|
PWSTR Stderr;
|
||||||
|
PVOID Reserved0[4];
|
||||||
ULONG JobControl;
|
ULONG JobControl;
|
||||||
ULONG Credentials;
|
ULONG Credentials;
|
||||||
ULONG AuthPackageId;
|
ULONG AuthPackageId;
|
||||||
ULONG Reserved1[5];
|
ULONG Recovery;
|
||||||
|
ULONG Reserved1[4];
|
||||||
UINT8 Buffer[];
|
UINT8 Buffer[];
|
||||||
} FSP_LAUNCH_REG_RECORD;
|
} FSP_LAUNCH_REG_RECORD;
|
||||||
```
|
```
|
||||||
@ -414,7 +416,7 @@ typedef struct _FSP_LAUNCH_REG_RECORD {
|
|||||||
<br/>
|
<br/>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<sub>
|
<sub>
|
||||||
Copyright © 2015-2020 Bill Zissimopoulos
|
Copyright © 2015-2021 Bill Zissimopoulos
|
||||||
<br/>
|
<br/>
|
||||||
Generated with <a href="https://github.com/billziss-gh/prettydoc">prettydoc</a>
|
Generated with <a href="https://github.com/billziss-gh/prettydoc">prettydoc</a>
|
||||||
</sub>
|
</sub>
|
||||||
|
@ -83,6 +83,8 @@ STATUS\_SUCCESS or error code.
|
|||||||
|
|
||||||
**Discussion**
|
**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
|
This function tests whether a file or directory can be safely deleted. This function does
|
||||||
not need to perform access checks, but may performs tasks such as check for empty
|
not need to perform access checks, but may performs tasks such as check for empty
|
||||||
directories, etc.
|
directories, etc.
|
||||||
@ -100,6 +102,7 @@ most file systems need only implement the CanDelete operation.
|
|||||||
|
|
||||||
- Cleanup
|
- Cleanup
|
||||||
- SetDelete
|
- SetDelete
|
||||||
|
- Delete
|
||||||
|
|
||||||
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
@ -129,6 +132,9 @@ VOID ( *Cleanup)(
|
|||||||
|
|
||||||
**Discussion**
|
**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
|
When CreateFile is used to open or create a file the kernel creates a kernel mode file
|
||||||
object (type FILE\_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
object (type FILE\_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
||||||
be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
||||||
@ -179,6 +185,7 @@ the file was modified/deleted.
|
|||||||
- Close
|
- Close
|
||||||
- CanDelete
|
- CanDelete
|
||||||
- SetDelete
|
- SetDelete
|
||||||
|
- Delete
|
||||||
|
|
||||||
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
@ -368,6 +375,90 @@ may contain extended attributes or a reparse point.
|
|||||||
NOTE: If both Create and CreateEx are defined, CreateEx takes precedence.
|
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>
|
</blockquote>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -1111,6 +1202,8 @@ STATUS\_SUCCESS or error code.
|
|||||||
|
|
||||||
**Discussion**
|
**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
|
This function sets a flag to indicates whether the FSD file should delete a file
|
||||||
when it is closed. This function does not need to perform access checks, but may
|
when it is closed. This function does not need to perform access checks, but may
|
||||||
performs tasks such as check for empty directories, etc.
|
performs tasks such as check for empty directories, etc.
|
||||||
@ -1128,6 +1221,7 @@ most file systems need only implement the CanDelete operation.
|
|||||||
|
|
||||||
- Cleanup
|
- Cleanup
|
||||||
- CanDelete
|
- CanDelete
|
||||||
|
- Delete
|
||||||
|
|
||||||
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
@ -1504,6 +1598,47 @@ This is a helper for implementing the GetEa operation.
|
|||||||
- GetEa
|
- GetEa
|
||||||
|
|
||||||
|
|
||||||
|
</blockquote>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
<b>FspFileSystemAddNotifyInfo</b> - Add notify information to a buffer.
|
||||||
|
</summary>
|
||||||
|
<blockquote>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
```c
|
||||||
|
FSP_API BOOLEAN FspFileSystemAddNotifyInfo(
|
||||||
|
FSP_FSCTL_NOTIFY_INFO *NotifyInfo,
|
||||||
|
PVOID Buffer,
|
||||||
|
ULONG Length,
|
||||||
|
PULONG PBytesTransferred);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- _NotifyInfo_ \- The notify information to add.
|
||||||
|
- _Buffer_ \- Pointer to a buffer that will receive the notify information.
|
||||||
|
- _Length_ \- Length of buffer.
|
||||||
|
- _PBytesTransferred_ \- [out]
|
||||||
|
Pointer to a memory location that will receive the actual number of bytes stored. This should
|
||||||
|
be initialized to 0 prior to the first call to FspFileSystemAddNotifyInfo for a particular
|
||||||
|
buffer.
|
||||||
|
|
||||||
|
**Return Value**
|
||||||
|
|
||||||
|
TRUE if the notify information was added, FALSE if there was not enough space to add it.
|
||||||
|
|
||||||
|
**Discussion**
|
||||||
|
|
||||||
|
This is a helper for filling a buffer to use with FspFileSystemNotify.
|
||||||
|
|
||||||
|
**See Also**
|
||||||
|
|
||||||
|
- FspFileSystemNotify
|
||||||
|
|
||||||
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -1851,6 +1986,123 @@ The current operation context is stored in thread local storage. It allows acces
|
|||||||
Request and Response associated with this operation.
|
Request and Response associated with this operation.
|
||||||
|
|
||||||
|
|
||||||
|
</blockquote>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
<b>FspFileSystemNotify</b> - Notify Windows that the file system has file changes.
|
||||||
|
</summary>
|
||||||
|
<blockquote>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
```c
|
||||||
|
FSP_API NTSTATUS FspFileSystemNotify(
|
||||||
|
FSP_FILE_SYSTEM *FileSystem,
|
||||||
|
FSP_FSCTL_NOTIFY_INFO *NotifyInfo,
|
||||||
|
SIZE_T Size);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- _FileSystem_ \- The file system object.
|
||||||
|
- _NotifyInfo_ \- Buffer containing information about file changes.
|
||||||
|
- _Size_ \- Size of buffer.
|
||||||
|
|
||||||
|
**Return Value**
|
||||||
|
|
||||||
|
STATUS\_SUCCESS or error code.
|
||||||
|
|
||||||
|
**Discussion**
|
||||||
|
|
||||||
|
A file system that wishes to notify Windows about file changes must
|
||||||
|
first issue an FspFileSystemBegin call, followed by 0 or more
|
||||||
|
FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||||
|
|
||||||
|
Note that FspFileSystemNotify requires file names to be normalized. A
|
||||||
|
normalized file name is one that contains the correct case of all characters
|
||||||
|
in the file name.
|
||||||
|
|
||||||
|
For case-sensitive file systems all file names are normalized by definition.
|
||||||
|
For case-insensitive file systems that implement file name normalization,
|
||||||
|
a normalized file name is the one that the file system specifies in the
|
||||||
|
response to Create or Open (see also FspFileSystemGetOpenFileInfo). For
|
||||||
|
case-insensitive file systems that do not implement file name normalization
|
||||||
|
a normalized file name is the upper case version of the file name used
|
||||||
|
to open the file.
|
||||||
|
|
||||||
|
|
||||||
|
</blockquote>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
<b>FspFileSystemNotifyBegin</b> - Begin notifying Windows that the file system has file changes.
|
||||||
|
</summary>
|
||||||
|
<blockquote>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
```c
|
||||||
|
FSP_API NTSTATUS FspFileSystemNotifyBegin(
|
||||||
|
FSP_FILE_SYSTEM *FileSystem,
|
||||||
|
ULONG Timeout);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- _FileSystem_ \- The file system object.
|
||||||
|
|
||||||
|
**Return Value**
|
||||||
|
|
||||||
|
STATUS\_SUCCESS or error code. The error code STATUS\_CANT\_WAIT means that
|
||||||
|
a file rename operation is currently in progress and the operation must be
|
||||||
|
retried at a later time.
|
||||||
|
|
||||||
|
**Discussion**
|
||||||
|
|
||||||
|
A file system that wishes to notify Windows about file changes must
|
||||||
|
first issue an FspFileSystemBegin call, followed by 0 or more
|
||||||
|
FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||||
|
|
||||||
|
This operation blocks concurrent file rename operations. File rename
|
||||||
|
operations may interfere with file notification, because a file being
|
||||||
|
notified may also be concurrently renamed. After all file change
|
||||||
|
notifications have been issued, you must make sure to call
|
||||||
|
FspFileSystemNotifyEnd to allow file rename operations to proceed.
|
||||||
|
|
||||||
|
|
||||||
|
</blockquote>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
<b>FspFileSystemNotifyEnd</b> - End notifying Windows that the file system has file changes.
|
||||||
|
</summary>
|
||||||
|
<blockquote>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
```c
|
||||||
|
FSP_API NTSTATUS FspFileSystemNotifyEnd(
|
||||||
|
FSP_FILE_SYSTEM *FileSystem);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
- _FileSystem_ \- The file system object.
|
||||||
|
|
||||||
|
**Return Value**
|
||||||
|
|
||||||
|
STATUS\_SUCCESS or error code.
|
||||||
|
|
||||||
|
**Discussion**
|
||||||
|
|
||||||
|
A file system that wishes to notify Windows about file changes must
|
||||||
|
first issue an FspFileSystemBegin call, followed by 0 or more
|
||||||
|
FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||||
|
|
||||||
|
This operation allows any blocked file rename operations to proceed.
|
||||||
|
|
||||||
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -2613,7 +2865,7 @@ in a clean manner by calling this function.
|
|||||||
<br/>
|
<br/>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<sub>
|
<sub>
|
||||||
Copyright © 2015-2020 Bill Zissimopoulos
|
Copyright © 2015-2021 Bill Zissimopoulos
|
||||||
<br/>
|
<br/>
|
||||||
Generated with <a href="https://github.com/billziss-gh/prettydoc">prettydoc</a>
|
Generated with <a href="https://github.com/billziss-gh/prettydoc">prettydoc</a>
|
||||||
</sub>
|
</sub>
|
||||||
|
124
doc/WinFsp-Delete-Redesign.asciidoc
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
= 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.
|
||||||
|
|
||||||
|
== Background
|
||||||
|
|
||||||
|
In this section we discuss how file deletion worked in Windows traditionally as well as the changes introduced in recent versions of Windows 10.
|
||||||
|
|
||||||
|
=== Traditional File Deletion
|
||||||
|
|
||||||
|
The traditional method for deleting a file or directory on Windows involves the following steps:
|
||||||
|
|
||||||
|
- Open the file using https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntopenfile[`NtOpenFile`] (or equivalent) with `DELETE` access.
|
||||||
|
- Set the "disposition" flag on the file handle using https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntsetinformationfile[`NtSetInformationFile`] with `FileDispositionInformation`. This only marks the file for deletion and does not delete the file.
|
||||||
|
- Close the file using https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntclose[`NtClose`] (or equivalent). Provided that there are no other open handles to the file, the file is actually deleted at this stage.
|
||||||
|
|
||||||
|
This is the method that https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-deletefilew[`DeleteFileW`] and https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectoryw[`RemoveDirectoryW`] use to delete a file or directory.
|
||||||
|
|
||||||
|
An alternative method involves the `FILE_DELETE_ON_CLOSE` flag:
|
||||||
|
|
||||||
|
- Open the file using https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntopenfile[`NtOpenFile`] (or equivalent) with the `FILE_DELETE_ON_CLOSE` option.
|
||||||
|
- Close the file using https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntclose[`NtClose`] (or equivalent). Provided that there are no other open handles to the file, the file is actually deleted at this stage.
|
||||||
|
|
||||||
|
This alternative method does not set the disposition flag and therefore does not have chance to perform any associated checks. An important disposition flag check is whether a directory is empty: attempting to remove a non-empty directory using `FILE_DELETE_ON_CLOSE` will fail silently, because there is no way to communicate a file deletion error from `NtClose`.
|
||||||
|
|
||||||
|
In order to better understand those scenarios let's examine what happens within the kernel and the file system driver (FSD).
|
||||||
|
|
||||||
|
When the kernel receives a file API call such as `NtOpenFile`, it packages the call arguments into a data structure called an "I/O Request Packet" (IRP) and forwards it to the appropriate FSD. Each IRP contains a field that describes its function, for example, `IRP_MJ_CREATE` for `NtOpenFile` and `IRP_MJ_SET_INFORMATION` for `NtSetInformationFile`.
|
||||||
|
|
||||||
|
With this knowledge we can now examine what happens in the `DeleteFileW` / `RemoveDirectoryW` scenario:
|
||||||
|
|
||||||
|
- 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`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Some important takeaways:
|
||||||
|
|
||||||
|
- It is possible for a file to already be open when a `DeleteFileW` / `RemoveDirectoryW` (or equivalent sequence of `NtOpenFile`, `NtSetInformationFile`, `NtClose`, etc.) is executed. This means that the file may NOT be deleted upon return from the `DeleteFileW` / `RemoveDirectoryW` call even though these API's report success. **Traditionally a successful return from `DeleteFileW` / `RemoveDirectory` signifies only that the file or directory has been successfully marked for deletion and not that it has been deleted!**
|
||||||
|
- The `NtClose` call does not return error codes from `IRP_MJ_CLEANUP`. This means that it is impossible for user mode to know whether a file marked for deletion was deleted or not.
|
||||||
|
- The `FILE_OBJECT` remains valid even after a file has been deleted in `IRP_MJ_CLEANUP`. It is therefore possible to receive additional I/O (e.g. read/write) on the file. Many Windows file systems (including at least some versions of NTFS) do not handle this case very well.
|
||||||
|
|
||||||
|
=== File Deletion in Recent Versions of Windows 10
|
||||||
|
|
||||||
|
In Windows 10 Redstone 1 Microsoft introduced the `FileDispositionInformationEx` information class. This new information class can be used to request POSIX semantics for file deletion during the `NtSetInformationFile` call. POSIX semantics for file deletion mean that when a file is deleted any open handles to it remain valid and can be used for I/O such as read/write.
|
||||||
|
|
||||||
|
Some time later (unclear exactly when) Microsoft changed the `DeleteFileW` / `RemoveDirectoryW` API's to use the `FileDispositionInformationEx` information class and only if this fails (e.g. because the file system is not capable) fall back to the old `FileDispositionInformation` information class. With this change `DeleteFileW` and `RemoveDirectoryW` actually delete the file or directory rather than simply mark it for deletion. (This change is in general a good thing, but can create incompatibility problems for applications that expect the traditional behavior.)
|
||||||
|
|
||||||
|
Let's examine the `DeleteFileW` / `RemoveDirectoryW` scenario again:
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- Intruct the file system to delete the file with POSIX semantics using `NtSetInformationFile` with `FileDispositionInformationEx`. 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 `FileDispositionInformationEx` information in it. The FSD performs some checks (e.g. if a directory is empty) and if they succeed it deletes the file or directory (as opposed to simply mark it 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. The FSD has already deleted the file and does not need to do anything else.
|
||||||
|
- As before an `IRP_MJ_CLOSE` IRP will also be sent to the FSD eventually.
|
||||||
|
|
||||||
|
Notice that the actual file deletion happens during `NtSetInformationFile` and the return code from this API reports on the success or failure of the file deletion. Thus we no longer have the problems discussed earlier and `DeleteFileW` / `RemoveDirectoryW` correctly report whether the file was deleted or not.
|
||||||
|
|
||||||
|
== WinFsp Support for POSIX Unlink
|
||||||
|
|
||||||
|
WinFsp gained support for POSIX Unlink (`FileDispositionInformationEx`) and POSIX Rename (`FileRenameInformationEx`) in release 2021.1 Beta3. To enable this support a native or .NET file system must set the `SupportsPosixUnlinkRename` flag when the file system is created. FUSE file systems have this flag enabled by default (but can be disabled with the command line option `-o LegacyUnlinkRename`).
|
||||||
|
|
||||||
|
The POSIX Unlink support spurred some changes in the WinFsp native and .NET API's with regards to file deletion. The WinFsp FUSE layer transparently supports these changes.
|
||||||
|
|
||||||
|
Prior to release 2021.1 Beta3, user mode file systems handled file deletion by implementing `CanDelete` / `SetDelete` to check the file disposition flag and `Cleanup` with the `FspCleanupDelete` flag to perform the actual file deletion. From release 2021.1 Beta3 forward the recommended method is to use the new `Delete` file system operation to handle all aspects of file deletion.
|
||||||
|
|
||||||
|
The new `Delete` operation follows the general pattern below:
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
|
||||||
|
PVOID FileContext, PWSTR FileName, ULONG Flags)
|
||||||
|
{
|
||||||
|
switch (Flags)
|
||||||
|
{
|
||||||
|
case FILE_DISPOSITION_DO_NOT_DELETE:
|
||||||
|
// set file disposition flag: do not delete file at Cleanup time
|
||||||
|
|
||||||
|
case FILE_DISPOSITION_DELETE:
|
||||||
|
// set file disposition flag: delete file at Cleanup time
|
||||||
|
|
||||||
|
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
|
||||||
|
// delete file now; open handles to file remain valid
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
// delete file now; called during Cleanup time
|
||||||
|
|
||||||
|
default:
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
As can be seen the `Delete` operation handles marking (`FILE_DISPOSITION_DELETE`) and unmarking (`FILE_DISPOSITION_DO_NOT_DELETE`) a file for deletion, performing file deletion with POSIX semantics (`FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS`) and performing file deletion with traditional Windows semantics (`-1`). If the Delete operation is defined it is used instead of `CanDelete` / `SetDelete` and `Cleanup` with the `FspCleanupDelete` flag, even if these operations are also defined.
|
||||||
|
|
||||||
|
A sensible implementation of `Delete` might look something similar to the following:
|
||||||
|
|
||||||
|
[source,c]
|
||||||
|
----
|
||||||
|
NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
|
||||||
|
PVOID FileContext, PWSTR FileName, ULONG Flags)
|
||||||
|
{
|
||||||
|
switch (Flags)
|
||||||
|
{
|
||||||
|
case FILE_DISPOSITION_DO_NOT_DELETE:
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
|
case FILE_DISPOSITION_DELETE:
|
||||||
|
if (IsNotEmptyDirectory(FileSystem, FileContext))
|
||||||
|
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
|
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
|
||||||
|
case -1:
|
||||||
|
if (IsNotEmptyDirectory(FileSystem, FileContext))
|
||||||
|
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||||
|
return RealDeleteFileOrDirectory(FileSystem, FileContext);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
@ -79,7 +79,7 @@ struct fuse_operations
|
|||||||
/* S */ int (*fsyncdir)(const char *path, int datasync, struct fuse_file_info *fi);
|
/* S */ int (*fsyncdir)(const char *path, int datasync, struct fuse_file_info *fi);
|
||||||
/* S */ void *(*init)(struct fuse_conn_info *conn);
|
/* S */ void *(*init)(struct fuse_conn_info *conn);
|
||||||
/* S */ void (*destroy)(void *data);
|
/* S */ void (*destroy)(void *data);
|
||||||
/* _ */ int (*access)(const char *path, int mask);
|
/* S */ int (*access)(const char *path, int mask);
|
||||||
/* S */ int (*create)(const char *path, fuse_mode_t mode, struct fuse_file_info *fi);
|
/* S */ int (*create)(const char *path, fuse_mode_t mode, struct fuse_file_info *fi);
|
||||||
/* S */ int (*ftruncate)(const char *path, fuse_off_t off, struct fuse_file_info *fi);
|
/* S */ int (*ftruncate)(const char *path, fuse_off_t off, struct fuse_file_info *fi);
|
||||||
/* S */ int (*fgetattr)(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi);
|
/* S */ int (*fgetattr)(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi);
|
||||||
|
@ -54,6 +54,7 @@ extern "C" {
|
|||||||
#define FSP_FUSE_CAP_READDIR_PLUS (1 << 21) /* file system supports enhanced readdir */
|
#define FSP_FUSE_CAP_READDIR_PLUS (1 << 21) /* file system supports enhanced readdir */
|
||||||
#define FSP_FUSE_CAP_READ_ONLY (1 << 22) /* file system is marked read-only */
|
#define FSP_FUSE_CAP_READ_ONLY (1 << 22) /* file system is marked read-only */
|
||||||
#define FSP_FUSE_CAP_STAT_EX (1 << 23) /* file system supports fuse_stat_ex */
|
#define FSP_FUSE_CAP_STAT_EX (1 << 23) /* file system supports fuse_stat_ex */
|
||||||
|
#define FSP_FUSE_CAP_DELETE_ACCESS (1 << 24) /* file system supports access with DELETE_OK */
|
||||||
#define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE
|
#define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE
|
||||||
|
|
||||||
#define FUSE_IOCTL_COMPAT (1 << 0)
|
#define FUSE_IOCTL_COMPAT (1 << 0)
|
||||||
@ -79,6 +80,9 @@ extern "C" {
|
|||||||
#define UF_ARCHIVE FSP_FUSE_UF_ARCHIVE
|
#define UF_ARCHIVE FSP_FUSE_UF_ARCHIVE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* delete access */
|
||||||
|
#define FSP_FUSE_DELETE_OK 0x40000000
|
||||||
|
|
||||||
/* notify extension */
|
/* notify extension */
|
||||||
#define FSP_FUSE_NOTIFY_MKDIR 0x0001
|
#define FSP_FUSE_NOTIFY_MKDIR 0x0001
|
||||||
#define FSP_FUSE_NOTIFY_RMDIR 0x0002
|
#define FSP_FUSE_NOTIFY_RMDIR 0x0002
|
||||||
|
@ -204,7 +204,8 @@ enum
|
|||||||
UINT32 WslFeatures:1; /* support features required for WSLinux */\
|
UINT32 WslFeatures:1; /* support features required for WSLinux */\
|
||||||
UINT32 DirectoryMarkerAsNextOffset:1; /* directory marker is next offset instead of last name */\
|
UINT32 DirectoryMarkerAsNextOffset:1; /* directory marker is next offset instead of last name */\
|
||||||
UINT32 RejectIrpPriorToTransact0:1; /* reject IRP's prior to FspFsctlTransact with 0 buffers */\
|
UINT32 RejectIrpPriorToTransact0:1; /* reject IRP's prior to FspFsctlTransact with 0 buffers */\
|
||||||
UINT32 KmReservedFlags:3;\
|
UINT32 SupportsPosixUnlinkRename:1; /* file system supports POSIX-style unlink and rename */\
|
||||||
|
UINT32 KmReservedFlags:2;\
|
||||||
WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\
|
WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\
|
||||||
WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)];
|
WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)];
|
||||||
#define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\
|
#define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\
|
||||||
@ -413,6 +414,10 @@ typedef struct
|
|||||||
UINT32 Delete:1;
|
UINT32 Delete:1;
|
||||||
} Disposition;
|
} Disposition;
|
||||||
struct
|
struct
|
||||||
|
{
|
||||||
|
UINT32 Flags;
|
||||||
|
} DispositionEx;
|
||||||
|
struct
|
||||||
{
|
{
|
||||||
UINT64 FileSize;
|
UINT64 FileSize;
|
||||||
} EndOfFile;
|
} EndOfFile;
|
||||||
@ -421,6 +426,12 @@ typedef struct
|
|||||||
FSP_FSCTL_TRANSACT_BUF NewFileName;
|
FSP_FSCTL_TRANSACT_BUF NewFileName;
|
||||||
UINT64 AccessToken; /* request access token (PID,HANDLE) */
|
UINT64 AccessToken; /* request access token (PID,HANDLE) */
|
||||||
} Rename;
|
} Rename;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
FSP_FSCTL_TRANSACT_BUF NewFileName;
|
||||||
|
UINT64 AccessToken; /* request access token (PID,HANDLE) */
|
||||||
|
UINT32 Flags;
|
||||||
|
} RenameEx;
|
||||||
} Info;
|
} Info;
|
||||||
} SetInformation;
|
} SetInformation;
|
||||||
struct
|
struct
|
||||||
|
@ -47,6 +47,19 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The FILE_DISPOSITION_* definitions appear to be missing from the user mode headers.
|
||||||
|
*/
|
||||||
|
#if !defined(FILE_DISPOSITION_DELETE)
|
||||||
|
#define FILE_DISPOSITION_DO_NOT_DELETE 0x00000000
|
||||||
|
#define FILE_DISPOSITION_DELETE 0x00000001
|
||||||
|
#define FILE_DISPOSITION_POSIX_SEMANTICS 0x00000002
|
||||||
|
/* remaining flags are not needed for user mode file systems but included for completeness */
|
||||||
|
#define FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK 0x00000004
|
||||||
|
#define FILE_DISPOSITION_ON_CLOSE 0x00000008
|
||||||
|
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x00000010
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The REPARSE_DATA_BUFFER definitions appear to be missing from the user mode headers.
|
* The REPARSE_DATA_BUFFER definitions appear to be missing from the user mode headers.
|
||||||
*/
|
*/
|
||||||
@ -348,6 +361,9 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
|||||||
/**
|
/**
|
||||||
* Cleanup a file.
|
* Cleanup a file.
|
||||||
*
|
*
|
||||||
|
* (NOTE: use of this function with the FspCleanupDelete flag is not recommended;
|
||||||
|
* use Delete instead.)
|
||||||
|
*
|
||||||
* When CreateFile is used to open or create a file the kernel creates a kernel mode file
|
* When CreateFile is used to open or create a file the kernel creates a kernel mode file
|
||||||
* object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
* object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
||||||
* be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
* be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
||||||
@ -402,6 +418,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
|||||||
* Close
|
* Close
|
||||||
* CanDelete
|
* CanDelete
|
||||||
* SetDelete
|
* SetDelete
|
||||||
|
* Delete
|
||||||
*/
|
*/
|
||||||
VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem,
|
VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem,
|
||||||
PVOID FileContext, PWSTR FileName, ULONG Flags);
|
PVOID FileContext, PWSTR FileName, ULONG Flags);
|
||||||
@ -575,6 +592,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
|||||||
/**
|
/**
|
||||||
* Determine whether a file or directory can be deleted.
|
* Determine whether a file or directory can be deleted.
|
||||||
*
|
*
|
||||||
|
* (NOTE: use of this function is not recommended; use Delete instead.)
|
||||||
|
*
|
||||||
* This function tests whether a file or directory can be safely deleted. This function does
|
* This function tests whether a file or directory can be safely deleted. This function does
|
||||||
* not need to perform access checks, but may performs tasks such as check for empty
|
* not need to perform access checks, but may performs tasks such as check for empty
|
||||||
* directories, etc.
|
* directories, etc.
|
||||||
@ -599,6 +618,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
|||||||
* @see
|
* @see
|
||||||
* Cleanup
|
* Cleanup
|
||||||
* SetDelete
|
* SetDelete
|
||||||
|
* Delete
|
||||||
*/
|
*/
|
||||||
NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem,
|
NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem,
|
||||||
PVOID FileContext, PWSTR FileName);
|
PVOID FileContext, PWSTR FileName);
|
||||||
@ -880,6 +900,8 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
|||||||
/**
|
/**
|
||||||
* Set the file delete flag.
|
* Set the file delete flag.
|
||||||
*
|
*
|
||||||
|
* (NOTE: use of this function is not recommended; use Delete instead.)
|
||||||
|
*
|
||||||
* This function sets a flag to indicates whether the FSD file should delete a file
|
* This function sets a flag to indicates whether the FSD file should delete a file
|
||||||
* when it is closed. This function does not need to perform access checks, but may
|
* when it is closed. This function does not need to perform access checks, but may
|
||||||
* performs tasks such as check for empty directories, etc.
|
* performs tasks such as check for empty directories, etc.
|
||||||
@ -908,6 +930,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
|||||||
* @see
|
* @see
|
||||||
* Cleanup
|
* Cleanup
|
||||||
* CanDelete
|
* CanDelete
|
||||||
|
* Delete
|
||||||
*/
|
*/
|
||||||
NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem,
|
NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem,
|
||||||
PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile);
|
PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile);
|
||||||
@ -1040,12 +1063,65 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
|
|||||||
PVOID FileContext,
|
PVOID FileContext,
|
||||||
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
|
PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength,
|
||||||
FSP_FSCTL_FILE_INFO *FileInfo);
|
FSP_FSCTL_FILE_INFO *FileInfo);
|
||||||
|
/**
|
||||||
|
* Set the file delete flag or delete a file.
|
||||||
|
*
|
||||||
|
* This function replaces CanDelete, SetDelete and uses of Cleanup with the FspCleanupDelete flag
|
||||||
|
* and is recommended for use in all new code.
|
||||||
|
*
|
||||||
|
* Due to the complexity of file deletion in the Windows file system this function is used
|
||||||
|
* in many scenarios. Its usage is controlled by the Flags parameter:
|
||||||
|
* <ul>
|
||||||
|
* <li>FILE_DISPOSITION_DO_NOT_DELETE: Unmark the file for deletion.
|
||||||
|
* Do <b>NOT</b> delete the file either now or at Cleanup time.</li>
|
||||||
|
* <li>FILE_DISPOSITION_DELETE: Mark the file for deletion,
|
||||||
|
* but do <b>NOT</b> delete the file. The file will be deleted at Cleanup time
|
||||||
|
* (via a call to Delete with Flags = -1).
|
||||||
|
* This function does not need to perform access checks, but may
|
||||||
|
* performs tasks such as check for empty directories, etc.</li>
|
||||||
|
* <li>FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS: Delete the file
|
||||||
|
* <b>NOW</b> using POSIX semantics. Open user mode handles to the file remain valid.
|
||||||
|
* This case will be received only when FSP_FSCTL_VOLUME_PARAMS :: SupportsPosixUnlinkRename is set.</li>
|
||||||
|
* <li>-1: Delete the file <b>NOW</b> using regular Windows semantics.
|
||||||
|
* Called during Cleanup with no open user mode handles remaining.
|
||||||
|
* If a file system implements Delete, Cleanup should <b>NOT</b> be used for deletion anymore.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* This function gets called in all file deletion scenarios:
|
||||||
|
* <ul>
|
||||||
|
* <li>When the DeleteFile or RemoveDirectory API's are used.</li>
|
||||||
|
* <li>When the SetInformationByHandle API with FileDispositionInfo or FileDispositionInfoEx is used.</li>
|
||||||
|
* <li>When a file is opened using FILE_DELETE_ON_CLOSE.</li>
|
||||||
|
* <li>Etc.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* NOTE: Delete takes precedence over CanDelete, SetDelete and Cleanup with the FspCleanupDelete flag.
|
||||||
|
* This means that if Delete is defined, CanDelete and SetDelete will never be called and
|
||||||
|
* Cleanup will never be called with the FspCleanupDelete flag.
|
||||||
|
*
|
||||||
|
* @param FileSystem
|
||||||
|
* The file system on which this request is posted.
|
||||||
|
* @param FileContext
|
||||||
|
* The file context of the file or directory to set the delete flag for.
|
||||||
|
* @param FileName
|
||||||
|
* The name of the file or directory to set the delete flag for.
|
||||||
|
* @param Flags
|
||||||
|
* File disposition flags
|
||||||
|
* @return
|
||||||
|
* STATUS_SUCCESS or error code.
|
||||||
|
* @see
|
||||||
|
* Cleanup
|
||||||
|
* CanDelete
|
||||||
|
* SetDelete
|
||||||
|
*/
|
||||||
|
NTSTATUS (*Delete)(FSP_FILE_SYSTEM *FileSystem,
|
||||||
|
PVOID FileContext, PWSTR FileName, ULONG Flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This ensures that this interface will always contain 64 function pointers.
|
* This ensures that this interface will always contain 64 function pointers.
|
||||||
* Please update when changing the interface as it is important for future compatibility.
|
* Please update when changing the interface as it is important for future compatibility.
|
||||||
*/
|
*/
|
||||||
NTSTATUS (*Reserved[33])();
|
NTSTATUS (*Reserved[32])();
|
||||||
} FSP_FILE_SYSTEM_INTERFACE;
|
} FSP_FILE_SYSTEM_INTERFACE;
|
||||||
FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()),
|
FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()),
|
||||||
"FSP_FILE_SYSTEM_INTERFACE must have 64 entries.");
|
"FSP_FILE_SYSTEM_INTERFACE must have 64 entries.");
|
||||||
@ -1383,7 +1459,8 @@ UINT32 FspFileSystemOperationProcessId(VOID)
|
|||||||
case FspFsctlTransactCreateKind:
|
case FspFsctlTransactCreateKind:
|
||||||
return FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.Create.AccessToken);
|
return FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.Create.AccessToken);
|
||||||
case FspFsctlTransactSetInformationKind:
|
case FspFsctlTransactSetInformationKind:
|
||||||
if (10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass)
|
if (10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass)
|
||||||
return FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.SetInformation.Info.Rename.AccessToken);
|
return FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.SetInformation.Info.Rename.AccessToken);
|
||||||
/* fall through! */
|
/* fall through! */
|
||||||
default:
|
default:
|
||||||
|
@ -477,6 +477,18 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request)
|
|||||||
UserContextBuf),
|
UserContextBuf),
|
||||||
Request->Req.SetInformation.Info.Disposition.Delete ? "Delete" : "Undelete");
|
Request->Req.SetInformation.Info.Disposition.Delete ? "Delete" : "Undelete");
|
||||||
break;
|
break;
|
||||||
|
case 64/*FileDispositionInformationEx*/:
|
||||||
|
FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [DispositionEx] %s%S%s%s, "
|
||||||
|
"Flags=%lx\n",
|
||||||
|
FspDiagIdent(), GetCurrentThreadId(), (PVOID)Request->Hint,
|
||||||
|
Request->FileName.Size ? "\"" : "",
|
||||||
|
Request->FileName.Size ? (PWSTR)Request->Buffer : L"",
|
||||||
|
Request->FileName.Size ? "\", " : "",
|
||||||
|
FspDebugLogUserContextString(
|
||||||
|
Request->Req.SetInformation.UserContext, Request->Req.SetInformation.UserContext2,
|
||||||
|
UserContextBuf),
|
||||||
|
Request->Req.SetInformation.Info.DispositionEx.Flags);
|
||||||
|
break;
|
||||||
case 10/*FileRenameInformation*/:
|
case 10/*FileRenameInformation*/:
|
||||||
FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [Rename] %s%S%s%s, "
|
FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [Rename] %s%S%s%s, "
|
||||||
"NewFileName=\"%S\", AccessToken=%p[PID=%lx]\n",
|
"NewFileName=\"%S\", AccessToken=%p[PID=%lx]\n",
|
||||||
@ -491,6 +503,21 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request)
|
|||||||
FSP_FSCTL_TRANSACT_REQ_TOKEN_HANDLE(Request->Req.SetInformation.Info.Rename.AccessToken),
|
FSP_FSCTL_TRANSACT_REQ_TOKEN_HANDLE(Request->Req.SetInformation.Info.Rename.AccessToken),
|
||||||
FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.SetInformation.Info.Rename.AccessToken));
|
FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.SetInformation.Info.Rename.AccessToken));
|
||||||
break;
|
break;
|
||||||
|
case 65/*FileRenameInformationEx*/:
|
||||||
|
FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [RenameEx] %s%S%s%s, "
|
||||||
|
"NewFileName=\"%S\", AccessToken=%p[PID=%lx], Flags=%lx\n",
|
||||||
|
FspDiagIdent(), GetCurrentThreadId(), (PVOID)Request->Hint,
|
||||||
|
Request->FileName.Size ? "\"" : "",
|
||||||
|
Request->FileName.Size ? (PWSTR)Request->Buffer : L"",
|
||||||
|
Request->FileName.Size ? "\", " : "",
|
||||||
|
FspDebugLogUserContextString(
|
||||||
|
Request->Req.SetInformation.UserContext, Request->Req.SetInformation.UserContext2,
|
||||||
|
UserContextBuf),
|
||||||
|
(PWSTR)(Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset),
|
||||||
|
FSP_FSCTL_TRANSACT_REQ_TOKEN_HANDLE(Request->Req.SetInformation.Info.Rename.AccessToken),
|
||||||
|
FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.SetInformation.Info.Rename.AccessToken),
|
||||||
|
Request->Req.SetInformation.Info.RenameEx.Flags);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [INVALID] %s%S%s%s\n",
|
FspDebugLog("%S[TID=%04lx]: %p: >>SetInformation [INVALID] %s%S%s%s\n",
|
||||||
FspDiagIdent(), GetCurrentThreadId(), (PVOID)Request->Hint,
|
FspDiagIdent(), GetCurrentThreadId(), (PVOID)Request->Hint,
|
||||||
|
@ -56,7 +56,10 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
(FspFsctlTransactCleanupKind == Request->Kind &&
|
(FspFsctlTransactCleanupKind == Request->Kind &&
|
||||||
Request->Req.Cleanup.Delete) ||
|
Request->Req.Cleanup.Delete) ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ == (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||||
0 == Request->Req.FlushBuffers.UserContext &&
|
0 == Request->Req.FlushBuffers.UserContext &&
|
||||||
@ -67,7 +70,9 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
else
|
else
|
||||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
|
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
|
||||||
{
|
{
|
||||||
@ -95,7 +100,10 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
(FspFsctlTransactCleanupKind == Request->Kind &&
|
(FspFsctlTransactCleanupKind == Request->Kind &&
|
||||||
Request->Req.Cleanup.Delete) ||
|
Request->Req.Cleanup.Delete) ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ == (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||||
0 == Request->Req.FlushBuffers.UserContext &&
|
0 == Request->Req.FlushBuffers.UserContext &&
|
||||||
@ -106,7 +114,9 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
else
|
else
|
||||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
|
FspFsctlTransactQueryVolumeInformationKind == Request->Kind)
|
||||||
{
|
{
|
||||||
@ -373,7 +383,10 @@ NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
Request->Req.SetInformation.Info.Rename.NewFileName.Size;
|
Request->Req.SetInformation.Info.Rename.NewFileName.Size;
|
||||||
CreateRequest->Kind = FspFsctlTransactCreateKind;
|
CreateRequest->Kind = FspFsctlTransactCreateKind;
|
||||||
CreateRequest->Req.Create.CreateOptions =
|
CreateRequest->Req.Create.CreateOptions =
|
||||||
FILE_DELETE_ON_CLOSE | /* force read-only check! */
|
(65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
0 != (0x40/*IGNORE_READONLY_ATTRIBUTE*/ & Request->Req.SetInformation.Info.RenameEx.Flags) ?
|
||||||
|
0 :
|
||||||
|
FILE_DELETE_ON_CLOSE) | /* force read-only check! */
|
||||||
FILE_OPEN_REPARSE_POINT; /* allow rename over reparse point */
|
FILE_OPEN_REPARSE_POINT; /* allow rename over reparse point */
|
||||||
CreateRequest->Req.Create.AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;
|
CreateRequest->Req.Create.AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;
|
||||||
CreateRequest->Req.Create.UserMode = TRUE;
|
CreateRequest->Req.Create.UserMode = TRUE;
|
||||||
@ -978,16 +991,37 @@ FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
FSP_API NTSTATUS FspFileSystemOpCleanup(FSP_FILE_SYSTEM *FileSystem,
|
FSP_API NTSTATUS FspFileSystemOpCleanup(FSP_FILE_SYSTEM *FileSystem,
|
||||||
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
|
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
|
||||||
{
|
{
|
||||||
|
ULONG CleanupFlags =
|
||||||
|
(0 != Request->Req.Cleanup.Delete ? FspCleanupDelete : 0) |
|
||||||
|
(0 != Request->Req.Cleanup.SetAllocationSize ? FspCleanupSetAllocationSize : 0) |
|
||||||
|
(0 != Request->Req.Cleanup.SetArchiveBit ? FspCleanupSetArchiveBit : 0) |
|
||||||
|
(0 != Request->Req.Cleanup.SetLastAccessTime ? FspCleanupSetLastAccessTime : 0) |
|
||||||
|
(0 != Request->Req.Cleanup.SetLastWriteTime ? FspCleanupSetLastWriteTime : 0) |
|
||||||
|
(0 != Request->Req.Cleanup.SetChangeTime ? FspCleanupSetChangeTime : 0);
|
||||||
|
|
||||||
|
if (Request->Req.Cleanup.Delete && 0 != FileSystem->Interface->Delete)
|
||||||
|
{
|
||||||
|
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)
|
if (0 != FileSystem->Interface->Cleanup)
|
||||||
FileSystem->Interface->Cleanup(FileSystem,
|
FileSystem->Interface->Cleanup(FileSystem,
|
||||||
(PVOID)ValOfFileContext(Request->Req.Cleanup),
|
(PVOID)ValOfFileContext(Request->Req.Cleanup),
|
||||||
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
|
0 != Request->FileName.Size ? (PWSTR)Request->Buffer : 0,
|
||||||
(0 != Request->Req.Cleanup.Delete ? FspCleanupDelete : 0) |
|
CleanupFlags);
|
||||||
(0 != Request->Req.Cleanup.SetAllocationSize ? FspCleanupSetAllocationSize : 0) |
|
|
||||||
(0 != Request->Req.Cleanup.SetArchiveBit ? FspCleanupSetArchiveBit : 0) |
|
|
||||||
(0 != Request->Req.Cleanup.SetLastAccessTime ? FspCleanupSetLastAccessTime : 0) |
|
|
||||||
(0 != Request->Req.Cleanup.SetLastWriteTime ? FspCleanupSetLastWriteTime : 0) |
|
|
||||||
(0 != Request->Req.Cleanup.SetChangeTime ? FspCleanupSetChangeTime : 0));
|
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -1134,7 +1168,9 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
&FileInfo);
|
&FileInfo);
|
||||||
break;
|
break;
|
||||||
case 13/*FileDispositionInformation*/:
|
case 13/*FileDispositionInformation*/:
|
||||||
if (0 != FileSystem->Interface->GetFileInfo)
|
case 64/*FileDispositionInformationEx*/:
|
||||||
|
if (0 == (0x10/*IGNORE_READONLY_ATTRIBUTE*/ & Request->Req.SetInformation.Info.DispositionEx.Flags) &&
|
||||||
|
0 != FileSystem->Interface->GetFileInfo)
|
||||||
{
|
{
|
||||||
Result = FileSystem->Interface->GetFileInfo(FileSystem,
|
Result = FileSystem->Interface->GetFileInfo(FileSystem,
|
||||||
(PVOID)ValOfFileContext(Request->Req.SetInformation), &FileInfo);
|
(PVOID)ValOfFileContext(Request->Req.SetInformation), &FileInfo);
|
||||||
@ -1144,7 +1180,17 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (0 != FileSystem->Interface->SetDelete)
|
if (0 != FileSystem->Interface->Delete)
|
||||||
|
{
|
||||||
|
Result = FileSystem->Interface->Delete(FileSystem,
|
||||||
|
(PVOID)ValOfFileContext(Request->Req.SetInformation),
|
||||||
|
(PWSTR)Request->Buffer,
|
||||||
|
Request->Req.SetInformation.Info.DispositionEx.Flags & 3/*DELETE|POSIX_SEMANTICS*/);
|
||||||
|
}
|
||||||
|
else if (0 != (2/*POSIX_SEMANTICS*/ & Request->Req.SetInformation.Info.DispositionEx.Flags))
|
||||||
|
/* only FSP_FILE_SYSTEM_INTERFACE::Delete can do POSIX semantics; return error if unimplemented */
|
||||||
|
Result = STATUS_INVALID_PARAMETER;
|
||||||
|
else if (0 != FileSystem->Interface->SetDelete)
|
||||||
{
|
{
|
||||||
Result = FileSystem->Interface->SetDelete(FileSystem,
|
Result = FileSystem->Interface->SetDelete(FileSystem,
|
||||||
(PVOID)ValOfFileContext(Request->Req.SetInformation),
|
(PVOID)ValOfFileContext(Request->Req.SetInformation),
|
||||||
@ -1162,6 +1208,7 @@ FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 10/*FileRenameInformation*/:
|
case 10/*FileRenameInformation*/:
|
||||||
|
case 65/*FileRenameInformationEx*/:
|
||||||
if (0 != FileSystem->Interface->Rename)
|
if (0 != FileSystem->Interface->Rename)
|
||||||
{
|
{
|
||||||
if (0 != Request->Req.SetInformation.Info.Rename.AccessToken)
|
if (0 != Request->Req.SetInformation.Info.Rename.AccessToken)
|
||||||
|
@ -96,6 +96,7 @@ static struct fuse_opt fsp_fuse_core_opts[] =
|
|||||||
FSP_FUSE_CORE_OPT("VolumeInfoTimeout=", set_VolumeInfoTimeout, 1),
|
FSP_FUSE_CORE_OPT("VolumeInfoTimeout=", set_VolumeInfoTimeout, 1),
|
||||||
FSP_FUSE_CORE_OPT("VolumeInfoTimeout=%d", VolumeParams.VolumeInfoTimeout, 0),
|
FSP_FUSE_CORE_OPT("VolumeInfoTimeout=%d", VolumeParams.VolumeInfoTimeout, 0),
|
||||||
FSP_FUSE_CORE_OPT("KeepFileCache=", set_KeepFileCache, 1),
|
FSP_FUSE_CORE_OPT("KeepFileCache=", set_KeepFileCache, 1),
|
||||||
|
FSP_FUSE_CORE_OPT("LegacyUnlinkRename=", set_LegacyUnlinkRename, 1),
|
||||||
FSP_FUSE_CORE_OPT("ThreadCount=%u", ThreadCount, 0),
|
FSP_FUSE_CORE_OPT("ThreadCount=%u", ThreadCount, 0),
|
||||||
FUSE_OPT_KEY("UNC=", 'U'),
|
FUSE_OPT_KEY("UNC=", 'U'),
|
||||||
FUSE_OPT_KEY("--UNC=", 'U'),
|
FUSE_OPT_KEY("--UNC=", 'U'),
|
||||||
@ -421,6 +422,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
|||||||
" -o EaTimeout=N extended attribute timeout (millis)\n"
|
" -o EaTimeout=N extended attribute timeout (millis)\n"
|
||||||
" -o VolumeInfoTimeout=N volume info timeout (millis)\n"
|
" -o VolumeInfoTimeout=N volume info timeout (millis)\n"
|
||||||
" -o KeepFileCache do not discard cache when files are closed\n"
|
" -o KeepFileCache do not discard cache when files are closed\n"
|
||||||
|
" -o LegacyUnlinkRename do not support new POSIX unlink/rename\n"
|
||||||
" -o ThreadCount number of file system dispatcher threads\n"
|
" -o ThreadCount number of file system dispatcher threads\n"
|
||||||
);
|
);
|
||||||
opt_data->help = 1;
|
opt_data->help = 1;
|
||||||
@ -563,6 +565,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
|||||||
opt_data.VolumeParams.Version = sizeof(FSP_FSCTL_VOLUME_PARAMS);
|
opt_data.VolumeParams.Version = sizeof(FSP_FSCTL_VOLUME_PARAMS);
|
||||||
opt_data.VolumeParams.FileInfoTimeout = 1000;
|
opt_data.VolumeParams.FileInfoTimeout = 1000;
|
||||||
opt_data.VolumeParams.FlushAndPurgeOnCleanup = TRUE;
|
opt_data.VolumeParams.FlushAndPurgeOnCleanup = TRUE;
|
||||||
|
opt_data.VolumeParams.SupportsPosixUnlinkRename = TRUE;
|
||||||
|
|
||||||
if (-1 == fsp_fuse_core_opt_parse(env, args, &opt_data, /*help=*/1))
|
if (-1 == fsp_fuse_core_opt_parse(env, args, &opt_data, /*help=*/1))
|
||||||
{
|
{
|
||||||
@ -623,6 +626,8 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
|||||||
opt_data.VolumeParams.VolumeInfoTimeoutValid = 1;
|
opt_data.VolumeParams.VolumeInfoTimeoutValid = 1;
|
||||||
if (opt_data.set_KeepFileCache)
|
if (opt_data.set_KeepFileCache)
|
||||||
opt_data.VolumeParams.FlushAndPurgeOnCleanup = FALSE;
|
opt_data.VolumeParams.FlushAndPurgeOnCleanup = FALSE;
|
||||||
|
if (opt_data.set_LegacyUnlinkRename)
|
||||||
|
opt_data.VolumeParams.SupportsPosixUnlinkRename = FALSE;
|
||||||
opt_data.VolumeParams.CaseSensitiveSearch = TRUE;
|
opt_data.VolumeParams.CaseSensitiveSearch = TRUE;
|
||||||
opt_data.VolumeParams.CasePreservedNames = TRUE;
|
opt_data.VolumeParams.CasePreservedNames = TRUE;
|
||||||
opt_data.VolumeParams.PersistentAcls = TRUE;
|
opt_data.VolumeParams.PersistentAcls = TRUE;
|
||||||
|
@ -34,7 +34,10 @@ VOID fsp_fuse_op_enter_lock(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
(FspFsctlTransactCleanupKind == Request->Kind &&
|
(FspFsctlTransactCleanupKind == Request->Kind &&
|
||||||
Request->Req.Cleanup.Delete) ||
|
Request->Req.Cleanup.Delete) ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ == (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||||
0 == Request->Req.FlushBuffers.UserContext &&
|
0 == Request->Req.FlushBuffers.UserContext &&
|
||||||
@ -48,7 +51,9 @@ VOID fsp_fuse_op_enter_lock(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
else
|
else
|
||||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
|
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
|
||||||
/* FSCTL_GET_REPARSE_POINT may access namespace */
|
/* FSCTL_GET_REPARSE_POINT may access namespace */
|
||||||
@ -78,7 +83,10 @@ VOID fsp_fuse_op_leave_unlock(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
(FspFsctlTransactCleanupKind == Request->Kind &&
|
(FspFsctlTransactCleanupKind == Request->Kind &&
|
||||||
Request->Req.Cleanup.Delete) ||
|
Request->Req.Cleanup.Delete) ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ == (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
|
||||||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
|
||||||
0 == Request->Req.FlushBuffers.UserContext &&
|
0 == Request->Req.FlushBuffers.UserContext &&
|
||||||
@ -92,7 +100,9 @@ VOID fsp_fuse_op_leave_unlock(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
else
|
else
|
||||||
if (FspFsctlTransactCreateKind == Request->Kind ||
|
if (FspFsctlTransactCreateKind == Request->Kind ||
|
||||||
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
(FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
|
(13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
(64/*FileDispositionInformationEx*/ == Request->Req.SetInformation.FileInformationClass &&
|
||||||
|
3/*DELETE|POSIX_SEMANTICS*/ != (3 & Request->Req.SetInformation.Info.DispositionEx.Flags)))) ||
|
||||||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
|
||||||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
|
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
|
||||||
/* FSCTL_GET_REPARSE_POINT may access namespace */
|
/* FSCTL_GET_REPARSE_POINT may access namespace */
|
||||||
@ -131,7 +141,8 @@ NTSTATUS fsp_fuse_op_enter(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
AccessToken = Request->Req.Create.AccessToken;
|
AccessToken = Request->Req.Create.AccessToken;
|
||||||
}
|
}
|
||||||
else if (FspFsctlTransactSetInformationKind == Request->Kind &&
|
else if (FspFsctlTransactSetInformationKind == Request->Kind &&
|
||||||
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass)
|
(10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass ||
|
||||||
|
65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass))
|
||||||
{
|
{
|
||||||
FileName = (PWSTR)(Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset);
|
FileName = (PWSTR)(Request->Buffer + Request->Req.SetInformation.Info.Rename.NewFileName.Offset);
|
||||||
AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;
|
AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;
|
||||||
@ -951,12 +962,12 @@ exit:
|
|||||||
if (CreateOptions & FILE_DIRECTORY_FILE)
|
if (CreateOptions & FILE_DIRECTORY_FILE)
|
||||||
{
|
{
|
||||||
if (0 != f->ops.releasedir)
|
if (0 != f->ops.releasedir)
|
||||||
f->ops.releasedir(filedesc->PosixPath, &fi);
|
f->ops.releasedir(contexthdr->PosixPath, &fi);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (0 != f->ops.release)
|
if (0 != f->ops.release)
|
||||||
f->ops.release(filedesc->PosixPath, &fi);
|
f->ops.release(contexthdr->PosixPath, &fi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -980,6 +991,19 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
int err;
|
int err;
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
if (0 != (CreateOptions & FILE_DELETE_ON_CLOSE) &&
|
||||||
|
0 != (f->conn_want & FSP_FUSE_CAP_DELETE_ACCESS) && 0 != f->ops.access)
|
||||||
|
{
|
||||||
|
err = f->ops.access(contexthdr->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;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, contexthdr->PosixPath, 0,
|
Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, contexthdr->PosixPath, 0,
|
||||||
&Uid, &Gid, &Mode, &FileInfoBuf);
|
&Uid, &Gid, &Mode, &FileInfoBuf);
|
||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
@ -1157,42 +1181,6 @@ static NTSTATUS fsp_fuse_intf_Overwrite(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
&Uid, &Gid, &Mode, FileInfo);
|
&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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
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,
|
static VOID fsp_fuse_intf_Close(FSP_FILE_SYSTEM *FileSystem,
|
||||||
PVOID FileDesc)
|
PVOID FileDesc)
|
||||||
{
|
{
|
||||||
@ -1605,6 +1593,72 @@ static NTSTATUS fsp_fuse_intf_CanDelete(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
return STATUS_SUCCESS;
|
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,
|
static NTSTATUS fsp_fuse_intf_Rename(FSP_FILE_SYSTEM *FileSystem,
|
||||||
PVOID FileDesc,
|
PVOID FileDesc,
|
||||||
PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists)
|
PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists)
|
||||||
@ -2424,7 +2478,7 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
|
|||||||
0,
|
0,
|
||||||
fsp_fuse_intf_Open,
|
fsp_fuse_intf_Open,
|
||||||
0,
|
0,
|
||||||
fsp_fuse_intf_Cleanup,
|
0,
|
||||||
fsp_fuse_intf_Close,
|
fsp_fuse_intf_Close,
|
||||||
fsp_fuse_intf_Read,
|
fsp_fuse_intf_Read,
|
||||||
fsp_fuse_intf_Write,
|
fsp_fuse_intf_Write,
|
||||||
@ -2432,7 +2486,7 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
|
|||||||
fsp_fuse_intf_GetFileInfo,
|
fsp_fuse_intf_GetFileInfo,
|
||||||
fsp_fuse_intf_SetBasicInfo,
|
fsp_fuse_intf_SetBasicInfo,
|
||||||
fsp_fuse_intf_SetFileSize,
|
fsp_fuse_intf_SetFileSize,
|
||||||
fsp_fuse_intf_CanDelete,
|
0,
|
||||||
fsp_fuse_intf_Rename,
|
fsp_fuse_intf_Rename,
|
||||||
fsp_fuse_intf_GetSecurity,
|
fsp_fuse_intf_GetSecurity,
|
||||||
fsp_fuse_intf_SetSecurity,
|
fsp_fuse_intf_SetSecurity,
|
||||||
@ -2449,6 +2503,7 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
|
|||||||
fsp_fuse_intf_Overwrite,
|
fsp_fuse_intf_Overwrite,
|
||||||
fsp_fuse_intf_GetEa,
|
fsp_fuse_intf_GetEa,
|
||||||
fsp_fuse_intf_SetEa,
|
fsp_fuse_intf_SetEa,
|
||||||
|
fsp_fuse_intf_Delete,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -88,6 +88,7 @@ static NTSTATUS fsp_fuse_loop_start(struct fuse *f)
|
|||||||
FSP_FUSE_CAP_READDIR_PLUS |
|
FSP_FUSE_CAP_READDIR_PLUS |
|
||||||
FSP_FUSE_CAP_READ_ONLY |
|
FSP_FUSE_CAP_READ_ONLY |
|
||||||
FSP_FUSE_CAP_STAT_EX |
|
FSP_FUSE_CAP_STAT_EX |
|
||||||
|
FSP_FUSE_CAP_DELETE_ACCESS |
|
||||||
FSP_FUSE_CAP_CASE_INSENSITIVE;
|
FSP_FUSE_CAP_CASE_INSENSITIVE;
|
||||||
if (0 != f->ops.init)
|
if (0 != f->ops.init)
|
||||||
{
|
{
|
||||||
|
@ -153,7 +153,8 @@ struct fsp_fuse_core_opt_data
|
|||||||
set_DirInfoTimeout,
|
set_DirInfoTimeout,
|
||||||
set_EaTimeout,
|
set_EaTimeout,
|
||||||
set_VolumeInfoTimeout,
|
set_VolumeInfoTimeout,
|
||||||
set_KeepFileCache;
|
set_KeepFileCache,
|
||||||
|
set_LegacyUnlinkRename;
|
||||||
unsigned ThreadCount;
|
unsigned ThreadCount;
|
||||||
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||||
UINT16 VolumeLabelLength;
|
UINT16 VolumeLabelLength;
|
||||||
|
@ -28,6 +28,29 @@ static NTSTATUS (NTAPI *FspNtMakeTemporaryObject)(
|
|||||||
HANDLE Handle);
|
HANDLE Handle);
|
||||||
static NTSTATUS (NTAPI *FspNtClose)(
|
static NTSTATUS (NTAPI *FspNtClose)(
|
||||||
HANDLE Handle);
|
HANDLE Handle);
|
||||||
|
static BOOLEAN FspMountDoNotUseLauncher;
|
||||||
|
|
||||||
|
static VOID FspMountInitializeFromRegistry(VOID)
|
||||||
|
{
|
||||||
|
HKEY RegKey;
|
||||||
|
LONG Result;
|
||||||
|
DWORD Size;
|
||||||
|
DWORD MountDoNotUseLauncher;
|
||||||
|
|
||||||
|
MountDoNotUseLauncher = 0;
|
||||||
|
|
||||||
|
Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\" FSP_FSCTL_PRODUCT_NAME,
|
||||||
|
0, KEY_READ | KEY_WOW64_32KEY, &RegKey);
|
||||||
|
if (ERROR_SUCCESS == Result)
|
||||||
|
{
|
||||||
|
Size = sizeof MountDoNotUseLauncher;
|
||||||
|
Result = RegGetValueW(RegKey, 0, L"MountDoNotUseLauncher",
|
||||||
|
RRF_RT_REG_DWORD, 0, &MountDoNotUseLauncher, &Size);
|
||||||
|
RegCloseKey(RegKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
FspMountDoNotUseLauncher = !!MountDoNotUseLauncher;
|
||||||
|
}
|
||||||
|
|
||||||
static BOOL WINAPI FspMountInitialize(
|
static BOOL WINAPI FspMountInitialize(
|
||||||
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
|
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
|
||||||
@ -49,6 +72,8 @@ static BOOL WINAPI FspMountInitialize(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FspMountInitializeFromRegistry();
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,7 +391,7 @@ static NTSTATUS FspMountSet_Drive(PWSTR VolumeName, PWSTR MountPoint, PHANDLE PM
|
|||||||
|
|
||||||
Result = FspServiceContextCheck(0, &IsLocalSystem);
|
Result = FspServiceContextCheck(0, &IsLocalSystem);
|
||||||
IsServiceContext = NT_SUCCESS(Result) && !IsLocalSystem;
|
IsServiceContext = NT_SUCCESS(Result) && !IsLocalSystem;
|
||||||
if (IsServiceContext)
|
if (IsServiceContext && !FspMountDoNotUseLauncher)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If the current process is in the service context but not LocalSystem,
|
* If the current process is in the service context but not LocalSystem,
|
||||||
|
@ -57,6 +57,14 @@ namespace Fsp
|
|||||||
public const UInt32 CleanupSetLastWriteTime = 0x40;
|
public const UInt32 CleanupSetLastWriteTime = 0x40;
|
||||||
public const UInt32 CleanupSetChangeTime = 0x80;
|
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 */
|
/* NTSTATUS */
|
||||||
public const Int32 STATUS_SUCCESS = unchecked((Int32)0x00000000);
|
public const Int32 STATUS_SUCCESS = unchecked((Int32)0x00000000);
|
||||||
public const Int32 STATUS_WAIT_1 = unchecked((Int32)0x00000001);
|
public const Int32 STATUS_WAIT_1 = unchecked((Int32)0x00000001);
|
||||||
|
@ -289,6 +289,9 @@ namespace Fsp
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <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
|
/// When CreateFile is used to open or create a file the kernel creates a kernel mode file
|
||||||
/// object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
/// object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may
|
||||||
/// be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
/// be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same
|
||||||
@ -342,6 +345,7 @@ namespace Fsp
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <seealso cref="CanDelete"/>
|
/// <seealso cref="CanDelete"/>
|
||||||
/// <seealso cref="SetDelete"/>
|
/// <seealso cref="SetDelete"/>
|
||||||
|
/// <seealso cref="Delete"/>
|
||||||
/// <seealso cref="Close"/>
|
/// <seealso cref="Close"/>
|
||||||
public virtual void Cleanup(
|
public virtual void Cleanup(
|
||||||
Object FileNode,
|
Object FileNode,
|
||||||
@ -598,12 +602,14 @@ namespace Fsp
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <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
|
/// This function tests whether a file or directory can be safely deleted. This function does
|
||||||
/// not need to perform access checks, but may performs tasks such as check for empty
|
/// not need to perform access checks, but may performs tasks such as check for empty
|
||||||
/// directories, etc.
|
/// directories, etc.
|
||||||
/// </para><para>
|
/// </para><para>
|
||||||
/// This function should <b>NEVER</b> delete the file or directory in question. Deletion should
|
/// This function should <b>NEVER</b> delete the file or directory in question. Deletion should
|
||||||
/// happen during Cleanup with the FspCleanupDelete flag set.
|
/// happen during Cleanup with the CleanupDelete flag set.
|
||||||
/// </para><para>
|
/// </para><para>
|
||||||
/// This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used.
|
/// This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used.
|
||||||
/// It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE.
|
/// It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE.
|
||||||
@ -624,6 +630,7 @@ namespace Fsp
|
|||||||
/// <returns>STATUS_SUCCESS or error code.</returns>
|
/// <returns>STATUS_SUCCESS or error code.</returns>
|
||||||
/// <seealso cref="Cleanup"/>
|
/// <seealso cref="Cleanup"/>
|
||||||
/// <seealso cref="SetDelete"/>
|
/// <seealso cref="SetDelete"/>
|
||||||
|
/// <seealso cref="Delete"/>
|
||||||
public virtual Int32 CanDelete(
|
public virtual Int32 CanDelete(
|
||||||
Object FileNode,
|
Object FileNode,
|
||||||
Object FileDesc,
|
Object FileDesc,
|
||||||
@ -1036,12 +1043,14 @@ namespace Fsp
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para>
|
/// <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
|
/// This function sets a flag to indicates whether the FSD file should delete a file
|
||||||
/// when it is closed. This function does not need to perform access checks, but may
|
/// when it is closed. This function does not need to perform access checks, but may
|
||||||
/// performs tasks such as check for empty directories, etc.
|
/// performs tasks such as check for empty directories, etc.
|
||||||
/// </para><para>
|
/// </para><para>
|
||||||
/// This function should <b>NEVER</b> delete the file or directory in question. Deletion should
|
/// This function should <b>NEVER</b> delete the file or directory in question. Deletion should
|
||||||
/// happen during Cleanup with the FspCleanupDelete flag set.
|
/// happen during Cleanup with the CleanupDelete flag set.
|
||||||
/// </para><para>
|
/// </para><para>
|
||||||
/// This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used.
|
/// This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used.
|
||||||
/// It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE.
|
/// It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE.
|
||||||
@ -1067,6 +1076,7 @@ namespace Fsp
|
|||||||
/// <returns>STATUS_SUCCESS or error code.</returns>
|
/// <returns>STATUS_SUCCESS or error code.</returns>
|
||||||
/// <seealso cref="Cleanup"/>
|
/// <seealso cref="Cleanup"/>
|
||||||
/// <seealso cref="CanDelete"/>
|
/// <seealso cref="CanDelete"/>
|
||||||
|
/// <seealso cref="Delete"/>
|
||||||
public virtual Int32 SetDelete(
|
public virtual Int32 SetDelete(
|
||||||
Object FileNode,
|
Object FileNode,
|
||||||
Object FileDesc,
|
Object FileDesc,
|
||||||
@ -1188,6 +1198,85 @@ namespace Fsp
|
|||||||
{
|
{
|
||||||
return STATUS_INVALID_DEVICE_REQUEST;
|
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 */
|
/* helpers */
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -312,6 +312,11 @@ namespace Fsp
|
|||||||
get { return 0 != (_VolumeParams.Flags & VolumeParams.RejectIrpPriorToTransact0); }
|
get { return 0 != (_VolumeParams.Flags & VolumeParams.RejectIrpPriorToTransact0); }
|
||||||
set { _VolumeParams.Flags |= (value ? VolumeParams.RejectIrpPriorToTransact0 : 0); }
|
set { _VolumeParams.Flags |= (value ? VolumeParams.RejectIrpPriorToTransact0 : 0); }
|
||||||
}
|
}
|
||||||
|
public Boolean SupportsPosixUnlinkRename
|
||||||
|
{
|
||||||
|
get { return 0 != (_VolumeParams.Flags & VolumeParams.SupportsPosixUnlinkRename); }
|
||||||
|
set { _VolumeParams.Flags |= (value ? VolumeParams.SupportsPosixUnlinkRename : 0); }
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the prefix for a network file system.
|
/// Gets or sets the prefix for a network file system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1343,28 +1348,6 @@ namespace Fsp
|
|||||||
return ExceptionHandler(FileSystem, ex);
|
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(
|
private static Int32 GetEa(
|
||||||
IntPtr FileSystemPtr,
|
IntPtr FileSystemPtr,
|
||||||
ref FullContext FullContext,
|
ref FullContext FullContext,
|
||||||
@ -1415,6 +1398,28 @@ namespace Fsp
|
|||||||
return ExceptionHandler(FileSystem, ex);
|
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()
|
static FileSystemHost()
|
||||||
{
|
{
|
||||||
@ -1443,11 +1448,14 @@ namespace Fsp
|
|||||||
_FileSystemInterface.GetStreamInfo = GetStreamInfo;
|
_FileSystemInterface.GetStreamInfo = GetStreamInfo;
|
||||||
_FileSystemInterface.GetDirInfoByName = GetDirInfoByName;
|
_FileSystemInterface.GetDirInfoByName = GetDirInfoByName;
|
||||||
_FileSystemInterface.Control = Control;
|
_FileSystemInterface.Control = Control;
|
||||||
_FileSystemInterface.SetDelete = SetDelete;
|
|
||||||
_FileSystemInterface.GetEa = GetEa;
|
_FileSystemInterface.GetEa = GetEa;
|
||||||
_FileSystemInterface.SetEa = SetEa;
|
_FileSystemInterface.SetEa = SetEa;
|
||||||
|
_FileSystemInterface.Delete = Delete;
|
||||||
|
|
||||||
_FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size);
|
_FileSystemInterfacePtr = Marshal.AllocHGlobal(FileSystemInterface.Size);
|
||||||
|
/* Marshal.AllocHGlobal does not zero memory; we must do it ourselves! */
|
||||||
|
for (int Offset = 0; FileSystemInterface.Size > Offset; Offset += IntPtr.Size)
|
||||||
|
Marshal.WriteIntPtr(_FileSystemInterfacePtr, Offset, IntPtr.Zero);
|
||||||
Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false);
|
Marshal.StructureToPtr(_FileSystemInterface, _FileSystemInterfacePtr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ namespace Fsp.Interop
|
|||||||
internal const UInt32 CasePreservedExtendedAttributes = 0x02000000;
|
internal const UInt32 CasePreservedExtendedAttributes = 0x02000000;
|
||||||
internal const UInt32 WslFeatures = 0x04000000;
|
internal const UInt32 WslFeatures = 0x04000000;
|
||||||
internal const UInt32 RejectIrpPriorToTransact0 = 0x10000000;
|
internal const UInt32 RejectIrpPriorToTransact0 = 0x10000000;
|
||||||
|
internal const UInt32 SupportsPosixUnlinkRename = 0x20000000;
|
||||||
internal const int PrefixSize = 192;
|
internal const int PrefixSize = 192;
|
||||||
internal const int FileSystemNameSize = 16;
|
internal const int FileSystemNameSize = 16;
|
||||||
|
|
||||||
@ -741,6 +742,12 @@ namespace Fsp.Interop
|
|||||||
IntPtr Ea,
|
IntPtr Ea,
|
||||||
UInt32 EaLength,
|
UInt32 EaLength,
|
||||||
out FileInfo FileInfo);
|
out FileInfo FileInfo);
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
internal delegate Int32 Delete(
|
||||||
|
IntPtr FileSystem,
|
||||||
|
ref FullContext FullContext,
|
||||||
|
[MarshalAs(UnmanagedType.LPWStr)] String FileName,
|
||||||
|
UInt32 Flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static int Size = IntPtr.Size * 64;
|
internal static int Size = IntPtr.Size * 64;
|
||||||
@ -776,7 +783,8 @@ namespace Fsp.Interop
|
|||||||
internal Proto.OverwriteEx OverwriteEx;
|
internal Proto.OverwriteEx OverwriteEx;
|
||||||
internal Proto.GetEa GetEa;
|
internal Proto.GetEa GetEa;
|
||||||
internal Proto.SetEa SetEa;
|
internal Proto.SetEa SetEa;
|
||||||
/* NTSTATUS (*Reserved[33])(); */
|
internal Proto.Delete Delete;
|
||||||
|
/* NTSTATUS (*Reserved[32])(); */
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressUnmanagedCodeSecurity]
|
[SuppressUnmanagedCodeSecurity]
|
||||||
@ -1531,13 +1539,13 @@ namespace Fsp.Interop
|
|||||||
#endif
|
#endif
|
||||||
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(
|
object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(
|
||||||
typeof(AssemblyProductAttribute), false);
|
typeof(AssemblyProductAttribute), false);
|
||||||
if (null != attributes &&
|
if (null != attributes &&
|
||||||
0 < attributes.Length &&
|
0 < attributes.Length &&
|
||||||
null != attributes[0] as AssemblyProductAttribute)
|
null != attributes[0] as AssemblyProductAttribute)
|
||||||
{
|
{
|
||||||
ProductName = (attributes[0] as AssemblyProductAttribute).Product;
|
ProductName = (attributes[0] as AssemblyProductAttribute).Product;
|
||||||
ProductFileName = ProductName.ToLowerInvariant();
|
ProductFileName = ProductName.ToLowerInvariant();
|
||||||
}
|
}
|
||||||
LoadProto(LoadDll());
|
LoadProto(LoadDll());
|
||||||
CheckVersion();
|
CheckVersion();
|
||||||
}
|
}
|
||||||
|
@ -1321,7 +1321,7 @@ exit:
|
|||||||
SvcInstance->StdioHandles[2] = INVALID_HANDLE_VALUE;
|
SvcInstance->StdioHandles[2] = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NT_SUCCESS(Result))
|
if (NT_SUCCESS(Result) && STATUS_TIMEOUT != Result)
|
||||||
{
|
{
|
||||||
SvcInstance->Argc = Argc;
|
SvcInstance->Argc = Argc;
|
||||||
SvcInstance->Argv = Argv;
|
SvcInstance->Argv = Argv;
|
||||||
|
@ -82,21 +82,21 @@ static NTSTATUS FspFsvolCleanup(
|
|||||||
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
||||||
FSP_FSCTL_TRANSACT_REQ *Request;
|
FSP_FSCTL_TRANSACT_REQ *Request;
|
||||||
ULONG CleanupFlags;
|
ULONG CleanupFlags;
|
||||||
BOOLEAN DeletePending, SetAllocationSize, FileModified;
|
BOOLEAN Delete, SetAllocationSize, FileModified;
|
||||||
|
|
||||||
ASSERT(FileNode == FileDesc->FileNode);
|
ASSERT(FileNode == FileDesc->FileNode);
|
||||||
|
|
||||||
FspFileNodeAcquireExclusive(FileNode, Main);
|
FspFileNodeAcquireExclusive(FileNode, Main);
|
||||||
|
|
||||||
FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags);
|
FspFileNodeCleanup(FileNode, FileObject, &CleanupFlags);
|
||||||
DeletePending = CleanupFlags & 1;
|
Delete = (CleanupFlags & 1) && !FileNode->PosixDelete;
|
||||||
SetAllocationSize = !!(CleanupFlags & 2);
|
SetAllocationSize = !!(CleanupFlags & 2);
|
||||||
FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
|
FileModified = BooleanFlagOn(FileObject->Flags, FO_FILE_MODIFIED);
|
||||||
|
|
||||||
/* if this is a directory inform the FSRTL Notify mechanism */
|
/* if this is a directory inform the FSRTL Notify mechanism */
|
||||||
if (FileNode->IsDirectory)
|
if (FileNode->IsDirectory)
|
||||||
{
|
{
|
||||||
if (DeletePending)
|
if (Delete)
|
||||||
FspNotifyDeletePending(
|
FspNotifyDeletePending(
|
||||||
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
|
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
|
||||||
|
|
||||||
@ -108,12 +108,12 @@ static NTSTATUS FspFsvolCleanup(
|
|||||||
FspFileNodeUnlockAll(FileNode, FileObject, IoGetRequestorProcess(Irp));
|
FspFileNodeUnlockAll(FileNode, FileObject, IoGetRequestorProcess(Irp));
|
||||||
|
|
||||||
/* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */
|
/* create the user-mode file system request; MustSucceed because IRP_MJ_CLEANUP cannot fail */
|
||||||
FspIopCreateRequestMustSucceedEx(Irp, DeletePending ? &FileNode->FileName : 0, 0,
|
FspIopCreateRequestMustSucceedEx(Irp, Delete ? &FileNode->FileName : 0, 0,
|
||||||
FspFsvolCleanupRequestFini, &Request);
|
FspFsvolCleanupRequestFini, &Request);
|
||||||
Request->Kind = FspFsctlTransactCleanupKind;
|
Request->Kind = FspFsctlTransactCleanupKind;
|
||||||
Request->Req.Cleanup.UserContext = FileNode->UserContext;
|
Request->Req.Cleanup.UserContext = FileNode->UserContext;
|
||||||
Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2;
|
Request->Req.Cleanup.UserContext2 = FileDesc->UserContext2;
|
||||||
Request->Req.Cleanup.Delete = DeletePending;
|
Request->Req.Cleanup.Delete = Delete;
|
||||||
Request->Req.Cleanup.SetAllocationSize = SetAllocationSize;
|
Request->Req.Cleanup.SetAllocationSize = SetAllocationSize;
|
||||||
Request->Req.Cleanup.SetArchiveBit = (FileModified || FileDesc->DidSetSecurity) &&
|
Request->Req.Cleanup.SetArchiveBit = (FileModified || FileDesc->DidSetSecurity) &&
|
||||||
!FileDesc->DidSetFileAttributes;
|
!FileDesc->DidSetFileAttributes;
|
||||||
@ -170,7 +170,12 @@ NTSTATUS FspFsvolCleanupComplete(
|
|||||||
ASSERT(FileNode == FileDesc->FileNode);
|
ASSERT(FileNode == FileDesc->FileNode);
|
||||||
|
|
||||||
/* send the appropriate notification; also invalidate dirinfo/etc. caches */
|
/* send the appropriate notification; also invalidate dirinfo/etc. caches */
|
||||||
if (Request->Req.Cleanup.Delete)
|
if (FileNode->PosixDelete)
|
||||||
|
{
|
||||||
|
NotifyFilter = 0;
|
||||||
|
NotifyAction = 0;
|
||||||
|
}
|
||||||
|
else if (Request->Req.Cleanup.Delete)
|
||||||
{
|
{
|
||||||
NotifyFilter = FileNode->IsDirectory ?
|
NotifyFilter = FileNode->IsDirectory ?
|
||||||
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;
|
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||||
|
@ -256,6 +256,9 @@ const char *FileInformationClassSym(FILE_INFORMATION_CLASS FileInformationClass)
|
|||||||
SYM(FileReplaceCompletionInformation)
|
SYM(FileReplaceCompletionInformation)
|
||||||
SYM(FileHardLinkFullIdInformation)
|
SYM(FileHardLinkFullIdInformation)
|
||||||
SYM(FileIdExtdBothDirectoryInformation)
|
SYM(FileIdExtdBothDirectoryInformation)
|
||||||
|
SYM(FileDispositionInformationEx)
|
||||||
|
SYM(FileRenameInformationEx)
|
||||||
|
SYM(FileRenameInformationExBypassAccessCheck)
|
||||||
case 68: return "FileStatInformation";
|
case 68: return "FileStatInformation";
|
||||||
case 70: return "FileStatLxInformation";
|
case 70: return "FileStatLxInformation";
|
||||||
default:
|
default:
|
||||||
|
@ -119,6 +119,17 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
|
|||||||
DirInfo->FileInfo.FileAttributes : FILE_ATTRIBUTE_NORMAL;\
|
DirInfo->FileInfo.FileAttributes : FILE_ATTRIBUTE_NORMAL;\
|
||||||
__VA_ARGS__\
|
__VA_ARGS__\
|
||||||
)
|
)
|
||||||
|
#define FILL_INFO_EASIZE()\
|
||||||
|
if (!FlagOn(DirInfo->FileInfo.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT))\
|
||||||
|
{\
|
||||||
|
Info->EaSize = ReturnEaSize ? DirInfo->FileInfo.EaSize : 0;\
|
||||||
|
/* magic computations are courtesy of NTFS */\
|
||||||
|
if (0 != Info->EaSize)\
|
||||||
|
Info->EaSize += 4;\
|
||||||
|
}\
|
||||||
|
else\
|
||||||
|
/* Fix GitHub issue #380: turns out that the EaSize field is also used for the reparse tag */\
|
||||||
|
Info->EaSize = DirInfo->FileInfo.ReparseTag
|
||||||
|
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
@ -240,18 +251,12 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
|
|||||||
break;
|
break;
|
||||||
case FileFullDirectoryInformation:
|
case FileFullDirectoryInformation:
|
||||||
FILL_INFO(FILE_FULL_DIR_INFORMATION,
|
FILL_INFO(FILE_FULL_DIR_INFORMATION,
|
||||||
Info->EaSize = ReturnEaSize ? DirInfo->FileInfo.EaSize : 0;
|
FILL_INFO_EASIZE();
|
||||||
/* magic computations are courtesy of NTFS */
|
|
||||||
if (0 != Info->EaSize)
|
|
||||||
Info->EaSize += 4;
|
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FileIdFullDirectoryInformation:
|
case FileIdFullDirectoryInformation:
|
||||||
FILL_INFO(FILE_ID_FULL_DIR_INFORMATION,
|
FILL_INFO(FILE_ID_FULL_DIR_INFORMATION,
|
||||||
Info->EaSize = ReturnEaSize ? DirInfo->FileInfo.EaSize : 0;
|
FILL_INFO_EASIZE();
|
||||||
/* magic computations are courtesy of NTFS */
|
|
||||||
if (0 != Info->EaSize)
|
|
||||||
Info->EaSize += 4;
|
|
||||||
Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber;
|
Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber;
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@ -260,20 +265,14 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
|
|||||||
break;
|
break;
|
||||||
case FileBothDirectoryInformation:
|
case FileBothDirectoryInformation:
|
||||||
FILL_INFO(FILE_BOTH_DIR_INFORMATION,
|
FILL_INFO(FILE_BOTH_DIR_INFORMATION,
|
||||||
Info->EaSize = ReturnEaSize ? DirInfo->FileInfo.EaSize : 0;
|
FILL_INFO_EASIZE();
|
||||||
/* magic computations are courtesy of NTFS */
|
|
||||||
if (0 != Info->EaSize)
|
|
||||||
Info->EaSize += 4;
|
|
||||||
Info->ShortNameLength = 0;
|
Info->ShortNameLength = 0;
|
||||||
RtlZeroMemory(Info->ShortName, sizeof Info->ShortName);
|
RtlZeroMemory(Info->ShortName, sizeof Info->ShortName);
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case FileIdBothDirectoryInformation:
|
case FileIdBothDirectoryInformation:
|
||||||
FILL_INFO(FILE_ID_BOTH_DIR_INFORMATION,
|
FILL_INFO(FILE_ID_BOTH_DIR_INFORMATION,
|
||||||
Info->EaSize = ReturnEaSize ? DirInfo->FileInfo.EaSize : 0;
|
FILL_INFO_EASIZE();
|
||||||
/* magic computations are courtesy of NTFS */
|
|
||||||
if (0 != Info->EaSize)
|
|
||||||
Info->EaSize += 4;
|
|
||||||
Info->ShortNameLength = 0;
|
Info->ShortNameLength = 0;
|
||||||
RtlZeroMemory(Info->ShortName, sizeof Info->ShortName);
|
RtlZeroMemory(Info->ShortName, sizeof Info->ShortName);
|
||||||
Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber;
|
Info->FileId.QuadPart = DirInfo->FileInfo.IndexNumber;
|
||||||
@ -331,6 +330,7 @@ static NTSTATUS FspFsvolQueryDirectoryCopy(
|
|||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
|
|
||||||
|
#undef FILL_INFO_EASIZE
|
||||||
#undef FILL_INFO
|
#undef FILL_INFO
|
||||||
#undef FILL_INFO_BASE
|
#undef FILL_INFO_BASE
|
||||||
}
|
}
|
||||||
|
@ -1462,6 +1462,7 @@ typedef struct FSP_FILE_NODE
|
|||||||
ULONG EaChangeNumber;
|
ULONG EaChangeNumber;
|
||||||
ULONG EaChangeCount;
|
ULONG EaChangeCount;
|
||||||
BOOLEAN TruncateOnClose;
|
BOOLEAN TruncateOnClose;
|
||||||
|
BOOLEAN PosixDelete;
|
||||||
FILE_LOCK FileLock;
|
FILE_LOCK FileLock;
|
||||||
#if (NTDDI_VERSION < NTDDI_WIN8)
|
#if (NTDDI_VERSION < NTDDI_WIN8)
|
||||||
OPLOCK Oplock;
|
OPLOCK Oplock;
|
||||||
@ -1561,6 +1562,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
|
|||||||
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
|
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
|
||||||
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||||
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||||
|
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||||
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
||||||
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
||||||
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
|
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
|
||||||
@ -1575,7 +1577,8 @@ NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
|
|||||||
PUNICODE_STRING StreamFileName);
|
PUNICODE_STRING StreamFileName);
|
||||||
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
||||||
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
||||||
PUNICODE_STRING FileName, BOOLEAN CheckingOldName);
|
PUNICODE_STRING FileName, BOOLEAN CheckingOldName,
|
||||||
|
BOOLEAN PosixRename);
|
||||||
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName);
|
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName);
|
||||||
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
||||||
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
||||||
|
101
src/sys/file.c
@ -43,6 +43,7 @@ NTSTATUS FspFileNodeOpen(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject,
|
|||||||
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
|
VOID FspFileNodeCleanup(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject, PULONG PCleanupFlags);
|
||||||
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
VOID FspFileNodeCleanupFlush(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||||
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||||
|
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject);
|
||||||
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
||||||
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
||||||
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
|
BOOLEAN HandleCleanup); /* TRUE to decrement handle count */
|
||||||
@ -57,7 +58,8 @@ NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
|
|||||||
PUNICODE_STRING StreamFileName);
|
PUNICODE_STRING StreamFileName);
|
||||||
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
||||||
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
||||||
PUNICODE_STRING FileName, BOOLEAN CheckingOldName);
|
PUNICODE_STRING FileName, BOOLEAN CheckingOldName,
|
||||||
|
BOOLEAN PosixRename);
|
||||||
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName);
|
VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName);
|
||||||
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
VOID FspFileNodeGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
||||||
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
BOOLEAN FspFileNodeTryGetFileInfo(FSP_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo);
|
||||||
@ -141,6 +143,7 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
|||||||
#pragma alloc_text(PAGE, FspFileNodeCleanup)
|
#pragma alloc_text(PAGE, FspFileNodeCleanup)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeCleanupFlush)
|
#pragma alloc_text(PAGE, FspFileNodeCleanupFlush)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeCleanupComplete)
|
#pragma alloc_text(PAGE, FspFileNodeCleanupComplete)
|
||||||
|
#pragma alloc_text(PAGE, FspFileNodePosixDelete)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeClose)
|
#pragma alloc_text(PAGE, FspFileNodeClose)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache)
|
#pragma alloc_text(PAGE, FspFileNodeFlushAndPurgeCache)
|
||||||
#pragma alloc_text(PAGE, FspFileNodeOverwriteStreams)
|
#pragma alloc_text(PAGE, FspFileNodeOverwriteStreams)
|
||||||
@ -909,7 +912,7 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
|
|||||||
DeletePending = 0 != FileNode->DeletePending;
|
DeletePending = 0 != FileNode->DeletePending;
|
||||||
MemoryBarrier();
|
MemoryBarrier();
|
||||||
|
|
||||||
if (DeletePending)
|
if (DeletePending && !FileNode->PosixDelete)
|
||||||
{
|
{
|
||||||
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
|
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
|
||||||
&DeletedFromContextTable);
|
&DeletedFromContextTable);
|
||||||
@ -1013,6 +1016,66 @@ VOID FspFileNodeCleanupComplete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject
|
|||||||
FspFileNodeDereference(FileNode);
|
FspFileNodeDereference(FileNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID FspFileNodePosixDelete(FSP_FILE_NODE *FileNode, PFILE_OBJECT FileObject)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Perform a POSIX delete of a FileNode. This removes the FileNode from the Context table.
|
||||||
|
*
|
||||||
|
* The FileNode must be acquired exclusive (Main or Full) when calling this function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||||
|
BOOLEAN DeletedFromContextTable = FALSE;
|
||||||
|
|
||||||
|
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
||||||
|
|
||||||
|
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &FileNode->FileName,
|
||||||
|
&DeletedFromContextTable);
|
||||||
|
ASSERT(DeletedFromContextTable);
|
||||||
|
|
||||||
|
FileNode->OpenCount = 0;
|
||||||
|
|
||||||
|
if (FsvolDeviceExtension->VolumeParams.NamedStreams &&
|
||||||
|
0 == FileNode->MainFileNode)
|
||||||
|
{
|
||||||
|
BOOLEAN StreamDeletedFromContextTable;
|
||||||
|
USHORT FileNameLength = FileNode->FileName.Length;
|
||||||
|
|
||||||
|
GATHER_DESCENDANTS(&FileNode->FileName, FALSE,
|
||||||
|
if (DescendantFileNode->FileName.Length > FileNameLength &&
|
||||||
|
L'\\' == DescendantFileNode->FileName.Buffer[FileNameLength / sizeof(WCHAR)])
|
||||||
|
break;
|
||||||
|
ASSERT(FileNode != DescendantFileNode);
|
||||||
|
ASSERT(0 != DescendantFileNode->OpenCount);
|
||||||
|
);
|
||||||
|
|
||||||
|
for (
|
||||||
|
DescendantFileNodeIndex = 0;
|
||||||
|
DescendantFileNodeCount > DescendantFileNodeIndex;
|
||||||
|
DescendantFileNodeIndex++)
|
||||||
|
{
|
||||||
|
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
|
||||||
|
|
||||||
|
FspFsvolDeviceDeleteContextByName(FsvolDeviceObject, &DescendantFileNode->FileName,
|
||||||
|
&StreamDeletedFromContextTable);
|
||||||
|
if (StreamDeletedFromContextTable)
|
||||||
|
{
|
||||||
|
DescendantFileNode->OpenCount = 0;
|
||||||
|
FspFileNodeDereference(DescendantFileNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCATTER_DESCENDANTS(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
||||||
|
|
||||||
|
FspFileNodeDereference(FileNode);
|
||||||
|
}
|
||||||
|
|
||||||
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
VOID FspFileNodeClose(FSP_FILE_NODE *FileNode,
|
||||||
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
PFILE_OBJECT FileObject, /* non-0 to remove share access */
|
||||||
BOOLEAN HandleCleanup) /* TRUE to decrement handle count */
|
BOOLEAN HandleCleanup) /* TRUE to decrement handle count */
|
||||||
@ -1293,7 +1356,8 @@ NTSTATUS FspFileNodeCheckBatchOplocksOnAllStreams(
|
|||||||
|
|
||||||
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp,
|
||||||
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
FSP_FILE_NODE *FileNode, ULONG AcquireFlags,
|
||||||
PUNICODE_STRING FileName, BOOLEAN CheckingOldName)
|
PUNICODE_STRING FileName, BOOLEAN CheckingOldName,
|
||||||
|
BOOLEAN PosixRename)
|
||||||
{
|
{
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
@ -1327,7 +1391,7 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
|
|||||||
* "rename" resource exclusively, which disallows new Opens.
|
* "rename" resource exclusively, which disallows new Opens.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!CheckingOldName)
|
if (!PosixRename && !CheckingOldName)
|
||||||
{
|
{
|
||||||
/* replaced file cannot be a directory or mapped as an image */
|
/* replaced file cannot be a directory or mapped as an image */
|
||||||
for (
|
for (
|
||||||
@ -1506,11 +1570,32 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
|
|||||||
|
|
||||||
if (DescendantFileNode != FileNode && 0 < DescendantFileNode->HandleCount)
|
if (DescendantFileNode != FileNode && 0 < DescendantFileNode->HandleCount)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* If we are doing a POSIX rename, then it is ok if we have open handles,
|
||||||
|
* provided that we do not have sharing violations.
|
||||||
|
*
|
||||||
|
* Check our share access:
|
||||||
|
*
|
||||||
|
* - If all openers are allowing FILE_SHARE_DELETE.
|
||||||
|
* - And all named streams openers are allowing FILE_SHARE_DELETE.
|
||||||
|
*
|
||||||
|
* Then we are good to go.
|
||||||
|
*
|
||||||
|
* (WinFsp cannot rename streams and there is no need to check MainFileDenyDeleteCount).
|
||||||
|
*
|
||||||
|
* NOTE: These are derived rules. AFAIK there is no documentation on how NTFS
|
||||||
|
* does this in the case of POSIX rename.
|
||||||
|
*/
|
||||||
|
if (PosixRename &&
|
||||||
|
DescendantFileNode->ShareAccess.OpenCount == DescendantFileNode->ShareAccess.SharedDelete &&
|
||||||
|
0 == DescendantFileNode->StreamDenyDeleteCount)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* release the FileNode and rename lock in case of failure! */
|
/* release the FileNode and rename lock in case of failure! */
|
||||||
FspFileNodeReleaseF(FileNode, AcquireFlags);
|
FspFileNodeReleaseF(FileNode, AcquireFlags);
|
||||||
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
|
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
|
||||||
|
|
||||||
Result = STATUS_ACCESS_DENIED;
|
Result = PosixRename ? STATUS_SHARING_VIOLATION : STATUS_ACCESS_DENIED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1599,14 +1684,14 @@ VOID FspFileNodeRename(FSP_FILE_NODE *FileNode, PUNICODE_STRING NewFileName)
|
|||||||
if (!Inserted)
|
if (!Inserted)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Handle files that have been Cleanup'ed but not Close'd.
|
* Handle files that have been replaced after a Rename.
|
||||||
* For example, this can happen when the user has mapped and closed a file
|
* For example, this can happen when the user has mapped and closed a file
|
||||||
* or immediately after breaking a Batch oplock.
|
* or immediately after breaking a Batch oplock or
|
||||||
|
* when doing a POSIX rename.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ASSERT(FspFileNodeIsValid(InsertedFileNode));
|
ASSERT(FspFileNodeIsValid(InsertedFileNode));
|
||||||
ASSERT(DescendantFileNode != InsertedFileNode);
|
ASSERT(DescendantFileNode != InsertedFileNode);
|
||||||
ASSERT(0 == InsertedFileNode->HandleCount);
|
|
||||||
ASSERT(0 != InsertedFileNode->OpenCount);
|
ASSERT(0 != InsertedFileNode->OpenCount);
|
||||||
|
|
||||||
InsertedFileNode->OpenCount = 0;
|
InsertedFileNode->OpenCount = 0;
|
||||||
|
@ -1441,7 +1441,8 @@ static NTSTATUS FspFsvolSetDispositionInformation(
|
|||||||
|
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||||
PFILE_DISPOSITION_INFORMATION Info = (PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
|
||||||
|
UINT32 DispositionFlags;
|
||||||
ULONG Length = IrpSp->Parameters.SetFile.Length;
|
ULONG Length = IrpSp->Parameters.SetFile.Length;
|
||||||
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
||||||
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
||||||
@ -1449,9 +1450,34 @@ static NTSTATUS FspFsvolSetDispositionInformation(
|
|||||||
BOOLEAN Success;
|
BOOLEAN Success;
|
||||||
|
|
||||||
ASSERT(FileNode == FileDesc->FileNode);
|
ASSERT(FileNode == FileDesc->FileNode);
|
||||||
|
ASSERT(
|
||||||
|
FileDispositionInformation == FileInformationClass ||
|
||||||
|
FileDispositionInformationEx == FileInformationClass);
|
||||||
|
|
||||||
|
if (FileDispositionInformation == FileInformationClass)
|
||||||
|
{
|
||||||
|
if (sizeof(FILE_DISPOSITION_INFORMATION) > Length)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
DispositionFlags = !!((PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->DeleteFile;
|
||||||
|
DispositionFlags |= FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK;
|
||||||
|
// old-school delete always did image section check; see below
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.SupportsPosixUnlinkRename)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (sizeof(FILE_DISPOSITION_INFORMATION_EX) > Length)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
DispositionFlags = ((PFILE_DISPOSITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer)->Flags;
|
||||||
|
|
||||||
|
/* !!!: REVISIT:
|
||||||
|
* For now we cannot handle the FILE_DISPOSITION_ON_CLOSE flag,
|
||||||
|
* as we need to understand the semantics better.
|
||||||
|
*/
|
||||||
|
if (FlagOn(DispositionFlags, FILE_DISPOSITION_ON_CLOSE))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
if (sizeof(FILE_DISPOSITION_INFORMATION) > Length)
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
|
||||||
if (FileNode->IsRootDirectory)
|
if (FileNode->IsRootDirectory)
|
||||||
/* cannot delete root directory */
|
/* cannot delete root directory */
|
||||||
return STATUS_CANNOT_DELETE;
|
return STATUS_CANNOT_DELETE;
|
||||||
@ -1459,7 +1485,7 @@ static NTSTATUS FspFsvolSetDispositionInformation(
|
|||||||
retry:
|
retry:
|
||||||
FspFileNodeAcquireExclusive(FileNode, Full);
|
FspFileNodeAcquireExclusive(FileNode, Full);
|
||||||
|
|
||||||
if (Info->DeleteFile)
|
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Perform oplock check.
|
* Perform oplock check.
|
||||||
@ -1487,15 +1513,40 @@ retry:
|
|||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
goto unlock_exit;
|
goto unlock_exit;
|
||||||
|
|
||||||
/* make sure no process is mapping the file as an image */
|
if (FlagOn(DispositionFlags, FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK))
|
||||||
Success = MmFlushImageSection(FileObject->SectionObjectPointer, MmFlushForDelete);
|
|
||||||
if (!Success)
|
|
||||||
{
|
{
|
||||||
Result = STATUS_CANNOT_DELETE;
|
/* make sure no process is mapping the file as an image */
|
||||||
goto unlock_exit;
|
Success = MmFlushImageSection(FileObject->SectionObjectPointer, MmFlushForDelete);
|
||||||
|
if (!Success)
|
||||||
|
{
|
||||||
|
Result = STATUS_CANNOT_DELETE;
|
||||||
|
goto unlock_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FlagOn(DispositionFlags, FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE))
|
||||||
|
{
|
||||||
|
/* if FileDesc does not have FILE_WRITE_ATTRIBUTE access, remove IGNORE_READONLY_ATTRIBUTE */
|
||||||
|
if (!FlagOn(FileDesc->GrantedAccess, FILE_WRITE_ATTRIBUTES))
|
||||||
|
DispositionFlags &= ~FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FileNode->PosixDelete)
|
||||||
|
{
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
goto unlock_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FlagOn(DispositionFlags, FILE_DISPOSITION_DELETE))
|
||||||
|
DispositionFlags &=
|
||||||
|
FILE_DISPOSITION_DO_NOT_DELETE |
|
||||||
|
FILE_DISPOSITION_DELETE |
|
||||||
|
FILE_DISPOSITION_POSIX_SEMANTICS |
|
||||||
|
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
|
||||||
|
else
|
||||||
|
DispositionFlags = FILE_DISPOSITION_DO_NOT_DELETE;
|
||||||
|
|
||||||
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, 0,
|
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, 0,
|
||||||
FspFsvolSetInformationRequestFini, &Request);
|
FspFsvolSetInformationRequestFini, &Request);
|
||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
@ -1504,8 +1555,8 @@ retry:
|
|||||||
Request->Kind = FspFsctlTransactSetInformationKind;
|
Request->Kind = FspFsctlTransactSetInformationKind;
|
||||||
Request->Req.SetInformation.UserContext = FileNode->UserContext;
|
Request->Req.SetInformation.UserContext = FileNode->UserContext;
|
||||||
Request->Req.SetInformation.UserContext2 = FileDesc->UserContext2;
|
Request->Req.SetInformation.UserContext2 = FileDesc->UserContext2;
|
||||||
Request->Req.SetInformation.FileInformationClass = FileDispositionInformation;
|
Request->Req.SetInformation.FileInformationClass = FileInformationClass;
|
||||||
Request->Req.SetInformation.Info.Disposition.Delete = Info->DeleteFile;
|
Request->Req.SetInformation.Info.DispositionEx.Flags = DispositionFlags;
|
||||||
|
|
||||||
FspFileNodeSetOwner(FileNode, Full, Request);
|
FspFileNodeSetOwner(FileNode, Full, Request);
|
||||||
FspIopRequestContext(Request, RequestFileNode) = FileNode;
|
FspIopRequestContext(Request, RequestFileNode) = FileNode;
|
||||||
@ -1525,23 +1576,51 @@ static NTSTATUS FspFsvolSetDispositionInformationSuccess(
|
|||||||
|
|
||||||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||||
PFILE_DISPOSITION_INFORMATION Info = (PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
||||||
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
||||||
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
|
||||||
|
UINT32 DispositionFlags = Request->Req.SetInformation.Info.DispositionEx.Flags;
|
||||||
|
BOOLEAN DeleteFile = BooleanFlagOn(DispositionFlags, FILE_DISPOSITION_DELETE);
|
||||||
|
|
||||||
FileNode->DeletePending = Info->DeleteFile;
|
FileNode->DeletePending = DeleteFile;
|
||||||
FileObject->DeletePending = Info->DeleteFile;
|
FileObject->DeletePending = DeleteFile;
|
||||||
|
|
||||||
/* fastfat does this, although it seems unnecessary */
|
if (FlagOn(DispositionFlags, FILE_DISPOSITION_POSIX_SEMANTICS))
|
||||||
#if 1
|
|
||||||
if (FileNode->IsDirectory && Info->DeleteFile)
|
|
||||||
{
|
{
|
||||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
ASSERT(DeleteFile);
|
||||||
FspFsvolDeviceExtension(IrpSp->DeviceObject);
|
|
||||||
FspNotifyDeletePending(
|
FileNode->PosixDelete = TRUE;
|
||||||
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
|
|
||||||
|
if (FileNode->IsDirectory)
|
||||||
|
{
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||||
|
FspFsvolDeviceExtension(IrpSp->DeviceObject);
|
||||||
|
FspNotifyDeletePending(
|
||||||
|
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send the appropriate notification; also invalidate dirinfo/etc. caches */
|
||||||
|
ULONG NotifyFilter, NotifyAction;
|
||||||
|
NotifyFilter = FileNode->IsDirectory ?
|
||||||
|
FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||||
|
NotifyAction = FILE_ACTION_REMOVED;
|
||||||
|
FspFileNodeNotifyChange(FileNode, NotifyFilter, NotifyAction, TRUE);
|
||||||
|
|
||||||
|
/* perform POSIX delete: remove file node from the context table */
|
||||||
|
FspFileNodePosixDelete(FileNode, FileObject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* fastfat does this, although it seems unnecessary */
|
||||||
|
#if 1
|
||||||
|
if (FileNode->IsDirectory && DeleteFile)
|
||||||
|
{
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||||
|
FspFsvolDeviceExtension(IrpSp->DeviceObject);
|
||||||
|
FspNotifyDeletePending(
|
||||||
|
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList, FileNode);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
FspIopRequestContext(Request, RequestFileNode) = 0;
|
FspIopRequestContext(Request, RequestFileNode) = 0;
|
||||||
FspFileNodeReleaseOwner(FileNode, Full, Request);
|
FspFileNodeReleaseOwner(FileNode, Full, Request);
|
||||||
@ -1560,7 +1639,9 @@ static NTSTATUS FspFsvolSetRenameInformation(
|
|||||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||||
PFILE_OBJECT TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
|
PFILE_OBJECT TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
|
||||||
BOOLEAN ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;
|
BOOLEAN ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;
|
||||||
|
UINT32 RenameFlags = !!ReplaceIfExists;
|
||||||
PFILE_RENAME_INFORMATION Info = (PFILE_RENAME_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
PFILE_RENAME_INFORMATION Info = (PFILE_RENAME_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||||||
ULONG Length = IrpSp->Parameters.SetFile.Length;
|
ULONG Length = IrpSp->Parameters.SetFile.Length;
|
||||||
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
||||||
@ -1575,11 +1656,21 @@ static NTSTATUS FspFsvolSetRenameInformation(
|
|||||||
PSECURITY_SUBJECT_CONTEXT SecuritySubjectContext = 0;
|
PSECURITY_SUBJECT_CONTEXT SecuritySubjectContext = 0;
|
||||||
|
|
||||||
ASSERT(FileNode == FileDesc->FileNode);
|
ASSERT(FileNode == FileDesc->FileNode);
|
||||||
|
ASSERT(
|
||||||
|
FileRenameInformation == FileInformationClass ||
|
||||||
|
FileRenameInformationEx == FileInformationClass);
|
||||||
|
|
||||||
if (sizeof(FILE_RENAME_INFORMATION) > Length)
|
if (sizeof(FILE_RENAME_INFORMATION) > Length)
|
||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
if (sizeof(WCHAR) > Info->FileNameLength)
|
if (sizeof(WCHAR) > Info->FileNameLength)
|
||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (FileRenameInformationEx == FileInformationClass)
|
||||||
|
{
|
||||||
|
if (!FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.SupportsPosixUnlinkRename)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
RenameFlags |= Info->Flags &
|
||||||
|
(FILE_RENAME_POSIX_SEMANTICS | FILE_RENAME_IGNORE_READONLY_ATTRIBUTE);
|
||||||
|
}
|
||||||
if (FileNode->IsRootDirectory)
|
if (FileNode->IsRootDirectory)
|
||||||
/* cannot rename root directory */
|
/* cannot rename root directory */
|
||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
@ -1657,13 +1748,14 @@ retry:
|
|||||||
Request->Kind = FspFsctlTransactSetInformationKind;
|
Request->Kind = FspFsctlTransactSetInformationKind;
|
||||||
Request->Req.SetInformation.UserContext = FileNode->UserContext;
|
Request->Req.SetInformation.UserContext = FileNode->UserContext;
|
||||||
Request->Req.SetInformation.UserContext2 = FileDesc->UserContext2;
|
Request->Req.SetInformation.UserContext2 = FileDesc->UserContext2;
|
||||||
Request->Req.SetInformation.FileInformationClass = FileRenameInformation;
|
Request->Req.SetInformation.FileInformationClass = FileInformationClass;
|
||||||
Request->Req.SetInformation.Info.Rename.NewFileName.Offset = Request->FileName.Size;
|
Request->Req.SetInformation.Info.Rename.NewFileName.Offset = Request->FileName.Size;
|
||||||
Request->Req.SetInformation.Info.Rename.NewFileName.Size = NewFileName.Length + sizeof(WCHAR);
|
Request->Req.SetInformation.Info.Rename.NewFileName.Size = NewFileName.Length + sizeof(WCHAR);
|
||||||
|
Request->Req.SetInformation.Info.RenameEx.Flags = RenameFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Special rules for renaming open files:
|
* Special rules for renaming open files without POSIX semantics:
|
||||||
* - A file cannot be renamed if it has any open handles,
|
* - A file cannot be renamed if it has any open handles,
|
||||||
* unless it is only open because of a batch opportunistic lock (oplock)
|
* unless it is only open because of a batch opportunistic lock (oplock)
|
||||||
* and the batch oplock can be broken immediately.
|
* and the batch oplock can be broken immediately.
|
||||||
@ -1675,13 +1767,15 @@ retry:
|
|||||||
|
|
||||||
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
|
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
|
||||||
FileNode, FspFileNodeAcquireFull,
|
FileNode, FspFileNodeAcquireFull,
|
||||||
&FileNode->FileName, TRUE);
|
&FileNode->FileName, TRUE,
|
||||||
|
0 != (FILE_RENAME_POSIX_SEMANTICS & RenameFlags));
|
||||||
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
|
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
|
||||||
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
|
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
|
||||||
goto retry;
|
goto retry;
|
||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
{
|
{
|
||||||
Result = STATUS_ACCESS_DENIED;
|
if (STATUS_SHARING_VIOLATION != Result)
|
||||||
|
Result = STATUS_ACCESS_DENIED;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1689,13 +1783,15 @@ retry:
|
|||||||
{
|
{
|
||||||
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
|
Result = FspFileNodeRenameCheck(FsvolDeviceObject, Irp,
|
||||||
FileNode, FspFileNodeAcquireFull,
|
FileNode, FspFileNodeAcquireFull,
|
||||||
&NewFileName, FALSE);
|
&NewFileName, FALSE,
|
||||||
|
0 != (FILE_RENAME_POSIX_SEMANTICS & RenameFlags));
|
||||||
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
|
/* FspFileNodeRenameCheck releases FileNode and rename lock on failure */
|
||||||
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
|
if (STATUS_OPLOCK_BREAK_IN_PROGRESS == Result)
|
||||||
goto retry;
|
goto retry;
|
||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
{
|
{
|
||||||
Result = STATUS_ACCESS_DENIED;
|
if (STATUS_SHARING_VIOLATION != Result)
|
||||||
|
Result = STATUS_ACCESS_DENIED;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1796,10 +1892,15 @@ static NTSTATUS FspFsvolSetInformation(
|
|||||||
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
|
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
|
||||||
|
|
||||||
/* special case FileDispositionInformation/FileRenameInformation */
|
/* special case FileDispositionInformation/FileRenameInformation */
|
||||||
if (FileDispositionInformation == FileInformationClass)
|
switch (FileInformationClass)
|
||||||
|
{
|
||||||
|
case FileDispositionInformation:
|
||||||
|
case FileDispositionInformationEx:
|
||||||
return FspFsvolSetDispositionInformation(FsvolDeviceObject, Irp, IrpSp);
|
return FspFsvolSetDispositionInformation(FsvolDeviceObject, Irp, IrpSp);
|
||||||
if (FileRenameInformation == FileInformationClass)
|
case FileRenameInformation:
|
||||||
|
case FileRenameInformationEx:
|
||||||
return FspFsvolSetRenameInformation(FsvolDeviceObject, Irp, IrpSp);
|
return FspFsvolSetRenameInformation(FsvolDeviceObject, Irp, IrpSp);
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||||
@ -1924,8 +2025,10 @@ NTSTATUS FspFsvolSetInformationPrepare(
|
|||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||||||
|
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
|
||||||
|
|
||||||
if (FileRenameInformation != IrpSp->Parameters.SetFile.FileInformationClass ||
|
if ((FileRenameInformation != FileInformationClass &&
|
||||||
|
FileRenameInformationEx != FileInformationClass) ||
|
||||||
0 == FspIopRequestContext(Request, RequestSubjectContextOrAccessToken))
|
0 == FspIopRequestContext(Request, RequestSubjectContextOrAccessToken))
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
@ -1997,10 +2100,15 @@ NTSTATUS FspFsvolSetInformationComplete(
|
|||||||
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
|
FILE_INFORMATION_CLASS FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
|
||||||
|
|
||||||
/* special case FileDispositionInformation/FileRenameInformation */
|
/* special case FileDispositionInformation/FileRenameInformation */
|
||||||
if (FileDispositionInformation == FileInformationClass)
|
switch (FileInformationClass)
|
||||||
|
{
|
||||||
|
case FileDispositionInformation:
|
||||||
|
case FileDispositionInformationEx:
|
||||||
FSP_RETURN(Result = FspFsvolSetDispositionInformationSuccess(Irp, Response));
|
FSP_RETURN(Result = FspFsvolSetDispositionInformationSuccess(Irp, Response));
|
||||||
if (FileRenameInformation == FileInformationClass)
|
case FileRenameInformation:
|
||||||
|
case FileRenameInformationEx:
|
||||||
FSP_RETURN(Result = FspFsvolSetRenameInformationSuccess(Irp, Response));
|
FSP_RETURN(Result = FspFsvolSetRenameInformationSuccess(Irp, Response));
|
||||||
|
}
|
||||||
|
|
||||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||||
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
||||||
|
@ -101,7 +101,8 @@ static NTSTATUS FspFsvolQueryFsAttributeInformation(
|
|||||||
(FsvolDeviceExtension->VolumeParams.NamedStreams ? FILE_NAMED_STREAMS : 0) |
|
(FsvolDeviceExtension->VolumeParams.NamedStreams ? FILE_NAMED_STREAMS : 0) |
|
||||||
//(FsvolDeviceExtension->VolumeParams.HardLinks ? FILE_SUPPORTS_HARD_LINKS : 0) |
|
//(FsvolDeviceExtension->VolumeParams.HardLinks ? FILE_SUPPORTS_HARD_LINKS : 0) |
|
||||||
(FsvolDeviceExtension->VolumeParams.ExtendedAttributes ? FILE_SUPPORTS_EXTENDED_ATTRIBUTES : 0) |
|
(FsvolDeviceExtension->VolumeParams.ExtendedAttributes ? FILE_SUPPORTS_EXTENDED_ATTRIBUTES : 0) |
|
||||||
(FsvolDeviceExtension->VolumeParams.ReadOnlyVolume ? FILE_READ_ONLY_VOLUME : 0);
|
(FsvolDeviceExtension->VolumeParams.ReadOnlyVolume ? FILE_READ_ONLY_VOLUME : 0) |
|
||||||
|
(FsvolDeviceExtension->VolumeParams.SupportsPosixUnlinkRename ? FILE_SUPPORTS_POSIX_UNLINK_RENAME : 0);
|
||||||
Info->MaximumComponentNameLength = FsvolDeviceExtension->VolumeParams.MaxComponentLength;
|
Info->MaximumComponentNameLength = FsvolDeviceExtension->VolumeParams.MaxComponentLength;
|
||||||
|
|
||||||
RtlInitUnicodeString(&FileSystemName, FsvolDeviceExtension->VolumeParams.FileSystemName);
|
RtlInitUnicodeString(&FileSystemName, FsvolDeviceExtension->VolumeParams.FileSystemName);
|
||||||
|
@ -76,7 +76,7 @@ if X%SignedPackage%==X (
|
|||||||
echo driver-x86.inf >>driver.ddf
|
echo driver-x86.inf >>driver.ddf
|
||||||
echo %MyProductFileName%-x86.sys >>driver.ddf
|
echo %MyProductFileName%-x86.sys >>driver.ddf
|
||||||
makecab /F driver.ddf
|
makecab /F driver.ddf
|
||||||
signtool sign /ac %CrossCert% /i %Issuer% /n %Subject% /t http://timestamp.digicert.com driver.cab
|
signtool sign /ac %CrossCert% /i %Issuer% /n %Subject% /fd sha256 /tr http://timestamp.digicert.com /td sha256 driver.cab
|
||||||
if errorlevel 1 set /a signfail=signfail+1
|
if errorlevel 1 set /a signfail=signfail+1
|
||||||
popd
|
popd
|
||||||
)
|
)
|
||||||
|
@ -32,6 +32,7 @@ set dfl_tests=^
|
|||||||
winfsp-tests-x64 ^
|
winfsp-tests-x64 ^
|
||||||
winfsp-tests-x64-case-randomize ^
|
winfsp-tests-x64-case-randomize ^
|
||||||
winfsp-tests-x64-flushpurge ^
|
winfsp-tests-x64-flushpurge ^
|
||||||
|
winfsp-tests-x64-legacy-unlink-rename ^
|
||||||
winfsp-tests-x64-mountpoint-drive ^
|
winfsp-tests-x64-mountpoint-drive ^
|
||||||
winfsp-tests-x64-mountpoint-dir ^
|
winfsp-tests-x64-mountpoint-dir ^
|
||||||
winfsp-tests-x64-mountpoint-dir-case-sensitive ^
|
winfsp-tests-x64-mountpoint-dir-case-sensitive ^
|
||||||
@ -52,6 +53,7 @@ set dfl_tests=^
|
|||||||
winfsp-tests-x86 ^
|
winfsp-tests-x86 ^
|
||||||
winfsp-tests-x86-case-randomize ^
|
winfsp-tests-x86-case-randomize ^
|
||||||
winfsp-tests-x86-flushpurge ^
|
winfsp-tests-x86-flushpurge ^
|
||||||
|
winfsp-tests-x86-legacy-unlink-rename ^
|
||||||
winfsp-tests-x86-mountpoint-drive ^
|
winfsp-tests-x86-mountpoint-drive ^
|
||||||
winfsp-tests-x86-mountpoint-dir ^
|
winfsp-tests-x86-mountpoint-dir ^
|
||||||
winfsp-tests-x86-mountpoint-dir-case-sensitive ^
|
winfsp-tests-x86-mountpoint-dir-case-sensitive ^
|
||||||
@ -191,6 +193,11 @@ winfsp-tests-x64 --flush-and-purge-on-cleanup * +ea*
|
|||||||
if !ERRORLEVEL! neq 0 goto fail
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
exit /b 0
|
exit /b 0
|
||||||
|
|
||||||
|
:winfsp-tests-x64-legacy-unlink-rename
|
||||||
|
winfsp-tests-x64 --legacy-unlink-rename * +ea*
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
:winfsp-tests-x64-mountpoint-drive
|
:winfsp-tests-x64-mountpoint-drive
|
||||||
winfsp-tests-x64 --mountpoint=X: --resilient * +ea*
|
winfsp-tests-x64 --mountpoint=X: --resilient * +ea*
|
||||||
if !ERRORLEVEL! neq 0 goto fail
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
@ -236,6 +243,11 @@ winfsp-tests-x86 --flush-and-purge-on-cleanup * +ea*
|
|||||||
if !ERRORLEVEL! neq 0 goto fail
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
exit /b 0
|
exit /b 0
|
||||||
|
|
||||||
|
:winfsp-tests-x86-legacy-unlink-rename
|
||||||
|
winfsp-tests-x86 --legacy-unlink-rename * +ea*
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
:winfsp-tests-x86-mountpoint-drive
|
:winfsp-tests-x86-mountpoint-drive
|
||||||
winfsp-tests-x86 --mountpoint=X: --resilient * +ea*
|
winfsp-tests-x86 --mountpoint=X: --resilient * +ea*
|
||||||
if !ERRORLEVEL! neq 0 goto fail
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
@ -286,6 +286,7 @@ namespace memfs
|
|||||||
Host.ExtendedAttributes = true;
|
Host.ExtendedAttributes = true;
|
||||||
Host.WslFeatures = true;
|
Host.WslFeatures = true;
|
||||||
Host.RejectIrpPriorToTransact0 = true;
|
Host.RejectIrpPriorToTransact0 = true;
|
||||||
|
Host.SupportsPosixUnlinkRename = true;
|
||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,19 +564,6 @@ namespace memfs
|
|||||||
AllocationUnit * AllocationUnit;
|
AllocationUnit * AllocationUnit;
|
||||||
SetFileSizeInternal(FileNode, AllocationSize, true);
|
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(
|
public override void Close(
|
||||||
@ -920,19 +908,6 @@ namespace memfs
|
|||||||
return STATUS_SUCCESS;
|
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(
|
public override Int32 Rename(
|
||||||
Object FileNode0,
|
Object FileNode0,
|
||||||
Object FileDesc,
|
Object FileDesc,
|
||||||
@ -1330,6 +1305,45 @@ namespace memfs
|
|||||||
FileNode.FileInfo.EaSize = FileNode.FileInfo.EaSize + EaSizePlus - EaSizeMinus;
|
FileNode.FileInfo.EaSize = FileNode.FileInfo.EaSize + EaSizePlus - EaSizeMinus;
|
||||||
return STATUS_SUCCESS;
|
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 FileNodeMap FileNodeMap;
|
||||||
private UInt32 MaxFileNodes;
|
private UInt32 MaxFileNodes;
|
||||||
|
@ -88,6 +88,13 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH,
|
|||||||
#define MEMFS_REJECT_EARLY_IRP
|
#define MEMFS_REJECT_EARLY_IRP
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define the MEMFS_DELETE macro to include new Delete support
|
||||||
|
* (instead of Cleanup/FspCleanupDelete). This is required to
|
||||||
|
* properly support POSIX unlink/rename.
|
||||||
|
*/
|
||||||
|
#define MEMFS_DELETE
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
|
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
|
||||||
* a check for the Write buffer to ensure that it is read-only.
|
* a check for the Write buffer to ensure that it is read-only.
|
||||||
@ -965,6 +972,8 @@ void SlowioReadDirectoryThread(
|
|||||||
* FSP_FILE_SYSTEM_INTERFACE
|
* FSP_FILE_SYSTEM_INTERFACE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
|
||||||
|
PVOID FileNode0, PWSTR FileName, ULONG Flags);
|
||||||
#if defined(MEMFS_REPARSE_POINTS)
|
#if defined(MEMFS_REPARSE_POINTS)
|
||||||
static NTSTATUS GetReparsePointByName(
|
static NTSTATUS GetReparsePointByName(
|
||||||
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
|
||||||
@ -1347,7 +1356,9 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
MEMFS_FILE_NODE *MainFileNode = FileNode;
|
MEMFS_FILE_NODE *MainFileNode = FileNode;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MEMFS_DELETE)
|
||||||
assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */
|
assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */
|
||||||
|
#endif
|
||||||
|
|
||||||
if (Flags & FspCleanupSetArchiveBit)
|
if (Flags & FspCleanupSetArchiveBit)
|
||||||
{
|
{
|
||||||
@ -1376,21 +1387,10 @@ static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE);
|
SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Flags & FspCleanupDelete) && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
#if !defined(MEMFS_DELETE)
|
||||||
{
|
if (Flags & FspCleanupDelete)
|
||||||
#if defined(MEMFS_NAMED_STREAMS)
|
Delete(FileSystem, FileNode0, FileName, -1);
|
||||||
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
|
|
||||||
ULONG Index;
|
|
||||||
|
|
||||||
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
|
|
||||||
MemfsFileNodeMapEnumerateFn, &Context);
|
|
||||||
for (Index = 0; Context.Count > Index; Index++)
|
|
||||||
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
|
|
||||||
MemfsFileNodeMapEnumerateFree(&Context);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VOID Close(FSP_FILE_SYSTEM *FileSystem,
|
static VOID Close(FSP_FILE_SYSTEM *FileSystem,
|
||||||
@ -1651,17 +1651,13 @@ static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(MEMFS_DELETE)
|
||||||
static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem,
|
static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem,
|
||||||
PVOID FileNode0, PWSTR FileName)
|
PVOID FileNode0, PWSTR FileName)
|
||||||
{
|
{
|
||||||
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
|
return Delete(FileSystem, FileNode0, FileName, FILE_DISPOSITION_DELETE);
|
||||||
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
|
|
||||||
|
|
||||||
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
|
||||||
return STATUS_DIRECTORY_NOT_EMPTY;
|
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem,
|
static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem,
|
||||||
PVOID FileNode0,
|
PVOID FileNode0,
|
||||||
@ -2231,6 +2227,54 @@ static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static NTSTATUS Delete(FSP_FILE_SYSTEM *FileSystem,
|
||||||
|
PVOID FileNode0, PWSTR FileName, ULONG Flags)
|
||||||
|
{
|
||||||
|
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
|
||||||
|
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
|
||||||
|
|
||||||
|
switch (Flags)
|
||||||
|
{
|
||||||
|
case FILE_DISPOSITION_DO_NOT_DELETE:
|
||||||
|
// set file disposition flag: do not delete file at Cleanup
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
|
case FILE_DISPOSITION_DELETE:
|
||||||
|
// set file disposition flag: delete file at Cleanup
|
||||||
|
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
||||||
|
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
|
case FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS:
|
||||||
|
// delete file now; open handles to file remain valid
|
||||||
|
/* fallthrough */
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
// delete file now; called during Cleanup
|
||||||
|
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
|
||||||
|
return STATUS_DIRECTORY_NOT_EMPTY;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if defined(MEMFS_NAMED_STREAMS)
|
||||||
|
MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE };
|
||||||
|
ULONG Index;
|
||||||
|
|
||||||
|
MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode,
|
||||||
|
MemfsFileNodeMapEnumerateFn, &Context);
|
||||||
|
for (Index = 0; Context.Count > Index; Index++)
|
||||||
|
MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]);
|
||||||
|
MemfsFileNodeMapEnumerateFree(&Context);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
|
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
|
||||||
{
|
{
|
||||||
GetVolumeInfo,
|
GetVolumeInfo,
|
||||||
@ -2255,7 +2299,11 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
|
|||||||
GetFileInfo,
|
GetFileInfo,
|
||||||
SetBasicInfo,
|
SetBasicInfo,
|
||||||
SetFileSize,
|
SetFileSize,
|
||||||
|
#if !defined(MEMFS_DELETE)
|
||||||
CanDelete,
|
CanDelete,
|
||||||
|
#else
|
||||||
|
0,
|
||||||
|
#endif
|
||||||
Rename,
|
Rename,
|
||||||
GetSecurity,
|
GetSecurity,
|
||||||
SetSecurity,
|
SetSecurity,
|
||||||
@ -2293,11 +2341,15 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
|
|||||||
#if defined(MEMFS_EA)
|
#if defined(MEMFS_EA)
|
||||||
Overwrite,
|
Overwrite,
|
||||||
GetEa,
|
GetEa,
|
||||||
SetEa
|
SetEa,
|
||||||
#else
|
#else
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
#endif
|
||||||
|
#if defined(MEMFS_DELETE)
|
||||||
|
Delete,
|
||||||
|
#else
|
||||||
0,
|
0,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@ -2323,6 +2375,7 @@ NTSTATUS MemfsCreateFunnel(
|
|||||||
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||||
BOOLEAN CaseInsensitive = !!(Flags & MemfsCaseInsensitive);
|
BOOLEAN CaseInsensitive = !!(Flags & MemfsCaseInsensitive);
|
||||||
BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & MemfsFlushAndPurgeOnCleanup);
|
BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & MemfsFlushAndPurgeOnCleanup);
|
||||||
|
BOOLEAN SupportsPosixUnlinkRename = !(Flags & MemfsLegacyUnlinkRename);
|
||||||
PWSTR DevicePath = MemfsNet == (Flags & MemfsDeviceMask) ?
|
PWSTR DevicePath = MemfsNet == (Flags & MemfsDeviceMask) ?
|
||||||
L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME;
|
L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME;
|
||||||
UINT64 AllocationUnit;
|
UINT64 AllocationUnit;
|
||||||
@ -2404,6 +2457,7 @@ NTSTATUS MemfsCreateFunnel(
|
|||||||
#if defined(MEMFS_REJECT_EARLY_IRP)
|
#if defined(MEMFS_REJECT_EARLY_IRP)
|
||||||
VolumeParams.RejectIrpPriorToTransact0 = 1;
|
VolumeParams.RejectIrpPriorToTransact0 = 1;
|
||||||
#endif
|
#endif
|
||||||
|
VolumeParams.SupportsPosixUnlinkRename = SupportsPosixUnlinkRename;
|
||||||
if (0 != VolumePrefix)
|
if (0 != VolumePrefix)
|
||||||
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
|
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
|
||||||
wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),
|
wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),
|
||||||
|
@ -37,9 +37,10 @@ enum
|
|||||||
MemfsDeviceMask = 0x0000000f,
|
MemfsDeviceMask = 0x0000000f,
|
||||||
MemfsCaseInsensitive = 0x80000000,
|
MemfsCaseInsensitive = 0x80000000,
|
||||||
MemfsFlushAndPurgeOnCleanup = 0x40000000,
|
MemfsFlushAndPurgeOnCleanup = 0x40000000,
|
||||||
|
MemfsLegacyUnlinkRename = 0x20000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MemfsCreate(Flags, FileInfoTimeout, MaxFileNodes, MaxFileSize, VolumePrefix, RootSddl, PMemfs)\
|
#define MemfsCreate(Flags, FileInfoTimeout, MaxFileNodes, MaxFileSize, VolumePrefix, RootSddl, PMemfs)\
|
||||||
MemfsCreateFunnel(\
|
MemfsCreateFunnel(\
|
||||||
Flags,\
|
Flags,\
|
||||||
FileInfoTimeout,\
|
FileInfoTimeout,\
|
||||||
|
@ -1784,6 +1784,179 @@ void rename_pid_dotest(ULONG Flags, PWSTR Prefix)
|
|||||||
ASSERT(0 < rename_pid_Pass);// && 0 == rename_pid_Fail);
|
ASSERT(0 < rename_pid_Pass);// && 0 == rename_pid_Fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rename_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;
|
||||||
|
HANDLE RootDirectory;
|
||||||
|
ULONG FileNameLength;
|
||||||
|
WCHAR FileName[1];
|
||||||
|
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
|
||||||
|
|
||||||
|
HANDLE Handle0, Handle1, Handle2;
|
||||||
|
WCHAR File0Path[MAX_PATH];
|
||||||
|
WCHAR File2Path[MAX_PATH];
|
||||||
|
union
|
||||||
|
{
|
||||||
|
FILE_RENAME_INFORMATION I;
|
||||||
|
UINT8 B[sizeof(FILE_RENAME_INFORMATION) + MAX_PATH * sizeof(WCHAR)];
|
||||||
|
} RenameInfo;
|
||||||
|
IO_STATUS_BLOCK IoStatus;
|
||||||
|
|
||||||
|
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\",
|
||||||
|
VolPrefix ? L"" : L"\\\\?\\GLOBALROOT", VolPrefix ? VolPrefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = GetVolumeInformationW(File0Path,
|
||||||
|
0, 0,
|
||||||
|
0, 0, &FileSystemFlags,
|
||||||
|
0, 0);
|
||||||
|
ASSERT(Success);
|
||||||
|
if (0 != (FileSystemFlags & 0x400/*FILE_SUPPORTS_POSIX_UNLINK_RENAME*/))
|
||||||
|
{
|
||||||
|
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\file0",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
StringCbPrintfW(File2Path, sizeof File2Path, L"%s%s\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Handle0 = CreateFileW(File0Path,
|
||||||
|
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);
|
||||||
|
CloseHandle(Handle0);
|
||||||
|
|
||||||
|
Handle0 = CreateFileW(File0Path,
|
||||||
|
DELETE, 0, 0,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||||
|
|
||||||
|
Handle1 = CreateFileW(File0Path,
|
||||||
|
FILE_READ_ATTRIBUTES, 0, 0,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != 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(0 == IoStatus.Status);
|
||||||
|
|
||||||
|
Handle2 = CreateFileW(File2Path,
|
||||||
|
FILE_READ_ATTRIBUTES, 0, 0,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle2);
|
||||||
|
|
||||||
|
CloseHandle(Handle2);
|
||||||
|
CloseHandle(Handle1);
|
||||||
|
CloseHandle(Handle0);
|
||||||
|
|
||||||
|
Handle0 = CreateFileW(File0Path,
|
||||||
|
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);
|
||||||
|
CloseHandle(Handle0);
|
||||||
|
|
||||||
|
Handle1 = CreateFileW(File0Path,
|
||||||
|
GENERIC_READ | GENERIC_WRITE, 0, 0,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle1);
|
||||||
|
|
||||||
|
Handle2 = CreateFileW(File2Path,
|
||||||
|
DELETE, 0, 0,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||||
|
|
||||||
|
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));
|
||||||
|
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,
|
||||||
|
65/*FileRenameInformationEx*/);
|
||||||
|
ASSERT(STATUS_SHARING_VIOLATION == IoStatus.Status);
|
||||||
|
|
||||||
|
CloseHandle(Handle2);
|
||||||
|
CloseHandle(Handle1);
|
||||||
|
|
||||||
|
Handle1 = CreateFileW(File0Path,
|
||||||
|
FILE_READ_ATTRIBUTES, 0, 0,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle1);
|
||||||
|
|
||||||
|
Handle2 = CreateFileW(File2Path,
|
||||||
|
DELETE, 0, 0,
|
||||||
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle0);
|
||||||
|
|
||||||
|
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));
|
||||||
|
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,
|
||||||
|
65/*FileRenameInformationEx*/);
|
||||||
|
ASSERT(0 == IoStatus.Status);
|
||||||
|
|
||||||
|
CloseHandle(Handle2);
|
||||||
|
CloseHandle(Handle1);
|
||||||
|
|
||||||
|
Success = DeleteFileW(File0Path);
|
||||||
|
ASSERT(Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
memfs_stop(memfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rename_ex_test(void)
|
||||||
|
{
|
||||||
|
if (OptLegacyUnlinkRename)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (NtfsTests)
|
||||||
|
{
|
||||||
|
WCHAR DirBuf[MAX_PATH], DriveBuf[3];
|
||||||
|
GetTestDirectoryAndDrive(DirBuf, DriveBuf);
|
||||||
|
rename_ex_dotest(-1, DriveBuf, DirBuf, 0);
|
||||||
|
}
|
||||||
|
if (WinFspDiskTests)
|
||||||
|
{
|
||||||
|
rename_ex_dotest(MemfsDisk, 0, 0, 0);
|
||||||
|
rename_ex_dotest(MemfsDisk, 0, 0, 1000);
|
||||||
|
}
|
||||||
|
if (WinFspNetTests)
|
||||||
|
{
|
||||||
|
rename_ex_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 0);
|
||||||
|
rename_ex_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void rename_pid_test(void)
|
void rename_pid_test(void)
|
||||||
{
|
{
|
||||||
if (NtfsTests)
|
if (NtfsTests)
|
||||||
@ -2060,6 +2233,8 @@ void info_tests(void)
|
|||||||
if (!OptShareName)
|
if (!OptShareName)
|
||||||
TEST(rename_mmap_test);
|
TEST(rename_mmap_test);
|
||||||
TEST(rename_standby_test);
|
TEST(rename_standby_test);
|
||||||
|
if (!OptLegacyUnlinkRename)
|
||||||
|
TEST(rename_ex_test);
|
||||||
if (!NtfsTests)
|
if (!NtfsTests)
|
||||||
TEST(rename_pid_test);
|
TEST(rename_pid_test);
|
||||||
TEST(getvolinfo_test);
|
TEST(getvolinfo_test);
|
||||||
|
@ -43,7 +43,8 @@ void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
|
|||||||
Result = MemfsCreateFunnel(
|
Result = MemfsCreateFunnel(
|
||||||
Flags |
|
Flags |
|
||||||
(OptCaseInsensitive ? MemfsCaseInsensitive : 0) |
|
(OptCaseInsensitive ? MemfsCaseInsensitive : 0) |
|
||||||
(OptFlushAndPurgeOnCleanup ? MemfsFlushAndPurgeOnCleanup : 0),
|
(OptFlushAndPurgeOnCleanup ? MemfsFlushAndPurgeOnCleanup : 0) |
|
||||||
|
(OptLegacyUnlinkRename ? MemfsLegacyUnlinkRename : 0),
|
||||||
FileInfoTimeout,
|
FileInfoTimeout,
|
||||||
1024,
|
1024,
|
||||||
1024 * 1024,
|
1024 * 1024,
|
||||||
|
@ -39,6 +39,7 @@ BOOLEAN OptCaseInsensitiveCmp = FALSE;
|
|||||||
BOOLEAN OptCaseInsensitive = FALSE;
|
BOOLEAN OptCaseInsensitive = FALSE;
|
||||||
BOOLEAN OptCaseRandomize = FALSE;
|
BOOLEAN OptCaseRandomize = FALSE;
|
||||||
BOOLEAN OptFlushAndPurgeOnCleanup = FALSE;
|
BOOLEAN OptFlushAndPurgeOnCleanup = FALSE;
|
||||||
|
BOOLEAN OptLegacyUnlinkRename = FALSE;
|
||||||
BOOLEAN OptNotify = FALSE;
|
BOOLEAN OptNotify = FALSE;
|
||||||
WCHAR OptOplock = 0;
|
WCHAR OptOplock = 0;
|
||||||
WCHAR OptMountPointBuf[MAX_PATH], *OptMountPoint;
|
WCHAR OptMountPointBuf[MAX_PATH], *OptMountPoint;
|
||||||
@ -296,6 +297,11 @@ int main(int argc, char *argv[])
|
|||||||
OptFlushAndPurgeOnCleanup = TRUE;
|
OptFlushAndPurgeOnCleanup = TRUE;
|
||||||
rmarg(argv, argc, argi);
|
rmarg(argv, argc, argi);
|
||||||
}
|
}
|
||||||
|
else if (0 == strcmp("--legacy-unlink-rename", a))
|
||||||
|
{
|
||||||
|
OptLegacyUnlinkRename = TRUE;
|
||||||
|
rmarg(argv, argc, argi);
|
||||||
|
}
|
||||||
else if (0 == strcmp("--notify", a))
|
else if (0 == strcmp("--notify", a))
|
||||||
{
|
{
|
||||||
OptNotify = TRUE;
|
OptNotify = TRUE;
|
||||||
|
@ -159,6 +159,7 @@ extern BOOLEAN OptCaseInsensitiveCmp;
|
|||||||
extern BOOLEAN OptCaseInsensitive;
|
extern BOOLEAN OptCaseInsensitive;
|
||||||
extern BOOLEAN OptCaseRandomize;
|
extern BOOLEAN OptCaseRandomize;
|
||||||
extern BOOLEAN OptFlushAndPurgeOnCleanup;
|
extern BOOLEAN OptFlushAndPurgeOnCleanup;
|
||||||
|
extern BOOLEAN OptLegacyUnlinkRename;
|
||||||
extern BOOLEAN OptNotify;
|
extern BOOLEAN OptNotify;
|
||||||
extern WCHAR OptOplock;
|
extern WCHAR OptOplock;
|
||||||
extern WCHAR OptMountPointBuf[], *OptMountPoint;
|
extern WCHAR OptMountPointBuf[], *OptMountPoint;
|
||||||
|