WDK Mini Filter Example
csvfs.c
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) 2011 Microsoft Corporation
4 
5 Module Name:
6 
7  csvfs.c
8 
9 Abstract:
10 
11  This is the csvfs specific module of the avscan mini-filter driver.
12  This filter demonstrates how to implement a transaction-aware
13  anti-virus filter.
14 
15  Av prefix denotes "Anti-virus" module.
16 
17 
18  CSVFS is a distributed file system where multiple nodes in a
19  cluster can expose the same volume namespace at the same time. To
20  achieve this the CSVFS volumes on each node act as a proxy for an
21  underlying NTFS volume where the NTFS volume runs only on one node
22  of the cluster. It is this NTFS volume where the file data and file
23  system metadata are stored. The node that exposes this NTFS volume
24  is the coordinator node. The underlying NTFS volume is made hidden
25  to keep applications from using it directly. All applications
26  should use the CSVFS namespace.
27 
28  CSVFS implements functionality known as Direct I/O. This
29  functionality allow the CSVFS proxy on each node to directly read
30  or write to the blocks on the disk and bypass the NTFS volume on the
31  coordinator node when it is safe to do so. A filter on node node
32  needs to be aware that it may not see all I/O to a file.
33 
34  In the CSVFS enviornment filters can layer on the CSVFS volume
35  stack, the hidden NTFS volume stack on the coordinator node and
36  also on the MUP stack. When on the MUP and the hidden NTFS stack
37  the filter should not scan any files that are opened with
38  GUID_ECP_CSV_DOWN_LEVEL_OPEN ECP attached. This ECP is used by
39  CSVFS for its internal file opens and should be ignored by filters.
40  Filters that layer on the CSVFS would be the components that scan
41  the files.
42 
43  It is recommended that filters be extremely careful when layered on
44  the hidden NTFS stack:
45 
46  Management issues
47  Volume is hidden and thus does not have volume guid,
48  mountpoint or drive letter that can be used to
49  represent this volume to the user in a command line or UI.
50 
51  The coordinator node for a CSVFS volume can move to another
52  node at any time. This would cause challenges with
53  maintaining filter configuration setttings and also be
54  challenging for the admin to know which node should be used.
55 
56  Interop with CSVFS issues
57  Changing file sizes in a filter layered above the hidden
58  NTFS volume may cause data corruption.
59 
60  Building and keeping a mapped section on hidden NTFS volume
61  might prevent direct IO from happening
62 
63  Building and keeping mapped section on hidden NTFS volume
64  might lead to cache coherency issues (stale cache) and
65  eventually to data corruption.
66 
67  Note that there is a feasible model for creating a distributed
68  filter with instances on the CSVFS and the hidden NTFS volume
69  stacks. The instance on the hidden NTFS volume would serve as a
70  centralized filter meta-data server where information shared
71  between node could be maintained. Each instance on CSVFS volume
72  would communicate with the hidden NTFS volume stack through a
73  downlevel file handle.
74 
75  CSVFS provides a very simple centralized meta-data cache which
76  contains revision numbers for tracking changes to files. This
77  sample shows how those revision numbers can be used to determine if
78  a file has been modified by another node and thus if it needs to be
79  scanned locally.
80 
81  Other things to be aware of:
82 
83  Filter should not have *any* global locks or else deadlock may
84  occur. File system requests will flow through the CSVFS proxy file
85  system and then be forwarded to the hidden NTFS file system. If the
86  filter is layered on both file systems and the filter instance on the
87  CSVFS volume takes a lock as the request passes through, when the
88  request is forwarded and reaches the filter instance on the hidden
89  NTFS or MUP volume, it will attempt to acquire the lock and deadlock will
90  occur.
91 
92  Do not make assumption that buffered IO will always go to cache and
93  eventually you will see paging IO. CsvFs, like RDR, does caching based
94  on the oplock it was able to get for this stream. For instance if a
95  file is being accessed from multiple nodes CsvFs would have oplock
96  level RH or R or none and all cached writes to the file will be sent
97  directly to NTFS without going to CC. You can think about that as if
98  from the perspective of a filter sitting above CSVFS cached IO is being
99  handles as if is it un buffered IO. Since in general filters would
100  not know when CsvFs loses/gains oplocks they should not make an
101  assumption that cached IO will go through CC.
102 
103 
104 Environment:
105 
106  Kernel mode
107 
108 --*/
109 
110 #include "avscan.h"
111 #include <ntdddisk.h>
112 
113 /*************************************************************************
114  Local Function Prototypes
115 *************************************************************************/
116 NTSTATUS
118  _Inout_ PFLT_CALLBACK_DATA Data
119  );
120 
121 NTSTATUS
123  _Inout_ PFLT_CALLBACK_DATA Data,
124  _Out_ LONGLONG *VolumeRevision,
125  _Out_ LONGLONG *CacheRevision,
126  _Out_ LONGLONG *FileRevision
127  );
128 
129 NTSTATUS
131  _In_ PCFLT_RELATED_OBJECTS FltObjects,
132  _Out_ LONGLONG *VolumeRevision,
133  _Out_ LONGLONG *CacheRevision,
134  _Out_ LONGLONG *FileRevision
135  );
136 
137 NTSTATUS
139  _Inout_ PFLT_CALLBACK_DATA Data,
140  _In_ LPCGUID EcpGuid,
141  _Out_ PVOID *Ecp,
142  _Out_ ULONG *EcpSize
143  );
144 
145 //
146 // Assign text sections for each routine.
147 //
148 
149 #ifdef ALLOC_PRAGMA
150 #pragma alloc_text(PAGE, AvIsVolumeOnCsvDisk)
151 #pragma alloc_text(PAGE, AvIsCsvDlEcpPresent)
152 #pragma alloc_text(PAGE, AvPreCreateCsvfs)
153 #pragma alloc_text(PAGE, AvPostCreateCsvfs)
154 #pragma alloc_text(PAGE, AvPreCleanupCsvfs)
155 #pragma alloc_text(PAGE, AvQueryCsvRevisionNumbers)
156 #pragma alloc_text(PAGE, AvReadCsvRevisionECP)
157 #pragma alloc_text(PAGE, AvAddCsvRevisionECP)
158 #pragma alloc_text(PAGE, AvFindAckedECP)
159 #endif
160 
161 BOOLEAN
163  _In_ PFLT_VOLUME Volume
164  )
165  /*++
166 
167  Routine Description:
168 
169  This routine checks if the volume indicated by Volume belongs to a disk that is CSV or not.
170 
171  Arguments:
172 
173  Volume - Pointer to the FLT_VOLUME.
174 
175  Return Value:
176 
177  The return value is TRUE or FALSE.
178 
179  --*/
180 {
181 
182  NTSTATUS status = STATUS_SUCCESS;
183  BOOLEAN retValue = FALSE;
184  PDEVICE_OBJECT disk = NULL, refDeviceObject=NULL;
185  PIRP irp;
186  IO_STATUS_BLOCK iosb;
187  ULONG controlCode = IOCTL_DISK_GET_CLUSTER_INFO;
188  DISK_CLUSTER_INFO outBuf;
189  KEVENT event;
190 
191  PAGED_CODE();
192 
193  status = FltGetDiskDeviceObject(Volume, &disk);
194  if (!NT_SUCCESS(status)) {
195  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
196  ("Failed to get disk object from volume, status 0x%x\n", status) );
197  goto Cleanup;
198  }
199 
200  refDeviceObject = IoGetAttachedDeviceReference(disk);
201 
202  iosb.Information = 0;
203  RtlZeroMemory(&outBuf, sizeof(outBuf));
204  KeInitializeEvent(&event, NotificationEvent, FALSE);
205 
206  irp = IoBuildDeviceIoControlRequest( controlCode,
207  refDeviceObject,
208  NULL,
209  0,
210  &outBuf,
211  sizeof(outBuf),
212  FALSE,
213  &event,
214  &iosb );
215  if (irp == NULL) {
216  status = STATUS_INSUFFICIENT_RESOURCES;
217  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
218  ("Failed to allocate Irp, status 0x%x\n", status) );
219  goto Cleanup;
220  }
221 
222  status = IoCallDriver( refDeviceObject, irp );
223  if (status == STATUS_PENDING) {
224  KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
225  status = iosb.Status;
226  }
227 
228  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
229  ("DeviceIoControl returned status 0x%x\n", status) );
230 
231  if(!NT_SUCCESS( status )) {
232  goto Cleanup;
233  }
234 
235  retValue = FlagOn( outBuf.Flags, DISK_CLUSTER_FLAG_CSV ) ? TRUE : FALSE;
236  if (FlagOn( outBuf.Flags, DISK_CLUSTER_FLAG_CSV) && FlagOn(outBuf.Flags, DISK_CLUSTER_FLAG_IN_MAINTENANCE )) {
237  //
238  // A CSV disk can be in maintenance mode. When in maintenance
239  // mode the CSV namespace is no longer exposed across the
240  // entire cluster but instead only exposed on the single node
241  // where the NTFS volume is exposed. In this case the filter
242  // should treat the volume as it would any other NTFS volume
243  //
244  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
245  ("Disk is CSV but in Maintenance\n") );
246  retValue = FALSE;
247  }
248 
249  if(retValue == TRUE) {
250  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
251  ("Disk is CSV\n") );
252  }
253  else {
254  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
255  ("Disk is not CSV\n") );
256  }
257 
258 Cleanup:
259 
260  if (refDeviceObject) {
261  KeEnterCriticalRegion();
262  ObDereferenceObject( refDeviceObject );
263  refDeviceObject = NULL;
264  KeLeaveCriticalRegion();
265  }
266 
267  if (disk) {
268  ObDereferenceObject( disk );
269  disk = NULL;
270  }
271 
272  return retValue;
273 }
274 
275 NTSTATUS
277  _Inout_ PFLT_CALLBACK_DATA Data
278  )
279 /*++
280 
281 Routine Description:
282 
283  This routine will include the Extra Create Parameter (ECP) that is
284  used on CSVFS file systems. This ECP will return the set of
285  revision numbers that are associated with the file being opened.
286 
287 Arguments:
288 
289  Data - Pointer to the filter callbackData that is passed to us.
290 
291 Return Value:
292 
293  The return value is the status of the operation.
294 
295 --*/
296 {
297  NTSTATUS status;
298  PECP_LIST ecpList = NULL;
299  PCSV_QUERY_FILE_REVISION_ECP_CONTEXT ecpContext;
300 
301  PAGED_CODE();
302 
303  AV_DBG_PRINT( AVDBG_TRACE_ROUTINES,
304  ("[AV] AvAddCsvRevisionECP: Entered\n") );
305 
306  status = FltGetEcpListFromCallbackData( Globals.Filter,
307  Data,
308  &ecpList );
309  if (!NT_SUCCESS( status )) {
310  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
311  ("[AV] AvAddCsvRevisionECP: FltGetEcpListFromCallbackData failed 0x%x\n", status) );
312  goto Cleanup;
313  }
314 
315  if (ecpList == NULL) {
316  //
317  // Create a new ecplist.
318  //
319  status = FltAllocateExtraCreateParameterList( Globals.Filter, 0, &ecpList );
320  if (!NT_SUCCESS(status)) {
321  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
322  ("[AV] AvAddCsvRevisionECP: FltAllocateExtraCreateParameterList failed 0x%x", status) );
323  goto Cleanup;
324  }
325  //
326  // Set it into CBD.
327  //
328  status = FltSetEcpListIntoCallbackData( Globals.Filter, Data, ecpList );
329  if (!NT_SUCCESS(status)) {
330  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
331  ("[AV] AvAddCsvRevisionECP: FltSetEcpListIntoCallbackData failed 0x%x", status) );
332  FltFreeExtraCreateParameterList( Globals.Filter, ecpList );
333  goto Cleanup;
334  }
335 
336  } else {
337  //
338  // See if the ECP has already been added to the ECP list
339  // already.
340  //
341  status = FltFindExtraCreateParameter( Globals.Filter,
342  ecpList,
343  &GUID_ECP_CSV_QUERY_FILE_REVISION,
344  NULL,
345  NULL );
346  if (status != STATUS_NOT_FOUND) {
347  goto Cleanup;
348  }
349 
350  }
351 
352  status = FltAllocateExtraCreateParameter( Globals.Filter,
353  &GUID_ECP_CSV_QUERY_FILE_REVISION,
354  sizeof(CSV_QUERY_FILE_REVISION_ECP_CONTEXT),
355  0,
356  NULL,
358  &ecpContext );
359 
360  if (!NT_SUCCESS(status)) {
361  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
362  ("[AV] AvAddCsvRevisionECP: FltAllocateExtraCreateParameterFromLookasideList failed 0x%x\n", status) );
363  goto Cleanup;
364  }
365 
366  RtlZeroMemory( ecpContext, sizeof(CSV_QUERY_FILE_REVISION_ECP_CONTEXT ));
367  status = FltInsertExtraCreateParameter( Globals.Filter,
368  ecpList,
369  ecpContext );
370  if (!NT_SUCCESS(status)) {
371  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
372  ("[AV] AvAddCsvRevisionECP: FltInsertExtraCreateParameter failed 0x%x\n", status) );
373  FltFreeExtraCreateParameter( Globals.Filter, ecpContext );
374  goto Cleanup;
375  }
376 
377 Cleanup:
378 
379  AV_DBG_PRINT( AVDBG_TRACE_ROUTINES,
380  ("[AV] AvAddCsvRevisionECP: Leave\n") );
381 
382  return status;
383 }
384 
385 NTSTATUS
387  _Inout_ PFLT_CALLBACK_DATA Data,
388  _In_ LPCGUID EcpGuid,
389  _Out_ PVOID *Ecp,
390  _Out_ ULONG *EcpSize
391  )
392 /*++
393 
394 Routine Description:
395 
396  This routine will find the Extra Create Parameter (ECP) and if it
397  exists then check if it has been acknowledged
398 
399 Arguments:
400 
401  Data - Pointer to the filter callbackData that is passed to us.
402 
403  EcpGuid - Pointer to the guid that represents the ECP to find
404 
405  *Ecp - returns with a pointer to the ECP data
406 
407  *EcpSize - returns with the size of the ECP data
408 
409 
410 Return Value:
411 
412  The return value is the status of the operation.
413 
414 --*/
415 {
416  NTSTATUS status;
417  PECP_LIST ecpList = NULL;
418  PVOID ecpContext = NULL;
419  ULONG ecpContextSize = 0;
420 
421  PAGED_CODE();
422 
423  AV_DBG_PRINT( AVDBG_TRACE_ROUTINES,
424  ("[AV] AvFindAckedECP: Entered\n") );
425 
426  status = FltGetEcpListFromCallbackData( Globals.Filter,
427  Data,
428  &ecpList);
429  if (NT_SUCCESS(status)) {
430 
431  if (ecpList != NULL) {
432 
433  status = FltFindExtraCreateParameter( Globals.Filter,
434  ecpList,
435  EcpGuid,
436  &ecpContext,
437  &ecpContextSize);
438 
439  if (NT_SUCCESS(status)) {
440 
441  if (FltIsEcpAcknowledged( Globals.Filter, ecpContext )) {
442  *Ecp = ecpContext;
443  *EcpSize = ecpContextSize;
444  } else {
445  status = STATUS_UNSUCCESSFUL;
446  }
447  }
448 
449  } else {
450  status = STATUS_UNSUCCESSFUL;
451  }
452  }
453 
454  AV_DBG_PRINT( AVDBG_TRACE_ROUTINES,
455  ("[AV] AvFindAckedECP: leave 0x%x\n", status) );
456 
457  return status;
458 }
459 
460 NTSTATUS
462  _Inout_ PFLT_CALLBACK_DATA Data,
463  _Out_ LONGLONG *VolumeRevision,
464  _Out_ LONGLONG *CacheRevision,
465  _Out_ LONGLONG *FileRevision
466  )
467 /*++
468 
469 Routine Description:
470 
471  This routine will read the Extra Create Parameter (ECP) that is
472  returned on CSVFS file systems. This ECP returns the set of
473  revision numbers that are associated with the file being opened.
474 
475 Arguments:
476 
477  Data - Pointer to the filter callbackData that is passed to us.
478 
479  *VolumeRevision returns with the volume revision number
480 
481  *CacheRevision returns with the cache revision number
482 
483  *FileRevision returns with the file revision number
484 
485 Return Value:
486 
487  The return value is the status of the operation.
488 
489 --*/
490 {
491  NTSTATUS status;
492  PCSV_QUERY_FILE_REVISION_ECP_CONTEXT ecpContext = NULL;
493  ULONG ecpContextSize = 0;
494 
495  PAGED_CODE();
496 
497  AV_DBG_PRINT( AVDBG_TRACE_ROUTINES,
498  ("[AV] AvReadCsvRevisionECP: Entered\n") );
499 
500 
501  status = AvFindAckedECP( Data,
502  &GUID_ECP_CSV_QUERY_FILE_REVISION,
503  &ecpContext,
504  &ecpContextSize );
505 
506  if (NT_SUCCESS( status )) {
507  *VolumeRevision = ecpContext->FileRevision[0];
508  *CacheRevision = ecpContext->FileRevision[1];
509  *FileRevision = ecpContext->FileRevision[2];
510  }
511 
512  AV_DBG_PRINT( AVDBG_TRACE_ROUTINES,
513  ("[AV] AvReadCsvRevisionECP: leave 0x%x\n", status) );
514 
515  return status;
516 }
517 
518 NTSTATUS
520  _In_ PCFLT_RELATED_OBJECTS FltObjects,
521  _Out_ LONGLONG *VolumeRevision,
522  _Out_ LONGLONG *CacheRevision,
523  _Out_ LONGLONG *FileRevision
524  )
525 /*++
526 
527 Routine Description:
528 
529  Obtain the most updated revision numbers for a file on CSVFS
530 
531 Arguments:
532 
533  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
534  opaque handles to this filter, instance, its associated volume and
535  file object.
536 
537  *VolumeRevision returns with the volume revision number
538 
539  *CacheRevision returns with the cache revision number
540 
541  *FileRevision returns with the file revision number
542 
543 Return Value:
544 
545  The return value is the status of the operation.
546 
547 --*/
548 {
549  NTSTATUS status;
550  CSV_CONTROL_PARAM request;
551  CSV_QUERY_FILE_REVISION revision;
552  ULONG bytesReturned;
553 
554  PAGED_CODE();
555 
556  RtlZeroMemory( &request, sizeof( request ) );
557  request.Operation = CsvControlQueryFileRevision;
558 
559  status = FltFsControlFile( FltObjects->Instance,
560  FltObjects->FileObject,
561  FSCTL_CSV_CONTROL,
562  &request,
563  sizeof(request),
564  &revision,
565  sizeof(revision),
566  &bytesReturned );
567 
568  if (NT_SUCCESS( status )) {
569  *VolumeRevision = revision.FileRevision[0];
570  *CacheRevision = revision.FileRevision[1];
571  *FileRevision = revision.FileRevision[2];
572  }
573 
574  return status;
575 }
576 
577 BOOLEAN
579  _In_ PFLT_FILTER Filter,
580  _In_ PFLT_CALLBACK_DATA Data
581  )
582 /*++
583 
584 Routine Description:
585 
586  This local function will determine if there is a CSVFS downlevel
587  ECP attached.
588 
589 Arguments:
590 
591  Filter - Pointer to the filter structure
592 
593  Data - Pointer to the filter callbackData that is passed to us.
594 
595 Return Value:
596 
597  TRUE - CSVFS downlevel ECP is present
598  FALSE - CSVFS downlevel ECP is not present or an error occured
599 
600 --*/
601 {
602  NTSTATUS status;
603  PECP_LIST ecpList;
604  PVOID ecpContext;
605 
606  PAGED_CODE();
607 
608  status = FltGetEcpListFromCallbackData( Filter, Data, &ecpList );
609 
610  if (NT_SUCCESS(status) && (ecpList != NULL)) {
611 
612  status = FltFindExtraCreateParameter( Filter,
613  ecpList,
614  &GUID_ECP_CSV_DOWN_LEVEL_OPEN,
615  &ecpContext,
616  NULL );
617 
618  if (NT_SUCCESS(status)) {
619 
620  return TRUE;
621  }
622  }
623 
624  return FALSE;
625 }
626 
627 NTSTATUS
629  _Inout_ PFLT_CALLBACK_DATA Data,
630  _In_ PCFLT_RELATED_OBJECTS FltObjects
631  )
632 /*++
633 
634 Routine Description:
635 
636  This function implements the PreCreate processing associated with a
637  CSVFS volume. The work done is to include the file revision ECP
638  into the ECP list so that CSVFS will return the revision numbers
639  when the create completes.
640 
641 Arguments:
642 
643  Data - Pointer to the filter callbackData that is passed to us.
644 
645  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
646  opaque handles to this filter, instance, its associated volume and
647  file object.
648 
649 Return Value:
650 
651  Status
652 
653 --*/
654 {
655  NTSTATUS status;
656  PAV_INSTANCE_CONTEXT instanceContext = NULL;
657 
658  PAGED_CODE();
659 
660  status = FltGetInstanceContext( FltObjects->Instance,
661  &instanceContext );
662 
663  if (NT_SUCCESS( status )) {
664 
665  if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) {
666  //
667  // Add ECP to retrieve the revision numbers
668  //
669  status = AvAddCsvRevisionECP( Data );
670 
671  //
672  // we don't worry if this fails since if we do not get the
673  // revision numbers then we just assume they have changed
674  //
675  }
676 
677  FltReleaseContext( instanceContext );
678  } else {
679  //
680  // If unable to get instance context then it is no problem. It
681  // means that the revision numbers won't be returned from
682  // CSVFS. It is not fatal but will result in the file being
683  // rescanned.
684  //
685  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
686  ("[AV] AvPreCreateCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) );
687  }
688 
689  return status;
690 }
691 
692 NTSTATUS
694  _Inout_ PFLT_CALLBACK_DATA Data,
695  _In_ PCFLT_RELATED_OBJECTS FltObjects,
696  _Inout_ PAV_STREAM_CONTEXT StreamContext,
697  _Out_ BOOLEAN *UpdateRevisionNumbers,
698  _Out_ LONGLONG *VolumeRevisionPtr,
699  _Out_ LONGLONG *CacheRevisionPtr,
700  _Out_ LONGLONG *FileRevisionPtr
701  )
702 /*++
703 
704 Routine Description:
705 
706  This function implements the PostCreate processing associated with a
707  CSVFS volume. The work done is to determine if the revision numbers
708  have been returned and if so determine if a rescan of the file is
709  needed. A recan would be needed if the revision numbers are not
710  able to be retrieved, are not valid or do not match the previously
711  recorded revision numbers.
712 
713 Arguments:
714 
715  Data - Pointer to the filter callbackData that is passed to us.
716 
717  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
718  opaque handles to this filter, instance, its associated volume and
719  file object.
720 
721  StreamContext - Pointer to the AV stream context
722 
723  *UpdateRevisionNumbers returns TRUE if the revision numbers should
724  be updated in the stream context upon a successful scan
725 
726  *VolumeRevisionPtr return with the updated volume revision number
727 
728  *CacheRevisionPtr return with the updated cache revision number
729 
730  *FileRevisionPtr return with the updated file revision number
731 
732 Return Value:
733 
734  Status
735 
736 --*/
737 {
738  PAV_INSTANCE_CONTEXT instanceContext = NULL;
739  NTSTATUS status;
740  LONGLONG VolumeRevision = 0, CacheRevision = 0, FileRevision = 0;
741  BOOLEAN needRescanOnCsvfs = FALSE;
742 
743  PAGED_CODE();
744 
745  *UpdateRevisionNumbers = FALSE;
746  *VolumeRevisionPtr = 0;
747  *CacheRevisionPtr = 0;
748  *FileRevisionPtr = 0;
749 
750  status = FltGetInstanceContext( FltObjects->Instance,
751  &instanceContext );
752 
753  if (NT_SUCCESS( status )) {
754 
755  if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) {
756 
757  //
758  // Read ECP to retrieve the revision numbers
759  //
760  status = AvReadCsvRevisionECP( Data,
761  &VolumeRevision,
762  &CacheRevision,
763  &FileRevision);
764 
765  if (NT_SUCCESS( status )) {
766  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
767  (" [AV] AvPostCreateCsvfs: %I64x:%I64x:%I64x\n",
768  VolumeRevision,
769  CacheRevision,
770  FileRevision) );
771 
772  //
773  // It is very possible that the file was changed by
774  // another node in the cluster and so we need to check
775  // this case. So if any of the revision number have
776  // changed we need to assume that the file has changed.
777  // Note that this is a very pessimistic assumption as
778  // the Volume and Cache revision numbers could change
779  // without a corresponding file change but rescanning
780  // when these change will ensure that a file changed
781  // on another node will not be opened without being
782  // rescanned on this node.
783  //
784  // Also note that there are cases where the revision
785  // numbers are zero and thus not at all valid. In this
786  // case the file must be rescanned as it is not known
787  // if it was changed or not.
788  //
789  needRescanOnCsvfs = ( (VolumeRevision == 0) ||
790  (CacheRevision == 0) ||
791  (FileRevision == 0) ||
792  (VolumeRevision != StreamContext->VolumeRevision) ||
793  (CacheRevision != StreamContext->CacheRevision) ||
794  (FileRevision != StreamContext->FileRevision) );
795  } else {
796  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
797  (" [AV] AvPostCreateCsvfs: Status 0x%x from AvReadCsvRevisionECP\n", status) );
798 
799  //
800  // In this case the revision numbers are not available.
801  // Since there is no way to know if the file was
802  // changed on another node, it is safest to rescan.
803  //
804  needRescanOnCsvfs = TRUE;
805  }
806  }
807  FltReleaseContext( instanceContext );
808  } else {
809  //
810  // If unable to get instance context then the code isn't sure
811  // if it is on a CSVFS volume or not. The safest assumption
812  // would be to rescan the file.
813  //
814  needRescanOnCsvfs = TRUE;
815 
816  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
817  ("[AV] AvPostCreateCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) );
818  }
819 
820  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
821  ("[Av]: AvPostCreateCsvfs: %ws need rescan\n",
822  needRescanOnCsvfs ? L"Does" : L"Does not")
823  );
824 
825 
826  //
827  // if it has been determined that a rescan is needed then set the
828  // file modified flag on the stream context to indicate this
829  //
830  if (needRescanOnCsvfs) {
831  if ( StreamContext->TxContext != NULL) {
832 
833  //
834  // Instead of updating State, we update TxState here,
835  // because the file is part of a transaction writer
836  //
837 
838  SET_FILE_TX_MODIFIED( StreamContext );
839 
840  } else {
841 
842  SET_FILE_MODIFIED( StreamContext );
843  }
844 
845  //
846  // If CSVFS has provided us with valid revison numbers then
847  // return them to the caller so it can update them in the
848  // stream context if the scan is successful.
849  //
850  if ((VolumeRevision != 0) &&
851  (CacheRevision != 0) &&
852  (FileRevision != 0)) {
853  *UpdateRevisionNumbers = TRUE;
854  *VolumeRevisionPtr = VolumeRevision;
855  *CacheRevisionPtr = CacheRevision;
856  *FileRevisionPtr = FileRevision;
857  }
858  }
859 
860  return status;
861 }
862 
863 NTSTATUS
865  _Unreferenced_parameter_ PFLT_CALLBACK_DATA Data,
866  _In_ PCFLT_RELATED_OBJECTS FltObjects,
867  _Inout_ PAV_STREAM_CONTEXT StreamContext,
868  _Out_ BOOLEAN *UpdateRevisionNumbers,
869  _Out_ LONGLONG *VolumeRevisionPtr,
870  _Out_ LONGLONG *CacheRevisionPtr,
871  _Out_ LONGLONG *FileRevisionPtr
872  )
873 /*++
874 
875 Routine Description:
876 
877  This function implements the PreCleanup processing associated with a
878  CSVFS volume. The work done is to retrieve the current revision
879  numbers and determine if a rescan of the file is needed. A recan
880  would be needed if the revision numbers are not
881  able to be retrieved, are not valid or do not match the previously
882  recorded revision numbers.
883 
884 Arguments:
885 
886  Data - Pointer to the filter callbackData that is passed to us.
887 
888  FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing
889  opaque handles to this filter, instance, its associated volume and
890  file object.
891 
892  StreamContext - Pointer to the AV stream context
893 
894  *UpdateRevisionNumbers returns TRUE if the revision numbers should
895  be updated in the stream context upon a successful scan
896 
897  *VolumeRevisionPtr return with the updated volume revision number
898 
899  *CacheRevisionPtr return with the updated cache revision number
900 
901  *FileRevisionPtr return with the updated file revision number
902 
903 Return Value:
904 
905  Status
906 
907 --*/
908 {
909  PAV_INSTANCE_CONTEXT instanceContext = NULL;
910  NTSTATUS status;
911  LONGLONG VolumeRevision = 0, CacheRevision = 0, FileRevision = 0;
912  BOOLEAN needRescanOnCsvfs = FALSE;
913 
914  UNREFERENCED_PARAMETER( Data );
915 
916  PAGED_CODE();
917 
918  *UpdateRevisionNumbers = FALSE;
919  *VolumeRevisionPtr = 0;
920  *CacheRevisionPtr = 0;
921  *FileRevisionPtr = 0;
922 
923  status = FltGetInstanceContext( FltObjects->Instance,
924  &instanceContext );
925 
926  if (NT_SUCCESS( status )) {
927 
928  if (instanceContext->VolumeFSType == FLT_FSTYPE_CSVFS) {
929 
930  //
931  // If this file is on CSVFS then we cannot completely rely
932  // upon tracking if the file is modified only on this node,
933  // but also need to track if the file has been modified on
934  // any node. So query for the updated revision numbers to
935  // see if a scan is needed.
936  //
937  status = AvQueryCsvRevisionNumbers( FltObjects,
938  &VolumeRevision,
939  &CacheRevision,
940  &FileRevision );
941 
942  if (NT_SUCCESS( status )) {
943  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
944  (" [AV] AvPreCleanupCsvfs: %I64x:%I64x:%I64x\n",
945  VolumeRevision,
946  CacheRevision,
947  FileRevision) );
948 
949  //
950  // It is very possible that the file was changed by
951  // another node in the cluster and so we need to check
952  // this case. So if any of the revision number have
953  // changed we need to assume that the file has changed.
954  // Note that this is a very pessimistic assumption as
955  // the Volume and Cache revision numbers could change
956  // without a corresponding file change but rescanning
957  // when these change will ensure that a file changed
958  // on another node will not be opened without being
959  // rescanned on this node.
960  //
961  // Also note that there are cases where the revision
962  // numbers are zero and thus not at all valid. In this
963  // case the file must be rescanned as it is not known
964  // if it was changed or not.
965  //
966  needRescanOnCsvfs = ( (VolumeRevision == 0) ||
967  (CacheRevision == 0) ||
968  (FileRevision == 0) ||
969  (VolumeRevision != StreamContext->VolumeRevision) ||
970  (CacheRevision != StreamContext->CacheRevision) ||
971  (FileRevision != StreamContext->FileRevision) );
972  } else {
973  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
974  (" [AV] AvPreCleanupCsvfs: Status 0x%x from AvReadCsvRevisionECP\n", status) );
975 
976  //
977  // In this case the revision numbers are not available.
978  // Since there is no way to know if the file was
979  // changed on another node, it is safest to rescan.
980  //
981  needRescanOnCsvfs = TRUE;
982  }
983  }
984  FltReleaseContext( instanceContext );
985  } else {
986  //
987  // If unable to get instance context then the code isn't sure
988  // if it is on a CSVFS volume or not. The safest assumption
989  // would be to rescan the file.
990  //
991  needRescanOnCsvfs = TRUE;
992 
993  AV_DBG_PRINT( AVDBG_TRACE_ERROR,
994  ("[AV] AvPreCleanupCsvfs: FltGetInstanceContext failed. status = 0x%x\n", status) );
995  }
996 
997 
998  AV_DBG_PRINT( AVDBG_TRACE_DEBUG,
999  ("[Av]: AvPreCleanupCsvfs: %ws need rescan\n",
1000  needRescanOnCsvfs ? L"Does" : L"Does not")
1001  );
1002 
1003 
1004  //
1005  // if it has been determined that a rescan is needed then set the
1006  // file modified flag on the stream context to indicate this
1007  //
1008  if (needRescanOnCsvfs) {
1009  if ( StreamContext->TxContext != NULL) {
1010 
1011  //
1012  // Instead of updating State, we update TxState here,
1013  // because the file is part of a transaction writer
1014  //
1015 
1016  SET_FILE_TX_MODIFIED( StreamContext );
1017 
1018  } else {
1019 
1020  SET_FILE_MODIFIED( StreamContext );
1021  }
1022 
1023  //
1024  // If CSVFS has provided us with valid revison numbers then
1025  // return them to the caller so it can update them in the
1026  // stream context if the scan is successful.
1027  //
1028  if ((VolumeRevision != 0) &&
1029  (CacheRevision != 0) &&
1030  (FileRevision != 0)) {
1031  *UpdateRevisionNumbers = TRUE;
1032  *VolumeRevisionPtr = VolumeRevision;
1033  *CacheRevisionPtr = CacheRevision;
1034  *FileRevisionPtr = FileRevision;
1035  }
1036  }
1037 
1038  return status;
1039 }
#define SET_FILE_TX_MODIFIED(_sCtx)
#define AV_SCAN_CTX_TAG
FLT_FILESYSTEM_TYPE VolumeFSType
return TRUE
AV_SCANNER_GLOBAL_DATA Globals
Definition: avscan.h:152
NTSTATUS AvPostCreateCsvfs(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Inout_ PAV_STREAM_CONTEXT StreamContext, _Out_ BOOLEAN *UpdateRevisionNumbers, _Out_ LONGLONG *VolumeRevisionPtr, _Out_ LONGLONG *CacheRevisionPtr, _Out_ LONGLONG *FileRevisionPtr)
Definition: csvfs.c:693
PFLT_FILTER Filter
Definition: avscan.h:86
#define SET_FILE_MODIFIED(_sCtx)
#define FlagOn(_F, _SF)
Definition: minispy.h:247
UNREFERENCED_PARAMETER(FileObject)
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
BOOLEAN AvIsCsvDlEcpPresent(_In_ PFLT_FILTER Filter, _In_ PFLT_CALLBACK_DATA Data)
Definition: csvfs.c:578
NTSTATUS AvAddCsvRevisionECP(_Inout_ PFLT_CALLBACK_DATA Data)
Definition: csvfs.c:276
NTSTATUS AvQueryCsvRevisionNumbers(_In_ PCFLT_RELATED_OBJECTS FltObjects, _Out_ LONGLONG *VolumeRevision, _Out_ LONGLONG *CacheRevision, _Out_ LONGLONG *FileRevision)
Definition: csvfs.c:519
BOOLEAN AvIsVolumeOnCsvDisk(_In_ PFLT_VOLUME Volume)
Definition: csvfs.c:162
PAGED_CODE()
NTSTATUS AvReadCsvRevisionECP(_Inout_ PFLT_CALLBACK_DATA Data, _Out_ LONGLONG *VolumeRevision, _Out_ LONGLONG *CacheRevision, _Out_ LONGLONG *FileRevision)
Definition: csvfs.c:461
NTSTATUS AvFindAckedECP(_Inout_ PFLT_CALLBACK_DATA Data, _In_ LPCGUID EcpGuid, _Out_ PVOID *Ecp, _Out_ ULONG *EcpSize)
Definition: csvfs.c:386
NTSTATUS AvPreCreateCsvfs(_Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects)
Definition: csvfs.c:628
NTSTATUS AvPreCleanupCsvfs(_Unreferenced_parameter_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Inout_ PAV_STREAM_CONTEXT StreamContext, _Out_ BOOLEAN *UpdateRevisionNumbers, _Out_ LONGLONG *VolumeRevisionPtr, _Out_ LONGLONG *CacheRevisionPtr, _Out_ LONGLONG *FileRevisionPtr)
Definition: csvfs.c:864
#define AV_DBG_PRINT(_dbgLevel, _string)
Definition: avscan.h:172

Social Network


Services Overview

Architect, implement and test file system filter drivers for a wide range of functionality. We can offer several levels of assistance to meet your specific.

Contact Us

You are welcome to contact us for salse or partnership.

Sales: sales@easefilter.com
Support: support@easefilter.com
Info: info@easefilter.com