From fbe46d8c813d6490ddac75d3d3347a5bc7048bca Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Sat, 4 Feb 2017 14:56:01 -0800 Subject: [PATCH] doc: update tutorial --- doc/WinFsp-Tutorial.asciidoc | 35 ++++++++++++++++-------------- doc/WinFsp-Tutorial/Installer.png | Bin 0 -> 15962 bytes 2 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 doc/WinFsp-Tutorial/Installer.png diff --git a/doc/WinFsp-Tutorial.asciidoc b/doc/WinFsp-Tutorial.asciidoc index 2ece9a08..eafd9552 100644 --- a/doc/WinFsp-Tutorial.asciidoc +++ b/doc/WinFsp-Tutorial.asciidoc @@ -1,4 +1,4 @@ -= Tutorial: creating a simple file system for Windows += Tutorial: creating a simple file system :toc: preamble :toc-title: ifdef::env-github[] @@ -11,6 +11,17 @@ endif::[] In this tutorial we describe the process of creating a simple user mode file system using WinFsp. The file system we will create is called "passthrough", because it simply passes through the file system operations requested by the kernel to an underlying file system (usually NTFS). +== Prerequisites + +This tutorial assumes that you have WinFsp and Visual Studio 2015 installed. The WinFsp installer can be downloaded from the WinFsp GitHub repository: https://github.com/billziss-gh/winfsp. The Microsoft Visual Studio Community 2015 can be downloaded for free from Microsoft's web site. + +When installing WinFsp make sure to choose "Developer" to ensure that all necessary header and library files are included in the installation. + +image::WinFsp-Tutorial/Installer.png[WinFsp Installer] + +With those prerequisites out of the way we are now ready to start creating our first file system. + + == Create the project skeleton We first start by creating the Visual Studio project. Choose "Win32 Console Application" and then select our preferred settings. For this project we will select empty project, because we will add all files ourselves. @@ -106,7 +117,7 @@ options: -m MountPoint [X:|*|directory] ---- -The full code to handle these command line parameters is straight forward and is omitted for brevity. It can be found in the passthrough.c file. The code sets a number of variables that are used to configure each run of the passthrough file system. +The full code to handle these command line parameters is straight forward and is omitted for brevity. It can be found in the passthrough.c sample file that ships with the WinFsp installer. The code sets a number of variables that are used to configure each run of the passthrough file system. .`*SvcStart excerpt*` [source,c] @@ -118,7 +129,7 @@ The full code to handle these command line parameters is straight forward and is PWSTR MountPoint = 0; ---- -The variable `DebugLogFile` is used to control the WinFsp debug logging mechanism. This debug logging mechanism can send messages to the debugger for display or log them into a file. The behavior is controlled by a call to `FspDebugLogSetHandle`: if this call is not made any debug log messages will be sent to the debugger; if this call is made debug log messages will be logged into the specified file handle. +The variable `DebugLogFile` is used to control the WinFsp debug logging mechanism. This mechanism can send messages to the debugger for display or log them into a file. The behavior is controlled by a call to `FspDebugLogSetHandle`: if this call is not made any debug log messages will be sent to the debugger; if this call is made debug log messages will be logged into the specified file handle. .`*SvcStart excerpt*` [source,c] @@ -1100,7 +1111,7 @@ NOTE: The WinFsp kernel driver maintains a `DeletePending` flag for every open f === CanDelete -There are two ways for deleting a file or directory on Windows. One is to supply the `FILE_FLAG_DELETE_ON_CLOSE` flag during a `CreateFileW` call. The other one is to use the `FileDispositionInfo` information class with a call to `SetInformationByHandle` (which is what `DeleteFileW` and `RemoveDirectory` effectively do). [It is also possible to delete an (unopened) file using `Rename` by we will ignore this case here.] +There are two ways for deleting a file or directory on Windows. One is to supply the `FILE_FLAG_DELETE_ON_CLOSE` flag during a `CreateFileW` call. The other one is to use the `FileDispositionInfo` information class with a call to `SetInformationByHandle` (which is what `DeleteFileW` and `RemoveDirectoryW` effectively do). [It is also possible to delete an (unopened) file using `Rename` by we will ignore this case here.] `CanDelete` is called in the `FileDispositionInfo` case (only). In general `CanDelete` needs to check whether deleting the file or directory is allowed and return `STATUS_SUCCESS` or an appropriate status code. Most file systems need only check whether a directory is empty and disallow deletion by returning `STATUS_DIRECTORY_NOT_EMPTY` if it is not. `CanDelete` need *not* mark a file for deletion, this flag is maintained by the WinFsp kernel driver. @@ -1237,13 +1248,13 @@ dirnotify_test......................... OK 1.01s <1> Run `winfsp-tests` with `--external`, `--resilient` switches which instructs it to run its external file system tests. <2> Disable tests that are not expected to pass because they test functionality that either we did not implement (`-reparse*`, `-stream*`) or is esoteric (`-create_allocation_test`, `-getfileinfo_name_test`, `-rename_flipflop_test`, `-rename_mmap_test`) or requires that the file system is run under an account with sufficient security rights (`-delete_access_test`). -== Running it as a service +== Running the file system as a service Our final task is to discuss how to convert our file system into a service that can be managed by the WinFsp launcher. This allows our file system to provide file services to all processes in the system. An important thing to consider is that our file system will be running in the SYSTEM account security context, which is different from the security context of any processes that want to use this file system. Recall that the passthrough file system is a simple layer over an underlying file system, therefore how the underlying file system handles security becomes important, particularly when the underlying file system is NTFS. -For this reason we modify the passthrough file system to enable the "backup" and "restore" privileges which are available to a process running under the SYSTEM account. Enabling these privileges allows us to circumvent some NTFS access checks and simply use NTFS as a storage medium. +For this reason we modify the passthrough file system to enable the "backup" and "restore" privileges which are available to a process running under the SYSTEM account. Enabling these privileges allows us to circumvent some NTFS access checks and simply use NTFS as a storage medium. With the `EnableBackupRestorePrivileges` implementation in place all that remains is to call it from `SvcStart`. .`*EnableBackupRestorePrivileges*` [source,c] @@ -1281,24 +1292,16 @@ static NTSTATUS EnableBackupRestorePrivileges(VOID) } ---- -With the `EnableBackupRestorePrivileges` implementation in place all that remains is to call it from `SvcStart`. - -.`*SvcStart excerpt*` -[source,c] ----- - EnableBackupRestorePrivileges(); ----- - We are now ready to register our file system to be managed by the WinFsp launcher. For this purpose we will use the `fsreg.bat` utility which can be found in the WinFsp `bin` directory. `Fsreg.bat` will create all necessary entries in the Windows registry. From an administrator prompt switch to the passthrough directory and run: -.`*fsreg*` +.`*fsreg.bat invocation*` ---- fsreg.bat passthrough build\Debug\passthrough-x64.exe "-u %1 -m %2" "D:P(A;;RPWPLC;;;WD)" ---- -With this step complete we can now launch our file system from the command prompt. +With this step complete we can now launch our file system from any command prompt. image::WinFsp-Tutorial/NetUse.png[First Run] diff --git a/doc/WinFsp-Tutorial/Installer.png b/doc/WinFsp-Tutorial/Installer.png new file mode 100644 index 0000000000000000000000000000000000000000..c82d0e62444a9801e6c5d6a3dad1ca77dce1691f GIT binary patch literal 15962 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 boxMD-bn6)L0u{jpA@gfD|0uq4@9BR7T8wO{ literal 0 HcmV?d00001