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
UDFDirControl(PDEVICE_OBJECT DeviceObject,PIRP Irp)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
UDFCommonDirControl(PtrUDFIrpContext PtrIrpContext,PIRP Irp)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 _SEH2_VOLATILE PVCB Vcb = NULL;
138 _SEH2_VOLATILE 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
UDFQueryDirectory(PtrUDFIrpContext PtrIrpContext,PIRP Irp,PIO_STACK_LOCATION IrpSp,PFILE_OBJECT FileObject,PtrUDFFCB Fcb,PtrUDFCCB Ccb)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 _SEH2_VOLATILE PVCB Vcb = NULL;
230 _SEH2_VOLATILE 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
UDFFindNextMatch(IN PVCB Vcb,IN PDIR_INDEX_HDR hDirIndex,IN PLONG CurrentNumber,IN PUNICODE_STRING PtrSearchPattern,IN UCHAR FNM_Flags,IN PHASH_ENTRY hashes,OUT PDIR_INDEX_ITEM * _DirNdx)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
UDFNotifyChangeDirectory(PtrUDFIrpContext PtrIrpContext,PIRP Irp,PIO_STACK_LOCATION IrpSp,PFILE_OBJECT FileObject,PtrUDFFCB Fcb,PtrUDFCCB Ccb)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 _SEH2_VOLATILE PVCB Vcb = NULL;
700 _SEH2_VOLATILE 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