1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*************************************************************************
7 *
8 * File: DirCntrl.cpp
9 *
10 * Module: UDF File System Driver (Kernel mode execution only)
11 *
12 * Description:
13 *   Contains code to handle the "directory control" dispatch entry point.
14 *
15 *************************************************************************/
16 
17 #include            "udffs.h"
18 
19 // define the file specific bug-check id
20 #define         UDF_BUG_CHECK_ID                UDF_FILE_DIR_CONTROL
21 
22 /*
23 // Local support routine(s):
24 */
25 
26 #define UDF_FNM_FLAG_CAN_BE_8D3    0x01
27 #define UDF_FNM_FLAG_IGNORE_CASE   0x02
28 #define UDF_FNM_FLAG_CONTAINS_WC   0x04
29 
30 NTSTATUS UDFFindNextMatch(
31     IN PVCB            Vcb,
32     IN PDIR_INDEX_HDR  hDirIndex,
33     IN PLONG           CurrentNumber,      // Must be modified
34     IN PUNICODE_STRING PtrSearchPattern,
35     IN UCHAR           FNM_Flags,
36     IN PHASH_ENTRY     hashes,
37    OUT PDIR_INDEX_ITEM* _DirNdx);
38 
39 /*************************************************************************
40 *
41 * Function: UDFDirControl()
42 *
43 * Description:
44 *   The I/O Manager will invoke this routine to handle a directory control
45 *   request
46 *
47 * Expected Interrupt Level (for execution) :
48 *
49 *  IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
50 *   to be deferred to a worker thread context)
51 *
52 * Return Value: STATUS_SUCCESS/Error
53 *
54 *************************************************************************/
55 NTSTATUS
56 NTAPI
57 UDFDirControl(
58     PDEVICE_OBJECT      DeviceObject,       // the logical volume device object
59     PIRP                Irp                 // I/O Request Packet
60     )
61 {
62     NTSTATUS            RC = STATUS_SUCCESS;
63     PtrUDFIrpContext    PtrIrpContext = NULL;
64     BOOLEAN             AreWeTopLevel = FALSE;
65 
66     TmPrint(("UDFDirControl: \n"));
67 
68     FsRtlEnterFileSystem();
69     ASSERT(DeviceObject);
70     ASSERT(Irp);
71 
72     // set the top level context
73     AreWeTopLevel = UDFIsIrpTopLevel(Irp);
74     ASSERT(!UDFIsFSDevObj(DeviceObject));
75 
76     _SEH2_TRY {
77 
78         // get an IRP context structure and issue the request
79         PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
80         if(PtrIrpContext) {
81             RC = UDFCommonDirControl(PtrIrpContext, Irp);
82         } else {
83             RC = STATUS_INSUFFICIENT_RESOURCES;
84             Irp->IoStatus.Status = RC;
85             Irp->IoStatus.Information = 0;
86             // complete the IRP
87             IoCompleteRequest(Irp, IO_DISK_INCREMENT);
88         }
89 
90     } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
91 
92         RC = UDFExceptionHandler(PtrIrpContext, Irp);
93 
94         UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
95     } _SEH2_END;
96 
97     if (AreWeTopLevel) {
98         IoSetTopLevelIrp(NULL);
99     }
100 
101     FsRtlExitFileSystem();
102 
103     return(RC);
104 } // end UDFDirControl()
105 
106 
107 
108 /*************************************************************************
109 *
110 * Function: UDFCommonDirControl()
111 *
112 * Description:
113 *   The actual work is performed here. This routine may be invoked in one'
114 *   of the two possible contexts:
115 *   (a) in the context of a system worker thread
116 *   (b) in the context of the original caller
117 *
118 * Expected Interrupt Level (for execution) :
119 *
120 *  IRQL_PASSIVE_LEVEL
121 *
122 * Return Value: STATUS_SUCCESS/Error
123 *
124 *************************************************************************/
125 NTSTATUS
126 NTAPI
127 UDFCommonDirControl(
128    PtrUDFIrpContext  PtrIrpContext,
129    PIRP              Irp
130    )
131 {
132     NTSTATUS                RC = STATUS_SUCCESS;
133     PIO_STACK_LOCATION      IrpSp = NULL;
134     PFILE_OBJECT            FileObject = NULL;
135     PtrUDFFCB               Fcb = NULL;
136     PtrUDFCCB               Ccb = NULL;
137     PVCB                    Vcb = NULL;
138     BOOLEAN                 AcquiredVcb = FALSE;
139 
140     TmPrint(("UDFCommonDirControl: \n"));
141 //    BrutePoint();
142 
143     _SEH2_TRY {
144         // First, get a pointer to the current I/O stack location
145         IrpSp = IoGetCurrentIrpStackLocation(Irp);
146         ASSERT(IrpSp);
147 
148         FileObject = IrpSp->FileObject;
149         ASSERT(FileObject);
150 
151         // Get the FCB and CCB pointers
152         Ccb = (PtrUDFCCB)(FileObject->FsContext2);
153         ASSERT(Ccb);
154         Fcb = Ccb->Fcb;
155         ASSERT(Fcb);
156 
157         Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
158         ASSERT(Vcb);
159         ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB);
160 //        Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
161 
162         UDFFlushTryBreak(Vcb);
163         UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
164         AcquiredVcb = TRUE;
165         // Get some of the parameters supplied to us
166         switch (IrpSp->MinorFunction) {
167         case IRP_MN_QUERY_DIRECTORY:
168             RC = UDFQueryDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb);
169             break;
170         case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
171             RC = UDFNotifyChangeDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb);
172             break;
173         default:
174             // This should not happen.
175             RC = STATUS_INVALID_DEVICE_REQUEST;
176             Irp->IoStatus.Status = RC;
177             Irp->IoStatus.Information = 0;
178 
179             // Free up the Irp Context
180             UDFReleaseIrpContext(PtrIrpContext);
181 
182             // complete the IRP
183             IoCompleteRequest(Irp, IO_NO_INCREMENT);
184             break;
185         }
186 
187 //try_exit: NOTHING;
188 
189     } _SEH2_FINALLY {
190 
191         if(AcquiredVcb) {
192             UDFReleaseResource(&(Vcb->VCBResource));
193             AcquiredVcb = FALSE;
194         }
195     } _SEH2_END;
196     return(RC);
197 } // end UDFCommonDirControl()
198 
199 
200 /*************************************************************************
201 *
202 * Function: UDFQueryDirectory()
203 *
204 * Description:
205 *   Query directory request.
206 *
207 * Expected Interrupt Level (for execution) :
208 *
209 *  IRQL_PASSIVE_LEVEL
210 *
211 * Return Value: STATUS_SUCCESS/Error
212 *
213 *************************************************************************/
214 NTSTATUS
215 NTAPI
216 UDFQueryDirectory(
217     PtrUDFIrpContext            PtrIrpContext,
218     PIRP                        Irp,
219     PIO_STACK_LOCATION          IrpSp,
220     PFILE_OBJECT                FileObject,
221     PtrUDFFCB                   Fcb,
222     PtrUDFCCB                   Ccb
223     )
224 {
225     NTSTATUS                    RC = STATUS_SUCCESS;
226     BOOLEAN                     PostRequest = FALSE;
227     PtrUDFNTRequiredFCB         NtReqFcb = NULL;
228     BOOLEAN                     CanWait = FALSE;
229     PVCB                        Vcb = NULL;
230     BOOLEAN                     AcquiredFCB = FALSE;
231     unsigned long               BufferLength = 0;
232     UNICODE_STRING              SearchPattern;
233     PUNICODE_STRING             PtrSearchPattern;
234     FILE_INFORMATION_CLASS      FileInformationClass;
235     BOOLEAN                     ReturnSingleEntry = FALSE;
236     PUCHAR                      Buffer = NULL;
237     BOOLEAN                     FirstTimeQuery = FALSE;
238     LONG                        NextMatch;
239     LONG                        PrevMatch = -1;
240     ULONG                       CurrentOffset;
241     ULONG                       BaseLength;
242     ULONG                       FileNameBytes;
243     ULONG                       Information = 0;
244     ULONG                       LastOffset = 0;
245     BOOLEAN                     AtLeastOneFound = FALSE;
246     PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp;
247     PUDF_FILE_INFO              DirFileInfo = NULL;
248     PDIR_INDEX_HDR              hDirIndex = NULL;
249     PFILE_BOTH_DIR_INFORMATION  DirInformation = NULL;      // Returned from udf_info module
250     PFILE_BOTH_DIR_INFORMATION  BothDirInformation = NULL;  // Pointer in callers buffer
251     PFILE_NAMES_INFORMATION     NamesInfo;
252     ULONG                       BytesRemainingInBuffer;
253     UCHAR                       FNM_Flags = 0;
254     PHASH_ENTRY                 cur_hashes = NULL;
255     PDIR_INDEX_ITEM             DirNdx;
256     // do some pre-init...
257     SearchPattern.Buffer = NULL;
258 
259     UDFPrint(("UDFQueryDirectory: @=%#x\n", &PtrIrpContext));
260 
261 #define CanBe8dot3    (FNM_Flags & UDF_FNM_FLAG_CAN_BE_8D3)
262 #define IgnoreCase    (FNM_Flags & UDF_FNM_FLAG_IGNORE_CASE)
263 #define ContainsWC    (FNM_Flags & UDF_FNM_FLAG_CONTAINS_WC)
264 
265     _SEH2_TRY
266     {
267 
268         // Validate the sent-in FCB
269         if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {
270             // We will only allow notify requests on directories.
271             try_return(RC = STATUS_INVALID_PARAMETER);
272         }
273 
274         // Obtain the callers parameters
275         NtReqFcb = Fcb->NTRequiredFCB;
276         CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE;
277         Vcb = Fcb->Vcb;
278         //Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
279         FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? 0 : UDF_FNM_FLAG_IGNORE_CASE;
280         DirFileInfo = Fcb->FileInfo;
281         BufferLength = pStackLocation->Parameters.QueryDirectory.Length;
282 
283         // If the caller does not want to block, it would be easier to
284         // simply post the request now.
285         if (!CanWait) {
286             PostRequest = TRUE;
287             try_return(RC = STATUS_PENDING);
288         }
289 
290         // Continue obtaining the callers parameters...
291         if(IgnoreCase && pStackLocation->Parameters.QueryDirectory.FileName) {
292             PtrSearchPattern = &SearchPattern;
293             if(!NT_SUCCESS(RC = RtlUpcaseUnicodeString(PtrSearchPattern, (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName), TRUE)))
294                 try_return(RC);
295         } else {
296             PtrSearchPattern = (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName);
297         }
298         FileInformationClass = pStackLocation->Parameters.QueryDirectory.FileInformationClass;
299 
300         // Calculate baselength (without name) for each InfoClass
301         switch (FileInformationClass) {
302 
303         case FileDirectoryInformation:
304             BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] );
305             break;
306         case FileFullDirectoryInformation:
307             BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,  FileName[0] );
308             break;
309         case FileNamesInformation:
310             BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,     FileName[0] );
311             break;
312         case FileBothDirectoryInformation:
313             BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,  FileName[0] );
314             break;
315         default:
316             try_return(RC = STATUS_INVALID_INFO_CLASS);
317         }
318 
319         // Some additional arguments that affect the FSD behavior
320         ReturnSingleEntry = (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) ? TRUE : FALSE;
321 
322         UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
323         UDFAcquireResourceShared(&(NtReqFcb->MainResource), TRUE);
324         AcquiredFCB = TRUE;
325 
326         // We must determine the buffer pointer to be used. Since this
327         // routine could either be invoked directly in the context of the
328         // calling thread, or in the context of a worker thread, here is
329         // a general way of determining what we should use.
330         if(Irp->MdlAddress) {
331             Buffer = (PUCHAR) MmGetSystemAddressForMdlSafer(Irp->MdlAddress);
332             if(!Buffer)
333                 try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
334         } else {
335             Buffer = (PUCHAR) Irp->UserBuffer;
336             if(!Buffer)
337                 try_return(RC = STATUS_INVALID_USER_BUFFER);
338         }
339 
340         // The method of determining where to look from and what to look for is
341         // unfortunately extremely confusing. However, here is a methodology
342         // we broadly adopt:
343         // (a) We have to maintain a search buffer per CCB structure.
344         // (b) This search buffer is initialized the very first time
345         //       a query directory operation is performed using the file object.
346         // (For the UDF FSD, the search buffer is stored in the
347         //   DirectorySearchPattern field)
348         // However, the caller still has the option of "overriding" this stored
349         // search pattern by supplying a new one in a query directory operation.
350         if(PtrSearchPattern &&
351            PtrSearchPattern->Buffer &&
352            !(PtrSearchPattern->Buffer[PtrSearchPattern->Length/sizeof(WCHAR) - 1])) {
353             PtrSearchPattern->Length -= sizeof(WCHAR);
354         }
355 
356         if(IrpSp->Flags & SL_INDEX_SPECIFIED) {
357             // Good idea from M$: we should continue search from NEXT item
358             // when FileIndex specified...
359             // Strange idea from M$: we should do it with EMPTY pattern...
360             PtrSearchPattern = NULL;
361             Ccb->CCBFlags |= UDF_CCB_MATCH_ALL;
362         } else if(PtrSearchPattern &&
363                   PtrSearchPattern->Buffer &&
364                   !UDFIsMatchAllMask(PtrSearchPattern, NULL) ) {
365 
366             Ccb->CCBFlags &= ~(UDF_CCB_MATCH_ALL |
367                                UDF_CCB_WILDCARD_PRESENT |
368                                UDF_CCB_CAN_BE_8_DOT_3);
369             // Once we have validated the search pattern, we must
370             // check whether we need to store this search pattern in
371             // the CCB.
372             if(Ccb->DirectorySearchPattern) {
373                 MyFreePool__(Ccb->DirectorySearchPattern->Buffer);
374                 MyFreePool__(Ccb->DirectorySearchPattern);
375                 Ccb->DirectorySearchPattern = NULL;
376             }
377             // This must be the very first query request.
378             FirstTimeQuery = TRUE;
379 
380             // Now, allocate enough memory to contain the caller
381             // supplied search pattern and fill in the DirectorySearchPattern
382             // field in the CCB
383             Ccb->DirectorySearchPattern = (PUNICODE_STRING)MyAllocatePool__(NonPagedPool,sizeof(UNICODE_STRING));
384             if(!(Ccb->DirectorySearchPattern)) {
385                 try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
386             }
387             Ccb->DirectorySearchPattern->Length = PtrSearchPattern->Length;
388             Ccb->DirectorySearchPattern->MaximumLength = PtrSearchPattern->MaximumLength;
389             Ccb->DirectorySearchPattern->Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool,PtrSearchPattern->MaximumLength);
390             if(!(Ccb->DirectorySearchPattern->Buffer)) {
391                 try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
392             }
393             RtlCopyMemory(Ccb->DirectorySearchPattern->Buffer,PtrSearchPattern->Buffer,
394                           PtrSearchPattern->MaximumLength);
395             if(FsRtlDoesNameContainWildCards(PtrSearchPattern)) {
396                 Ccb->CCBFlags |= UDF_CCB_WILDCARD_PRESENT;
397             } else {
398                 UDFBuildHashEntry(Vcb, PtrSearchPattern, cur_hashes = &(Ccb->hashes), HASH_POSIX | HASH_ULFN);
399             }
400             if(UDFCanNameBeA8dot3(PtrSearchPattern))
401                 Ccb->CCBFlags |= UDF_CCB_CAN_BE_8_DOT_3;
402 
403         } else if(!Ccb->DirectorySearchPattern &&
404                   !(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) ) {
405 
406             // If the filename is not specified or is a single '*' then we will
407             // match all names.
408             FirstTimeQuery = TRUE;
409             PtrSearchPattern = NULL;
410             Ccb->CCBFlags |= UDF_CCB_MATCH_ALL;
411 
412         } else {
413             // The caller has not supplied any search pattern that we are
414             // forced to use. However, the caller had previously supplied
415             // a pattern (or we must have invented one) and we will use it.
416             // This is definitely not the first query operation on this
417             // directory using this particular file object.
418             if(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) {
419                 PtrSearchPattern = NULL;
420 /*                if(Ccb->CurrentIndex)
421                     Ccb->CurrentIndex++;*/
422             } else {
423                 PtrSearchPattern = Ccb->DirectorySearchPattern;
424                 if(!(Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT)) {
425                     cur_hashes = &(Ccb->hashes);
426                 }
427             }
428         }
429 
430         if(IrpSp->Flags & SL_INDEX_SPECIFIED) {
431             // Caller has told us wherefrom to begin.
432             // We may need to round this to an appropriate directory entry
433             // entry alignment value.
434             NextMatch = pStackLocation->Parameters.QueryDirectory.FileIndex + 1;
435         } else if(IrpSp->Flags & SL_RESTART_SCAN) {
436             NextMatch = 0;
437         } else {
438             // Get the starting offset from the CCB.
439             // Remember to update this value on our way out from this function.
440             // But, do not update the CCB CurrentByteOffset field if our reach
441             // the end of the directory (or get an error reading the directory)
442             // while performing the search.
443             NextMatch = Ccb->CurrentIndex + 1; // Last good index
444         }
445 
446         FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT) ? UDF_FNM_FLAG_CONTAINS_WC : 0;
447         // this is used only when mask is supplied
448         FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CAN_BE_8_DOT_3) ? UDF_FNM_FLAG_CAN_BE_8D3 : 0;
449 
450         // This is an additional verifying
451         if(!UDFIsADirectory(DirFileInfo)) {
452             try_return(RC = STATUS_INVALID_PARAMETER);
453         }
454 
455         hDirIndex = DirFileInfo->Dloc->DirIndex;
456         if(!hDirIndex) {
457             try_return(RC = STATUS_INVALID_PARAMETER);
458         }
459 
460         RC = STATUS_SUCCESS;
461         // Allocate buffer enough to save both DirInformation and FileName
462         DirInformation = (PFILE_BOTH_DIR_INFORMATION)MyAllocatePool__(NonPagedPool,
463                             sizeof(FILE_BOTH_DIR_INFORMATION)+((ULONG)UDF_NAME_LEN*sizeof(WCHAR)) );
464         if(!DirInformation) {
465             try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
466         }
467         CurrentOffset=0;
468         BytesRemainingInBuffer = pStackLocation->Parameters.QueryDirectory.Length;
469         RtlZeroMemory(Buffer,BytesRemainingInBuffer);
470 
471         if((!FirstTimeQuery) && !UDFDirIndex(hDirIndex, (uint_di)NextMatch) ) {
472             try_return( RC = STATUS_NO_MORE_FILES);
473         }
474 
475         // One final note though:
476         // If we do not find a directory entry OR while searching we reach the
477         // end of the directory, then the return code should be set as follows:
478 
479         // (a) If any files have been returned (i.e. ReturnSingleEntry was FALSE
480         //       and we did find at least one match), then return STATUS_SUCCESS
481         // (b) If no entry is being returned then:
482         //       (i) If this is the first query i.e. FirstTimeQuery is TRUE
483         //            then return STATUS_NO_SUCH_FILE
484         //       (ii) Otherwise, return STATUS_NO_MORE_FILES
485 
486         while(TRUE) {
487             // If the user had requested only a single match and we have
488             // returned that, then we stop at this point.
489             if(ReturnSingleEntry && AtLeastOneFound) {
490                 try_return(RC);
491             }
492             // We call UDFFindNextMatch to look down the next matching dirent.
493             RC = UDFFindNextMatch(Vcb, hDirIndex,&NextMatch,PtrSearchPattern, FNM_Flags, cur_hashes, &DirNdx);
494             // If we didn't receive next match, then we are at the end of the
495             // directory.  If we have returned any files, we exit with
496             // success, otherwise we return STATUS_NO_MORE_FILES.
497             if(!NT_SUCCESS(RC)) {
498                 RC = AtLeastOneFound ? STATUS_SUCCESS :
499                                       (FirstTimeQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES);
500                 try_return(RC);
501             }
502             // We found at least one matching file entry
503             AtLeastOneFound = TRUE;
504             if(!NT_SUCCESS(RC = UDFFileDirInfoToNT(Vcb, DirNdx, DirInformation))) {
505                 // this happends when we can't allocate tmp buffers
506                 try_return(RC);
507             }
508             DirInformation->FileIndex = NextMatch;
509             FileNameBytes = DirInformation->FileNameLength;
510 
511             if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) {
512                 // We haven't successfully transfered current data &
513                 // later NextMatch will be incremented. Thus we should
514                 // prevent loosing information in such a way:
515                 if(NextMatch) NextMatch --;
516                 // If this won't fit and we have returned a previous entry then just
517                 // return STATUS_SUCCESS. Otherwise
518                 // use a status code of STATUS_BUFFER_OVERFLOW.
519                 if(CurrentOffset) {
520                     try_return(RC = STATUS_SUCCESS);
521                 }
522                 // strange policy...
523                 ReturnSingleEntry = TRUE;
524                 FileNameBytes = BaseLength + FileNameBytes - BytesRemainingInBuffer;
525                 RC = STATUS_BUFFER_OVERFLOW;
526             }
527             //  Now we have an entry to return to our caller.
528             //  We'll case on the type of information requested and fill up
529             //  the user buffer if everything fits.
530             switch (FileInformationClass) {
531 
532             case FileBothDirectoryInformation:
533             case FileFullDirectoryInformation:
534             case FileDirectoryInformation:
535 
536                 BothDirInformation = (PFILE_BOTH_DIR_INFORMATION)(Buffer + CurrentOffset);
537                 RtlCopyMemory(BothDirInformation,DirInformation,BaseLength);
538                 BothDirInformation->FileIndex = NextMatch;
539                 BothDirInformation->FileNameLength = FileNameBytes;
540                 break;
541 
542             case FileNamesInformation:
543 
544                 NamesInfo = (PFILE_NAMES_INFORMATION)(Buffer + CurrentOffset);
545                 NamesInfo->FileIndex = NextMatch;
546                 NamesInfo->FileNameLength = FileNameBytes;
547                 break;
548 
549             default:
550                 break;
551             }
552             if (FileNameBytes) {
553                 //  This is a Unicode name, we can copy the bytes directly.
554                 RtlCopyMemory( (PVOID)(Buffer + CurrentOffset + BaseLength),
555                                DirInformation->FileName, FileNameBytes );
556             }
557 
558             Information = CurrentOffset + BaseLength + FileNameBytes;
559 
560             //  ((..._INFORMATION)(PointerToPreviousEntryInBuffer))->NextEntryOffset = CurrentOffset - LastOffset;
561             *((PULONG)(Buffer+LastOffset)) = CurrentOffset - LastOffset;
562             //  Set up our variables for the next dirent.
563             FirstTimeQuery = FALSE;
564 
565             LastOffset    = CurrentOffset;
566             PrevMatch     = NextMatch;
567             NextMatch++;
568             CurrentOffset = UDFQuadAlign(Information);
569             BytesRemainingInBuffer = BufferLength - CurrentOffset;
570         }
571 
572 try_exit:   NOTHING;
573 
574 
575     } _SEH2_FINALLY {
576 
577         if (PostRequest) {
578 
579             if (AcquiredFCB) {
580                 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
581                 UDFReleaseResource(&(NtReqFcb->MainResource));
582             }
583             // Map the users buffer and then post the request.
584             RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, BufferLength);
585             ASSERT(NT_SUCCESS(RC));
586 
587             RC = UDFPostRequest(PtrIrpContext, Irp);
588 
589         } else {
590 #ifdef UDF_DBG
591             if(!NT_SUCCESS(RC)) {
592                UDFPrint(("    Not found\n"));
593             }
594 #endif // UDF_DBG
595             // Remember to update the CurrentByteOffset field in the CCB if required.
596             if(Ccb) Ccb->CurrentIndex = PrevMatch;
597 
598             if (AcquiredFCB) {
599                 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
600                 UDFReleaseResource(&(NtReqFcb->MainResource));
601             }
602             if (!_SEH2_AbnormalTermination()) {
603                 // complete the IRP
604                 Irp->IoStatus.Status = RC;
605                 Irp->IoStatus.Information = Information;
606                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
607                 // Free up the Irp Context
608                 UDFReleaseIrpContext(PtrIrpContext);
609             }
610         }
611 
612         if(SearchPattern.Buffer) RtlFreeUnicodeString(&SearchPattern);
613         if(DirInformation) MyFreePool__(DirInformation);
614     } _SEH2_END;
615 
616     return(RC);
617 } // end UDFQueryDirectory()
618 
619 /*
620   Return: STATUS_NO_SUCH_FILE if no more files found
621 */
622 NTSTATUS
623 UDFFindNextMatch(
624     IN PVCB Vcb,
625     IN PDIR_INDEX_HDR  hDirIndex,
626     IN PLONG           CurrentNumber,      // Must be modified in case, when we found next match
627     IN PUNICODE_STRING PtrSearchPattern,
628     IN UCHAR           FNM_Flags,
629     IN PHASH_ENTRY     hashes,
630    OUT PDIR_INDEX_ITEM* _DirNdx
631     )
632 {
633     LONG    EntryNumber = (*CurrentNumber);
634     PDIR_INDEX_ITEM DirNdx;
635 
636 #define CanBe8dot3    (FNM_Flags & UDF_FNM_FLAG_CAN_BE_8D3)
637 #define IgnoreCase    (FNM_Flags & UDF_FNM_FLAG_IGNORE_CASE)
638 #define ContainsWC    (FNM_Flags & UDF_FNM_FLAG_CONTAINS_WC)
639 
640     for(;(DirNdx = UDFDirIndex(hDirIndex, EntryNumber));EntryNumber++) {
641         if(!DirNdx->FName.Buffer ||
642            UDFIsDeleted(DirNdx))
643             continue;
644         if(hashes &&
645            (DirNdx->hashes.hLfn != hashes->hLfn) &&
646            (DirNdx->hashes.hPosix != hashes->hPosix) &&
647            (!CanBe8dot3 || ((DirNdx->hashes.hDos != hashes->hLfn) && (DirNdx->hashes.hDos != hashes->hPosix))) )
648             continue;
649         if(UDFIsNameInExpression(Vcb, &(DirNdx->FName),PtrSearchPattern, NULL,IgnoreCase,
650                                 ContainsWC, CanBe8dot3 && !(DirNdx->FI_Flags & UDF_FI_FLAG_DOS),
651                                 EntryNumber < 2) &&
652            !(DirNdx->FI_Flags & UDF_FI_FLAG_FI_INTERNAL))
653             break;
654     }
655 
656     if(DirNdx) {
657         // Modify CurrentNumber to appropriate value
658         *CurrentNumber = EntryNumber;
659         *_DirNdx = DirNdx;
660         return STATUS_SUCCESS;
661     } else {
662         // Do not modify CurrentNumber because we have not found next match entry
663         return STATUS_NO_MORE_FILES;
664     }
665 } // end UDFFindNextMatch()
666 
667 /*************************************************************************
668 *
669 * Function: UDFNotifyChangeDirectory()
670 *
671 * Description:
672 *   Handle the notify request.
673 *
674 * Expected Interrupt Level (for execution) :
675 *
676 *  IRQL_PASSIVE_LEVEL
677 *
678 * Return Value: STATUS_SUCCESS/Error
679 *
680 *************************************************************************/
681 NTSTATUS
682 NTAPI
683 UDFNotifyChangeDirectory(
684     PtrUDFIrpContext            PtrIrpContext,
685     PIRP                        Irp,
686     PIO_STACK_LOCATION          IrpSp,
687     PFILE_OBJECT                FileObject,
688     PtrUDFFCB                   Fcb,
689     PtrUDFCCB                   Ccb
690     )
691 {
692     NTSTATUS                    RC = STATUS_SUCCESS;
693     BOOLEAN                     CompleteRequest = FALSE;
694     BOOLEAN                     PostRequest = FALSE;
695     PtrUDFNTRequiredFCB         NtReqFcb = NULL;
696     BOOLEAN                     CanWait = FALSE;
697     ULONG                       CompletionFilter = 0;
698     BOOLEAN                     WatchTree = FALSE;
699     PVCB                        Vcb = NULL;
700     BOOLEAN                     AcquiredFCB = FALSE;
701     PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp;
702 
703     UDFPrint(("UDFNotifyChangeDirectory\n"));
704 
705     _SEH2_TRY {
706 
707         // Validate the sent-in FCB
708         if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) ||
709             !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {
710 
711             CompleteRequest = TRUE;
712             try_return(RC = STATUS_INVALID_PARAMETER);
713         }
714 
715         NtReqFcb = Fcb->NTRequiredFCB;
716         CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE;
717         Vcb = Fcb->Vcb;
718 
719         // Acquire the FCB resource shared
720         UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
721         if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
722             PostRequest = TRUE;
723             try_return(RC = STATUS_PENDING);
724         }
725         AcquiredFCB = TRUE;
726 
727         //  If the file is marked as DELETE_PENDING then complete this
728         //  request immediately.
729         if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) {
730             ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
731             try_return(RC = STATUS_DELETE_PENDING);
732         }
733 
734         // Obtain some parameters sent by the caller
735         CompletionFilter = pStackLocation ->Parameters.NotifyDirectory.CompletionFilter;
736         WatchTree = (IrpSp->Flags & SL_WATCH_TREE) ? TRUE : FALSE;
737 
738         // If we wish to capture the subject context, we can do so as
739         // follows:
740         // {
741         //      PSECURITY_SUBJECT_CONTEXT SubjectContext;
742         //  SubjectContext = MyAllocatePool__(PagedPool,
743         //                                  sizeof(SECURITY_SUBJECT_CONTEXT));
744         //      SeCaptureSubjectContext(SubjectContext);
745         //  }
746 
747         FsRtlNotifyFullChangeDirectory(Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP), (PVOID)Ccb,
748                             (Fcb->FileInfo->ParentFile) ? (PSTRING)&(Fcb->FCBName->ObjectName) : (PSTRING)&(UDFGlobalData.UnicodeStrRoot),
749                             WatchTree, FALSE, CompletionFilter, Irp,
750                             NULL,   // UDFTraverseAccessCheck(...) ?
751                             NULL);  // SubjectContext ?
752 
753         RC = STATUS_PENDING;
754 
755         try_exit:   NOTHING;
756 
757     } _SEH2_FINALLY {
758 
759         if (PostRequest) {
760             // Perform appropriate related post processing here
761             if (AcquiredFCB) {
762                 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
763                 UDFReleaseResource(&(NtReqFcb->MainResource));
764                 AcquiredFCB = FALSE;
765             }
766             RC = UDFPostRequest(PtrIrpContext, Irp);
767         } else if (CompleteRequest) {
768 
769             if (!_SEH2_AbnormalTermination()) {
770                 Irp->IoStatus.Status = RC;
771                 Irp->IoStatus.Information = 0;
772                 // Free up the Irp Context
773                 UDFReleaseIrpContext(PtrIrpContext);
774                 // complete the IRP
775                 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
776             }
777 
778         } else {
779             // Simply free up the IrpContext since the IRP has been queued
780             if (!_SEH2_AbnormalTermination())
781                 UDFReleaseIrpContext(PtrIrpContext);
782         }
783 
784         // Release the FCB resources if acquired.
785         if (AcquiredFCB) {
786             UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
787             UDFReleaseResource(&(NtReqFcb->MainResource));
788             AcquiredFCB = FALSE;
789         }
790 
791     } _SEH2_END;
792 
793     return(RC);
794 } // end UDFNotifyChangeDirectory()
795