Pismo Technic Inc., Copyright 2005-2012 Joe Lowe
2012.05.16
This document is targeted towards software developers wishing to integrate Pismo File Mount into their applications.
Portions of the document are also useful to users of the Pismo File Mount Audit Package.
Pismo File Mount is a portable operating system extension that enables the development of file systems and file system extensions using common user mode application development tools.
Applications can utilize Pismo File Mount to expose data through the file system as mounted volumes. Volumes are exposed through drive letters, UNC paths, through a mount point overlaying an exsting file on a system volume, or through a mount point overlaying an empty folder on a system volume.
The core of the Pismo File Mount system extension is a kernel mode device driver called pfmfs*.sys . This driver interfaces with the various core Windows system interfaces including the file system, virtual memory, and caching. Additionally this driver interacts with the built in and 3rd party file systems such as NTFS, CDFS, and FAT.
The drivers main purpose is to redirect file system requests to a user mode file system, or formatter.
The communication between the driver and formatter (user mode file system) is done over a socket using the PFM Protocol. This protocol is defined in pfmprotocol.h and pfmenum.h .
Support for each virtual file system or container file format is implemented as a formatter. Each formatter exists as a DLL that exposes the PfmFormatter interface. This interface is used by the PFM mount logic to locate the correct formatter to mount a given file, and to connect the formatter to the driver.
Once a formatter has been connected to the driver, the driver redirects file system requests to the formatter using the PFM Protocol. Most formatters utilize the PfmMarshaller implementation provided in the PFM API DLL to convert the protocol into the more easily implemented PfmReadOnlyFormatterOps or PfmFormatterOps interface.
It is also possible for applications to contain embedded formatters in the application executable. These applications would bypass the normal mount logic, and directly create mounts through the PFM API.
Applications interact with and control PFM using the PfmApi interface and the associated interfaces PfmMount, PfmFileMount , and PfmMarshaller. These interfaces are defined in pfmapi.h and pfmmarshaller.h.
Pismo File Mount includes a command line interface. This is implemented as the "pfm" command, usable from a command or terminal prompt. Using pfm it is possible to:
The command line interface includes basic help information. Examples:
>pfm -h >pfm mount -h >pfm mount myphotos.zip >pfm unmount myphotos.zip
Like most file systems, PFM provides read and write data caching to increase file system performance. Since write caching delays the write of application data, it is not always possible to inform applications when writes fail. Operating systems typically contain a notification mechanism that notifies the user when delayed writes fail with the built-in file systems. PFM includes a dedicated delayed write failure notification mechanism, the alerter.
The alerter is implemented as an executable called pfmstat. Pfmstat is started anytime a PFM volume is mounted, and automatically exits shortly after the last PFM volume is unmounted. On Windows pfmstat adds a notification icon to the system tray (Windows only).
The PFM Audit Package and 3rd party applications that incorporate PFM install the core PFM files using the provided installer, pfminst.exe. Example:
The PFM installer has been designed to reduce points of failure during install/upgrade/uninstall, to simplify integration with 3rd party applications, and to reduce support costs.
Careful attention to the design of PFM allows it to be installed, upgraded, and uninstalled, without restarting the system.
PFM utilizes non state based install and uninstall logic. This makes fixing corrupted installations as simple as re-running the installer. The uninstaller can be run regardless of the state of the install, eliminating need for a separate installation cleanup tool for support.
Numerous applications that utilize PFM can simultaneously be installed. Removing one application will not cause files needed by another application to be removed.
Installing a new application will not cause older PFM files to replace newer files, potentially breaking existing installed applications. Installing a new application will not remove optional PFM files that are used by existing installed applications.
The Pismo File Mount Audit Package (PFMAP) is a utility application that utilizes the PFM system extension to allow users to mount the contents of files to the file system as read-write or read-only folders. PFMAP is built using the same PFM interfaces available to 3rd party application developers.
In addition to being a useful stand-alone application, PFMAP can be used with the PFM Developer Kit by software developers.
PFMAP includes an Explorer shell extension. This shell extension allows convenient control of PFMAP directly from Explorer.
The Mount Control application, pfmcontrol.exe, is the graphical user interface to PFMAP. All functionality in PFMAP can be accessed through this application, independent of explorer or the PFM command line interface. Mount Control is particularly useful to view all currently mounted files.
Pfmisofs.dll is a read-only DVD and CD image file formatter. It supports all or part of the following formats and extensions:
Pfmzipfs.dll is a read-only ZIP archive file formatter. It supports the following formats and compression modes:
The implementation is missing the following potentially useful features:
Pfmtempfs.dll implements a read-write temporary virtual file system. This formatter is primarily useful as an example formatter. The source code is provided with the development kit in tempfs.cpp .
The temp file system stores all file data in system memory. This limits the maximum amount of stored file data based on available physical memory and swap file space. On 32 bit systems the amount of stored file data cannot exceed 2GB due to limited process address space.
Pfmpfolderfs.dll implements a read, write, encrypted, compressed container file formatter. It allows storing sensitive files in a secure container file, accessible only with the correct password.
The user supplied password is converted to an encryption key using the PKCS5V2 algorithm. Data is encrypted using the AES encryption algorithm in XTS chaining mode.
Data compression uses the Zlib implementation of the deflate compression format. This is the same compression used in PNG images and ZIP archives.
The Private Folder data format documentation is not yet ready for public release. Pre-release information will be provided to interested parties upon request. For more information contact Pismo Technic Inc. support.
The PFM Development Kit allows developers to build file systems. The kit also can be used to integrate existing PFM file systems into 3rd party applications.
The development kit supports development in C, C++, and C#.
The documentation in this kit uses the C++ syntax when describing functions and interfaces. C developers should refer to the various header files, and to the differences between the various C and C++ samples. C# developers should refer to the interfaces in the provided C# API shim clrapi, and to the various samples.
Developers using PFMAP have the option of downloading the free Windows SDK and Debugging Tools for Windows from Microsoft and using them to compile and work with the samples.
The C, C++, and C# applications developed in Microsoft Developer Studio can easily utilize or implement the PFM API and other interfaces.
For Mac and Linux development, GCC is recommended, but any other compilers capable of building native applications should also work.
For Windows development, developers are encouraged to use the C/C++ compiler included with the free Windows SDK (2007 and later) or DevStudio Express editions, in preference to GCC based compilers.
Though parts of the following header files currently lack documentation, all of the interfaces and definitions are public and supported. Any questions regarding undocumented interfaces should be directed to Pismo Technic Inc. support.
Pfmapi.h contains the definitions of the PFM API, including the interfaces PfmApi, PfmFileMount, PfmFileMountUi, PfmMount, PfmIterator, PfmMonitor, and PfmAlerter.
Pfmenum.h contains definitions of constants and flags that are used in both the PFM API and the PFM Protocol.
Pfmprotocol.h contains the definitions that make up the PFM Protocol.
It is not necessary for most applications or formatters to use these definitions.
Pfmmarshaller.h contains the definition of the PfmMarshaller interface implemented in the PFM API DLL for use by file systems.
hellofs.c contains source code to a very simple C language virtual file system application. When run, this application presents a virtual file system containing a single read-only file, named readme.txt, containing the text "hello world".
The following commands can be executed in a Windows SDK CMD shell to build and test the application. A Windows SDK CMD shell can be created by executing the "Start Menu - Programs - Microsoft Windows SDK - CMD Shell" menu shortcut created when a 2007 or later version of the Windows SDK is installed.
Windows:
>cl -I../include hellofs.c advapi32.lib >echo any data > anyfile >start hellofs anyfile >type anyfile\readme.txt
Mac/Linux
>gcc -I../include hellofs.c -o hellofs >echo any data > anyfile >./hellofs anyfile
hellofs.cpp contains source code to the C++ equivalent of the above sample.
>cl -I../include hellofs.cpp advapi32.lib >echo any data > anyfile >start hellofs anyfile >type anyfile\readme.txthellofs.cs contains source code to the C# equivalent of the above sample. This sample can be compiled with the included project file using Visual Studio 2010 or Visual C# 2010 Express Edition. This sample requires that you first compile the clrapi sample.
Tempfs.cpp contains source code to a read-write temporary virtual file system written in C++. When mounted it creates an empty folder. While mounted files and folders can be created. When unmounted all contained data is discarded.
tempfs.cs and contain source code to an equivalent temporary file system written in C#.
The mounter samples will mount files via the registered PFMAP formatters. mounter.cs in C#, and mounter.cpp in C++. These samples can be compiled with the included project files using Visual Studio 2010 or from the command line using the Platform SDK on Windows or GCC on Mac/Linux.
Applications written in C# (or other Microsoft CLR languages) cannot directly use the PFMAPI. The sample API shim pfmclrapi.cxx can be used from C# applications. This shim can be compiled and freely bundled with C# applications, and can be customized as necessary for each application.
The sample API shim can be compiled with the included project files using Visual Studio 2010 or Visual C# Express Edition.
The PFM API allows direct integration of PFM with applications written in C and C++.
Applications written in C# can integrate with PFM using a shim such as the sample shim provided with the development kit.
Integration with PFM allows applications to perform a number of useful functions.
The PFM API interfaces return system error codes such as ERROR_ACCESS_DENIED on Windows or EACCESS on Mac and Linux.
Each time a new mount is created it is assigned a unique identifier. This identifier is used in the various interfaces to identify the mount. MountIds are not reused until after the system is restarted.
PFM maintains a change instance count for the mount list and for each individual mount. Each time mount status changes the mounts change instance is incremented. Each time a mount is created or destroyed the mount list change instance is incremented. These change instances are used with PfmApi::Iterate and PfmMonitor::Wait to allow applications to efficiently monitor for changes.
The PfmApi interface is defined in pfmapi.h . Some constants and flags are defined in pfmenum.h .
This interface allows applications to interact with and control PFM.
int /*error*/ PfmApiFactory ( PfmApi** api )
This function loads the API DLL and retrieves an instance of the PfmApi interface.
void PfmApi::Release ( )
Free the interface instance and any associated resources.
int /*error*/ PfmApi::MountCreate ( const PfmMountCreateParams* params, PfmMount** mount )
Create a new file mount whose data will be served using the PFM Protocol through the mountCreateParams.toFormatterWrite and mountCreateParams.fromFormatterRead pipes or socket.
The params structure should be initialized as follows:
PfmMountCreateParams params; memset(¶ms,0,sizeof(params)); params.paramsSize = sizeof(params); params.mountFileName = ...; params.mountFlags = ...; params.toFormatterWrite = ...; params.fromFormatterRead = ...;
Applications that only want to initiate mounts, but not create their own virtual file systems, do not use this function. Instead they would use the command line interface of pfm.exe or pfmhost.exe to mount files.
Developers who wish to use this function should contact Pismo Technic Inc. support for additional information and sample code.
int /*error*/ PfmApi::MountOpen ( const wchar_t* mountFileName , PfmMount** mount )
Opens the file mount using the name of the mounted file. The mount must be visible to the calling process. If the mount is not visible to the calling process then it must be opened by mountId using the PfmApi::MountOpenId function.
int /*error*/ PfmApi::MountOpenId ( int mountId , PfmMount** mount )
Open the file mount identified by the mountId parameter. The mountId parameter is typically retrieved from the PfmIterator::Next function.
int /*error*/ PfmApi::Iterate ( int64_t startChangeInstance , int64_t* nextChangeInstance , PfmIterator** iterator )
This function creates and returns an instance of the PfmIterator interface, which allows applications to query a complete or partial list of file mounts.
To iterate all mounts, zero should be passed for the startChangeInstance parameter.
The nextChangeInstance parameter is the location to store the current change instance of the mount list that will be returned by the iterator. This change instance can be used as the startChangeInstance parameter in future calls to iterate only mounts that have changed.
int /*error*/ PfmApi::Monitor ( PfmMonitor** monitor )
This function creates and returns an instance of the PfmMonitor interface, which allows applications to efficiently maintain an updated list of all file mounts and mount states.
int/*error*/ PfmApi::Alerter ( const wchar_t* reserved1 , PfmAlerter** alerter )
This function starts an instance of the pfmstat user mode helper application and returns a alerter object that holds a reference to the pfmstat application to prevent it from exiting.
The PfmFileMount interface is defined in pfmapi.h . Some constants and flags are defined in pfmenum.h .
This interface allows applications to mount a container file system via a PFM Audit Package formatter. This interface is returned from the PfmApi::FileMountCreate function.
void PfmFileMount::Release ( )
Free the interface instance and any associated resources. If the file mount has completed and was not detached, an unmount will be performed on the resulting mount.
void PfmFileMount::Cancel ( )
Asynchronously cancel the ongoing file mount operation and close any related user interface dialogs.
int /*error*/ PfmFileMount::Start ( const PfmFileMountCreateParams* params )
Start a file mount operation. This will result in user interface dialogs being displayed as necessary.
void PfmFileMount::Send ( const wchar_t* data, int/*bool*/ newLine);
This function can be used to send data back to the formatter during authentication. This may be needed for user interface that is mounting formatters with authentication that require more than simple password queries.
void PfmFileMount::Status ( const wchar_t* data, int/*bool*/ newLine )
This function is used to print mount status messages to the file mount user interface. It generally results in a matching call to PfmFileMountUi::Status . Any status prefixed with the string "ERROR: " may be used as the message in the final error dialog that is displayed if the mount fails.
int /*error*/ PfmFileMount::WaitReady ( )
This function will block until the mount operation completes successfully or fails due to an error or is cancelled.
PfmMount* PfmFileMount::GetMount ( )
This function returns an unreferenced ptr to the PfmMount object associated with a successful file mount operation. The caller is responsible to add a reference to the object if it will be using it beyond the lifetime of the PfmFileMount object. If called before the file mount is ready, or on a file mount that has failed, it will return null.
void PfmFileMount::Detach ( )
This function detaches a successful out-of-process file mount from the current process. This allows the mount to survive after the file mount object is releases and after the current process exits.
If this function is called on an in-process file mount then it will block until an unmount occurs.
The PfmFileMountUi interface is defined in pfmapi.h .
This interface is implemented by applications that are performing file mount operations, to allow the display of a custom user interface.
void PfmFileMountUi::Start ( )
void PfmFileMountUi::Complete ( const wchar_t* errorMessage )
void PfmFileMountUi::Status ( const wchar_t* data , int/*bool*/ newLine )
const wchar_t* PfmFileMountUi::QueryPassword ( int count )
void PfmFileMountUi::ClearPassword ( )
The PfmMount interface is defined in pfmapi.h . Some constants and flags are defined in pfmenum.h .
This interface allows applications to query information about, and control, an existing file mount. This interface is returned from the PfmApi::MountCreate, PfmApi::MountOpen, PfmApi::MountOpenId, and PfmFileMount::GetMount functions.
void PfmMount::Release ( )
Free the interface instance and any associated resources.
Once this function is called, any previous data returned from this interface should no longer be used. In particular, any pointers to strings are no longer valid.
int /*error*/ PfmMount::Refresh ( )
Update to match any changes to the state of the mount.
Once this function is called, any previous data returned from this interface should no longer be used. In particular, any pointers to strings will no longer be valid.
int /*error*/ PfmMount::Unmount ( int unmountFlags )
End an existing file mount. The mount will remain visible through the PfmApi::Iterate function until all PfmMount instances referring to the mount are released.
int /*mountId*/ PfmMount::GetMountId ( )
int /*mountFlags*/ PfmMount::GetMountFlags ( )
int /*statusFlags*/ PfmMount::GetStatusFlags ( )
int /*volumeFlags*/ PfmMount::GetVolumeFlags ( )
int64_t /*changeInstance*/ PfmMount::GetChangeInstance ( )
int PfmMount::GetVisibleSessionId ( )
int PfmMount::GetVisibleProcessId ( )
const wchar_t* PfmMount::GetFileName ( )
The returned pointer to a string is only valid until the next call to the PfmMount::Refresh function or until the PfmMount instance is released.
const wchar_t* PfmMount::GetUncName ( )
The returned pointer to a string is only valid until the next call to the PfmMount::Refresh function or until the PfmMount instance is released.
wchar_t PfmMount::GetDriveLetter ( )
const wchar_t* PfmMount::GetOwnerSid ( )
The returned pointer to a string is only valid until the next call to the PfmMount::Refresh function or until the PfmMount instance is released.
const wchar_t* PfmMount::GetOwnerName ( )
The returned pointer to a string is only valid until the next call to the PfmMount::Refresh function or until the PfmMount instance is released.
const wchar_t* PfmMount::GetFormatterName ( )
The returned pointer to a string is only valid until the next call to the PfmMount::Refresh function or until the PfmMount instance is released.
int /*error*/ PfmMount::Flush ( )
int /*error*/ PfmMount::Hide ( )
int /*error*/ PfmMount::Control ( int controlCode , const void* input , size_t inputSize , void* output , size_t maxOutputSize , size_t* outputSize )
Send a formatter specific control code through to the formatter. Formatters using PfmMarshaller will see a the control code via a call to PfmReadOnlyFormatterOps::Control or PfmFormatterOps::Control.
Since control codes are formatter specific, applications must identify the formatter before sending control codes. This can be done using PfmMount::GetFormatterName function.
Formatters should number their control codes starting at zero or one and not leave gaps. The range of available control codes is limited and is not guaranteed to remain constant.
int /*error*/ PfmMount::WaitReady ( int timeoutMsecs )
Wait for the mount to become ready.
The PfmIterator interface is defined in pfmapi.h .
This interface is returned from the PfmApi::Iterate function. It allows applications to retrieve the mountId and current changeInstance of all file mounts.
void PfmIterator::Release ( )
Free the interface instance and any associated resources.
int /*mountId*/ PfmIterator::Next ( int64_t* changeInstance )
The PfmMonitor interface is defined in pfmapi.h .
This interface is returned from the PfmApi::Monitor function. It allows applications to monitor for the creation and deletion of file mounts.
void PfmMonitor::Release(void)
Free the interface instance and any associated resources.
int /*error*/ PfmMonitor::Wait ( int64_t nextChangeInstance , int timeoutMsecs )
Wait for the change instance of the mount list to be different from the nextChangeInstance parameter, or until another thread calls the PfmMonitor::Cancel function.
void PfmMonitor::Cancel ( )
Return early from any calls to PfmMonitor::Wait .
The PfmAlerter interface is defined in pfmapi.h .
This interface is returned from the PfmApi::Alerter function. It holds an instance of a user mode helper application that will display critical file system user messages, and that performs verious other utility tasks for the file system.
void PfmAlerter::Release(void)
Free the interface instance and any associated resources.
New formatter developers should first compile and test one of the sample formatters using their chosen compiler. The steps outlined for Hellofs are a good start with the SDK compiler.
When debugging applications that are exposing PFM file system volumes, it is important that the debugger never access the PFM volume. If the debugger does access the PFM volume then it will deadlock. If a deadlock occurs, you can perform an unmount of the volume from another command prompt to get things moving again, but you will probably need to restart your debugging session. You also can kill the process to resolve a deadlock.
The PfmMarshaller interface provides some built in tracing functionality that is useful during development. To see these traces you will need to download and install Pismo Trace Monitor, available on the Pismo Technic Inc. website, http://www.pismotechnic.com/ .
You start the trace monitor on Windows using the "Start Menu - Programs - Pismo Trace Monitor" menu link, or by running tracemon.exe . New trace channels are hidden by default. You will need to use the unhide command from the trace monitor window menu to make channels visible. Do this after you have mounted a file.
On Mac and Linux you can view traces using the tracecmd command line tools.
The PFM Protocol and the PfmReadOnlyFormatterOps and PfmFormatterOps interfaces return error codes as defined in pfmenum.h , such as pfmErrorAccessDenied. The other interfaces return Win32 error codes such as ERROR_ACCESS_DENIED. The protocol is remotable and portable, so the use of system error codes is not appropriate.
The term file is regularly used in this document to mean file or folder. Unless stated otherwise, all PFM Protocol requests and related marshalled functions work the same for files and folders.
The PFM Protocol represents all file names in UTF8. The marshaller converts UTF8 file names to and from wchar_t file names for use by the formatters.
The protocol and the driver support multiple named files, or hard links. Formatters that support hard links make use of the driver maintained parentFileId and endName in order to identify which name for a file is being manipulated.
The protocol allows the formatter to return a case corrected spelling for the last element of the name of opened files. This case corrected name is used by the driver when emulating short name aliases (DOS 8.3 file names). Short names are still used by many applications, including portions of Windows itself. Formatters that do not provide the case corrected end name will have reduced application compatibility.
OpenId and openSequence are integral parts of the PFM protocol. The openId is the mechanism used to identify open files and folders. OpenSequence is maintained by the driver and used by the formatter to identify when the last reference to an open file or folder has closed.
OpenIds are not file IDs. In particular the openId values for new files is assigned by the driver, where file IDs are assigned by the formatter.
The use of openIds and openSequence allows PFM to provide atomicity guarantees during file system name space changes and share mode checks. Without this mechanism the driver would have to make assumptions about the formatters name space, and hold locks while waiting for the formatter to process many requests.
The PFM Protocol handles file deletion using a unix model, where deletion applies to file names as opposed to underlying file data. After a file has been deleted, the file data must remain accessible until the file is finally closed.
The unix file deletion model requires extra code for some formatters. For other formatters it is trivially supported. Regardless, it is a requirement for formatters and is utilized by the driver.
Formatter developers should understand that the NT file system model is more complex than is apparent to casual users of the Win32 API. Using a unix model for file delete allows the driver to achieve a high level of application compatibility in a more portable and supportable way than if the native NT delete model were directly supported by the protocol.
The Windows kernel is massively multi-threaded. The PFM driver runs in the kernel, and therefore must also be multi-threaded. Likewise, the PFM Protocol used by the driver to communicate with formatters supports requests being processed in parallel and in any order, so formatters can also be implemented multi-threaded.
Formatters are not required to be multi-threaded. Both the driver and the protocol have been carefully designed to support multi-threaded formatters and formatters that serially process requests. All formatters that implement PfmFormatterOps or PfmReadOnlyFormatterOps are inherently single-threaded.
Formatters that require concurrency or multi-threaded operation must either directly process the PFM protocol, or must implement the PfmFormatterDispatch interface. Developers who wish to implement multi-threaded formatters should contact Pismo Technic Inc. support for additional information and sample code.
The PfmMarshaller interface is defined in pfmmarshaller.h . Some constants and flags are defined in pfmenum.h .
This interface is implemented in the PFM API DLL. It is used to convert the PFM Protocol into the PfmReadOnlyFormatterOps and PfmFormatterOps interfaces that are implemented by formatters.
int /*error*/ PfmMarshallerFactory ( PfmMarshaller** marshaller )
This function creates an instance of the PfmMarshaller interface.
void PfmMarshaller::Release ( )
Formatters should call this function when they are finished using an instance of the PfmMarshaller interface.
void PfmMarshaller::SetTrace ( const wchar_t* traceChannelName )
Formaters can optionally call this function to initialize a diagnostic trace channel to help with testing and field troubleshooting.
The Pismo Trace Monitor application must be installed to view the traces that are generated when this function is used. This applications is available on the Pismo Technic Inc. website, http://www.pismotechnic.com/ .
void PfmMarshaller::SetStatus ( HANDLE write )
Formatters can call this function to set a write handle to a pipe to send status text that is written using the status functions (Print, Vprintf, Printf, Line). This is primarily useful in the implementation of PfmFormatter::Identify.
The marshaller does not duplicate or reference the supplied handle. Formatters must make a second call to this function, specifying INVALID_HANDLE_VALUE to prevent the marshaller from later using the handle.
int /*pfmError*/ PfmMarshaller::ConvertSystemError ( int error )
This function can optionally be used by formatters to convert system (win32) error codes to the equivalent PFM error codes needed with the PfmReadOnlyFormatterOps and PfmFormatterOps interfaces.
int /*error*/ PfmMarshaller::Identify ( const char* mountFileData , size_t mountFileDataLen , const char* formatterName )
This function can optionally be used by formatters to identify mount files that are using a unix style shell specification as a file type identifier. This is primarily useful for virtual file systems, where the mount file is only a marker file. The PFM sample formatters use this.
For example, passing a value of "sampleformatter" for formatterName would match a file containing the following text in the first line of the file:
#!sampleformatter
int /*error*/ PfmMarshaller::GetPassword ( HANDLE read , const wchar_t* prompt , const wchar_t** password )
Formatters that require passwords can use this function to query the user to enter a password. The prompt value is the text to display with the prompt, typically "user name:" or "password:". The returned password is valid until the next call to either PfmMarshaller::GetPassword or PfmMarshaller::ClearPassword.
void PfmMarshaller::ClearPassword ( )
This function should be called after PfmMarshaller::GetPassword, when the returned password is no longer needed. This clears the password from system memory, reducing the chance that it will persist in the page file or appear in subsequent uninitialized memory allocations.
int /*error*/ PfmMarshaller::ServeReadWrite ( PfmFormatterOps* formatter , int volumeFlags , const char* formatterName , HANDLE toFormatterRead , HANDLE fromFormatterWrite )
This function is called by read-write formatters when they are ready to begin processing requests from the driver. The serve function sends the PFM Protocol ready string to the driver, which results in the mount status dialog closing. It then goes into a loop, reading protocol requests from the driver, calling the associated functions in the PfmFormatterOps interface, and sending results back to the driver.
The serve function will return when the driver disconnects from the formatter by closing its end of the pipe, or by sending a disconnect request.
The volumeFlags and formatterName parameters are provided to the driver, which uses the information to satisfy volume information queries made by applications.
int /*error*/ PfmMarshaller::ServeDispatch ( PfmMarshallerServeParams* params )
This function is used instead of PfmMarshaller::ServeReadWrite , by formatters that require concurrent or multi-threaded operation.
The params structure should be initialized as follows:PfmMarshallerServeParams params; PfmMarshallerServeParams_Init(¶ms); params.dispatch = ...; params.volumeFlags = ...; params.formatterName = ...; params.toFormatterRead = ...; params.fromFormatterWrite = ...;
Developers who wish to use this function should contact Pismo Technic Inc. support for additional information and sample code.
void PfmMarshaller::Print ( const wchar_t* data )
This function can be used by formatters during identify and mount initialization, to send status data to the mount status dialog through the handle specified in an earlier call to PfmMarshaller::SetStatus.
This function can be used during processing of requests from the driver to send traces to the trace channel specified in an earlier call to PfmMarshaller::SetTrace.
void PfmMarshaller::Vprintf ( const wchar_t* format , va_list args )
This function can be used by formatters during identify and mount initialization, to send status data to the mount status dialog through the handle specified in an earlier call to PfmMarshaller::SetStatus.
This function can be used during processing of requests from the driver to send traces to the trace channel specified in an earlier call to PfmMarshaller::SetTrace.
void PfmMarshaller::Printf ( const wchar_t* format , ... )
This function can be used by formatters during identify and mount initialization, to send status data to the mount status dialog through the handle specified in an earlier call to PfmMarshaller::SetStatus.
This function can be used during processing of requests from the driver to send traces to the trace channel specified in an earlier call to PfmMarshaller::SetTrace.
void PfmMarshaller::Line ( const wchar_t* data , int/*bool*/ newLine )
This function can be used by formatters during identify and mount initialization, to send status data to the mount status dialog through the handle specified in an earlier call to PfmMarshaller::SetStatus.
This function can be used during processing of requests from the driver to send traces to the trace channel specified in an earlier call to PfmMarshaller::SetTrace.
The PfmMarshallerListResult interface is defined in pfmmarshaller.h .
This interface is implemented in the PFM API DLL. It is used by formatters to return the variable number of results for the PfmReadOnlyFormatterOps::List and PfmFormatterOps::List functions.
bool /*added*/ PfmMarshallerListResult::Add ( const PfmAttribs* attribs , const wchar_t* endName , bool* needMore )
While processing a call to the PfmReadOnlyFormatterOps::List or PfmFormatterOps::List functions, a formatter will make repeated calls to this function to add files to the results to be returned to the driver.
Formatters should continue to call this function until either the needMore variable is set to false by the add function, or there are no more files to add to the results.
In the event the function returns false, the file was not added to the result. The formatter must add this same file on the next call to the list function with the same openId and listId.
bool /*added*/ PfmMarshallerListResult::Add8 ( const PfmAttribs* attribs , const char* endName , bool* needMore )
This function is equivalent to PfmMarshallerListResult::Add except that it takes a UTF8 file name.
void PfmMarshallerListResult::NoMore ( )
While processing a call to the PfmReadOnlyFormatterOps::List or PfmFormatterOps::List functions, a formatter should call this function when all files have been added to the result. This lets the driver know there is no need to issue an additional list request.
The PfmFormatterOps interface is defined in pfmmarshaller.h .
This interface is implemented by read-write formatters, to process file system requests from the driver that are marshalled through PfmMarshaller .
void PfmFormatterOps::ReleaseName ( wchar_t* name )
The marshaller calls this function to allow the formatter to free memory that was used to return case corrected file name information from the PfmFormatterOps::Open and PfmFormatterOps::Move functions, or to return the media label from the PfmFormatterOps::MediaInfo function.
int /*pfmError*/ PfmFormatterOps::Open ( const PfmNamePart* nameParts , size_t namePartCount , int8_t createFileType , uint8_t createFileFlags , int64_t writeTime , int64_t newCreateOpenId , int8_t existingAccessLevel , int64_t newExistingOpenId , bool* existed , PfmOpenAttribs* openAttribs , int64_t* parentFileId , wchar_t** endName )
This function is called by the marshaller to process file open and create requests from the driver. Correct use of newCreateOpenId parameter, the newExistingOpenId parameter, the existed result, the openAttribs->openId result, and the openAttribs->openSequence result, are critical to proper functioning of the atomicity guarantees provided by the driver.
If the formatter returns no error (0) then it must return the information about the newly opened/created file through the openAttribs parameter, the existed parameter, and optionally through the parentFileId parameter the endName parameter. All of the openAttribs->attribs fields must be filled. The four time fields in openAttribs->attribs should be filled with valid times, or with constant pfmTimeInvalid.
The name of the file being opened is indicated by the nameParts and namePartCount parameters.
If the formatter determines that the indicated file exists but has not already been opened then it must associate the driver specified newExistingOpenId with the file, and return this same value in openAttribs->openId. The openAttribs->openSequence value must be initialized to a non zero positive value, and the value must be associated with the open file for use during subsequent close processing. The variable referenced by the existed parameter must be set to true.
If the formatter determines that the indicated file is already open then it must return in openAttribs->openId the same openId that is already associated with the open file. The openAttribs->openSequence value must be initialized to a positive value that is greater than the openSequence returned by any previous open of the same file. The new openSequence value must be associated with the open file for use during subsequent close processing. The variable referenced by the existed parameter must be set to true.
If the parent folder of the indicated file does not exist then the formatter should return pfmErrorParentNotFound.
If the indicated file does not exist and the newCreateOpenId parameter is zero then the formatter should return pfmErrorNotFound.
If the indicated file does not exist and the newCreateOpenId parameter is non-zero then the formatter should create the file. The createFileType, createFileFlags, and writeTime parameters should be used to to initialize the new file. The formatter must associate the driver specified newCreateOpenId with the file, and return this same value in openAttribs->openId. The openAttribs->openSequence value must be initialized to a non zero positive value, and the value must be associated with the open file for use during subsequent close processing. The variable referenced by the existed parameter must be set to false.
If the formatter is opening a file with different spelling for the last name component than was specified by the driver then it must return the correct spelling through the endName parameter. If the endName is returned then the marshaller will call the PfmFormatterOps::ReleaseName function to allow the formatter to free any related memory.
The openAttribs->accessLevel field should be filled with the highest access level currently available for the file. For most formatters this field can always be initialized with pfmAccessWriteData. For redirectors, when opening existing files, the existingAccessLevel parameters can be used to avoid opening files with higher access levels than are needed for the current request.
int /*pfmError*/ PfmFormatterOps::Replace ( int64_t targetOpenId , int64_t targetParentFileId , const PfmNamePart* targetEndName , Uuint8_t createFileFlags , int64_t writeTime , int64_t newCreateOpenId , PfmOpenAttribs* openAttribs )
The marshaller calls this function when processing a replace request from the driver. This request is made when an existing file is being replaced by a new file with the same name. The replaced file name becomes deleted.
The file being replaced has already been opened and is identified with the targetOpenId parameter.
If the target file type is a folder and the formatter does not support replace for folders then the formatter should return pfmErrorInvalid. Formatters must support replace for files.
If the target file type is a folder and the folder is not empty then the formatter should return pfmErrorNotEmpty.
Formatters that support multiple names for a single file (hard links) should use the targetParentFileId and targetEndName parameters to identify which name is being replaced.
The createFileFlags and writeTime parameters should be used to initialize the new file. The file type is always the same as the target.
The newCreateOpenId and openAttribs parameters should be treated the same as is described for the PfmFormatterOps::Open function when a new file is created.
int /*pfmError*/ PfmFormatterOps::Move ( int64_t sourceOpenId , int64_t sourceParentFileId , const PfmNamePart* sourceEndName , const PfmNamePart* targetNameParts , size_t targetNamePartCount , bool deleteSource , int64_t writeTime , int64_t newExistingOpenId , bool* existed , PfmOpenAttribs* openAttribs , int64_t* parentFileId , wchar_t** endName )
This function is called by the marshaller to process move requests from the driver. This request is made when a file is being renamed. Proper handling of the newExistingOpenId parameter, exists result, openAttribs->openId result, and openAttribs->openSequence result, are critical to proper functioning of the atomicity guarantees provided by the driver.
The sourceOpenId parameter specifies the previously opened file that is being renamed. The sourceParentFileId and sourceEndName parameters can be used by formatters that support hard links to determine which name for the file is being renamed.
The targetNameParts and targetNamePartCount parameters specified the new file name (target) for the file.
If the parent folder of the target file name does not exist then the formatter should return pfmErrorParentNotFound.
If a file already exists with the target file name, then the existing target file should be opened. In this case the source file is left unmodified and no rename or move operation is performed. The variable pointed to by the existed parameter must be set to true. The newExistingOpenId, openAttribs, parentFileId, and endName parameters should be used in the same manner as is described for the PfmFormatterOps::Open function when an existing file is opened.
If the source file name has been deleted then the move request is the equivalent of an undelete. Formatters must support this for files, but can return pfmErrorInvalid for folders.
If the deleteSource parameter is false, and the formatter does not determine that the source file name is deleted, then the move request is creating an additional name for the file (hard link). Formatters that do not support hard links should fail the request with pfmErrorInvalid.
If the formatter is able to create the new name for the file then updated information about the source file is returned through the openAttribs and parentFileId parameters, openAttribs->openId must contain sourceOpenId, openAttribs->openSequence must contain a value equal to or higher than the largest openSequence value returned in any previous open for the file. The variable pointed to by the existed parameter must be set to false.
int /*pfmError*/ PfmFormatterOps::MoveReplace ( int64_t sourceOpenId , int64_t sourceParentFileId , const PfmNamePart* sourceEndName , int64_t targetOpenId , int64_t targetParentFileId , const PfmNamePart* targetEndName , bool deleteSource , int64_t writeTime )
The marshaller calls this function when processing a move-replace request from the driver. This request is made when a an opened source file is being renamed to the same name as an opened target file. The replaced target file name becomes deleted.
Some processing for this function is similar to PfmFormatterOps::Replace, except that the source file is guaranteed to exist since it is already open.
Some processing for this function is similar to PfmFormatterOps::Move, specifically with handling of deleted source files and the deleteSource parameter.
int /*pfmError*/ PfmFormatterOps::Delete ( int64_t openId , int64_t parentFileId , const PfmNamePart* endName , int64_t writeTime )
The marshaller calls this function when processing a delete request from the driver. This request is made when a file name is being deleted.
Formatters that support multiple file names for a single file (hard links) can use the parentFileId and endName parameters to identify which file name is being deleted.
int /*pfmError*/ PfmFormatterOps::Close ( int64_t openId , int64_t openSequence )
See PfmReadOnlyFormatterOps::Close.
int /*pfmError*/ PfmFormatterOps::FlushFile ( int64_t openId , uint8_t fileFlags , int64_t createTime , int64_t accessTime , int64_t writeTime , int64_t changeTime )
Updated attributes for modified files are supplied to the formatter in the flush file request.
If the fileFlags parameter is the value pfmFileFlagsInvalid then the formatter should skip updating the file flags.
If any of the time parameters are the value pfmTimeInvald then the formatter should skip updating the associated time value.
Errors from this function are ignored by the driver. If a formatter wants the user to be notified of a flush related error, it must return an error from a subsequent call to PfmFormatterOps::FlushMedia.
int /*pfmError*/ PfmFormatterOps::List ( int64_t openId , int64_t listId , PfmMarshallerListResult* listResult )
See PfmReadOnlyFormatterOps::List.
int /*pfmError*/ PfmFormatterOps::ListEnd ( int64_t openId , int64_t listId )
See PfmReadOnlyFormatterOps::ListEnd.
int /*pfmError*/ PfmFormatterOps::Read ( int64_t openId , uint64_t fileOffset , void* data , size_t requestedSize , size_t* outActualSize )
The behavior of this function for folders is undefined. Formatters are free to handle this in whatever way is convenient.
int /*pfmError*/ PfmFormatterOps::Write ( int64_t openId , uint64_t fileOffset , const void* data , size_t requestedSize , size_t* outActualSize )
The behavior of this function for folders is undefined. Formatters are free to handle this in whatever way is convenient.
The behavior of zero length writes is undefined. Formatters are free to handle this in whatever way is convenient.
int /*pfmError*/ PfmFormatterOps::SetSize ( int64_t openId , uint64_t fileSize )
int /*pfmError*/ PfmFormatterOps::Capacity ( uint64_t* totalCapacity , uint64_t* availableCapacity )
int /*pfmError*/ PfmFormatterOps::FlushMedia ( bool* mediaClean )
See PfmReadOnlyFormatterOps::FlushMedia .
int /*pfmError*/ PfmFormatterOps::Control ( int64_t openId , int8_t accessLevel , int controlCode , const void* input , size_t inputSize , void* output , size_t maxOutputSize , size_t* outputSize )
int /*pfmError*/ PfmFormatterOps::MediaInfo ( int64_t openId , PfmMediaInfo* mediaInfo , wchar_t** mediaLabel )
See PfmReadOnlyFormatterOps::MediaInfo.
The PfmFormatterDispatch interface is defined in pfmmarshaller.h .
This interface is implemented by multi-threaded formatters to process file system requests from the driver that are marshalled through PfmMarshaller.
Developers who wish to implement this interface should contact Pismo Technic Inc. support for additional information and sample code.
All file times used in the PFM Protocol and with the PfmReadOnlyFormatterOps and PfmFormatterOps interfaces are in the Windows FILETIME format, 64 bit count of 100 nano second units since Jan 1 1601 UTC.
struct PfmAttribs
{
int8_t fileType ;
uint8_t fileFlags ;
int64_t fileId ;
uint64_t fileSize ;
int64_t createTime ;
int64_t accessTime ;
int64_t writeTime ;
int64_t changeTime ;
}
struct PfmOpenAttribs
{
int64_t openId ;
int64_t openSequence ;
int8_t accessLevel ;
PfmAttribs attribs ;
}
struct PfmNamePart
{
const wchar_t* name ;
size_t len ;
const char* name8 ;
size_t len8 ;
}
The name8 field points to a UTF8 string, not to an ANSI or OEM codepage string.
The name and name8 fields point to the same string in two different encodings. Formatters should use the name that is most convenient. Both names are zero terminated with the length available in the respective len field.
struct PfmMediaInfo
{
GUID mediaUuid ;
uint64_t mediaId64 ;
uint32_t mediaId32 ;
uint8_t mediaFlags ;
int64_t createTime ;
}
struct PfmFileMountCreateParams
{
size_t paramsSize ;
const wchar_t* mountFileName ;
int mountFlags ;
int fileMountFlags ;
wchar_t driveLetter ;
const wchar_t* password;
PfmFileMountUi* ui;
}
struct PfmMountCreateParams
{
size_t paramsSize ;
const wchar_t* mountFileName ;
int mountFlags ;
wchar_t driveLetter ;
HANDLE toFormatterWrite ;
HANDLE fromFormatterRead ;
}
struct PfmMarshallerServeParams
{
size_t paramsSize ;
PfmFormatterDispatch* dispatch ;
int volumeFlags ;
const char* formatterName;
HANDLE toFormatterRead ;
HANDLE fromFormatterWrite ;
}