/*++ Copyright (c) 1998-1999 Microsoft Corporation Module Name: srpriv.h Abstract: This is a local header file for sr private structures and macros Author: Paul McDaniel (paulmcd) 23-Jan-2000 Revision History: --*/ #ifndef _SRPRIV_H_ #define _SRPRIV_H_ // // If CONFIG_LOGGING_VIA_REGISTRY is enables, SR will read the LogBufferSize, // LogAllocationUnit and LogFlushFrequency from the registry. While this is // useful for performance tuning, we don't want this functionality in the // shipping filter, so this should NOT be defined for shipping code. // //#define CONFIG_LOGGING_VIA_REGISTRY #ifdef CONFIG_LOGGING_VIA_REGISTRY #define REGISTRY_LOG_BUFFER_SIZE L"LogBufferSize" #define REGISTRY_LOG_ALLOCATION_UNIT L"LogAllocationUnit" #define REGISTRY_LOG_FLUSH_FREQUENCY L"LogFlushFrequency" #endif // // If SYNC_LOG_WRITE is defined, the filter will write all log entries // synchronously to the change log file. This has a significant impact on the // performance of the driver, therefore we are NOT doing this. We buffer // the log entries and write them to disk periodically. // // #define SYNC_LOG_WRITE // // forward definitions for srlog.h // typedef struct _SR_LOG_CONTEXT * PSR_LOG_CONTEXT; typedef struct _SR_LOGGER_CONTEXT * PSR_LOGGER_CONTEXT; // // allocation utilities // #define SR_ALLOCATE_POOL( type, len, tag ) \ ExAllocatePoolWithTag( \ (type), \ (len), \ (tag)|PROTECTED_POOL ) #define SR_FREE_POOL( ptr, tag ) \ ExFreePoolWithTag(ptr, (tag)|PROTECTED_POOL) #define SR_FREE_POOL_WITH_SIG(a,t) \ { \ ASSERT((a)->Signature == (t)); \ (a)->Signature = MAKE_FREE_TAG(t); \ SR_FREE_POOL(a,t); \ (a) = NULL; \ } #define SR_ALLOCATE_STRUCT(pt,ot,t) \ (ot *)(SR_ALLOCATE_POOL(pt,sizeof(ot),t)) #define SR_ALLOCATE_ARRAY(pt,et,c,t) \ (et *)(SR_ALLOCATE_POOL(pt,sizeof(et)*(c),t)) // BUGBUG: ALIGN_UP(PVOID) won't work, it needs to be the type of the first entry of the // following data (paulmcd 4/29/99) #define SR_ALLOCATE_STRUCT_WITH_SPACE(pt,ot,cb,t) \ (ot *)(SR_ALLOCATE_POOL(pt,ALIGN_UP(sizeof(ot),PVOID)+(cb),t)) #define MAKE_FREE_TAG(Tag) (((Tag) & 0xffffff00) | (ULONG)'x') #define IS_VALID_TAG(Tag) (((Tag) & 0x0000ffff) == 'rS' ) // // ISSUE-2001-05-01-mollybro Restore can only handle file paths of at most 1000 characters // Restore should be fixed to cope with this, but for now, the filter is just // going to treat operations on files with names that are longer than 1000 // characters as uninteresting (where the 1000 characters includes the // terminating NULL that will be added when the name is logged). // When this limit is eventually removed, we should remove these checks by // the filter (search for the places that use this macro). // // SR will only log filenames where the full path is 1000 characters // or less. This macro tests to make sure a name is within our valid range. // #define IS_FILENAME_VALID_LENGTH( pExtension, pFileName, StreamLength ) \ (((((pFileName)->Length + (StreamLength)) - (pExtension)->pNtVolumeName->Length) < \ (SR_MAX_FILENAME_PATH * sizeof( WCHAR ))) ? \ TRUE : \ FALSE ) // // This is an internal error code this is used when we detect that a // volume has been disabled. Note that this status should NEVER be // returned from a dispatch routine. This is designed to be an error // code. // #define SR_STATUS_VOLUME_DISABLED ((NTSTATUS)-1) #define SR_STATUS_CONTEXT_NOT_SUPPORTED ((NTSTATUS)-2) #define SR_STATUS_IGNORE_FILE ((NTSTATUS)-3) // // Local event type definitions // // // these are copied manually // #define SR_MANUAL_COPY_EVENTS \ (SrEventStreamChange) // // these are logged and only cared about once // #define SR_ONLY_ONCE_EVENT_TYPES \ (SrEventAclChange|SrEventAttribChange) // // this we ignore after any of the SR_FULL_BACKUP_EVENT_TYPES occur // note: SrEventFileCreate is added to this list to prevent recording // of stream creates if we have recorded the unnamed stream create // already. // #define SR_IGNORABLE_EVENT_TYPES \ (SrEventStreamChange|SrEventAclChange|SrEventAttribChange \ |SrEventFileDelete|SrEventStreamCreate) // // these cause us to start ignoring SR_IGNORABLE_EVENT_TYPES // #define SR_FULL_BACKUP_EVENT_TYPES \ (SrEventStreamChange|SrEventFileCreate \ |SrEventFileDelete|SrEventStreamCreate) // // these we always need to log, even if we are ignoring them // #define SR_ALWAYS_LOG_EVENT_TYPES \ (SrEventFileDelete) // // Certain events are relevant to the entire file and not just // the current stream. Flag these types of events so that we know // to put the ignorable events in the backup history keyed by the file // name instead of by the file and stream name. // #define SR_FILE_LEVEL_EVENTS \ (SrEventStreamChange|SrEventFileDelete|SrEventAclChange|SrEventAttribChange) /***************************************************************************++ Routine Description: This macro determines from the bits set in the EventType whether to log the current operation against the file name with the stream component or without the stream component. It returns 0 if this operation should be logged against the file without the stream name, or the StreamNameLength otherwise. Arguments: EventType - the event that just occured StreamNameLength - the length of the stream component of the name Return Value: 0 - If the filename without the stream component is to be used. StreamNameLength - If the filename with the stream component is to be used. --***************************************************************************/ #define RECORD_AGAINST_STREAM( EventType, StreamNameLength ) \ (FlagOn( (EventType), SR_FILE_LEVEL_EVENTS ) ? \ 0 : \ (StreamNameLength) ) // // pool tags (please keep this in alpha order - reading backwards) // #define SR_BACKUP_DIRECTORY_CONTEXT_TAG MAKE_TAG('CBrS') #define SR_BACKUP_FILE_CONTEXT_TAG MAKE_TAG('FBrS') #define SR_COPY_BUFFER_TAG MAKE_TAG('BCrS') #define SR_CREATE_COMPLETION_TAG MAKE_TAG('CCrS') #define SR_COUNTED_EVENT_TAG MAKE_TAG('ECrS') #define SR_CONTROL_OBJECT_TAG MAKE_TAG('OCrS') #define SR_STREAM_CONTEXT_TAG MAKE_TAG('CSrS') #define SR_DEBUG_BLOB_TAG MAKE_TAG('BDrS') #define SR_DEVICE_EXTENSION_TAG MAKE_TAG('EDrS') #define SR_DEVICE_LIST_TAG MAKE_TAG('LDrS') #define SR_EA_DATA_TAG MAKE_TAG('DErS') #define SR_EXTENSION_LIST_TAG MAKE_TAG('LErS') #define SR_EVENT_RECORD_TAG MAKE_TAG('RErS') #define SR_FILE_ENTRY_TAG MAKE_TAG('EFrS') #define SR_FILENAME_BUFFER_TAG MAKE_TAG('NFrS') #define SR_GLOBALS_TAG MAKE_TAG('LGrS') #define SR_GET_OBJECT_NAME_CONTEXT_TAG MAKE_TAG('OGrS') #define HASH_BUCKET_TAG MAKE_TAG('BHrS') #define SR_HOOKED_DRIVER_ENTRY_TAG MAKE_TAG('DHrS') #define HASH_HEADER_TAG MAKE_TAG('HHrS') #define HASH_KEY_TAG MAKE_TAG('KHrS') #define SR_LOG_ACLINFO_TAG MAKE_TAG('AIrS') #define SR_KEVENT_TAG MAKE_TAG('EKrS') #define SR_LOG_BUFFER_TAG MAKE_TAG('BLrS') #define SR_LOG_CONTEXT_TAG MAKE_TAG('CLrS') #define SR_LOG_ENTRY_TAG MAKE_TAG('ELrS') #define SR_LOOKUP_TABLE_TAG MAKE_TAG('TLrS') #define SR_MOUNT_POINTS_TAG MAKE_TAG('PMrS') #define SR_OVERWRITE_INFO_TAG MAKE_TAG('IOrS') #define SR_PERSISTENT_CONFIG_TAG MAKE_TAG('CPrS') #define SR_RENAME_BUFFER_TAG MAKE_TAG('BRrS') #define SR_LOGGER_CONTEXT_TAG MAKE_TAG('GRrS') #define SR_REPARSE_HEADER_TAG MAKE_TAG('HRrS') #define SR_REGISTRY_TAG MAKE_TAG('RRrS') #define SR_SECURITY_DATA_TAG MAKE_TAG('DSrS') #define SR_STREAM_DATA_TAG MAKE_TAG('TSrS') #define SR_TRIGGER_ITEM_TAG MAKE_TAG('ITrS') #define SR_VOLUME_INFO_TAG MAKE_TAG('IVrS') #define SR_VOLUME_NAME_TAG MAKE_TAG('NVrS') #define SR_WORK_ITEM_TAG MAKE_TAG('IWrS') #define SR_WORK_CONTEXT_TAG MAKE_TAG('CWrS') // // We use a "trick" of hiding the stream name in the unicode string // between the Length and MaximumLength of the buffer. We then track the // StreamName length separately. This macro checks to make sure that // all is still in sync. // #define IS_VALID_SR_STREAM_STRING( pFileName, StreamLength ) \ ((((pFileName)->Length + (StreamLength)) <= (pFileName)->MaximumLength) && \ (((StreamLength) > 0) ? \ (((pFileName)->Length < (pFileName)->MaximumLength) && \ ((pFileName)->Buffer[(pFileName)->Length/sizeof(WCHAR)] == ':')) : \ TRUE )) #define SR_FILE_READ_ACCESS READ_CONTROL | \ FILE_READ_DATA | \ FILE_READ_ATTRIBUTES | \ FILE_READ_EA #define SR_FILE_WRITE_ACCESS WRITE_DAC | \ WRITE_OWNER | \ FILE_WRITE_DATA | \ FILE_APPEND_DATA | \ FILE_WRITE_ATTRIBUTES | \ FILE_WRITE_EA // // The maximum number of characters in a short name. // #define SR_SHORT_NAME_CHARS (8 + 1 + 3) // // Error handlers. // #if DBG NTSTATUS SrDbgStatus( IN NTSTATUS Status, IN PSTR pFileName, IN USHORT LineNumber ); #define CHECK_STATUS(status) SrDbgStatus((status),__FILE__,__LINE__) #define RETURN(status) return CHECK_STATUS(status) // // in debug builds i want the chance to DbgBreak on an error encountered // from a lower level api call. // #undef NT_SUCCESS #define NT_SUCCESS(status) ((NTSTATUS)(CHECK_STATUS((status))) >= 0) #define NT_SUCCESS_NO_DBGBREAK(status) ((NTSTATUS)(status) >= 0) #else #define RETURN(status) return (status) #define CHECK_STATUS(status) ((void)0) #define NT_SUCCESS_NO_DBGBREAK(status) NT_SUCCESS((status)) #endif // DBG #define DebugFlagSet(a)\ (FlagOn(_globals.DebugControl, SR_DEBUG_ ## a)) // // Debug spew control. // #define SR_DEBUG_FUNC_ENTRY 0x00000001 #define SR_DEBUG_CANCEL 0x00000002 #define SR_DEBUG_NOTIFY 0x00000004 #define SR_DEBUG_LOG_EVENT 0x00000008 #define SR_DEBUG_INIT 0x00000020 #define SR_DEBUG_HASH 0x00000040 #define SR_DEBUG_LOOKUP 0x00000080 #define SR_DEBUG_LOG 0x00000100 #define SR_DEBUG_RENAME 0x00000200 #define SR_DEBUG_LOAD_UNLOAD 0x00000400 #define SR_DEBUG_BYTES_WRITTEN 0x00000800 #define SR_DEBUG_PNP 0x00001000 #define SR_DEBUG_EXPAND_SHORT_NAMES 0x00002000 #define SR_DEBUG_BLOB_VERIFICATION 0x00004000 #define SR_DEBUG_IOCTL 0x00008000 #define SR_DEBUG_BREAK_ON_ERROR 0x00010000 #define SR_DEBUG_VERBOSE_ERRORS 0x00020000 #define SR_DEBUG_BREAK_ON_LOAD 0x00040000 #define SR_DEBUG_ENABLE_UNLOAD 0x00080000 #define SR_DEBUG_ADD_DEBUG_INFO 0x00100000 #define SR_DEBUG_DELAY_DPC 0x00200000 #define SR_DEBUG_KEEP_CONTEXT_NAMES 0x10000000 #define SR_DEBUG_CONTEXT_LOG 0x20000000 #define SR_DEBUG_CONTEXT_LOG_DETAILED 0x40000000 #define SR_DEBUG_DEFAULTS (SR_DEBUG_VERBOSE_ERRORS) // // config file structures (store in \_restore\_driver.cfg) // // these can't go in the registry and they need to survive a restore, and // the registry is reverted along with the system , during a restore // typedef struct _SR_PERSISTENT_CONFIG { // // = SR_PERSISTENT_CONFIG_TAG // ULONG Signature; // // the number to use for the next temp file name (e.g. A0000001.exe = 1) // ULONG FileNameNumber; // // the number to use for the next seq number // INT64 FileSeqNumber; // // the number for the current restore point subdirectory (e.g. // "\_restore\rp5" = 5) // ULONG CurrentRestoreNumber; } SR_PERSISTENT_CONFIG, * PSR_PERSISTENT_CONFIG; #define RESTORE_CONFIG_LOCATION RESTORE_LOCATION L"\\_driver.cfg" // // Tracing. // #if DBG #define SrTrace(a, _b_) \ { \ if (DebugFlagSet(##a)) \ { \ try { \ KdPrint( _b_ ); \ } except (EXCEPTION_EXECUTE_HANDLER) { \ /* do nothing, just catch it and ignore. bug#177569 */ \ /* long strings with non-english characters can trigger */ \ /* an exception with KdPrint. */ \ } \ } \ } // // a version of SrTrace that does not wrap KdPrint with try, so should // only be used in cases where it is certain there is no risk of exception. // this is needed in termination handlers which cannot nest exception // handling // #define SrTraceSafe(a, _b_) \ (DebugFlagSet(##a) ? KdPrint( _b_ ) : TRUE) /* { \ IF_DEBUG(##a) \ { \ KdPrint( _b_ ); \ } \ } */ #else //DBG #define SrTrace(a, _b_) #define SrTraceSafe(a, _b_) #endif //DBG // // Object types exported by the kernel but not in any header file. // extern POBJECT_TYPE *IoDeviceObjectType; // // Macro to clear pointers only in the DEBUG version // #if DBG # define NULLPTR(_p) ((_p) = NULL) #else # define NULLPTR(_p) #endif ///////////////////////////////////////////////////////////////////////////// // // Public globals. // ///////////////////////////////////////////////////////////////////////////// // // e.g. "{64bdb2bb-d3b0-41d6-a28e-275057d7740d}" = 38 characters // #define SR_GUID_BUFFER_LENGTH (38 * sizeof(WCHAR)) #define SR_GUID_BUFFER_COUNT 40 #define IS_VALID_GLOBALS(pObject) \ (((pObject) != NULL) && ((pObject)->Signature == SR_GLOBALS_TAG)) typedef struct _SR_GLOBALS { // // NonPagedPool // // // = SR_GLOBALS_TAG // ULONG Signature; // // Offset of process name in PEPROCESS struct, set to default // in logfmt.h but can be overridden using registry // ULONG ProcNameOffset; // // For controlling debug functions (like SrTrace) at runtime // ULONG DebugControl; // // the global DRIVER_OBJECT // PDRIVER_OBJECT pDriverObject; // // the sr device that people can open to get a control object // PDEVICE_OBJECT pControlDevice; // // OPTIONAL: a control object if it is open on this system // struct _SR_CONTROL_OBJECT * pControlObject; // // Are we currently monitoring the system? This is TRUE when the registry // says to disable, or the filter has received the STOP_MONITORING_IOCTL. // It only gets cleared when the filter receives the START_MONITORING_IOCTL. // // NOTE: This is NOT used for errors that require the filter to shut off. // The following flag provides that. // BOOLEAN Disabled; // // If we hit an error reading the blob and generated a volume error // on the system volume. Set this flag to true so that we don't // continue to try to load the blob until all the volumes have // been disabled by the service. // BOOLEAN HitErrorLoadingBlob; // // have we loaded our disk based config values yet? we delay load these // as we load pretty early in the boot sequence so our DriverEntry could // not do this // BOOLEAN FileConfigLoaded; // // have we loaded the blob info (lookup.c) // BOOLEAN BlobInfoLoaded; // // a debug flag for doing all of the normal work except for backups. // BOOLEAN DontBackup; // // our persistent config values // SR_PERSISTENT_CONFIG FileConfig; // // this is the number we used for the last backup file // ULONG LastFileNameNumber; // // this is the seq number // INT64 LastSeqNumber; // // the location to read our registry our of (from DriverEntry) // PUNICODE_STRING pRegistryLocation; // // in-memory blob information // BLOB_INFO BlobInfo; // // these resources are always acquired in order if nested acquired. // the activity lock is outermost. // // // Blob synchronization stuff (acquired sometimes with the global lock held) // ERESOURCE BlobLock; // // This resource locks pControlObject + this global structure + hash lists // ERESOURCE GlobalLock; // // the registry configured machine guid as a string // (e.g. "{03e692d7-b392-4a01-babf-1efd2c11d449}" ) // WCHAR MachineGuid[SR_GUID_BUFFER_COUNT]; #ifdef USE_LOOKASIDE // // lookaside lists for speedy allocations // PAGED_LOOKASIDE_LIST FileNameBufferLookaside; #endif // // Logger Context // PSR_LOGGER_CONTEXT pLogger; // // FsRtl fast I/O call backs // FAST_IO_DISPATCH FastIoDispatch; // // anchors the list of all device extensions (attached volumes) // ERESOURCE DeviceExtensionListLock; LIST_ENTRY DeviceExtensionListHead; // // keeps track of whether or not we have already attached to the system // volume. // struct _SR_DEVICE_EXTENSION * pSystemVolumeExtension; #ifndef SYNC_LOG_WRITE // // SR log buffer size // ULONG LogBufferSize; // // The time interval at which the logs are flushed, in seconds. // ULONG LogFlushFrequency; // // This is the calculated value that translate the LogFlushFrequency // to the form of the time that the KeTimer apis need (100-nanosecond // intervals). // LARGE_INTEGER LogFlushDueTime; #endif // // The unit used to extend the log files. // ULONG LogAllocationUnit; } SR_GLOBALS, *PSR_GLOBALS; extern PSR_GLOBALS global; extern SR_GLOBALS _globals; // // used as a window for persisting our file numbers to the disk. // a large enough number is used so that we don't have a problem during // power failures even if there is a lot of activity. otherwise the // number chosen is random. // #define SR_SEQ_NUMBER_INCREMENT 1000 #define SR_FILE_NUMBER_INCREMENT 1000 ///////////////////////////////////////////////////////////////////////////// // // File Context related information // ///////////////////////////////////////////////////////////////////////////// // // Structure for tracking an individual stream context. Note that the buffer // for the FileName is allocated as part of this structure and follows // immediatly after it. // typedef struct _SR_STREAM_CONTEXT { // // OS Structure used to track contexts per stream. Note how we use // the following fields: // OwnerID -> Holds pointer to our DeviceExtension // InstanceId -> Holds Pointer to FsContext associated // with this structure // We use these values to get back to these structures // FSRTL_PER_STREAM_CONTEXT ContextCtrl; // // Linked list used to track contexts per device (in our device // extension). // LIST_ENTRY ExtensionLink; // // This is a counter of how many threads are currently using this // context. The count is used in this way: // - It is set to 1 when it is created. // - It is incremented every time it is returned to a thread // - It is decremented when the thread is done with it. // - It is decremented when the underlying stream that is using it is freed // - The context is deleted when this count goes to zero // LONG UseCount; // // Maintain the link count -- currently for debugging purposes only. // ULONG LinkCount; // // Holds the name of the file // UNICODE_STRING FileName; // // This holds the length of the stream name portion of the // FileName. Note that this length is not included in the // length inside the FileName string but the characters // are there during debug. // USHORT StreamNameLength; // // Flags for this context. All flags are set or cleared via // the interlocked bit routines except when the entry is being // created, at this time we know nobody is using this entry. // ULONG Flags; } SR_STREAM_CONTEXT, *PSR_STREAM_CONTEXT; // // If set, this entry is interesting to SR // #define CTXFL_IsInteresting 0x00000001 // // If set, this entry is for a directory // #define CTXFL_IsDirectory 0x00000002 // // If set, this entry is for a volume open. We will not have a name // and this object will not be interesting. // #define CTXFL_IsVolumeOpen 0x00000004 // // If set, this is a temporary context and should not be linked into // any of the context lists. It will be freed as soom as the user is // done with this operation. // #define CTXFL_Temporary 0x00000010 // // If set, we are performing a significant operation that affects the state // of this context so we should not use it. If someone tries to get this // context then create a temporary context and return it. Cases where this // occurs: // - Source file of a rename. // - Source file for the creation of a hardlink // #define CTXFL_DoNotUse 0x00000020 // // If set, we need to query the link count before linking this context into // the filter contexts. // #define CTXFL_QueryLinkCount 0x00000040 // // If set, then we are currently linked into the device extension linked // list. // #define CTXFL_InExtensionList 0x00000100 // // If set, then we are linked into the stream list. Note that there is // a small period of time when we might be unlinked with this flag still // set (when the file system is calling SrpDeleteContextCallback). This is // fine because we still handle not being found in the list when we do // the search. This flag handles the case when the file has been completly // closed (and the memory freed) on us. // #define CTXFL_InStreamList 0x00000200 // // Macro used to set the Renaming flag in an individual context. We use the // list lock in the extension to protect this. We can get away with this // because rename operations are rare. // //#define SrSetRenamingFlag(ext,ctx) \ //{ \ // SrAcquireContextLockExclusive((ext)); \ // SetFlag((ctx)->Flags,CTXFL_Renaming); \ // SrReleaseContextLock((ext)); \ //} // // We use this structure to keep track of all contexts associated with this // device. This way one we unload or disable monitoring we can walk // through and free all contexts. // typedef struct _SR_CONTEXT_CTRL { // // Lock used for accessing the linked list. We also acquire this lock // shared as we look up contexts. This way they can't disappear until // we get the use count updated. // ERESOURCE Lock; // // The linked list of contexts. // LIST_ENTRY List; // // If this count is non-zero then all contexts become temporary. // This count is presently used to track how many pending directory // renames are in progress in the system. While this count is non-zero // any contexts that are created become temporary and are freed // when the current operation is completed. // ULONG AllContextsTemporary; } SR_CONTEXT_CTRL, *PSR_CONTEXT_CTRL; // // Macros for locking the context lock // #define SrAcquireContextLockShared(pExt) \ SrAcquireResourceShared( &(pExt)->ContextCtrl.Lock, TRUE ) #define SrAcquireContextLockExclusive(pExt) \ SrAcquireResourceExclusive( &(pExt)->ContextCtrl.Lock, TRUE ) #define SrReleaseContextLock(pExt) \ SrReleaseResource( &(pExt)->ContextCtrl.Lock ) ///////////////////////////////////////////////////////////////////////////// // // Name Control Structure related fields // ///////////////////////////////////////////////////////////////////////////// // // This structure is used to retrieve the name of a file object. To prevent // allocating memory every time we get a name this structure contains a small // buffer (which should handle 90+% of all names). If we do overflow this // buffer we will allocate a buffer big enough for the name. // typedef struct _SRP_NAME_CONTROL { UNICODE_STRING Name; ULONG BufferSize; PUCHAR AllocatedBuffer; USHORT StreamNameLength; CHAR SmallBuffer[254]; } SRP_NAME_CONTROL, *PSRP_NAME_CONTROL; ///////////////////////////////////////////////////////////////////////////// // // Device Extension related definitions // ///////////////////////////////////////////////////////////////////////////// #define IS_VALID_SR_DEVICE_EXTENSION( _ext ) \ (((_ext) != NULL) && \ ((_ext)->Signature == SR_DEVICE_EXTENSION_TAG)) #define IS_SR_DEVICE_OBJECT( _devObj ) \ (((_devObj) != NULL) && \ ((_devObj)->DriverObject == _globals.pDriverObject) && \ (IS_VALID_SR_DEVICE_EXTENSION(((PSR_DEVICE_EXTENSION)(_devObj)->DeviceExtension)))) #define DEVICE_NAME_SZ 64 typedef enum _SR_FILESYSTEM_TYPE { SrNtfs = 0x01, SrFat = 0x02, // Flag to determine whether or not this is attached to the filesystem's // control device object. SrFsControlDeviceObject = 0x80000000 } SR_FILESYSTEM_TYPE, *PSR_FILESYSTEM_TYPE; typedef struct _SR_DEVICE_EXTENSION { // // NonPagedPool // // // SR_DEVICE_EXTENSION_TAG // ULONG Signature; // // links all extensions to global->DeviceExtensionListHead // LIST_ENTRY ListEntry; // // Activity lock for this volume. // ERESOURCE ActivityLock; BOOLEAN ActivityLockHeldExclusive; // // the dyanamic unnamed device created by sr.sys used to attach // to the target device // PDEVICE_OBJECT pDeviceObject; // // the target device.. that device that we attached to in the attachment // chain, when we hooked into the file system driver. might not be the // actual file system device, but another filter in the chain. // PDEVICE_OBJECT pTargetDevice; // // NT volume name (needs to be free'd if non-null) // PUNICODE_STRING pNtVolumeName; // // This lock is to synchronize volumes the work needed to do // to setup a volume (get the volume GUID, create the restore // location, etc) for logging or actually log an operation. // // NOTE: When this lock is acquired, the volume's ActivityLock // **MUST** be acquired either shared or exclusive (the SrAcquireLogLock // macro tests for this in DBG builds). For this reason, there are times // when we access the logging structures when we just have the // ActivityLock exclusive since this will be sufficient to get exclusive // access to the logging structures. The main reason for doing this is // performance -- we can save a few instructions by not making the call to // acquire the LogLock. Since we have exclusive access to the volume, // there should be no wait to get the log lock at these times. // ERESOURCE LogLock; // // the string version of the nt volume guid (e.g. "{xxx}" ). // UNICODE_STRING VolumeGuid; WCHAR VolumeGuidBuffer[SR_GUID_BUFFER_COUNT]; // // the amount of bytes written to this volume since the last // notification. this is reset after each notification or volume // dismount // ULONGLONG BytesWritten; // // this struct contains the logging context for this volume // PSR_LOG_CONTEXT pLogContext; // // Volume information // SR_FILESYSTEM_TYPE FsType; // // Used to manage Contexts for a given volume. // SR_CONTEXT_CTRL ContextCtrl; // // Cached Volume attributes: valid only if CachedFsAttributes == TRUE // ULONG FsAttributes; BOOLEAN CachedFsAttributes; // // this drive will be temporarily disabled by the filter if it runs // out of space. this is reset by SrReloadConfiguration. // BOOLEAN Disabled; // // do we need to check the restore store on this drive, this is reset // on a SrCreateRestorePoint // BOOLEAN DriveChecked; // // this is used by filelist.c to provide a backup histore to prevent // duplicate backups to a file that changes multiple times within the same // restore point. This list is flushed on restore point creation, and // can be trimmed due to resource constraints // PHASH_HEADER pBackupHistory; } SR_DEVICE_EXTENSION, *PSR_DEVICE_EXTENSION; // // Macro used to see if we should LOG on this device object // #define SR_LOGGING_ENABLED(_devExt) \ (!global->Disabled && !(_devExt)->Disabled) // // We don't need to log IO that is directed at the control device object // of a file system in most cases. This macro does the quick check of the // flags in our device extension to see if this is the control device object // for a file system. // #define SR_IS_FS_CONTROL_DEVICE(_devExt) \ (FlagOn((_devExt)->FsType, SrFsControlDeviceObject)) // // Definitions for posting operations // typedef NTSTATUS (*PSR_SYNCOP_ROUTINE) ( IN PVOID Parameter ); typedef struct _SR_WORK_CONTEXT { // // Work item used to queue // WORK_QUEUE_ITEM WorkItem; // // Actual caller supplied work routine // PSR_SYNCOP_ROUTINE SyncOpRoutine; // // Parameter to the routine // PVOID Parameter; // // Return status of routine // NTSTATUS Status; // // Event to sync with main-line thread // KEVENT SyncEvent; } SR_WORK_CONTEXT, *PSR_WORK_CONTEXT; // // Op. posting routines // VOID SrSyncOpWorker( IN PSR_WORK_CONTEXT WorkContext ); NTSTATUS SrPostSyncOperation( IN PSR_SYNCOP_ROUTINE SyncOpRoutine, IN PVOID Parameter ); // // Other stuff // PDEVICE_OBJECT SrGetFilterDevice ( PDEVICE_OBJECT pDeviceObject ); NTSTATUS SrCreateAttachmentDevice ( IN PDEVICE_OBJECT pRealDevice OPTIONAL, IN PDEVICE_OBJECT pDeviceObject, OUT PDEVICE_OBJECT *ppNewDeviceObject ); VOID SrDeleteAttachmentDevice ( IN PDEVICE_OBJECT pDeviceObject ); NTSTATUS SrAttachToDevice ( IN PDEVICE_OBJECT pRealDevice OPTIONAL, IN PDEVICE_OBJECT pDeviceObject, IN PDEVICE_OBJECT pNewDeviceObject OPTIONAL, OUT PSR_DEVICE_EXTENSION * ppExtension OPTIONAL ); NTSTATUS SrAttachToVolumeByName ( IN PUNICODE_STRING pVolumeName, OUT PSR_DEVICE_EXTENSION * ppExtension OPTIONAL ); VOID SrDetachDevice( IN PDEVICE_OBJECT pDeviceObject, IN BOOLEAN RemoveFromDeviceList ); #if DBG // // In DBG mode, define a SR_MUTEX as a RESOURCE so that we get the // benefit of the thread information stored in ERESOURCES for debugging // purposes. // #define SR_MUTEX ERESOURCE #define SrInitializeMutex( mutex ) \ ExInitializeResourceLite( (mutex) ); #define SrAcquireMutex( mutex ) \ { \ ASSERT( !ExIsResourceAcquiredExclusive( (mutex) ) && \ !ExIsResourceAcquiredShared( (mutex) ) ); \ KeEnterCriticalRegion(); \ ExAcquireResourceExclusive( (mutex), TRUE ); \ } #define SrReleaseMutex( mutex ) \ { \ ASSERT( ExIsResourceAcquiredExclusive( (mutex) ) ); \ ExReleaseResourceEx( (mutex) ); \ KeLeaveCriticalRegion(); \ } #else // // In non-DBG mode, define a SR_MUTEX as a FAST_MUTEX so that it is more // efficient for what we use this synchronization for than ERESOURCES. // #define SR_MUTEX FAST_MUTEX #define SrInitializeMutex( mutex ) \ ExInitializeFastMutex( (mutex) ); #define SrAcquireMutex( mutex ) \ ExAcquireFastMutex( (mutex) ); #define SrReleaseMutex( mutex ) \ ExReleaseFastMutex( (mutex) ); #endif /* DBG */ #define SR_RESOURCE ERESOURCE; #define SrAcquireResourceExclusive( resource, wait ) \ { \ ASSERT( ExIsResourceAcquiredExclusiveLite((resource)) || \ !ExIsResourceAcquiredSharedLite((resource)) ); \ KeEnterCriticalRegion(); \ ExAcquireResourceExclusiveLite( (resource), (wait) ); \ } #define SrAcquireResourceShared( resource, wait ) \ { \ KeEnterCriticalRegion(); \ ExAcquireResourceSharedLite( (resource), (wait) ); \ } #define SrReleaseResource( resource ) \ { \ ASSERT( ExIsResourceAcquiredSharedLite((resource)) || \ ExIsResourceAcquiredExclusiveLite((resource)) ); \ ExReleaseResourceLite( (resource) ); \ KeLeaveCriticalRegion(); \ } #define IS_RESOURCE_INITIALIZED( resource ) \ ((resource)->SystemResourcesList.Flink != NULL) #define IS_LOOKASIDE_INITIALIZED( lookaside ) \ ((lookaside)->L.ListEntry.Flink != NULL) // // macro that should probably be in ntos\inc\io.h. // #define SrUnmarkIrpPending( Irp ) ( \ IoGetCurrentIrpStackLocation( (Irp) )->Control &= ~SL_PENDING_RETURNED ) // // Miscellaneous validators. // #define IS_VALID_DEVICE_OBJECT( pDeviceObject ) \ ( ((pDeviceObject) != NULL) && \ ((pDeviceObject)->Type == IO_TYPE_DEVICE) ) // ((pDeviceObject)->Size == sizeof(DEVICE_OBJECT)) ) #define IS_VALID_FILE_OBJECT( pFileObject ) \ ( ((pFileObject) != NULL) && \ ((pFileObject)->Type == IO_TYPE_FILE) ) // ((pFileObject)->Size == sizeof(FILE_OBJECT)) ) #define IS_VALID_IRP( pIrp ) \ ( ((pIrp) != NULL) && \ ((pIrp)->Type == IO_TYPE_IRP) && \ ((pIrp)->Size >= IoSizeOfIrp((pIrp)->StackCount)) ) // // Calculate the dimension of an array. // #define DIMENSION(x) ( sizeof(x) / sizeof(x[0]) ) // // The DIFF macro should be used around an expression involving pointer // subtraction. The expression passed to DIFF is cast to a size_t type, // allowing the result to be easily assigned to any 32-bit variable or // passed to a function expecting a 32-bit argument. // #define DIFF(x) ((size_t)(x)) // // the wait is in 100 nanosecond units to 10,000,000 = 1 second // #define NANO_FULL_SECOND (10000000) // // The default buffer size we use to buffer the log entries. // #define SR_DEFAULT_LOG_BUFFER_SIZE (2 * 1024) // // The frequency with which we should fire the log flush timer, in seconds. // #define SR_DEFAULT_LOG_FLUSH_FREQUENCY 1 // // By default, we will extend the log file 16K at a time. // #define SR_DEFAULT_LOG_ALLOCATION_UNIT (16 * 1024) // // Returns TRUE if the given device type matches one of the device types // we support. If not, it returns FALSE. // #define SR_IS_SUPPORTED_DEVICE(_do) \ ((_do)->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) #define SR_IS_SUPPORTED_REAL_DEVICE(_rd) \ (((_rd)->Characteristics & FILE_REMOVABLE_MEDIA) == 0) #define SR_IS_SUPPORTED_VOLUME(_vpb) \ (((_vpb)->DeviceObject->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM) && \ ((_vpb)->RealDevice->Characteristics & FILE_REMOVABLE_MEDIA) == 0) #define IS_VALID_WORK_ITEM(pObject) \ (((pObject) != NULL) && ((pObject)->Signature == SR_WORK_ITEM_TAG)) typedef struct _SR_WORK_ITEM { // // NonPagedPool // // // = SR_WORK_ITEM_TAG // ULONG Signature; WORK_QUEUE_ITEM WorkItem; PVOID Parameter1; PVOID Parameter2; PVOID Parameter3; // // The event to signal when the work item has been completed. // KEVENT Event; // // For return a status code // NTSTATUS Status; } SR_WORK_ITEM, * PSR_WORK_ITEM; #define SR_COPY_BUFFER_LENGTH (64 * 1024) // // used for file entries for ZwQueryDirectory #define SR_FILE_ENTRY_LENGTH (1024*4) // // used just in case we switch to dos volume names // #define VOLUME_FORMAT L"%wZ" // // used for exception detection in finally // #if DBG #define FinallyUnwind(FuncName, StatusCode) \ (AbnormalTermination()) ? \ ( SrTraceSafe( VERBOSE_ERRORS, \ ("sr!%s failed due to an unhandled exception!\n", #FuncName)),\ ( ( (global == NULL || FlagOn(global->DebugControl, SR_DEBUG_BREAK_ON_ERROR)) ? \ RtlAssert("AbnormalTermination() == FALSE", __FILE__, __LINE__, NULL) : \ 0 ), \ STATUS_UNHANDLED_EXCEPTION ) ) \ : (StatusCode) #else #define FinallyUnwind(FuncName, StatusCode) \ (AbnormalTermination()) ? \ STATUS_UNHANDLED_EXCEPTION \ : (StatusCode) #endif // DBG // // stolen from sertlp.h // #define LongAlignPtr(Ptr) ((PVOID)(((ULONG_PTR)(Ptr) + 3) & -4)) #define LongAlignSize(Size) (((ULONG)(Size) + 3) & -4) // // the number of characters the largest ULONG takes in base 10 . // 4294967295 = 10 chars // #define MAX_ULONG_CHARS (10) #define MAX_ULONG_LENGTH (MAX_ULONG_CHARS*sizeof(WCHAR)) // // flags to check if we are in the middle of text mode setup // #define UPGRADE_SETUPDD_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Setupdd" #define UPGRADE_SETUPDD_VALUE_NAME L"Start" // // flags to check if we are in the middle of gui mode setup // #define UPGRADE_CHECK_SETUP_KEY_NAME L"\\Registry\\Machine\\System\\Setup" #define UPGRADE_CHECK_SETUP_VALUE_NAME L"SystemSetupInProgress" // // this is a filter as to what sort of errors cause volume error's to be // triggered. If we get the error STATUS_VOLUME_DISMOUNTED, we have already // shutdown everything correctly and we don't want to treat this as an error. // Also, if we get STATUS_FILE_CORRUPT_ERROR, the user's operation is also // going to fail, so don't treat this as a volume error. // #define CHECK_FOR_VOLUME_ERROR(Status) \ ((STATUS_VOLUME_DISMOUNTED != Status) && \ (STATUS_FILE_CORRUPT_ERROR != Status) && \ (SR_STATUS_VOLUME_DISABLED != Status) && \ (SR_STATUS_CONTEXT_NOT_SUPPORTED != Status) && \ (SR_STATUS_IGNORE_FILE != Status) && \ !NT_SUCCESS((Status))) // // Macro that defines what a "volume name" mount point is. This macro can // be used to scan the result from QUERY_POINTS to discover which mount points // are "volume name" mount points. // // stolen + modified from mountmgr.h // #define MOUNTMGR_VOLUME_NAME_PREFIX_COUNT (49) #define MOUNTMGR_VOLUME_NAME_PREFIX_LENGTH (49*sizeof(WCHAR)) #define MOUNTMGR_IS_VOLUME_NAME_PREFIX(s) ( \ (s)->Length >= MOUNTMGR_VOLUME_NAME_PREFIX_LENGTH && \ (s)->Buffer[0] == '\\' && \ (s)->Buffer[1] == '?' && \ (s)->Buffer[2] == '?' && \ (s)->Buffer[3] == '\\' && \ (s)->Buffer[4] == 'V' && \ (s)->Buffer[5] == 'o' && \ (s)->Buffer[6] == 'l' && \ (s)->Buffer[7] == 'u' && \ (s)->Buffer[8] == 'm' && \ (s)->Buffer[9] == 'e' && \ (s)->Buffer[10] == '{' && \ (s)->Buffer[19] == '-' && \ (s)->Buffer[24] == '-' && \ (s)->Buffer[29] == '-' && \ (s)->Buffer[34] == '-' && \ (s)->Buffer[47] == '}' && \ (s)->Buffer[48] == '\\' ) #if DBG #define SrAcquireActivityLockShared(pExtension) \ { \ if (ExIsResourceAcquiredShared(&(pExtension)->ActivityLock) == FALSE) \ { \ /* no other locks better be held. the activity lock is the */ \ /* outermost lock */ \ ASSERT(!ExIsResourceAcquiredShared(&global->GlobalLock)); \ ASSERT(!ExIsResourceAcquiredExclusive( &global->GlobalLock)); \ ASSERT(!ExIsResourceAcquiredShared(&global->BlobLock)); \ ASSERT(!ExIsResourceAcquiredExclusive( &global->BlobLock)); \ ASSERT(!ExIsResourceAcquiredShared(&(pExtension)->LogLock)); \ ASSERT(!ExIsResourceAcquiredExclusive( &(pExtension)->LogLock)); \ } \ KeEnterCriticalRegion(); \ ExAcquireSharedStarveExclusive(&(pExtension)->ActivityLock, TRUE); \ } #define SrAcquireActivityLockExclusive(pExtension) \ { \ if (!ExIsResourceAcquiredExclusive(&(pExtension)->ActivityLock)) \ { \ /* no other locks better be held. the activity lock is the */ \ /* outermost lock */ \ ASSERT(!ExIsResourceAcquiredShared(&global->GlobalLock)); \ ASSERT(!ExIsResourceAcquiredExclusive( &global->GlobalLock)); \ ASSERT(!ExIsResourceAcquiredShared(&global->BlobLock)); \ ASSERT(!ExIsResourceAcquiredExclusive( &global->BlobLock)); \ ASSERT(!ExIsResourceAcquiredShared(&(pExtension)->LogLock)); \ ASSERT(!ExIsResourceAcquiredExclusive( &(pExtension)->LogLock)); \ } \ SrAcquireResourceExclusive(&(pExtension)->ActivityLock, TRUE); \ } #define SrAcquireLogLockShared(pExtension) \ { \ ASSERT(ExIsResourceAcquiredShared(&(pExtension)->ActivityLock) || \ ExIsResourceAcquiredExclusive( &(pExtension)->ActivityLock )); \ SrAcquireResourceShared(&(pExtension)->LogLock, TRUE); \ } #define SrAcquireLogLockExclusive(pExtension) \ { \ ASSERT(ExIsResourceAcquiredShared(&(pExtension)->ActivityLock) || \ ExIsResourceAcquiredExclusive( &(pExtension)->ActivityLock )); \ SrAcquireResourceExclusive(&(pExtension)->LogLock, TRUE); \ } #else #define SrAcquireActivityLockShared(pExtension) \ { \ KeEnterCriticalRegion(); \ ExAcquireSharedStarveExclusive(&(pExtension)->ActivityLock, TRUE); \ } #define SrAcquireActivityLockExclusive(pExtension) \ SrAcquireResourceExclusive(&(pExtension)->ActivityLock, TRUE) #define SrAcquireLogLockShared(pExtension) \ SrAcquireResourceShared(&((pExtension)->LogLock), TRUE) #define SrAcquireLogLockExclusive(pExtension) \ SrAcquireResourceExclusive(&((pExtension)->LogLock), TRUE) #endif // DBG #define IS_ACTIVITY_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) \ ExIsResourceAcquiredExclusiveLite( &(pExtension)->ActivityLock ) #define IS_ACTIVITY_LOCK_ACQUIRED_SHARED( pExtension ) \ ExIsResourceAcquiredSharedLite( &(pExtension)->ActivityLock ) #define IS_LOG_LOCK_ACQUIRED_EXCLUSIVE( pExtension ) \ ExIsResourceAcquiredExclusiveLite( &(pExtension)->LogLock ) #define IS_LOG_LOCK_ACQUIRED_SHARED( pExtension ) \ ExIsResourceAcquiredSharedLite( &(pExtension)->LogLock ) #define SrReleaseActivityLock(pExtension) \ SrReleaseResource(&(pExtension)->ActivityLock) #define SrReleaseLogLock(pExtension) \ SrReleaseResource(&(pExtension)->LogLock) #define IS_GLOBAL_LOCK_ACQUIRED() \ (ExIsResourceAcquiredExclusiveLite(&(global->GlobalLock)) || \ ExIsResourceAcquiredSharedLite( &(global->GlobalLock) )) #define SrAcquireGlobalLockExclusive() \ SrAcquireResourceExclusive( &(global->GlobalLock), TRUE ) #define SrReleaseGlobalLock() \ SrReleaseResource( &(global->GlobalLock) ) #define IS_BLOB_LOCK_ACQUIRED_EXCLUSIVE() \ (ExIsResourceAcquiredExclusiveLite(&(global->BlobLock))) #define IS_BLOB_LOCK_ACQUIRED() \ (ExIsResourceAcquiredExclusiveLite(&(global->BlobLock)) || \ ExIsResourceAcquiredSharedLite( &(global->BlobLock) )) #define SrAcquireBlobLockExclusive() \ SrAcquireResourceExclusive( &(global->BlobLock), TRUE ) #define SrAcquireBlobLockShared() \ SrAcquireResourceShared( &(global->BlobLock), TRUE ) #define SrReleaseBlobLock() \ SrReleaseResource( &(global->BlobLock) ) #define IS_DEVICE_EXTENSION_LIST_LOCK_ACQUIRED() \ (ExIsResourceAcquiredExclusiveLite(&(global->DeviceExtensionListLock)) || \ ExIsResourceAcquiredSharedLite( &(global->DeviceExtensionListLock) )) #define SrAcquireDeviceExtensionListLockExclusive() \ SrAcquireResourceExclusive( &(global->DeviceExtensionListLock), TRUE ) #define SrAcquireDeviceExtensionListLockShared() \ SrAcquireResourceShared( &(global->DeviceExtensionListLock), TRUE ) #define SrReleaseDeviceExtensionListLock() \ SrReleaseResource( &(global->DeviceExtensionListLock) ) #define SrAcquireBackupHistoryLockShared( pExtension ) \ SrAcquireResourceShared( &((pExtension)->pBackupHistory->Lock), TRUE ) #define SrAcquireBackupHistoryLockExclusive( pExtension ) \ SrAcquireResourceExclusive( &((pExtension)->pBackupHistory->Lock), TRUE ) #define SrReleaseBackupHistoryLock( pExtension ) \ SrReleaseResource( &((pExtension)->pBackupHistory->Lock) ) // // we require 50mb free to function // #define SR_MIN_DISK_FREE_SPACE (50 * 1024 * 1024) // // the temp unique filename used in SrHandleFileOverwrite // #define SR_UNIQUE_TEMP_FILE L"\\4bf03598-d7dd-4fbe-98b3-9b70a23ee8d4" #define SR_UNIQUE_TEMP_FILE_LENGTH (37 * sizeof(WCHAR)) #define VALID_FAST_IO_DISPATCH_HANDLER(FastIoDispatchPtr, FieldName) \ (((FastIoDispatchPtr) != NULL) && \ (((FastIoDispatchPtr)->SizeOfFastIoDispatch) >= \ (FIELD_OFFSET(FAST_IO_DISPATCH, FieldName) + sizeof(VOID *))) && \ ((FastIoDispatchPtr)->FieldName != NULL)) #define FILE_OBJECT_IS_NOT_POTENTIALLY_INTERESTING(FileObject) \ ((FileObject) == NULL || \ FlagOn((FileObject)->Flags, FO_STREAM_FILE)) #define FILE_OBJECT_DOES_NOT_HAVE_VPB(FileObject) \ (FileObject)->Vpb == NULL #define USE_DO_HINT #ifdef USE_DO_HINT #define SrIoCreateFile( F, D, O, I, A, FA, SA, CD, CO, EB, EL, FL, DO ) \ IoCreateFileSpecifyDeviceObjectHint((F), \ (D), \ (O), \ (I), \ (A), \ (FA), \ (SA), \ (CD), \ (CO), \ (EB), \ (EL), \ CreateFileTypeNone, \ NULL, \ (FL), \ (DO) ); #else #define SrIoCreateFile( F, D, O, I, A, FA, SA, CD, CO, EB, EL, FL, DO ) \ ZwCreateFile((F), \ (D), \ (O), \ (I), \ (A), \ (FA), \ (SA), \ (CD), \ (CO), \ (EB), \ (EL) ); #endif /* USE_DO_HINT */ // // Macros so that we can check for expected errors in debug code. // #if DBG #define DECLARE_EXPECT_ERROR_FLAG( _ErrorFlag ) \ BOOLEAN _ErrorFlag = FALSE #define SET_EXPECT_ERROR_FLAG( _ErrorFlag ) \ ((_ErrorFlag) = TRUE) #define CLEAR_EXPECT_ERROR_FLAG( _ErrorFlag ) \ ((_ErrorFlag) = FALSE) #define CHECK_FOR_EXPECTED_ERROR( _ErrorFlag, _Status ) \ { \ if ((_ErrorFlag)) \ { \ ASSERT( !NT_SUCCESS_NO_DBGBREAK( (_Status) ) );\ } \ } #else #define DECLARE_EXPECT_ERROR_FLAG( _ErrorFlag ) #define SET_EXPECT_ERROR_FLAG( _ErrorFlag ) #define CLEAR_EXPECT_ERROR_FLAG( _ErrorFlag ) #define CHECK_FOR_EXPECTED_ERROR( _ErrorFlag, _Status ) #endif #endif // _SRPRIV_H_