Control Capabilities of CBFS Filter


Summary

CBFilter is a component of CBFS Filter, an SDK and library designed for monitoring and controlling various system activities, including filesystem operations, registry operations, and the life cycle of processes and threads. This component—available as a class, struct, or trait depending on your programming language—enables you to monitor and control all types of filesystem operations within your Windows applications. The article reviews the available mechanisms for control of filesystem operations using the CBFilter component.

Introduction

CBFilter fires events when handling filesystem requests. These events can occur in three ways:

  1. Synchronously, before the request reaches the filesystem (known as "Before* events")
  2. Synchronously, after the request is processed by the filesystem (known as "After* events")
  3. Asynchronously, at some point after the request has been processed by the filesystem (known as "Notify* events")

All Before* events have corresponding After* and Notify* events, but the information available to event handlers varies slightly based on the event type.

Before* events carry the parameters passed by the caller, which is the application or system component that initiated the request. If another filesystem filter driver modifies the request before it reaches CBFilter, the parameters passed to the event handler will reflect those changes. Most Before* events allow handlers to modify the parameters before they are passed further or to handle the request without forwarding it to the filesystem. This handling can include either fulfilling the request successfully or returning an error code.

After* events events typically include the same data as Before* events, with the addition of the data and status code (result or error code) set by the request handler—the filesystem or a lower-level filter driver. The added data may include file data, metadata, security attributes, or extended attributes queried by the originating process and returned by the handler. By default, After* events are triggered only when the request is completed successfully, and they are not fired for failed requests. Applications, however, can adjust settings to fire events for failed requests as well.

Notify* events events cannot be used to control filesystem operations; therefore, they are covered in the article about monitoring capabilities.

Control Mechanisms of CBFilter

When a Before* event is handled, the event handler can typically change the request parameters. The handler can also complete the request without passing it further down the filter stack or to the filesystem. The request is completed with a status code, which can be either STATUS_SUCCESS or an appropriate error code.

If the request is successfully completed by an event handler, the handler is expected to fulfill the request by returning the requested information or by performing the corresponding action.

If an error is returned, it should be one of the error codes recognized by the operating system for that type of request. In most cases, STATUS_ACCESS_DENIED is the appropriate code for an application that wants to deny the request.

For requests that require processing after they reach the filesystem, applications use After* events. Most After* events allow event handlers to modify parameters before they are returned to the request initiator. Additionally, event handlers can change the returned status code, converting the request from successful to failed or vice versa.

Events and Available Parameters

All events may include the name of the file or directory associated with the request. An application can disable the passing of file names or enable the collection and transmission of all names related to the file or directory available to the filter driver. For details about names and how to collect them, please refer to the relevant topic in the CBFilter documentation.

The following Before* events are present in the component (listed in functional order):

  • BeforeEnumerateDirectory - This event is fired when a request to enumerate the directory is sent to a filesystem. The available parameters include the flags, the mask, and the index of the directory entry that must be reported first. The filesystem would respond with zero or more entries.
  • BeforeQueryFileInfo - This event is fired before information about a file or directory is retrieved. An event handler receives the information class (the type of a structure with information) that is being requested and a reference to the buffer for the information. The handler can (and in the case of isolation, should) handle the request by filling the buffer with data.
  • BeforeGetFileSecurity - This event is fired before a file or directory’s security attributes are retrieved. The event parameters include the type of security information being requested and the buffer for this information.
  • BeforeQueryEa - This event is fired before information about extended attributes of a file is retrieved. The parameters include the buffer for the extended attributes and supplementary request parameters. All of the listed parameters can be modified by the event handler.
  • BeforeSetFileInfo - This event is fired before information about a file or directory is set. An event handler receives the information class (the type of the structure with information) that is being set and a reference to the buffer with the information.
  • BeforeSetFileAttributes - This event is fired before a file’s attributes or times are updated. The parameters include time and attribute values. This event is a simplified version of the BeforeSetFileInfo event, which is fired in more cases.
  • BeforeSetFileSecurity - This event is fired before a file or directory’s security attributes are changed. The event parameters include the type of security information being set and the buffer with this information.
  • BeforeSetEa - This event is fired before an extended attribute is set on a file. The buffer with the extended attribute data is passed to an event handler.
  • BeforeCanFileBeDeleted - This event is fired to check if the file may be deleted. The event specifies which of the deletion mechanisms is used (these can be the FILE_FLAG_DELETE_ON_CLOSE flag during opening of a file or the Disposition flag set via the NtSetInformationFile function) and lets a handler forbid the deletion.
  • BeforeDeleteFile - This event is fired before a file or directory is deleted. There is no system I/O Request Packet (IRP) for deletion of files and directories; they are deleted when the last handle to a file is closed. Thus, the event is emulated. It is fired by CBFilter before the file is closed if there are no more open handles to it. Deletion cannot be prevented using this event; use BeforeCanFileBeDeleted to influence the deletion process.
  • BeforeRenameOrMoveFile - This event is fired before a file or directory is renamed or moved. The parameters include the new name of the file or directory and a flag that specifies if an existing file with the same name as the target name may be overwritten with a file being dealt with.
  • BeforeCreateHardLink - This event is fired before a hard link to a file is created. The parameters include the name of the link and a flag that specifies if an existing file with the same name as the new link name may be overwritten with a hard link.
  • BeforeCreateFile and BeforeOpenFile - This event is fired when a process sends a file creation or opening request. The choice of event to fire depends on the flags specified in the request; before a file or directory is opened, the filter does not know if a file will be opened or created. The event enables the handler to enable isolation on the file handle being opened (isolation is covered in a separate article), and includes DesiredAccess, Attributes, ShareMode, Options (the Flags part of the FlagsAndAttributes WinAPI parameter), CreateDisposition, and, optionally, a security descriptor that should be set for a file when a file is created. All of the listed parameters can be modified by the event handler, but some modifications may have no effect depending on the OS version, installed third-party filters, and other factors.
  • BeforeReadFile - This event is fired before data are read from a file. The component passes the buffer, the position in the file to read from, and the size of the requested data block. Additionally, the component indicates whether the data are going to be read from the system cache or from the disk (or other medium). An event handler can (and in the case of isolation, should) satisfy the request by filling the buffer with the requested data.
  • BeforeWriteFile - This event is fired before data are written to a file. The component passes the buffer with data, the position in the file to write to, and the size of the data block. Additionally, the component indicates whether the data are to be written to the system cache or to the disk (or other medium). An event handler can (and in the case of isolation, should) modify the data or complete the request.
  • BeforeSetAllocationSize - This event is fired before a file’s allocation size is changed. The event handler receives the new value of the allocation size and may adjust it if needed.
  • BeforeSetFileSize - This event is fired before the size of the open file is changed either explicitly (e.g., through truncation of a file) or implicitly (e.g., when new data are appended to the file). An event handler receives the new size value and may adjust it if needed.
  • BeforeLock - This event is fired before a range of bytes in a file is locked. An event handler receives the starting position and the length of the range being locked, a key for the lock, and some supplementary information. All of the listed parameters can be modified by the event handler.
  • BeforeUnlockAll, BeforeUnlockAllByKey, BeforeUnlockSingle - These events are fired when one or more file ranges, previously locked using a Lock File operation, should be unlocked. The parameters include the key and the range to unlock (depending on the event).
  • BeforeCleanupFile - This event is fired before a file handle is closed.
  • BeforeCloseFile - This event is fired before a file is closed for the application. File closing takes place later than cleanup of a handle. One possible scenario occurs when an application maps a file into memory, and then closes the file handle and continues to use the memory mapping.
  • BeforeGetReparsePoint - This event is fired when the information about the reparse point is requested. The component passes the buffer for the reparse data in the request.
  • BeforeSetReparsePoint - This event is fired when the reparse point is set or updated. The component passes the reparse tag and the buffer with the reparse data in the request. The tag specifies the type of reparse point and helps applications and system components recognize "their" reparse points. The tag and the contents of the buffer may be modified by an event handler.
  • BeforeDeleteReparsePoint - This event is fired before the reparse point is deleted. Removal of a reparse point is performed by using a dedicated IRP (FSCTL_DELETE_REPARSE_POINT), and the file or directory behind a reparse point is not deleted. The event handler receives the reparse buffer as a parameter. The contents of the buffer may be modified by an event handler.
  • BeforeFsctl - This event is fired before a custom request is sent using the IRP_MJ_FILE_SYSTEM_CONTROL request packet. This IRP covers various rarely used operations on a filesystem. The control code, the input buffer with data, and the output data for the response data are made available to the event handler. The contents of the buffers may be modified by an event handler.
  • BeforeIoctl - This event is fired before a custom request is sent using the IRP_MJ_DEVICE_CONTROL request packet. This IRP covers various rarely used operations on a device behind the filesystem. The control code, the input buffer with data, and the output buffer for the response data are made available to the event handler. The contents of the buffers may be modified by an event handler.

The following After* events are present in the component (listed in functional order):

  • AfterEnumerateDirectory - This event is fired after a request to enumerate the directory is sent to a filesystem. It is fired for each entry reported by the filesystem (or a lower-level filter driver). The included parameters are the index of the entry in the directory, the name, the times, and attributes of an entry, the size and the allocation size of a file if the entry is a file, and an Id of the entry. A handler can alter the parameters or hide the entry from the listing.
  • AfterQueryFileInfo - This event is fired after the detailed information about a file or directory is retrieved. An event handler receives the information class (the type of a structure with information) that was requested and a reference to the buffer with the requested information. The handler can alter the data by modifying the buffer contents.
  • AfterGetFileSecurity - This event is fired after a file or directory’s security attributes are retrieved. The event parameters include the type of security information being requested and the buffer for this information.
  • AfterQueryEa - This event is fired after information about extended attributes of a file is retrieved. The parameters include the buffer for the extended attributes and supplementary request parameters. The buffer contents can be modified by an event handler.
  • AfterSetFileInfo - This event is fired after information about a file or directory is set. An event handler receives the information class (the type of the structure with information) that is being set and a reference to the buffer with the information.
  • AfterSetFileAttributes - This event is fired after a file’s attributes or times are updated. The parameters include time and attribute values. This event is a simplified version of the AfterSetFileInfo event, which is fired in more cases.
  • AfterSetFileSecurity - This event is fired after a file or directory’s security attributes are changed. The event parameters include the type of security information being set and the buffer with this information.
  • AfterSetEa - This event is fired after an extended attribute is set on a file. The buffer with the extended attribute data is passed to an event handler.
  • AfterCanFileBeDeleted - This event is fired after the flesystem marks the file for deletion or removes such a mark. The event specifies which of the deletion mechanisms is used (these can be the FILE_FLAG_DELETE_ON_CLOSE flag during opening of a file or the Disposition flag set via the NtSetInformationFile function).
  • AfterDeleteFile - This event is fired after a file or directory is deleted. There is no system I/O Request Packet (IRP) for deletion of files and directories; they are deleted when the last handle to a file is closed. Thus, the event is emulated. It is fired by CBFilter before the AfterCloseFile event is fired if there are no more open handles to the file.
  • AfterRenameOrMoveFile - This event is fired after a file or directory is renamed or moved. The parameters include the new name of the file or directory.
  • AfterCreateHardLink - This event is fired after a hard link to a file is created. The parameters include the name of the link.
  • AfterCreateFile and AfterOpenFile - This event is fired when a process sends a file creation or opening request, after the file has been opened. The event notifies the handler about the file isolation mode (if one was set in the corresponding Before* event or if a file is known to be virtual), and includes the existing attributes of the file or directory being created/opened, DesiredAccess, Attributes, ShareMode, Options (the Flags part of the FlagsAndAttributes WinAPI parameter), CreateDisposition, and, optionally, a security descriptor that was passed with a request.
  • AfterReadFile - This event is fired after data are read from a file. The component passes the buffer, the position in the file that the data were read from, the size of the requested data block, and the amount of data actually read. Additionally, the component indicates whether the data were to be read from the system cache or from the disk (or other medium). An event handler can modify the data buffer.
  • AfterWriteFile - This event is fired after data are written to a file. The component passes the buffer with data, the position in the file at which the data were written, the size of the data block, and the number of bytes written to the file. Additionally, the component indicates whether the data were to be written to the system cache or to the disk (or other medium).
  • AfterSetAllocationSize - This event is fired after a file’s allocation size is changed. The event handler receives the new value of the allocation size.
  • AfterSetFileSize - This event is fired after the size of the open file is changed, either explicitly (e.g., through truncation of a file) or implicitly (e.g., when new data are appended to the file). An event handler receives the new size value.
  • AfterLock - This event is fired after a range of bytes in a file is locked. An event handler receives the starting position and the length of the locked range, a key for the lock, and some supplementary information.
  • AfterUnlockAll, AfterUnlockAllByKey, AfterUnlockSingle - These events are fired after one or more file ranges, previously locked using a Lock File operation, were unlocked. The parameters include the key and the range to unlock (depending on the event).
  • AfterCleanupFile - This event is fired after a file handle is closed.
  • AfterCloseFile - This event is fired after a file is closed for the application. File closing takes place later than cleanup of a handle. One possible scenario occurs when an application maps a file into memory, and then closes the file handle and continues to use the memory mapping.
  • AfterGetReparsePoint - This event is fired after the information about the reparse point was provided by the filesystem or the lower-level filter driver. The component passes the buffer with the reparse data in the request, and an event handler may modify the buffer contents.
  • AfterSetReparsePoint - This event is fired after the reparse point was set or updated. The component passes the reparse tag and the buffer with the reparse data in the request. The tag specifies the type of a reparse point and helps applications and system components recognize "their" reparse points.
  • AfterDeleteReparsePoint - This event is fired after the reparse point is deleted. The event handler receives the reparse buffer as a parameter.
  • AfterFsctl - This event is fired after a custom request is sent using the IRP_MJ_FILE_SYSTEM_CONTROL request packet and processed by a filesystem. This IRP covers various rarely used operations on a filesystem. The control code, the input buffer with data, and the output data with the response data are made available to the event handler. The handler can modify the contents of the output buffer.
  • AfterIoctl - This event is fired before a custom request is sent using the IRP_MJ_DEVICE_CONTROL request packet. This IRP covers various rarely used operations on a device behind the filesystem. The control code, the input buffer with data, and the output buffer with the response data are made available to the event handler. The handler can modify the contents of the output buffer.

CBFilter includes events related to reparsing and handling reparse points, allowing applications to redirect file requests and manage special files marked as reparse points.

  • ReparseFileName - This event is fired when a file is created or opened, before the BeforeCreateFile or BeforeOpenFile events. It provides a mechanism for an application to redirect the request from one file or directory to another on a per-operation basis, potentially depending on specific criteria.
  • ReparseWithTag - This event is triggered when the file or directory being opened has a reparse point set on it. Reparse points are used to implement symbolic links or to mark files and directories that require special handling. For example, OneDrive marks placeholders (empty files that represent files in the cloud) as reparse points. When access is made to such a file, a filter driver can intercept it and provide special handling. This event serves as the hook for such handling, provided by CBFilter to user-mode applications.

If an application selectively monitors files—such as only on certain drives—it may need to track the insertion and removal of media to dynamically add and remove filter rules.

To facilitate this, CBFilter includes a set of events that are triggered when a volume (typically removable media) is added to or removed from the system:

  • BeforeFilterAttachToVolume - This event is fired before the volume is fully added to the system.
  • AfterFilterAttachToVolume - This event is fired after the volume is added to the system and its volume name becomes available. In this event handler, you can add new rules specific to the added volume.
  • AfterFilterDetachFromVolume - This event is fired after the volume is removed from the system. In this event handler, you can remove rules specific to the volume that is no longer present.
  • NotifyFilterAttachToVolume - This asynchronous event is fired for monitoring purposes; it indicates the addition of a new device to the system.
  • NotifyFilterDetachFromVolume - This asynchronous event is fired for monitoring purposes; it indicates the removal of a device from the system.

Additional Information About Requests

CBFilter can provide additional information about requests that is available to event handlers. This includes the following information:

  • the name of the process that opened the file handle related to the request;
  • the PID (Process Id) of the process that opened the file handle related to the request;
  • the Id of the thread that opened the file handle associated with the request;
  • the security token of the process that opened the file handle (this token can be used to obtain the SID of the user account, along with the user name and other related information);
  • the name of the process that initiated the request (which may differ from the process which opened the handle);
  • the PID (process Id) of the process that initiated the request (which may differ from the process that opened the handle);
  • the Id of the thread that initiated the request (which may differ from the thread which opened the handle);
  • the security token of the process that initiated the request;
  • the time when the request reached the filter; and
  • the information about remote access if a local file was accessed over the network.

Event Timeouts

To prevent situations in which an event handler becomes stuck and blocks the system, CBFilter implements a timeout mechanism. The desired timeout is specified as a parameter when filtering is initiated by an application (in a call to the StartFilter method). When the driver makes a call to user mode, it starts a timer and expects the call to complete within the predefined timeout. If this does not occur, execution continues, and the call is treated as abandoned—any returned data will be ignored.

The only exceptions are the BeforeCreateFile and BeforeOpenFile events; if a call to the handler times out, the file is not created or opened, serving as a security measure.

If the handler requires more time to complete the event handling, it can call the ResetTimeout method to reset or disable the timer for that specific event.

Getting Started with CBFilter

You can find an evaluation version of the SDK for your platform and programming language in the Download Center. After downloading and installing the SDK, you will receive a library, sample code, and comprehensive documentation on your system. The documentation includes a "Getting Started" section with programming instructions. Additionally, free technical support is available during the evaluation phase.

We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at support@callback.com.