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