Windows: DirOpenReferenceCount reorganizing completed
[openafs.git] / src / WINNT / afsrdr / kernel / lib / AFSDirControl.cpp
1 /*
2  * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
3  * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistributions of source code must retain the above copyright notice,
11  *   this list of conditions and the following disclaimer.
12  * - Redistributions in binary form must reproduce the above copyright
13  *   notice,
14  *   this list of conditions and the following disclaimer in the
15  *   documentation
16  *   and/or other materials provided with the distribution.
17  * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
18  *   nor the names of their contributors may be used to endorse or promote
19  *   products derived from this software without specific prior written
20  *   permission from Kernel Drivers, LLC and Your File System, Inc.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 //
36 // File: AFSDirControl.cpp
37 //
38
39 #include "AFSCommon.h"
40
41 //
42 // Function: AFSDirControl
43 //
44 // Description:
45 //
46 //      This function is the IRP_MJ_DIRECTORY_CONTROL dispatch handler
47 //
48 // Return:
49 //
50 //       A status is returned for the handling of this request
51 //
52
53 NTSTATUS
54 AFSDirControl( IN PDEVICE_OBJECT LibDeviceObject,
55                IN PIRP Irp)
56 {
57     UNREFERENCED_PARAMETER(LibDeviceObject);
58     NTSTATUS ntStatus = STATUS_SUCCESS;
59     IO_STACK_LOCATION *pIrpSp = IoGetCurrentIrpStackLocation( Irp);
60
61     __try
62     {
63
64         switch( pIrpSp->MinorFunction )
65         {
66
67             case IRP_MN_QUERY_DIRECTORY:
68             {
69
70                 ntStatus = AFSQueryDirectory( Irp);
71
72                 break;
73             }
74
75             case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
76             {
77
78                 ntStatus = AFSNotifyChangeDirectory( Irp);
79
80                 break;
81             }
82
83             default:
84
85                 ntStatus = STATUS_INVALID_DEVICE_REQUEST;
86
87                 break;
88         }
89     }
90     __except( AFSExceptionFilter( __FUNCTION__, GetExceptionCode(), GetExceptionInformation()) )
91     {
92
93         AFSDbgLogMsg( 0,
94                       0,
95                       "EXCEPTION - AFSDirControl\n");
96
97         AFSDumpTraceFilesFnc();
98     }
99
100     if( ntStatus != STATUS_PENDING)
101     {
102
103         AFSCompleteRequest( Irp,
104                             ntStatus);
105     }
106
107     return ntStatus;
108 }
109
110 NTSTATUS
111 AFSQueryDirectory( IN PIRP Irp)
112 {
113
114     NTSTATUS ntStatus = STATUS_SUCCESS;
115     NTSTATUS dStatus = STATUS_SUCCESS;
116     AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
117     PIO_STACK_LOCATION pIrpSp;
118     AFSFcb *pFcb = NULL;
119     AFSCcb *pCcb = NULL;
120     BOOLEAN bInitialQuery = FALSE;
121     PUCHAR pBuffer;
122     ULONG ulUserBufferLength;
123     PUNICODE_STRING puniArgFileName = NULL;
124     UNICODE_STRING uniTmpMaskName;
125     WCHAR wchMaskBuffer[ 4];
126     FILE_INFORMATION_CLASS FileInformationClass;
127     ULONG ulFileIndex;
128     BOOLEAN bRestartScan;
129     BOOLEAN bReturnSingleEntry;
130     BOOLEAN bIndexSpecified;
131     ULONG ulNextEntry = 0;
132     ULONG ulLastEntry = 0;
133     PFILE_DIRECTORY_INFORMATION pDirInfo;
134     PFILE_FULL_DIR_INFORMATION pFullDirInfo;
135     PFILE_BOTH_DIR_INFORMATION pBothDirInfo;
136     PFILE_NAMES_INFORMATION pNamesInfo;
137     ULONG ulBaseLength;
138     ULONG ulBytesConverted;
139     AFSDirectoryCB *pDirEntry = NULL;
140     BOOLEAN bReleaseMain = FALSE;
141     BOOLEAN bReleaseFcb = FALSE;
142     AFSFileInfoCB       stFileInfo;
143     AFSObjectInfoCB *pObjectInfo = NULL;
144     ULONG ulAdditionalAttributes = 0;
145     LONG lCount;
146
147     __Enter
148     {
149
150         //  Get the current Stack location
151         pIrpSp = IoGetCurrentIrpStackLocation( Irp);
152
153         pFcb = (AFSFcb *)pIrpSp->FileObject->FsContext;
154         pCcb = (AFSCcb *)pIrpSp->FileObject->FsContext2;
155
156         if( pFcb == NULL)
157         {
158
159             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
160                           AFS_TRACE_LEVEL_ERROR,
161                           "AFSQueryDirectory Attempted access (%p) when pFcb == NULL\n",
162                           Irp);
163
164             try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
165         }
166
167         if( pFcb->Header.NodeTypeCode != AFS_DIRECTORY_FCB &&
168             pFcb->Header.NodeTypeCode != AFS_ROOT_FCB &&
169             pFcb->Header.NodeTypeCode != AFS_ROOT_ALL)
170         {
171
172             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
173                           AFS_TRACE_LEVEL_ERROR,
174                           "AFSQueryDirectory Attempted access (%p) to non-directory Fcb %p NodeType %u\n",
175                           Irp,
176                           pFcb,
177                           pFcb->Header.NodeTypeCode);
178
179             pFcb = NULL;
180
181             try_return( ntStatus = STATUS_INVALID_PARAMETER);
182         }
183
184         //
185         // Set the enumeration event ...
186         //
187
188         AFSSetEnumerationEvent( pFcb);
189
190         //  Reference our input parameters to make things easier
191         ulUserBufferLength = pIrpSp->Parameters.QueryDirectory.Length;
192
193         FileInformationClass = pIrpSp->Parameters.QueryDirectory.FileInformationClass;
194         ulFileIndex = pIrpSp->Parameters.QueryDirectory.FileIndex;
195
196         puniArgFileName = (PUNICODE_STRING)pIrpSp->Parameters.QueryDirectory.FileName;
197
198         bRestartScan       = BooleanFlagOn( pIrpSp->Flags, SL_RESTART_SCAN);
199         bReturnSingleEntry = BooleanFlagOn( pIrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
200         bIndexSpecified    = BooleanFlagOn( pIrpSp->Flags, SL_INDEX_SPECIFIED);
201
202         bInitialQuery = (BOOLEAN)( !BooleanFlagOn( pCcb->Flags, CCB_FLAG_DIRECTORY_QUERY_MAPPED));
203
204         if( bInitialQuery)
205         {
206
207             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
208                           AFS_TRACE_LEVEL_VERBOSE,
209                           "AFSQueryDirectory Enumerating content for parent %wZ Initial Query\n",
210                           &pCcb->DirectoryCB->NameInformation.FileName);
211
212             AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
213                           AFS_TRACE_LEVEL_VERBOSE,
214                           "AFSQueryDirectory Acquiring Dcb lock %p EXCL %08lX\n",
215                           &pFcb->NPFcb->Resource,
216                           PsGetCurrentThread());
217
218             AFSAcquireExcl( &pFcb->NPFcb->Resource,
219                             TRUE);
220
221             bReleaseFcb = TRUE;
222         }
223         else
224         {
225
226             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
227                           AFS_TRACE_LEVEL_VERBOSE,
228                           "AFSQueryDirectory Enumerating content for parent %wZ Subsequent\n",
229                           &pCcb->DirectoryCB->NameInformation.FileName);
230
231             AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
232                           AFS_TRACE_LEVEL_VERBOSE,
233                           "AFSQueryDirectory Acquiring Dcb lock %p SHARED %08lX\n",
234                           &pFcb->NPFcb->Resource,
235                           PsGetCurrentThread());
236
237             AFSAcquireShared( &pFcb->NPFcb->Resource,
238                               TRUE);
239
240             bReleaseFcb = TRUE;
241         }
242
243         //
244         // Grab the directory node hdr tree lock while parsing the directory
245         // contents
246         //
247
248         AFSAcquireExcl( pFcb->ObjectInformation->Specific.Directory.DirectoryNodeHdr.TreeLock,
249                         TRUE);
250
251         bReleaseMain = TRUE;
252
253         //
254         // Before attempting to insert the new entry, check if we need to validate the parent
255         //
256
257         if( BooleanFlagOn( pFcb->ObjectInformation->Flags, AFS_OBJECT_FLAGS_VERIFY))
258         {
259
260             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
261                           AFS_TRACE_LEVEL_VERBOSE,
262                           "AFSQueryDirectory Verifying parent %wZ FID %08lX-%08lX-%08lX-%08lX\n",
263                           &pCcb->DirectoryCB->NameInformation.FileName,
264                           pFcb->ObjectInformation->FileId.Cell,
265                           pFcb->ObjectInformation->FileId.Volume,
266                           pFcb->ObjectInformation->FileId.Vnode,
267                           pFcb->ObjectInformation->FileId.Unique);
268
269             ntStatus = AFSVerifyEntry( &pCcb->AuthGroup,
270                                        pCcb->DirectoryCB);
271
272             if( !NT_SUCCESS( ntStatus))
273             {
274
275                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
276                               AFS_TRACE_LEVEL_ERROR,
277                               "AFSQueryDirectory Failed to verify parent %wZ FID %08lX-%08lX-%08lX-%08lX Status %08lX\n",
278                               &pCcb->DirectoryCB->NameInformation.FileName,
279                               pFcb->ObjectInformation->FileId.Cell,
280                               pFcb->ObjectInformation->FileId.Volume,
281                               pFcb->ObjectInformation->FileId.Vnode,
282                               pFcb->ObjectInformation->FileId.Unique,
283                               ntStatus);
284
285                 try_return( ntStatus);
286             }
287
288             //
289             // Perform a new snapshot of the directory
290             //
291
292             AFSAcquireExcl( &pCcb->NPCcb->CcbLock,
293                             TRUE);
294
295             ntStatus = AFSSnapshotDirectory( pFcb,
296                                              pCcb,
297                                              FALSE);
298
299             AFSReleaseResource( &pCcb->NPCcb->CcbLock);
300
301             if( !NT_SUCCESS( ntStatus))
302             {
303
304                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
305                               AFS_TRACE_LEVEL_ERROR,
306                               "AFSQueryDirectory Snapshot directory failure for parent %wZ Mask %wZ Status %08lX\n",
307                               &pCcb->DirectoryCB->NameInformation.FileName,
308                               &pCcb->MaskName,
309                               ntStatus);
310
311                 try_return( ntStatus);
312             }
313         }
314
315         AFSConvertToShared( pFcb->ObjectInformation->Specific.Directory.DirectoryNodeHdr.TreeLock);
316
317         //
318         // We can now safely drop the lock on the node
319         //
320
321         AFSReleaseResource( &pFcb->NPFcb->Resource);
322
323         bReleaseFcb = FALSE;
324
325         //
326         // Start processing the data
327         //
328
329         pBuffer = (PUCHAR)AFSLockSystemBuffer( Irp,
330                                                ulUserBufferLength);
331
332         if( pBuffer == NULL)
333         {
334
335             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
336         }
337
338         AFSAcquireExcl( &pCcb->NPCcb->CcbLock,
339                         TRUE);
340
341         // Check if initial on this map
342         if( bInitialQuery)
343         {
344
345             ntStatus = AFSSnapshotDirectory( pFcb,
346                                              pCcb,
347                                              TRUE);
348
349             if( !NT_SUCCESS( ntStatus))
350             {
351
352                 AFSReleaseResource( &pCcb->NPCcb->CcbLock);
353
354                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
355                               AFS_TRACE_LEVEL_ERROR,
356                               "AFSQueryDirectory Snapshot directory failure for parent %wZ Mask %wZ Status %08lX\n",
357                               &pCcb->DirectoryCB->NameInformation.FileName,
358                               &pCcb->MaskName,
359                               ntStatus);
360
361                 try_return( ntStatus);
362             }
363
364             SetFlag( pCcb->Flags, CCB_FLAG_DIRECTORY_QUERY_MAPPED);
365
366             ClearFlag( pCcb->Flags, CCB_FLAG_DIR_OF_DIRS_ONLY);
367
368             // build mask if none
369             if( puniArgFileName == NULL)
370             {
371                 puniArgFileName = &uniTmpMaskName;
372                 puniArgFileName->Length = 0;
373                 puniArgFileName->Buffer = NULL;
374             }
375
376             if( puniArgFileName->Length == 0)
377             {
378
379                 puniArgFileName->Length = sizeof(WCHAR);
380                 puniArgFileName->MaximumLength = (USHORT)4;
381             }
382
383             if( puniArgFileName->Buffer == NULL)
384             {
385
386                 puniArgFileName->Buffer = wchMaskBuffer;
387
388                 RtlZeroMemory( wchMaskBuffer,
389                                4);
390
391                 RtlCopyMemory( &puniArgFileName->Buffer[ 0],
392                                L"*",
393                                sizeof(WCHAR));
394             }
395
396             if( (( puniArgFileName->Length == sizeof(WCHAR)) &&
397                  ( puniArgFileName->Buffer[0] == L'*')))
398             {
399
400                 SetFlag( pCcb->Flags, CCB_FLAG_FULL_DIRECTORY_QUERY);
401             }
402             else
403             {
404
405                 if( (( puniArgFileName->Length == sizeof(WCHAR)) &&
406                      ( puniArgFileName->Buffer[0] == L'<')) ||
407                     (( puniArgFileName->Length == 2*sizeof(WCHAR)) &&
408                     ( RtlEqualMemory( puniArgFileName->Buffer, L"*.", 2*sizeof(WCHAR) ))))
409                 {
410
411                     SetFlag( pCcb->Flags, CCB_FLAG_DIR_OF_DIRS_ONLY);
412                 }
413
414                 //
415                 // Build the name for procesisng
416                 //
417
418                 pCcb->MaskName.Length = puniArgFileName->Length;
419                 pCcb->MaskName.MaximumLength = pCcb->MaskName.Length;
420
421                 pCcb->MaskName.Buffer = (WCHAR *)AFSExAllocatePoolWithTag( PagedPool,
422                                                                            pCcb->MaskName.Length,
423                                                                            AFS_GENERIC_MEMORY_6_TAG);
424
425                 if( pCcb->MaskName.Buffer == NULL)
426                 {
427
428                     try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
429                 }
430
431                 if( FsRtlDoesNameContainWildCards( puniArgFileName))
432                 {
433
434                     RtlUpcaseUnicodeString( &pCcb->MaskName,
435                                             puniArgFileName,
436                                             FALSE);
437
438                     SetFlag( pCcb->Flags, CCB_FLAG_MASK_CONTAINS_WILD_CARDS);
439
440                     if( FsRtlIsNameInExpression( &pCcb->MaskName,
441                                                  &AFSPIOCtlName,
442                                                  TRUE,
443                                                  NULL))
444                     {
445                         SetFlag( pCcb->Flags, CCB_FLAG_MASK_PIOCTL_QUERY);
446                     }
447                 }
448                 else
449                 {
450
451                     RtlCopyMemory( pCcb->MaskName.Buffer,
452                                    puniArgFileName->Buffer,
453                                    pCcb->MaskName.Length);
454
455                     if( RtlCompareUnicodeString( &AFSPIOCtlName,
456                                                  &pCcb->MaskName,
457                                                  TRUE) == 0)
458                     {
459                         SetFlag( pCcb->Flags, CCB_FLAG_MASK_PIOCTL_QUERY);
460                     }
461                 }
462
463                 if( BooleanFlagOn( pCcb->Flags, CCB_FLAG_MASK_PIOCTL_QUERY))
464                 {
465                     if( pFcb->ObjectInformation->Specific.Directory.PIOCtlDirectoryCB == NULL)
466                     {
467
468                         AFSReleaseResource( &pCcb->NPCcb->CcbLock);
469
470                         AFSReleaseResource( pFcb->ObjectInformation->Specific.Directory.DirectoryNodeHdr.TreeLock);
471
472                         bReleaseMain = FALSE;
473
474                         AFSAcquireExcl( &pFcb->NPFcb->Resource,
475                                         TRUE);
476
477                         if( pFcb->ObjectInformation->Specific.Directory.PIOCtlDirectoryCB == NULL)
478                         {
479
480                             ntStatus = AFSInitPIOCtlDirectoryCB( pFcb->ObjectInformation);
481
482                             if( !NT_SUCCESS( ntStatus))
483                             {
484
485                                 AFSReleaseResource( &pFcb->NPFcb->Resource);
486
487                                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
488                                               AFS_TRACE_LEVEL_ERROR,
489                                               "AFSQueryDirectory Init PIOCtl directory failure for parent %wZ Mask %wZ Status %08lX\n",
490                                               &pCcb->DirectoryCB->NameInformation.FileName,
491                                               &pCcb->MaskName,
492                                               ntStatus);
493
494                                 try_return( ntStatus);
495                             }
496                         }
497
498                         AFSAcquireShared( pFcb->ObjectInformation->Specific.Directory.DirectoryNodeHdr.TreeLock,
499                                           TRUE);
500
501                         bReleaseMain = TRUE;
502
503                         AFSReleaseResource( &pFcb->NPFcb->Resource);
504
505                         AFSAcquireExcl( &pCcb->NPCcb->CcbLock,
506                                         TRUE);
507                     }
508                 }
509
510                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
511                               AFS_TRACE_LEVEL_VERBOSE,
512                               "AFSQueryDirectory Enumerating content for parent %wZ Mask %wZ\n",
513                               &pCcb->DirectoryCB->NameInformation.FileName,
514                               &pCcb->MaskName);
515             }
516         }
517
518         // Check if we need to start from index
519         if( bIndexSpecified)
520         {
521
522             //
523             // Need to set up the initial point for the query
524             //
525
526             pCcb->CurrentDirIndex = ulFileIndex - 1;
527         }
528
529         // Check if we need to restart the scan
530         else if( bRestartScan)
531         {
532
533             //
534             // Reset the current scan processing
535             //
536
537             if( BooleanFlagOn( pCcb->Flags, CCB_FLAG_RETURN_RELATIVE_ENTRIES))
538             {
539
540                 pCcb->CurrentDirIndex = AFS_DIR_ENTRY_INITIAL_DIR_INDEX;
541             }
542             else
543             {
544
545                 pCcb->CurrentDirIndex = AFS_DIR_ENTRY_INITIAL_ROOT_INDEX;
546             }
547         }
548
549         AFSReleaseResource( &pCcb->NPCcb->CcbLock);
550
551         AFSReleaseResource( pFcb->ObjectInformation->Specific.Directory.DirectoryNodeHdr.TreeLock);
552
553         bReleaseMain = FALSE;
554
555         switch( FileInformationClass)
556         {
557
558             case FileDirectoryInformation:
559
560                 ulBaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
561                                              FileName[0] );
562                 break;
563
564             case FileFullDirectoryInformation:
565
566                 ulBaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
567                                              FileName[0] );
568                 break;
569
570             case FileNamesInformation:
571
572                 ulBaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
573                                              FileName[0] );
574                 break;
575
576             case FileBothDirectoryInformation:
577
578                 ulBaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
579                                              FileName[0] );
580                 break;
581
582             case FileIdBothDirectoryInformation:
583
584                 ulBaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
585                                              FileName[0] );
586
587                 break;
588
589             case FileIdFullDirectoryInformation:
590
591                 ulBaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION,
592                                              FileName[0] );
593
594                 break;
595
596             default:
597
598                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
599                               AFS_TRACE_LEVEL_ERROR,
600                               "AFSQueryDirectory (%p) Unknown FileInformationClass %u\n",
601                               Irp,
602                               FileInformationClass);
603
604                 try_return( ntStatus = STATUS_INVALID_INFO_CLASS);
605         }
606
607         while( TRUE)
608         {
609
610             ULONG ulBytesRemainingInBuffer;
611
612             //
613             // Drop the DirOpenReferenceCount held during a prior
614             // execution of the loop
615             //
616
617             if ( pDirEntry != NULL)
618             {
619
620                 lCount = InterlockedDecrement( &pDirEntry->DirOpenReferenceCount);
621
622                 AFSDbgLogMsg( AFS_SUBSYSTEM_DIRENTRY_REF_COUNTING,
623                               AFS_TRACE_LEVEL_VERBOSE,
624                               "AFSQueryDirectory Decrement count on %wZ DE %p Ccb %p Cnt %d\n",
625                               &pDirEntry->NameInformation.FileName,
626                               pDirEntry,
627                               pCcb,
628                               lCount);
629
630                 ASSERT( lCount >= 0);
631
632                 pDirEntry = NULL;
633             }
634
635             ulAdditionalAttributes = 0;
636
637             //
638             //  If the user had requested only a single match and we have
639             //  returned that, then we stop at this point.
640             //
641
642             if( bReturnSingleEntry && ulNextEntry != 0)
643             {
644
645                 try_return( ntStatus);
646             }
647
648             //
649             // On Success, pDirEntry has a held DirOpenReferenceCount
650             //
651
652             pDirEntry = AFSLocateNextDirEntry( pFcb->ObjectInformation,
653                                                pCcb);
654
655             if( pDirEntry == NULL)
656             {
657
658                 if( ulNextEntry == 0)
659                 {
660
661                     if( ( bInitialQuery ||
662                           bRestartScan) &&
663                         pCcb->MaskName.Buffer != NULL)
664                     {
665                         ntStatus = STATUS_NO_SUCH_FILE;
666                     }
667                     else
668                     {
669                         ntStatus = STATUS_NO_MORE_FILES;
670                     }
671                 }
672
673                 try_return( ntStatus);
674             }
675
676             //
677             // Skip the entry if it is pending delete or deleted
678             //
679
680             else if( BooleanFlagOn( pDirEntry->Flags, AFS_DIR_ENTRY_PENDING_DELETE) ||
681                      BooleanFlagOn( pDirEntry->Flags, AFS_DIR_ENTRY_DELETED))
682             {
683
684                 continue;
685             }
686
687             pObjectInfo = pDirEntry->ObjectInformation;
688
689             //
690             // Apply the name filter if there is one
691             //
692
693             if( !BooleanFlagOn( pCcb->Flags, CCB_FLAG_FULL_DIRECTORY_QUERY))
694             {
695
696                 //
697                 // Only returning directories?
698                 //
699
700                 if( BooleanFlagOn( pCcb->Flags, CCB_FLAG_DIR_OF_DIRS_ONLY))
701                 {
702
703                     if( !FlagOn( pObjectInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY))
704                     {
705
706                         continue;
707                     }
708                 }
709                 else if( !BooleanFlagOn( pCcb->Flags, CCB_FLAG_MASK_PIOCTL_QUERY))
710                 {
711
712                     //
713                     // Are we doing a wild card search?
714                     //
715
716                     if( BooleanFlagOn( pCcb->Flags, CCB_FLAG_MASK_CONTAINS_WILD_CARDS))
717                     {
718
719                         if( !FsRtlIsNameInExpression( &pCcb->MaskName,
720                                                       &pDirEntry->NameInformation.FileName,
721                                                       TRUE,
722                                                       NULL))
723                         {
724
725                             continue;
726                         }
727                     }
728                     else
729                     {
730
731                         if( RtlCompareUnicodeString( &pDirEntry->NameInformation.FileName,
732                                                      &pCcb->MaskName,
733                                                      FALSE))
734                         {
735
736                             //
737                             // See if this is a match for a case insensitive search
738                             //
739
740                             if( RtlCompareUnicodeString( &pDirEntry->NameInformation.FileName,
741                                                          &pCcb->MaskName,
742                                                          TRUE))
743                             {
744
745                                 continue;
746                             }
747                         }
748                     }
749                 }
750             }
751
752             //
753             // Be sure the information is valid
754             // We don't worry about entries while enumerating the directory
755             //
756
757             if ( BooleanFlagOn( pDirEntry->ObjectInformation->Flags, AFS_OBJECT_FLAGS_VERIFY))
758             {
759
760                 ntStatus = AFSValidateEntry( pDirEntry,
761                                              &pCcb->AuthGroup,
762                                              FALSE,
763                                              FALSE);
764                 if ( NT_SUCCESS( ntStatus))
765                 {
766
767                     ClearFlag( pDirEntry->ObjectInformation->Flags, AFS_OBJECT_FLAGS_VERIFY);
768                 }
769                 else
770                 {
771
772                     ntStatus = STATUS_SUCCESS;
773                 }
774             }
775
776             pObjectInfo = pDirEntry->ObjectInformation;
777
778             //  Here are the rules concerning filling up the buffer:
779             //
780             //  1.  The Io system guarantees that there will always be
781             //      enough room for at least one base record.
782             //
783             //  2.  If the full first record (including file name) cannot
784             //      fit, as much of the name as possible is copied and
785             //      STATUS_BUFFER_OVERFLOW is returned.
786             //
787             //  3.  If a subsequent record cannot completely fit into the
788             //      buffer, none of it (as in 0 bytes) is copied, and
789             //      STATUS_SUCCESS is returned.  A subsequent query will
790             //      pick up with this record.
791
792             ulBytesRemainingInBuffer = ulUserBufferLength - ulNextEntry;
793
794             if( ( ulNextEntry != 0) &&
795                 ( ( ulBaseLength + pDirEntry->NameInformation.FileName.Length > ulBytesRemainingInBuffer) ||
796                   ( ulUserBufferLength < ulNextEntry) ) )
797             {
798
799                 //
800                 // Back off our current index
801                 //
802
803                 pCcb->CurrentDirIndex--;
804
805                 try_return( ntStatus = STATUS_SUCCESS);
806             }
807
808
809             //
810             // For Symlinks and Mount Points the reparse point attribute
811             // must be associated with the directory entry.  In addition,
812             // for Symlinks it must be determined if the target object is
813             // a directory or not.  If so, the directory attribute must be
814             // specified.  Mount points always refer to directories and
815             // must have the directory attribute set.
816             //
817
818             switch( pObjectInfo->FileType)
819             {
820
821             case AFS_FILE_TYPE_MOUNTPOINT:
822             case AFS_FILE_TYPE_DFSLINK:
823             {
824
825                 ulAdditionalAttributes = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT;
826
827                 break;
828             }
829
830             case AFS_FILE_TYPE_SYMLINK:
831             {
832
833                 //
834                 // Go grab the file information for this entry
835                 // No worries on failures since we will just display
836                 // pseudo information
837                 //
838
839                 RtlZeroMemory( &stFileInfo,
840                                sizeof( AFSFileInfoCB));
841
842                 if( NT_SUCCESS( AFSRetrieveFileAttributes( pCcb->DirectoryCB,
843                                                            pDirEntry,
844                                                            &pCcb->FullFileName,
845                                                            pCcb->NameArray,
846                                                            &pCcb->AuthGroup,
847                                                            &stFileInfo)))
848                 {
849
850                     if ( stFileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
851                     {
852
853                         ulAdditionalAttributes = FILE_ATTRIBUTE_DIRECTORY;
854                     }
855                 }
856
857                 ulAdditionalAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
858
859                 break;
860             }
861             }
862
863             //
864             // Check if the name begins with a . and we are hiding them
865             //
866
867             if( !BooleanFlagOn( pDirEntry->Flags, AFS_DIR_ENTRY_FAKE) &&
868                 pDirEntry->NameInformation.FileName.Buffer[ 0] == L'.' &&
869                 BooleanFlagOn( pDeviceExt->DeviceFlags, AFS_DEVICE_FLAG_HIDE_DOT_NAMES))
870             {
871
872                 ulAdditionalAttributes |= FILE_ATTRIBUTE_HIDDEN;
873             }
874
875
876             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
877                           AFS_TRACE_LEVEL_VERBOSE,
878                           "AFSQueryDirectory Insert into parent %wZ Entry %wZ\n",
879                           &pCcb->DirectoryCB->NameInformation.FileName,
880                           &pDirEntry->NameInformation.FileName);
881
882             //  Zero the base part of the structure.
883             RtlZeroMemory( &pBuffer[ ulNextEntry],
884                            ulBaseLength);
885
886             switch( FileInformationClass)
887             {
888
889                 //  Now fill the base parts of the structure that are applicable.
890                 case FileIdBothDirectoryInformation:
891                 case FileBothDirectoryInformation:
892                 {
893                     pBothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&pBuffer[ ulNextEntry];
894
895                     pBothDirInfo->ShortNameLength = (CHAR)pDirEntry->NameInformation.ShortNameLength;
896
897                     if( pDirEntry->NameInformation.ShortNameLength > 0)
898                     {
899                         RtlCopyMemory( &pBothDirInfo->ShortName[ 0],
900                                        &pDirEntry->NameInformation.ShortName[ 0],
901                                        pBothDirInfo->ShortNameLength);
902                     }
903                 }
904                 case FileIdFullDirectoryInformation:
905                 case FileFullDirectoryInformation:
906                 {
907                     pFullDirInfo = (PFILE_FULL_DIR_INFORMATION)&pBuffer[ ulNextEntry];
908                     pFullDirInfo->EaSize = 0;
909                 }
910                 case FileDirectoryInformation:
911                 {
912                     pDirInfo = (PFILE_DIRECTORY_INFORMATION)&pBuffer[ ulNextEntry];
913
914                     if( BooleanFlagOn( pDirEntry->Flags, AFS_DIR_ENTRY_FAKE))
915                     {
916
917                         pDirInfo->CreationTime = pFcb->ObjectInformation->CreationTime;
918                         pDirInfo->LastWriteTime = pFcb->ObjectInformation->LastWriteTime;
919                         pDirInfo->LastAccessTime = pFcb->ObjectInformation->LastAccessTime;
920                         pDirInfo->ChangeTime = pFcb->ObjectInformation->ChangeTime;
921
922                         pDirInfo->EndOfFile = pFcb->ObjectInformation->EndOfFile;
923                         pDirInfo->AllocationSize = pFcb->ObjectInformation->AllocationSize;
924
925                         if( BooleanFlagOn( pCcb->Flags, CCB_FLAG_MASK_PIOCTL_QUERY))
926                         {
927                             pDirInfo->FileAttributes = pObjectInfo->FileAttributes;
928                         }
929                         else
930                         {
931                             pDirInfo->FileAttributes = pFcb->ObjectInformation->FileAttributes;
932                         }
933                     }
934                     else
935                     {
936
937                         pDirInfo->CreationTime = pObjectInfo->CreationTime;
938                         pDirInfo->LastWriteTime = pObjectInfo->LastWriteTime;
939                         pDirInfo->LastAccessTime = pObjectInfo->LastAccessTime;
940                         pDirInfo->ChangeTime = pObjectInfo->ChangeTime;
941
942                         pDirInfo->EndOfFile = pObjectInfo->EndOfFile;
943                         pDirInfo->AllocationSize = pObjectInfo->AllocationSize;
944
945                         if ( ulAdditionalAttributes && pObjectInfo->FileAttributes == FILE_ATTRIBUTE_NORMAL)
946                         {
947
948                             pDirInfo->FileAttributes = ulAdditionalAttributes;
949                         }
950                         else
951                         {
952
953                             pDirInfo->FileAttributes = pObjectInfo->FileAttributes | ulAdditionalAttributes;
954                         }
955                     }
956
957                     pDirInfo->FileIndex = pDirEntry->FileIndex;
958                     pDirInfo->FileNameLength = pDirEntry->NameInformation.FileName.Length;
959
960                     break;
961                 }
962
963                 case FileNamesInformation:
964                 {
965                     pNamesInfo = (PFILE_NAMES_INFORMATION)&pBuffer[ ulNextEntry];
966                     pNamesInfo->FileIndex = pDirEntry->FileIndex;
967                     pNamesInfo->FileNameLength = pDirEntry->NameInformation.FileName.Length;
968
969                     break;
970                 }
971
972                 default:
973                 {
974                     AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
975                                   AFS_TRACE_LEVEL_ERROR,
976                                   "AFSQueryDirectory (%p) Unknown FileInformationClass %u\n",
977                                   Irp,
978                                   FileInformationClass);
979
980                     try_return( ntStatus = STATUS_INVALID_INFO_CLASS);
981                 }
982             }
983
984             ulBytesConverted = ulBytesRemainingInBuffer - ulBaseLength >= pDirEntry->NameInformation.FileName.Length ?
985                                             pDirEntry->NameInformation.FileName.Length :
986                                             ulBytesRemainingInBuffer - ulBaseLength;
987
988             RtlCopyMemory( &pBuffer[ ulNextEntry + ulBaseLength],
989                            pDirEntry->NameInformation.FileName.Buffer,
990                            ulBytesConverted);
991
992             //  Set up the previous next entry offset
993             *((PULONG)(&pBuffer[ ulLastEntry])) = ulNextEntry - ulLastEntry;
994
995             //  And indicate how much of the user buffer we have currently
996             //  used up.
997             Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information) + ulBaseLength + ulBytesConverted;
998
999             //  Check for the case that a single entry doesn't fit.
1000             //  This should only get this far on the first entry.
1001             if( ulBytesConverted < pDirEntry->NameInformation.FileName.Length)
1002             {
1003
1004                 try_return( ntStatus = STATUS_BUFFER_OVERFLOW);
1005             }
1006
1007             dStatus = STATUS_SUCCESS;
1008
1009             //  Set ourselves up for the next iteration
1010             ulLastEntry = ulNextEntry;
1011             ulNextEntry += (ULONG)QuadAlign( ulBaseLength + ulBytesConverted);
1012         }
1013
1014 try_exit:
1015
1016         if ( pDirEntry != NULL)
1017         {
1018
1019             lCount = InterlockedDecrement( &pDirEntry->DirOpenReferenceCount);
1020
1021             AFSDbgLogMsg( AFS_SUBSYSTEM_DIRENTRY_REF_COUNTING,
1022                           AFS_TRACE_LEVEL_VERBOSE,
1023                           "AFSQueryDirectory Decrement8 count on %wZ DE %p Ccb %p Cnt %d\n",
1024                           &pDirEntry->NameInformation.FileName,
1025                           pDirEntry,
1026                           pCcb,
1027                           lCount);
1028
1029             ASSERT( lCount >= 0);
1030         }
1031
1032         if( bReleaseMain)
1033         {
1034
1035             AFSReleaseResource( pFcb->ObjectInformation->Specific.Directory.DirectoryNodeHdr.TreeLock);
1036         }
1037
1038         if ( bReleaseFcb)
1039         {
1040
1041             AFSReleaseResource( &pFcb->NPFcb->Resource);
1042         }
1043
1044         if( pFcb != NULL)
1045         {
1046
1047             AFSClearEnumerationEvent( pFcb);
1048         }
1049     }
1050
1051     return ntStatus;
1052 }
1053
1054 NTSTATUS
1055 AFSNotifyChangeDirectory( IN PIRP Irp)
1056 {
1057
1058     NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
1059     PIO_STACK_LOCATION pIrpSp;
1060     AFSFcb *pFcb = NULL;
1061     AFSCcb *pCcb = NULL;
1062     ULONG ulCompletionFilter;
1063     BOOLEAN bWatchTree;
1064     BOOLEAN bReleaseLock = FALSE;
1065
1066     __Enter
1067     {
1068
1069         //  Get the current Stack location
1070         pIrpSp = IoGetCurrentIrpStackLocation( Irp );
1071
1072         pFcb = (AFSFcb *)pIrpSp->FileObject->FsContext;
1073         pCcb = (AFSCcb *)pIrpSp->FileObject->FsContext2;
1074
1075         if( pFcb == NULL)
1076         {
1077
1078             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1079                           AFS_TRACE_LEVEL_ERROR,
1080                           "AFSNotifyChangeDirectory Attempted access (%p) when pFcb == NULL\n",
1081                           Irp);
1082
1083             try_return( ntStatus = STATUS_INVALID_DEVICE_REQUEST);
1084         }
1085
1086         if( pFcb->Header.NodeTypeCode != AFS_DIRECTORY_FCB &&
1087             pFcb->Header.NodeTypeCode != AFS_ROOT_FCB &&
1088             pFcb->Header.NodeTypeCode != AFS_ROOT_ALL)
1089         {
1090
1091             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1092                           AFS_TRACE_LEVEL_ERROR,
1093                           "AFSNotifyChangeDirectory NodeTypeCode !AFS_DIRECTORY_FCB && !AFS_ROOT_FCB %wZ NodeTypeCode 0x%x\n",
1094                           &pCcb->DirectoryCB->NameInformation.FileName,
1095                           pFcb->Header.NodeTypeCode);
1096
1097             try_return( ntStatus = STATUS_INVALID_PARAMETER);
1098         }
1099
1100         //  Reference our input parameter to make things easier
1101         ulCompletionFilter = pIrpSp->Parameters.NotifyDirectory.CompletionFilter;
1102         bWatchTree = BooleanFlagOn( pIrpSp->Flags, SL_WATCH_TREE);
1103
1104         AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
1105                       AFS_TRACE_LEVEL_VERBOSE,
1106                       "AFSNotifyChangeDirectory Acquiring Dcb lock %p EXCL %08lX\n",
1107                       &pFcb->NPFcb->Resource,
1108                       PsGetCurrentThread());
1109
1110         AFSAcquireExcl( &pFcb->NPFcb->Resource,
1111                           TRUE);
1112
1113         bReleaseLock = TRUE;
1114
1115         //
1116         // Check if the node has already been deleted
1117         //
1118
1119         if( BooleanFlagOn( pFcb->ObjectInformation->Flags, AFS_OBJECT_FLAGS_DELETED))
1120         {
1121
1122             try_return( ntStatus = STATUS_FILE_DELETED);
1123         }
1124         else if( BooleanFlagOn( pCcb->DirectoryCB->Flags, AFS_DIR_ENTRY_PENDING_DELETE))
1125         {
1126
1127             try_return( ntStatus = STATUS_DELETE_PENDING);
1128         }
1129
1130         //  Call the Fsrtl package to process the request.
1131         ntStatus = AFSFsRtlNotifyFullChangeDirectory( pFcb->ObjectInformation,
1132                                                       pCcb,
1133                                                       bWatchTree,
1134                                                       ulCompletionFilter,
1135                                                       Irp);
1136
1137         if( !NT_SUCCESS( ntStatus))
1138         {
1139             try_return( ntStatus);
1140         }
1141
1142         ntStatus = STATUS_PENDING;
1143
1144 try_exit:
1145
1146         if( bReleaseLock)
1147         {
1148
1149             AFSReleaseResource( &pFcb->NPFcb->Resource);
1150         }
1151     }
1152
1153     return ntStatus;
1154 }
1155
1156 AFSDirectoryCB *
1157 AFSLocateNextDirEntry( IN AFSObjectInfoCB *ObjectInfo,
1158                        IN AFSCcb *Ccb)
1159 {
1160
1161     AFSDirectoryCB *pDirEntry = NULL;
1162     NTSTATUS ntStatus = STATUS_SUCCESS;
1163     AFSSnapshotHdr *pSnapshotHdr = NULL;
1164     AFSSnapshotEntry *pSnapshotEntry = NULL;
1165     ULONG ulCount = 0;
1166     LONG lCount;
1167
1168     __Enter
1169     {
1170
1171         AFSAcquireShared( ObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock,
1172                           TRUE);
1173
1174         AFSAcquireExcl( &Ccb->NPCcb->CcbLock,
1175                         TRUE);
1176
1177         //
1178         // Is this a PIOCtl query
1179         //
1180
1181         if( BooleanFlagOn( Ccb->Flags, CCB_FLAG_MASK_PIOCTL_QUERY))
1182         {
1183
1184             if( Ccb->CurrentDirIndex == (ULONG)AFS_DIR_ENTRY_INITIAL_DIR_INDEX ||
1185                 Ccb->CurrentDirIndex == (ULONG)AFS_DIR_ENTRY_INITIAL_ROOT_INDEX)
1186             {
1187
1188                 pDirEntry = ObjectInfo->Specific.Directory.PIOCtlDirectoryCB;
1189
1190                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1191                               AFS_TRACE_LEVEL_VERBOSE,
1192                               "AFSLocateNextDirEntry Returning PIOctl entry %wZ in parent FID %08lX-%08lX-%08lX-%08lX\n",
1193                               &pDirEntry->NameInformation.FileName,
1194                               ObjectInfo->FileId.Cell,
1195                               ObjectInfo->FileId.Volume,
1196                               ObjectInfo->FileId.Vnode,
1197                               ObjectInfo->FileId.Unique);
1198             }
1199
1200             Ccb->CurrentDirIndex++;
1201
1202             try_return( ntStatus);
1203         }
1204
1205         Ccb->CurrentDirIndex++;
1206
1207         if( Ccb->CurrentDirIndex == (ULONG)AFS_DIR_ENTRY_DOT_INDEX)
1208         {
1209
1210             //
1211             // Return the .. entry
1212             //
1213
1214             pDirEntry = AFSGlobalDotDirEntry;
1215
1216             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1217                           AFS_TRACE_LEVEL_VERBOSE,
1218                           "AFSLocateNextDirEntry Returning1 snapshot entry %wZ in parent FID %08lX-%08lX-%08lX-%08lX\n",
1219                           &pDirEntry->NameInformation.FileName,
1220                           ObjectInfo->FileId.Cell,
1221                           ObjectInfo->FileId.Volume,
1222                           ObjectInfo->FileId.Vnode,
1223                           ObjectInfo->FileId.Unique);
1224         }
1225         else if( Ccb->CurrentDirIndex == (ULONG)AFS_DIR_ENTRY_DOT_DOT_INDEX)
1226         {
1227
1228             //
1229             // Return the .. entry
1230             //
1231
1232             pDirEntry = AFSGlobalDotDotDirEntry;
1233
1234             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1235                           AFS_TRACE_LEVEL_VERBOSE,
1236                           "AFSLocateNextDirEntry Returning2 snapshot entry %wZ in parent FID %08lX-%08lX-%08lX-%08lX\n",
1237                           &pDirEntry->NameInformation.FileName,
1238                           ObjectInfo->FileId.Cell,
1239                           ObjectInfo->FileId.Volume,
1240                           ObjectInfo->FileId.Vnode,
1241                           ObjectInfo->FileId.Unique);
1242         }
1243         else
1244         {
1245
1246             pSnapshotHdr = Ccb->DirectorySnapshot;
1247
1248             if( pSnapshotHdr == NULL ||
1249                 Ccb->CurrentDirIndex >= pSnapshotHdr->EntryCount)
1250             {
1251
1252                 try_return( ntStatus);
1253             }
1254
1255             pSnapshotEntry = &pSnapshotHdr->TopEntry[ Ccb->CurrentDirIndex];
1256
1257             ulCount = Ccb->CurrentDirIndex;
1258
1259             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1260                           AFS_TRACE_LEVEL_VERBOSE,
1261                           "AFSLocateNextDirEntry CurrentDirIndex %08lX\n",
1262                           ulCount);
1263
1264             //
1265             // Get to a valid entry
1266             //
1267
1268             while( ulCount < pSnapshotHdr->EntryCount)
1269             {
1270
1271                 pDirEntry = NULL;
1272
1273                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1274                               AFS_TRACE_LEVEL_VERBOSE,
1275                               "AFSLocateNextDirEntry Searching for hash %08lX\n",
1276                               pSnapshotEntry->NameHash);
1277
1278                 if( pSnapshotEntry->NameHash == 0)
1279                 {
1280
1281                     break;
1282                 }
1283
1284                 ntStatus = AFSLocateCaseSensitiveDirEntry( ObjectInfo->Specific.Directory.DirectoryNodeHdr.CaseSensitiveTreeHead,
1285                                                            pSnapshotEntry->NameHash,
1286                                                            &pDirEntry);
1287
1288                 if( !NT_SUCCESS( ntStatus) ||
1289                     pDirEntry != NULL)
1290                 {
1291
1292                     if( pDirEntry != NULL)
1293                     {
1294
1295                         AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1296                                       AFS_TRACE_LEVEL_VERBOSE,
1297                                       "AFSLocateNextDirEntry Returning3 snapshot entry %wZ (%08lX) in parent FID %08lX-%08lX-%08lX-%08lX\n",
1298                                       &pDirEntry->NameInformation.FileName,
1299                                       (ULONG)pDirEntry->CaseInsensitiveTreeEntry.HashIndex,
1300                                       ObjectInfo->FileId.Cell,
1301                                       ObjectInfo->FileId.Volume,
1302                                       ObjectInfo->FileId.Vnode,
1303                                       ObjectInfo->FileId.Unique);
1304                     }
1305                     else
1306                     {
1307
1308
1309                         AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1310                                       AFS_TRACE_LEVEL_VERBOSE,
1311                                       "AFSLocateNextDirEntry Returning3 NO snapshot entry in parent FID %08lX-%08lX-%08lX-%08lX\n",
1312                                       ObjectInfo->FileId.Cell,
1313                                       ObjectInfo->FileId.Volume,
1314                                       ObjectInfo->FileId.Vnode,
1315                                       ObjectInfo->FileId.Unique);
1316                     }
1317
1318                     break;
1319                 }
1320
1321                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1322                               AFS_TRACE_LEVEL_VERBOSE,
1323                               "AFSLocateNextDirEntry Entry %08lX not found in parent FID %08lX-%08lX-%08lX-%08lX\n",
1324                               pSnapshotEntry->NameHash,
1325                               ObjectInfo->FileId.Cell,
1326                               ObjectInfo->FileId.Volume,
1327                               ObjectInfo->FileId.Vnode,
1328                               ObjectInfo->FileId.Unique);
1329
1330                 pSnapshotEntry++;
1331
1332                 ulCount++;
1333
1334                 Ccb->CurrentDirIndex++;
1335             }
1336         }
1337
1338 try_exit:
1339
1340         if( pDirEntry != NULL)
1341         {
1342
1343             lCount = InterlockedIncrement( &pDirEntry->DirOpenReferenceCount);
1344
1345             AFSDbgLogMsg( AFS_SUBSYSTEM_DIRENTRY_REF_COUNTING,
1346                           AFS_TRACE_LEVEL_VERBOSE,
1347                           "AFSLocateNextDirEntry Increment count on %wZ DE %p Ccb %p Cnt %d\n",
1348                           &pDirEntry->NameInformation.FileName,
1349                           pDirEntry,
1350                           Ccb,
1351                           lCount);
1352
1353             ASSERT( lCount >= 0);
1354         }
1355
1356         AFSReleaseResource( &Ccb->NPCcb->CcbLock);
1357
1358         AFSReleaseResource( ObjectInfo->Specific.Directory.DirectoryNodeHdr.TreeLock);
1359     }
1360
1361     return pDirEntry;
1362 }
1363
1364 AFSDirectoryCB *
1365 AFSLocateDirEntryByIndex( IN AFSObjectInfoCB *ObjectInfo,
1366                           IN AFSCcb *Ccb,
1367                           IN ULONG DirIndex)
1368 {
1369
1370     AFSDirectoryCB *pDirEntry = NULL;
1371     NTSTATUS ntStatus = STATUS_SUCCESS;
1372     AFSSnapshotHdr *pSnapshotHdr = NULL;
1373     AFSSnapshotEntry *pSnapshotEntry = NULL;
1374     ULONG ulCount = 0;
1375
1376     __Enter
1377     {
1378
1379         AFSAcquireExcl( &Ccb->NPCcb->CcbLock,
1380                         TRUE);
1381
1382         Ccb->CurrentDirIndex = DirIndex;
1383
1384         if( DirIndex == (ULONG)AFS_DIR_ENTRY_DOT_INDEX)
1385         {
1386
1387             //
1388             // Return the .. entry
1389             //
1390
1391             pDirEntry = AFSGlobalDotDirEntry;
1392         }
1393         else if( DirIndex == (ULONG)AFS_DIR_ENTRY_DOT_DOT_INDEX)
1394         {
1395
1396             //
1397             // Return the .. entry
1398             //
1399
1400             pDirEntry = AFSGlobalDotDotDirEntry;
1401         }
1402         else
1403         {
1404
1405             pSnapshotHdr = Ccb->DirectorySnapshot;
1406
1407             if( pSnapshotHdr == NULL ||
1408                 Ccb->CurrentDirIndex >= pSnapshotHdr->EntryCount)
1409             {
1410
1411                 try_return( ntStatus);
1412             }
1413
1414             pSnapshotEntry = &pSnapshotHdr->TopEntry[ Ccb->CurrentDirIndex];
1415
1416             ulCount = Ccb->CurrentDirIndex;
1417
1418             //
1419             // Get to a valid entry
1420             //
1421
1422             while( ulCount < pSnapshotHdr->EntryCount)
1423             {
1424
1425                 pDirEntry = NULL;
1426
1427                 ntStatus = AFSLocateCaseSensitiveDirEntry( ObjectInfo->Specific.Directory.DirectoryNodeHdr.CaseSensitiveTreeHead,
1428                                                            pSnapshotEntry->NameHash,
1429                                                            &pDirEntry);
1430
1431                 if( !NT_SUCCESS( ntStatus) ||
1432                     ( pDirEntry != NULL &&
1433                       pDirEntry->FileIndex == DirIndex))
1434                 {
1435
1436                     break;
1437                 }
1438
1439                 pSnapshotEntry++;
1440
1441                 ulCount++;
1442             }
1443
1444             if( pDirEntry != NULL)
1445             {
1446
1447                 Ccb->CurrentDirIndex = ulCount;
1448             }
1449         }
1450
1451 try_exit:
1452
1453         AFSReleaseResource( &Ccb->NPCcb->CcbLock);
1454     }
1455
1456     return pDirEntry;
1457 }
1458
1459 NTSTATUS
1460 AFSSnapshotDirectory( IN AFSFcb *Fcb,
1461                       IN AFSCcb *Ccb,
1462                       IN BOOLEAN ResetIndex)
1463 {
1464
1465     NTSTATUS ntStatus = STATUS_SUCCESS;
1466     AFSSnapshotHdr *pSnapshotHdr = NULL;
1467     AFSSnapshotEntry *pSnapshotEntry = NULL;
1468     AFSDirectoryCB *pDirEntry = NULL;
1469
1470     __Enter
1471     {
1472
1473         if( ResetIndex)
1474         {
1475
1476             //
1477             // Set it up so we still get the . and .. entries for empty directories
1478             //
1479
1480             if( BooleanFlagOn( Ccb->Flags, CCB_FLAG_RETURN_RELATIVE_ENTRIES))
1481             {
1482
1483                 Ccb->CurrentDirIndex = AFS_DIR_ENTRY_INITIAL_DIR_INDEX;
1484             }
1485             else
1486             {
1487
1488                 Ccb->CurrentDirIndex = AFS_DIR_ENTRY_INITIAL_ROOT_INDEX;
1489             }
1490         }
1491
1492         if( Fcb->ObjectInformation->Specific.Directory.DirectoryNodeCount == 0)
1493         {
1494
1495             //
1496             // If we have a snapshot then clear it out
1497             //
1498
1499             if( Ccb->DirectorySnapshot != NULL)
1500             {
1501
1502                 AFSExFreePoolWithTag( Ccb->DirectorySnapshot, AFS_DIR_SNAPSHOT_TAG);
1503
1504                 Ccb->DirectorySnapshot = NULL;
1505             }
1506
1507             try_return( ntStatus);
1508         }
1509
1510         //
1511         // Allocate our snapshot buffer for this enumeration
1512         //
1513
1514         pSnapshotHdr = (AFSSnapshotHdr *)AFSExAllocatePoolWithTag( PagedPool,
1515                                                                    sizeof( AFSSnapshotHdr) +
1516                                                                         ( Fcb->ObjectInformation->Specific.Directory.DirectoryNodeCount *
1517                                                                                 sizeof( AFSSnapshotEntry)),
1518                                                                    AFS_DIR_SNAPSHOT_TAG);
1519
1520         if( pSnapshotHdr == NULL)
1521         {
1522
1523             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
1524         }
1525
1526         RtlZeroMemory( pSnapshotHdr,
1527                        sizeof( AFSSnapshotHdr) +
1528                             ( Fcb->ObjectInformation->Specific.Directory.DirectoryNodeCount *
1529                                     sizeof( AFSSnapshotEntry)));
1530
1531         pSnapshotHdr->EntryCount = Fcb->ObjectInformation->Specific.Directory.DirectoryNodeCount;
1532
1533         pSnapshotHdr->TopEntry = (AFSSnapshotEntry *)((char *)pSnapshotHdr + sizeof( AFSSnapshotHdr));
1534
1535         //
1536         // Populate our snapshot
1537         //
1538
1539         pSnapshotEntry = pSnapshotHdr->TopEntry;
1540
1541         pDirEntry = Fcb->ObjectInformation->Specific.Directory.DirectoryNodeListHead;
1542
1543         while( pDirEntry != NULL)
1544         {
1545
1546             if( !BooleanFlagOn( pDirEntry->Flags, AFS_DIR_ENTRY_DELETED) &&
1547                 !BooleanFlagOn( pDirEntry->Flags, AFS_DIR_ENTRY_PENDING_DELETE) &&
1548                 !AFSIsNameInSnapshot( pSnapshotHdr,
1549                                       (ULONG)pDirEntry->CaseSensitiveTreeEntry.HashIndex))
1550             {
1551
1552                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1553                               AFS_TRACE_LEVEL_VERBOSE,
1554                               "AFSSnapshotDirectory Snapshot (%08lX) Inserting entry %wZ (%08lX) Flags %08lX in parent FID %08lX-%08lX-%08lX-%08lX\n",
1555                               pSnapshotHdr->EntryCount,
1556                               &pDirEntry->NameInformation.FileName,
1557                               (ULONG)pDirEntry->CaseSensitiveTreeEntry.HashIndex,
1558                               pDirEntry->Flags,
1559                               Fcb->ObjectInformation->FileId.Cell,
1560                               Fcb->ObjectInformation->FileId.Volume,
1561                               Fcb->ObjectInformation->FileId.Vnode,
1562                               Fcb->ObjectInformation->FileId.Unique);
1563
1564                 pSnapshotEntry->NameHash = (ULONG)pDirEntry->CaseSensitiveTreeEntry.HashIndex;
1565
1566                 pSnapshotEntry++;
1567             }
1568             else
1569             {
1570
1571                 AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1572                               AFS_TRACE_LEVEL_VERBOSE,
1573                               "AFSSnapshotDirectory Snapshot (%08lX) Skipping entry %wZ (%08lX) Flags %08lX in parent FID %08lX-%08lX-%08lX-%08lX\n",
1574                               pSnapshotHdr->EntryCount,
1575                               &pDirEntry->NameInformation.FileName,
1576                               (ULONG)pDirEntry->CaseSensitiveTreeEntry.HashIndex,
1577                               pDirEntry->Flags,
1578                               Fcb->ObjectInformation->FileId.Cell,
1579                               Fcb->ObjectInformation->FileId.Volume,
1580                               Fcb->ObjectInformation->FileId.Vnode,
1581                               Fcb->ObjectInformation->FileId.Unique);
1582             }
1583
1584             pDirEntry = (AFSDirectoryCB *)pDirEntry->ListEntry.fLink;
1585         }
1586
1587         if( Ccb->DirectorySnapshot != NULL)
1588         {
1589
1590             AFSExFreePoolWithTag( Ccb->DirectorySnapshot, AFS_DIR_SNAPSHOT_TAG);
1591
1592             Ccb->DirectorySnapshot = NULL;
1593         }
1594
1595         Ccb->DirectorySnapshot = pSnapshotHdr;
1596
1597 try_exit:
1598
1599         NOTHING;
1600     }
1601
1602     return ntStatus;
1603 }
1604
1605 NTSTATUS
1606 AFSFsRtlNotifyFullChangeDirectory( IN AFSObjectInfoCB *ObjectInfo,
1607                                    IN AFSCcb *Ccb,
1608                                    IN BOOLEAN WatchTree,
1609                                    IN ULONG CompletionFilter,
1610                                    IN PIRP NotifyIrp)
1611 {
1612
1613     NTSTATUS ntStatus = STATUS_SUCCESS;
1614     AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
1615     size_t sztLength;
1616
1617     __Enter
1618     {
1619
1620         AFSAcquireExcl( &Ccb->NPCcb->CcbLock,
1621                         TRUE);
1622
1623         //
1624         // Build a dir name based on the FID of the file
1625         //
1626
1627         if( Ccb->NotifyMask.Buffer == NULL)
1628         {
1629
1630             Ccb->NotifyMask.Length = 0;
1631             Ccb->NotifyMask.MaximumLength = 1024;
1632
1633             Ccb->NotifyMask.Buffer = (WCHAR *)AFSExAllocatePoolWithTag( PagedPool,
1634                                                                         Ccb->NotifyMask.MaximumLength,
1635                                                                         AFS_GENERIC_MEMORY_7_TAG);
1636
1637             if( Ccb->NotifyMask.Buffer == NULL)
1638             {
1639
1640                 try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
1641             }
1642
1643             ntStatus = RtlStringCbPrintfW( Ccb->NotifyMask.Buffer,
1644                                            Ccb->NotifyMask.MaximumLength,
1645                                            L"\\%08lX.%08lX.%08lX.%08lX",
1646                                            ObjectInfo->FileId.Cell,
1647                                            ObjectInfo->FileId.Volume,
1648                                            ObjectInfo->FileId.Vnode,
1649                                            ObjectInfo->FileId.Unique);
1650
1651             if( !NT_SUCCESS( ntStatus))
1652             {
1653
1654                 try_return( ntStatus);
1655             }
1656
1657             ntStatus = RtlStringCbLengthW( Ccb->NotifyMask.Buffer,
1658                                            (size_t)Ccb->NotifyMask.MaximumLength,
1659                                            &sztLength);
1660
1661             if( !NT_SUCCESS( ntStatus))
1662             {
1663
1664                 try_return( ntStatus);
1665             }
1666
1667             Ccb->NotifyMask.Length = (USHORT)sztLength;
1668         }
1669
1670         AFSDbgLogMsg( AFS_SUBSYSTEM_DIR_NOTIF_PROCESSING,
1671                       AFS_TRACE_LEVEL_VERBOSE,
1672                       "AFSFsRtlNotifyFullChangeDirectory Registering notification on %wZ Irp %p Filter %08lX Tree %02lX\n",
1673                       &Ccb->NotifyMask,
1674                       NotifyIrp,
1675                       CompletionFilter,
1676                       WatchTree);
1677
1678         FsRtlNotifyFilterChangeDirectory( pDeviceExt->Specific.Control.NotifySync,
1679                                         &pDeviceExt->Specific.Control.DirNotifyList,
1680                                         (void *)Ccb,
1681                                         (PSTRING)&Ccb->NotifyMask,
1682                                         WatchTree,
1683                                         TRUE,
1684                                         CompletionFilter,
1685                                         NotifyIrp,
1686                                         NULL,
1687                                         NULL,
1688                                         NULL);
1689
1690 try_exit:
1691
1692         if( !NT_SUCCESS( ntStatus))
1693         {
1694
1695             if( Ccb->NotifyMask.Buffer != NULL)
1696             {
1697
1698                 AFSExFreePoolWithTag( Ccb->NotifyMask.Buffer, AFS_GENERIC_MEMORY_7_TAG);
1699
1700                 Ccb->NotifyMask.Buffer = NULL;
1701             }
1702         }
1703
1704         AFSReleaseResource( &Ccb->NPCcb->CcbLock);
1705     }
1706
1707     return ntStatus;
1708 }
1709
1710 void
1711 AFSFsRtlNotifyFullReportChange( IN AFSObjectInfoCB *ParentObjectInfo,
1712                                 IN AFSCcb *Ccb,
1713                                 IN ULONG NotifyFilter,
1714                                 IN ULONG NotificationAction)
1715 {
1716
1717     NTSTATUS ntStatus = STATUS_SUCCESS;
1718     AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
1719     UNICODE_STRING uniName, uniComponentName;
1720     size_t sztLength;
1721     USHORT usNameOffset = 0;
1722
1723     __Enter
1724     {
1725
1726         uniName.Buffer = NULL;
1727
1728         if( ParentObjectInfo == NULL ||
1729             AFSGlobalRoot == NULL)
1730         {
1731
1732             try_return( ntStatus);
1733         }
1734
1735         if( Ccb == NULL)
1736         {
1737
1738             RtlInitUnicodeString( &uniComponentName,
1739                                   L"_AFSChange.dat");
1740         }
1741         else
1742         {
1743
1744             uniComponentName = Ccb->DirectoryCB->NameInformation.FileName;
1745         }
1746
1747         //
1748         // Build a dir name based on the FID of the file
1749         //
1750
1751         uniName.Length = 0;
1752         uniName.MaximumLength = 1024 + uniComponentName.Length;
1753
1754         uniName.Buffer = (WCHAR *)AFSExAllocatePoolWithTag( PagedPool,
1755                                                             uniName.MaximumLength,
1756                                                             AFS_GENERIC_MEMORY_8_TAG);
1757
1758         if( uniName.Buffer == NULL)
1759         {
1760
1761             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
1762         }
1763
1764         ntStatus = RtlStringCbPrintfW( uniName.Buffer,
1765                                        uniName.MaximumLength,
1766                                        L"\\%08lX.%08lX.%08lX.%08lX\\%wZ",
1767                                        ParentObjectInfo->FileId.Cell,
1768                                        ParentObjectInfo->FileId.Volume,
1769                                        ParentObjectInfo->FileId.Vnode,
1770                                        ParentObjectInfo->FileId.Unique,
1771                                        &uniComponentName);
1772
1773         if( !NT_SUCCESS( ntStatus))
1774         {
1775
1776             try_return( ntStatus);
1777         }
1778
1779         ntStatus = RtlStringCbLengthW( uniName.Buffer,
1780                                        (size_t)uniName.MaximumLength,
1781                                        &sztLength);
1782
1783         if( !NT_SUCCESS( ntStatus))
1784         {
1785
1786             try_return( ntStatus);
1787         }
1788
1789         uniName.Length = (USHORT)sztLength;
1790
1791         usNameOffset = uniName.Length - uniComponentName.Length;
1792
1793         AFSDbgLogMsg( AFS_SUBSYSTEM_DIR_NOTIF_PROCESSING,
1794                       AFS_TRACE_LEVEL_VERBOSE,
1795                       "AFSFsRtlNotifyFullReportChange Notification call for %wZ Filter %08lX Action %08lX Offset %08lX Len %08lX CompLen %08lX\n",
1796                       &uniName,
1797                       NotifyFilter,
1798                       NotificationAction,
1799                       usNameOffset,
1800                       uniName.Length,
1801                       uniComponentName.Length);
1802
1803         FsRtlNotifyFilterReportChange( pDeviceExt->Specific.Control.NotifySync,
1804                                        &pDeviceExt->Specific.Control.DirNotifyList,
1805                                        (PSTRING)&uniName,
1806                                        usNameOffset,
1807                                        NULL,
1808                                        NULL,
1809                                        NotifyFilter,
1810                                        NotificationAction,
1811                                        NULL,
1812                                        (void *)Ccb);
1813
1814 try_exit:
1815
1816         if( uniName.Buffer != NULL)
1817         {
1818
1819             AFSExFreePoolWithTag( uniName.Buffer, AFS_GENERIC_MEMORY_8_TAG);
1820         }
1821     }
1822
1823     return;
1824 }
1825
1826 // For use with FsRtlNotifyFilterChangeDirectory but must
1827 // be implemented in the Framework because the library can
1828 // be unloaded.
1829
1830 BOOLEAN
1831 AFSNotifyReportChangeCallback( IN void *NotifyContext,
1832                                IN void *FilterContext)
1833 {
1834     UNREFERENCED_PARAMETER(NotifyContext);
1835     UNREFERENCED_PARAMETER(FilterContext);
1836     BOOLEAN bReturn = TRUE;
1837
1838     __Enter
1839     {
1840
1841     }
1842
1843     return bReturn;
1844 }
1845
1846 BOOLEAN
1847 AFSIsNameInSnapshot( IN AFSSnapshotHdr *SnapshotHdr,
1848                      IN ULONG HashIndex)
1849 {
1850
1851     BOOLEAN bIsInSnapshot = FALSE;
1852     AFSSnapshotEntry *pSnapshotEntry = SnapshotHdr->TopEntry;
1853     ULONG ulCount = 0;
1854
1855     while( ulCount < SnapshotHdr->EntryCount)
1856     {
1857
1858         if( pSnapshotEntry->NameHash == HashIndex)
1859         {
1860
1861             bIsInSnapshot = TRUE;
1862
1863             AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
1864                           AFS_TRACE_LEVEL_VERBOSE,
1865                           "AFSIsNameInSnapshot  Hash index %08lX already in snapshot\n",
1866                           HashIndex);
1867
1868             break;
1869         }
1870         else if( pSnapshotEntry->NameHash == 0)
1871         {
1872
1873             break;
1874         }
1875
1876         pSnapshotEntry++;
1877
1878         ulCount++;
1879     }
1880
1881     return bIsInSnapshot;
1882 }