Remove unnecessary executable bits
[reactos.git] / drivers / filesystems / cdfs_new / dirctrl.c
1 /*++
2
3 Copyright (c) 1989-2000 Microsoft Corporation
4
5 Module Name:
6
7 DirCtrl.c
8
9 Abstract:
10
11 This module implements the File Directory Control routines for Cdfs called
12 by the Fsd/Fsp dispatch drivers.
13
14
15 --*/
16
17 #include "cdprocs.h"
18
19 //
20 // The Bug check file id for this module
21 //
22
23 #define BugCheckFileId (CDFS_BUG_CHECK_DIRCTRL)
24
25 //
26 // Local support routines
27 //
28
29 _Requires_lock_held_(_Global_critical_region_)
30 NTSTATUS
31 CdQueryDirectory (
32 _Inout_ PIRP_CONTEXT IrpContext,
33 _Inout_ PIRP Irp,
34 _In_ PIO_STACK_LOCATION IrpSp,
35 _In_ PFCB Fcb,
36 _In_ PCCB Ccb
37 );
38
39 _Requires_lock_held_(_Global_critical_region_)
40 NTSTATUS
41 CdNotifyChangeDirectory (
42 _Inout_ PIRP_CONTEXT IrpContext,
43 _Inout_ PIRP Irp,
44 _In_ PIO_STACK_LOCATION IrpSp,
45 _In_ PCCB Ccb
46 );
47
48 VOID
49 CdInitializeEnumeration (
50 _In_ PIRP_CONTEXT IrpContext,
51 _In_ PIO_STACK_LOCATION IrpSp,
52 _In_ PFCB Fcb,
53 _Inout_ PCCB Ccb,
54 _Inout_ PFILE_ENUM_CONTEXT FileContext,
55 _Out_ PBOOLEAN ReturnNextEntry,
56 _Out_ PBOOLEAN ReturnSingleEntry,
57 _Out_ PBOOLEAN InitialQuery
58 );
59
60 BOOLEAN
61 CdEnumerateIndex (
62 _In_ PIRP_CONTEXT IrpContext,
63 _In_ PCCB Ccb,
64 _Inout_ PFILE_ENUM_CONTEXT FileContext,
65 _In_ BOOLEAN ReturnNextEntry
66 );
67
68 #ifdef ALLOC_PRAGMA
69 #pragma alloc_text(PAGE, CdCommonDirControl)
70 #pragma alloc_text(PAGE, CdEnumerateIndex)
71 #pragma alloc_text(PAGE, CdInitializeEnumeration)
72 #pragma alloc_text(PAGE, CdNotifyChangeDirectory)
73 #pragma alloc_text(PAGE, CdQueryDirectory)
74 #endif
75
76 \f
77
78 _Requires_lock_held_(_Global_critical_region_)
79 NTSTATUS
80 CdCommonDirControl (
81 _Inout_ PIRP_CONTEXT IrpContext,
82 _Inout_ PIRP Irp
83 )
84
85 /*++
86
87 Routine Description:
88
89 This routine is the entry point for the directory control operations. These
90 are directory enumerations and directory notify calls. We verify the
91 user's handle is for a directory and then call the appropriate routine.
92
93 Arguments:
94
95 Irp - Irp for this request.
96
97 Return Value:
98
99 NTSTATUS - Status returned from the lower level routines.
100
101 --*/
102
103 {
104 NTSTATUS Status;
105 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
106
107 PFCB Fcb;
108 PCCB Ccb;
109
110 PAGED_CODE();
111
112 //
113 // Decode the user file object and fail this request if it is not
114 // a user directory.
115 //
116
117 if (CdDecodeFileObject( IrpContext,
118 IrpSp->FileObject,
119 &Fcb,
120 &Ccb ) != UserDirectoryOpen) {
121
122 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
123 return STATUS_INVALID_PARAMETER;
124 }
125
126 //
127 // We know this is a directory control so we'll case on the
128 // minor function, and call a internal worker routine to complete
129 // the irp.
130 //
131
132 switch (IrpSp->MinorFunction) {
133
134 case IRP_MN_QUERY_DIRECTORY:
135
136 Status = CdQueryDirectory( IrpContext, Irp, IrpSp, Fcb, Ccb );
137 break;
138
139 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
140
141 Status = CdNotifyChangeDirectory( IrpContext, Irp, IrpSp, Ccb );
142 break;
143
144 default:
145
146 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
147 Status = STATUS_INVALID_DEVICE_REQUEST;
148 break;
149 }
150
151 return Status;
152 }
153
154 \f
155 //
156 // Local support routines
157 //
158
159 _Requires_lock_held_(_Global_critical_region_)
160 NTSTATUS
161 CdQueryDirectory (
162 _Inout_ PIRP_CONTEXT IrpContext,
163 _Inout_ PIRP Irp,
164 _In_ PIO_STACK_LOCATION IrpSp,
165 _In_ PFCB Fcb,
166 _In_ PCCB Ccb
167 )
168
169 /*++
170
171 Routine Description:
172
173 This routine performs the query directory operation. It is responsible
174 for either completing of enqueuing the input Irp. We store the state of the
175 search in the Ccb.
176
177 Arguments:
178
179 Irp - Supplies the Irp to process
180
181 IrpSp - Stack location for this Irp.
182
183 Fcb - Fcb for this directory.
184
185 Ccb - Ccb for this directory open.
186
187 Return Value:
188
189 NTSTATUS - The return status for the operation
190
191 --*/
192
193 {
194 NTSTATUS Status = STATUS_SUCCESS;
195 ULONG Information = 0;
196
197 ULONG LastEntry = 0;
198 ULONG NextEntry = 0;
199
200 ULONG FileNameBytes;
201 ULONG SeparatorBytes;
202 ULONG VersionStringBytes;
203
204 FILE_ENUM_CONTEXT FileContext;
205 PDIRENT ThisDirent = NULL;
206 BOOLEAN InitialQuery;
207 BOOLEAN ReturnNextEntry = FALSE;
208 BOOLEAN ReturnSingleEntry;
209 BOOLEAN Found;
210 BOOLEAN DoCcbUpdate = FALSE;
211
212 PCHAR UserBuffer;
213 ULONG BytesRemainingInBuffer;
214
215 ULONG BaseLength;
216
217 PFILE_BOTH_DIR_INFORMATION DirInfo = NULL;
218 PFILE_NAMES_INFORMATION NamesInfo;
219 PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo;
220 PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo;
221
222 PAGED_CODE();
223
224 //
225 // Check if we support this search mode. Also remember the size of the base part of
226 // each of these structures.
227 //
228
229 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
230
231 case FileDirectoryInformation:
232
233 BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
234 FileName[0] );
235 break;
236
237 case FileFullDirectoryInformation:
238
239 BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
240 FileName[0] );
241 break;
242
243 case FileIdFullDirectoryInformation:
244
245 BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION,
246 FileName[0] );
247 break;
248
249 case FileNamesInformation:
250
251 BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
252 FileName[0] );
253 break;
254
255 case FileBothDirectoryInformation:
256
257 BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
258 FileName[0] );
259 break;
260
261 case FileIdBothDirectoryInformation:
262
263 BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
264 FileName[0] );
265 break;
266
267 default:
268
269 CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS );
270 return STATUS_INVALID_INFO_CLASS;
271 }
272
273 //
274 // Get the user buffer.
275 //
276
277 CdMapUserBuffer( IrpContext, &UserBuffer);
278
279 //
280 // Initialize our search context.
281 //
282
283 CdInitializeFileContext( IrpContext, &FileContext );
284
285 //
286 // Acquire the directory.
287 //
288
289 CdAcquireFileShared( IrpContext, Fcb );
290
291 //
292 // Use a try-finally to facilitate cleanup.
293 //
294
295 _SEH2_TRY {
296
297 //
298 // Verify the Fcb is still good.
299 //
300
301 CdVerifyFcbOperation( IrpContext, Fcb );
302
303 //
304 // Start by getting the initial state for the enumeration. This will set up the Ccb with
305 // the initial search parameters and let us know the starting offset in the directory
306 // to search.
307 //
308
309 CdInitializeEnumeration( IrpContext,
310 IrpSp,
311 Fcb,
312 Ccb,
313 &FileContext,
314 &ReturnNextEntry,
315 &ReturnSingleEntry,
316 &InitialQuery );
317
318 //
319 // The current dirent is stored in the InitialDirent field. We capture
320 // this here so that we have a valid restart point even if we don't
321 // find a single entry.
322 //
323
324 ThisDirent = &FileContext.InitialDirent->Dirent;
325
326 //
327 // At this point we are about to enter our query loop. We have
328 // determined the index into the directory file to begin the
329 // search. LastEntry and NextEntry are used to index into the user
330 // buffer. LastEntry is the last entry we've added, NextEntry is
331 // current one we're working on. If NextEntry is non-zero, then
332 // at least one entry was added.
333 //
334
335 while (TRUE) {
336
337 //
338 // If the user had requested only a single match and we have
339 // returned that, then we stop at this point. We update the Ccb with
340 // the status based on the last entry returned.
341 //
342
343 if ((NextEntry != 0) && ReturnSingleEntry) {
344
345 DoCcbUpdate = TRUE;
346 try_leave( Status );
347 }
348
349 //
350 // We try to locate the next matching dirent. Our search if based on a starting
351 // dirent offset, whether we should return the current or next entry, whether
352 // we should be doing a short name search and finally whether we should be
353 // checking for a version match.
354 //
355
356 Found = CdEnumerateIndex( IrpContext, Ccb, &FileContext, ReturnNextEntry );
357
358 //
359 // Initialize the value for the next search.
360 //
361
362 ReturnNextEntry = TRUE;
363
364 //
365 // If we didn't receive a dirent, then we are at the end of the
366 // directory. If we have returned any files, we exit with
367 // success, otherwise we return STATUS_NO_MORE_FILES.
368 //
369
370 if (!Found) {
371
372 if (NextEntry == 0) {
373
374 Status = STATUS_NO_MORE_FILES;
375
376 if (InitialQuery) {
377
378 Status = STATUS_NO_SUCH_FILE;
379 }
380 }
381
382 DoCcbUpdate = TRUE;
383 try_leave( Status );
384 }
385
386 //
387 // Remember the dirent for the file we just found.
388 //
389
390 ThisDirent = &FileContext.InitialDirent->Dirent;
391
392 //
393 // Here are the rules concerning filling up the buffer:
394 //
395 // 1. The Io system garentees that there will always be
396 // enough room for at least one base record.
397 //
398 // 2. If the full first record (including file name) cannot
399 // fit, as much of the name as possible is copied and
400 // STATUS_BUFFER_OVERFLOW is returned.
401 //
402 // 3. If a subsequent record cannot completely fit into the
403 // buffer, none of it (as in 0 bytes) is copied, and
404 // STATUS_SUCCESS is returned. A subsequent query will
405 // pick up with this record.
406 //
407
408 //
409 // Let's compute the number of bytes we need to transfer the current entry.
410 //
411
412 SeparatorBytes =
413 VersionStringBytes = 0;
414
415 //
416 // We can look directly at the dirent that we found.
417 //
418
419 FileNameBytes = ThisDirent->CdFileName.FileName.Length;
420
421 //
422 // Compute the number of bytes for the version string if
423 // we will return this. Allow directories with illegal ";".
424 //
425
426 if (((Ccb->SearchExpression.VersionString.Length != 0) ||
427 (FlagOn(ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY))) &&
428 (ThisDirent->CdFileName.VersionString.Length != 0)) {
429
430 SeparatorBytes = 2;
431
432 VersionStringBytes = ThisDirent->CdFileName.VersionString.Length;
433 }
434
435 //
436 // If the slot for the next entry would be beyond the length of the
437 // user's buffer just exit (we know we've returned at least one entry
438 // already). This will happen when we align the pointer past the end.
439 //
440
441 if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) {
442
443 ReturnNextEntry = FALSE;
444 DoCcbUpdate = TRUE;
445 try_leave( Status = STATUS_SUCCESS );
446 }
447
448 //
449 // Compute the number of bytes remaining in the buffer. Round this
450 // down to a WCHAR boundary so we can copy full characters.
451 //
452
453 BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry;
454 ClearFlag( BytesRemainingInBuffer, 1 );
455
456 //
457 // If this won't fit and we have returned a previous entry then just
458 // return STATUS_SUCCESS.
459 //
460
461 if ((BaseLength + FileNameBytes + SeparatorBytes + VersionStringBytes) > BytesRemainingInBuffer) {
462
463 //
464 // If we already found an entry then just exit.
465 //
466
467 if (NextEntry != 0) {
468
469 ReturnNextEntry = FALSE;
470 DoCcbUpdate = TRUE;
471 try_leave( Status = STATUS_SUCCESS );
472 }
473
474 //
475 // Don't even try to return the version string if it doesn't all fit.
476 // Reduce the FileNameBytes to just fit in the buffer.
477 //
478
479 if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) {
480
481 FileNameBytes = BytesRemainingInBuffer - BaseLength;
482 }
483
484 //
485 // Don't return any version string bytes.
486 //
487
488 VersionStringBytes =
489 SeparatorBytes = 0;
490
491 //
492 // Use a status code of STATUS_BUFFER_OVERFLOW. Also set
493 // ReturnSingleEntry so that we will exit the loop at the top.
494 //
495
496 Status = STATUS_BUFFER_OVERFLOW;
497 ReturnSingleEntry = TRUE;
498 }
499
500 //
501 // Protect access to the user buffer with an exception handler.
502 // Since (at our request) IO doesn't buffer these requests, we have
503 // to guard against a user messing with the page protection and other
504 // such trickery.
505 //
506
507 _SEH2_TRY {
508
509 //
510 // Zero and initialize the base part of the current entry.
511 //
512
513 RtlZeroMemory( Add2Ptr( UserBuffer, NextEntry, PVOID ),
514 BaseLength );
515
516 //
517 // Now we have an entry to return to our caller.
518 // We'll case on the type of information requested and fill up
519 // the user buffer if everything fits.
520 //
521
522 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
523
524 case FileBothDirectoryInformation:
525 case FileFullDirectoryInformation:
526 case FileIdBothDirectoryInformation:
527 case FileIdFullDirectoryInformation:
528 case FileDirectoryInformation:
529
530 DirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_BOTH_DIR_INFORMATION );
531
532 //
533 // Use the create time for all the time stamps.
534 //
535
536 CdConvertCdTimeToNtTime( IrpContext,
537 FileContext.InitialDirent->Dirent.CdTime,
538 &DirInfo->CreationTime );
539
540 DirInfo->LastWriteTime = DirInfo->ChangeTime = DirInfo->CreationTime;
541
542 //
543 // Set the attributes and sizes separately for directories and
544 // files.
545 //
546
547 if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) {
548
549 DirInfo->EndOfFile.QuadPart = DirInfo->AllocationSize.QuadPart = 0;
550
551 SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
552
553 } else {
554
555 DirInfo->EndOfFile.QuadPart = FileContext.FileSize;
556 DirInfo->AllocationSize.QuadPart = LlSectorAlign( FileContext.FileSize );
557
558 SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_READONLY);
559 }
560
561 if (FlagOn( ThisDirent->DirentFlags,
562 CD_ATTRIBUTE_HIDDEN )) {
563
564 SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_HIDDEN );
565 }
566
567 DirInfo->FileIndex = ThisDirent->DirentOffset;
568
569 DirInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes;
570
571 break;
572
573 case FileNamesInformation:
574
575 NamesInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_NAMES_INFORMATION );
576
577 NamesInfo->FileIndex = ThisDirent->DirentOffset;
578
579 NamesInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes;
580
581 break;
582
583 /* ReactOS Change: GCC "enumeration value not handled in switch" */
584 default: break;
585 }
586
587 //
588 // Fill in the FileId
589 //
590
591 switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
592
593 case FileIdBothDirectoryInformation:
594
595 IdBothDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_BOTH_DIR_INFORMATION );
596 CdSetFidFromParentAndDirent( IdBothDirInfo->FileId, Fcb, ThisDirent );
597 break;
598
599 case FileIdFullDirectoryInformation:
600
601 IdFullDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_FULL_DIR_INFORMATION );
602 CdSetFidFromParentAndDirent( IdFullDirInfo->FileId, Fcb, ThisDirent );
603 break;
604
605 default:
606 break;
607 }
608
609 //
610 // Now copy as much of the name as possible. We also may have a version
611 // string to copy.
612 //
613
614 if (FileNameBytes != 0) {
615
616 //
617 // This is a Unicode name, we can copy the bytes directly.
618 //
619
620 RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength, PVOID ),
621 ThisDirent->CdFileName.FileName.Buffer,
622 FileNameBytes );
623
624 if (SeparatorBytes != 0) {
625
626 *(Add2Ptr( UserBuffer,
627 NextEntry + BaseLength + FileNameBytes,
628 PWCHAR )) = L';';
629
630 if (VersionStringBytes != 0) {
631
632 RtlCopyMemory( Add2Ptr( UserBuffer,
633 NextEntry + BaseLength + FileNameBytes + sizeof( WCHAR ),
634 PVOID ),
635 ThisDirent->CdFileName.VersionString.Buffer,
636 VersionStringBytes );
637 }
638 }
639 }
640
641 //
642 // Fill in the short name if we got STATUS_SUCCESS. The short name
643 // may already be in the file context. Otherwise we will check
644 // whether the long name is 8.3. Special case the self and parent
645 // directory names.
646 //
647
648 if ((Status == STATUS_SUCCESS) &&
649 (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
650 IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) &&
651 (Ccb->SearchExpression.VersionString.Length == 0) &&
652 !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
653
654 //
655 // If we already have the short name then copy into the user's buffer.
656 //
657
658 if (FileContext.ShortName.FileName.Length != 0) {
659
660 RtlCopyMemory( DirInfo->ShortName,
661 FileContext.ShortName.FileName.Buffer,
662 FileContext.ShortName.FileName.Length );
663
664 DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length;
665
666 //
667 // If the short name length is currently zero then check if
668 // the long name is not 8.3. We can copy the short name in
669 // unicode form directly into the caller's buffer.
670 //
671
672 } else {
673
674 if (!CdIs8dot3Name( IrpContext,
675 ThisDirent->CdFileName.FileName )) {
676
677 CdGenerate8dot3Name( IrpContext,
678 &ThisDirent->CdCaseFileName.FileName,
679 ThisDirent->DirentOffset,
680 DirInfo->ShortName,
681 &FileContext.ShortName.FileName.Length );
682
683 DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length;
684 }
685 }
686
687 }
688
689 //
690 // Sum the total number of bytes for the information field.
691 //
692
693 FileNameBytes += SeparatorBytes + VersionStringBytes;
694
695 //
696 // Update the information with the number of bytes stored in the
697 // buffer. We quad-align the existing buffer to add any necessary
698 // pad bytes.
699 //
700
701 Information = NextEntry + BaseLength + FileNameBytes;
702
703 //
704 // Go back to the previous entry and fill in the update to this entry.
705 //
706
707 *(Add2Ptr( UserBuffer, LastEntry, PULONG )) = NextEntry - LastEntry;
708
709 //
710 // Set up our variables for the next dirent.
711 //
712
713 InitialQuery = FALSE;
714
715 LastEntry = NextEntry;
716 NextEntry = QuadAlign( Information );
717
718 #ifdef _MSC_VER
719 #pragma warning(suppress: 6320)
720 #endif
721 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
722
723 //
724 // We had a problem filling in the user's buffer, so stop and
725 // fail this request. This is the only reason any exception
726 // would have occured at this level.
727 //
728
729 Information = 0;
730 try_leave( Status = _SEH2_GetExceptionCode());
731 } _SEH2_END;
732 }
733
734 DoCcbUpdate = TRUE;
735
736 } _SEH2_FINALLY {
737
738 //
739 // Cleanup our search context - *before* aquiring the FCB mutex exclusive,
740 // else can block on threads in cdcreateinternalstream/purge which
741 // hold the FCB but are waiting for all maps in this stream to be released.
742 //
743
744 CdCleanupFileContext( IrpContext, &FileContext );
745
746 //
747 // Now we can safely aqure the FCB mutex if we need to.
748 //
749
750 if (DoCcbUpdate && !NT_ERROR( Status )) {
751
752 //
753 // Update the Ccb to show the current state of the enumeration.
754 //
755
756 CdLockFcb( IrpContext, Fcb );
757
758 Ccb->CurrentDirentOffset = ThisDirent->DirentOffset;
759
760 ClearFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
761
762 if (ReturnNextEntry) {
763
764 SetFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
765 }
766
767 CdUnlockFcb( IrpContext, Fcb );
768 }
769
770 //
771 // Release the Fcb.
772 //
773
774 CdReleaseFile( IrpContext, Fcb );
775 } _SEH2_END;
776
777 //
778 // Complete the request here.
779 //
780
781 Irp->IoStatus.Information = Information;
782
783 CdCompleteRequest( IrpContext, Irp, Status );
784 return Status;
785 }
786
787 \f
788 //
789 // Local support routines
790 //
791
792 _Requires_lock_held_(_Global_critical_region_)
793 NTSTATUS
794 CdNotifyChangeDirectory (
795 _Inout_ PIRP_CONTEXT IrpContext,
796 _Inout_ PIRP Irp,
797 _In_ PIO_STACK_LOCATION IrpSp,
798 _In_ PCCB Ccb
799 )
800
801 /*++
802
803 Routine Description:
804
805 This routine performs the notify change directory operation. It is
806 responsible for either completing of enqueuing the input Irp. Although there
807 will never be a notify signalled on a CDROM disk we still support this call.
808
809 We have already checked that this is not an OpenById handle.
810
811 Arguments:
812
813 Irp - Supplies the Irp to process
814
815 IrpSp - Io stack location for this request.
816
817 Ccb - Handle to the directory being watched.
818
819 Return Value:
820
821 NTSTATUS - STATUS_PENDING, any other error will raise.
822
823 --*/
824
825 {
826 PAGED_CODE();
827
828 //
829 // Always set the wait bit in the IrpContext so the initial wait can't fail.
830 //
831
832 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
833
834 //
835 // Acquire the Vcb shared.
836 //
837
838 CdAcquireVcbShared( IrpContext, IrpContext->Vcb, FALSE );
839
840 //
841 // Use a try-finally to facilitate cleanup.
842 //
843
844 _SEH2_TRY {
845
846 //
847 // Verify the Vcb.
848 //
849
850 CdVerifyVcb( IrpContext, IrpContext->Vcb );
851
852 //
853 // Call the Fsrtl package to process the request. We cast the
854 // unicode strings to ansi strings as the dir notify package
855 // only deals with memory matching.
856 //
857
858 FsRtlNotifyFullChangeDirectory( IrpContext->Vcb->NotifySync,
859 &IrpContext->Vcb->DirNotifyList,
860 Ccb,
861 (PSTRING) &IrpSp->FileObject->FileName,
862 BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ),
863 FALSE,
864 IrpSp->Parameters.NotifyDirectory.CompletionFilter,
865 Irp,
866 NULL,
867 NULL );
868
869 } _SEH2_FINALLY {
870
871 //
872 // Release the Vcb.
873 //
874
875 CdReleaseVcb( IrpContext, IrpContext->Vcb );
876 } _SEH2_END;
877
878 //
879 // Cleanup the IrpContext.
880 //
881
882 CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
883
884 return STATUS_PENDING;
885 }
886
887 \f
888 //
889 // Local support routine
890 //
891
892 VOID
893 CdInitializeEnumeration (
894 _In_ PIRP_CONTEXT IrpContext,
895 _In_ PIO_STACK_LOCATION IrpSp,
896 _In_ PFCB Fcb,
897 _Inout_ PCCB Ccb,
898 _Inout_ PFILE_ENUM_CONTEXT FileContext,
899 _Out_ PBOOLEAN ReturnNextEntry,
900 _Out_ PBOOLEAN ReturnSingleEntry,
901 _Out_ PBOOLEAN InitialQuery
902 )
903
904 /*++
905
906 Routine Description:
907
908 This routine is called to initialize the enumeration variables and structures.
909 We look at the state of a previous enumeration from the Ccb as well as any
910 input values from the user. On exit we will position the FileContext at
911 a file in the directory and let the caller know whether this entry or the
912 next entry should be returned.
913
914 Arguments:
915
916 IrpSp - Irp stack location for this request.
917
918 Fcb - Fcb for this directory.
919
920 Ccb - Ccb for the directory handle.
921
922 FileContext - FileContext to use for this enumeration.
923
924 ReturnNextEntry - Address to store whether we should return the entry at
925 the FileContext position or the next entry.
926
927 ReturnSingleEntry - Address to store whether we should only return
928 a single entry.
929
930 InitialQuery - Address to store whether this is the first enumeration
931 query on this handle.
932
933 Return Value:
934
935 None.
936
937 --*/
938
939 {
940 NTSTATUS Status;
941
942 PUNICODE_STRING FileName;
943 CD_NAME WildCardName;
944 CD_NAME SearchExpression;
945
946 ULONG CcbFlags;
947
948 ULONG DirentOffset;
949 ULONG LastDirentOffset;
950 BOOLEAN KnownOffset;
951
952 BOOLEAN Found;
953
954 PAGED_CODE();
955
956 //
957 // If the user has specified that the scan be restarted, and has specicified
958 // a new query pattern, reinitialize the CCB.
959 //
960
961 if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {
962
963 CdLockFcb( IrpContext, Fcb );
964
965 FileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName;
966 if (FileName && FileName->Length > 0) {
967
968 if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {
969
970 CdFreePool( &Ccb->SearchExpression.FileName.Buffer );
971 }
972
973 ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL);
974 ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED);
975 ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD);
976 }
977
978 CdUnlockFcb( IrpContext, Fcb );
979 }
980
981 //
982 // If this is the initial query then build a search expression from the input
983 // file name.
984 //
985
986 if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
987
988 FileName = IrpSp->Parameters.QueryDirectory.FileName;
989
990 CcbFlags = 0;
991
992 //
993 // If the filename is not specified or is a single '*' then we will
994 // match all names.
995 //
996
997 if ((FileName == NULL) ||
998 (FileName->Buffer == NULL) ||
999 (FileName->Length == 0) ||
1000 ((FileName->Length == sizeof( WCHAR )) &&
1001 (FileName->Buffer[0] == L'*'))) {
1002
1003 SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL );
1004 RtlZeroMemory( &SearchExpression, sizeof( SearchExpression ));
1005
1006 //
1007 // Otherwise build the CdName from the name in the stack location.
1008 // This involves building both the name and version portions and
1009 // checking for wild card characters. We also upcase the string if
1010 // this is a case-insensitive search.
1011 //
1012
1013 } else {
1014
1015 //
1016 // Create a CdName to check for wild cards.
1017 //
1018
1019 WildCardName.FileName = *FileName;
1020
1021 CdConvertNameToCdName( IrpContext, &WildCardName );
1022
1023 //
1024 // The name better have at least one character.
1025 //
1026
1027 if (WildCardName.FileName.Length == 0) {
1028
1029 CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER );
1030 }
1031
1032 //
1033 // Check for wildcards in the separate components.
1034 //
1035
1036 if (FsRtlDoesNameContainWildCards( &WildCardName.FileName)) {
1037
1038 SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD );
1039 }
1040
1041 if ((WildCardName.VersionString.Length != 0) &&
1042 (FsRtlDoesNameContainWildCards( &WildCardName.VersionString ))) {
1043
1044 SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD );
1045
1046 //
1047 // Check if this is a wild card only and match all version
1048 // strings.
1049 //
1050
1051 if ((WildCardName.VersionString.Length == sizeof( WCHAR )) &&
1052 (WildCardName.VersionString.Buffer[0] == L'*')) {
1053
1054 SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL );
1055 }
1056 }
1057
1058 //
1059 // Now create the search expression to store in the Ccb.
1060 //
1061
1062 SearchExpression.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
1063 FileName->Length,
1064 TAG_ENUM_EXPRESSION );
1065
1066 SearchExpression.FileName.MaximumLength = FileName->Length;
1067
1068 //
1069 // Either copy the name directly or perform the upcase.
1070 //
1071
1072 if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) {
1073
1074 Status = RtlUpcaseUnicodeString( (PUNICODE_STRING) &SearchExpression.FileName,
1075 FileName,
1076 FALSE );
1077
1078 //
1079 // This should never fail.
1080 //
1081 __analysis_assert( Status == STATUS_SUCCESS );
1082 NT_ASSERT( Status == STATUS_SUCCESS );
1083
1084 } else {
1085
1086 RtlCopyMemory( SearchExpression.FileName.Buffer,
1087 FileName->Buffer,
1088 FileName->Length );
1089 }
1090
1091 //
1092 // Now split into the separate name and version components.
1093 //
1094
1095 SearchExpression.FileName.Length = WildCardName.FileName.Length;
1096 SearchExpression.VersionString.Length = WildCardName.VersionString.Length;
1097 SearchExpression.VersionString.MaximumLength = WildCardName.VersionString.MaximumLength;
1098
1099 SearchExpression.VersionString.Buffer = Add2Ptr( SearchExpression.FileName.Buffer,
1100 SearchExpression.FileName.Length + sizeof( WCHAR ),
1101 PWCHAR );
1102 }
1103
1104 //
1105 // But we do not want to return the constant "." and ".." entries for
1106 // the root directory, for consistency with the rest of Microsoft's
1107 // filesystems.
1108 //
1109
1110 if (Fcb == Fcb->Vcb->RootIndexFcb) {
1111
1112 SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY );
1113 }
1114
1115 //
1116 // Now lock the Fcb in order to update the Ccb with the inital
1117 // enumeration values.
1118 //
1119
1120 CdLockFcb( IrpContext, Fcb );
1121
1122 //
1123 // Check again that this is the initial search.
1124 //
1125
1126 if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
1127
1128 //
1129 // Update the values in the Ccb.
1130 //
1131
1132 Ccb->CurrentDirentOffset = Fcb->StreamOffset;
1133 Ccb->SearchExpression = SearchExpression;
1134
1135 //
1136 // Set the appropriate flags in the Ccb.
1137 //
1138
1139 SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED );
1140
1141 //
1142 // Otherwise cleanup any buffer allocated here.
1143 //
1144
1145 } else {
1146
1147 if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) {
1148
1149 CdFreePool( &SearchExpression.FileName.Buffer );
1150 }
1151 }
1152
1153 //
1154 // Otherwise lock the Fcb so we can read the current enumeration values.
1155 //
1156
1157 } else {
1158
1159 CdLockFcb( IrpContext, Fcb );
1160 }
1161
1162 //
1163 // Capture the current state of the enumeration.
1164 //
1165 // If the user specified an index then use his offset. We always
1166 // return the next entry in this case.
1167 //
1168
1169 if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )) {
1170
1171 KnownOffset = FALSE;
1172 DirentOffset = IrpSp->Parameters.QueryDirectory.FileIndex;
1173 *ReturnNextEntry = TRUE;
1174
1175 //
1176 // If we are restarting the scan then go from the self entry.
1177 //
1178
1179 } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {
1180
1181 KnownOffset = TRUE;
1182 DirentOffset = Fcb->StreamOffset;
1183 *ReturnNextEntry = FALSE;
1184
1185 //
1186 // Otherwise use the values from the Ccb.
1187 //
1188
1189 } else {
1190
1191 KnownOffset = TRUE;
1192 DirentOffset = Ccb->CurrentDirentOffset;
1193 *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
1194 }
1195
1196 //
1197 // Unlock the Fcb.
1198 //
1199
1200 CdUnlockFcb( IrpContext, Fcb );
1201
1202 //
1203 // We have the starting offset in the directory and whether to return
1204 // that entry or the next. If we are at the beginning of the directory
1205 // and are returning that entry, then tell our caller this is the
1206 // initial query.
1207 //
1208
1209 *InitialQuery = FALSE;
1210
1211 if ((DirentOffset == Fcb->StreamOffset) &&
1212 !(*ReturnNextEntry)) {
1213
1214 *InitialQuery = TRUE;
1215 }
1216
1217 //
1218 // If there is no file object then create it now.
1219 //
1220
1221 CdVerifyOrCreateDirStreamFile( IrpContext, Fcb);
1222
1223 //
1224 // Determine the offset in the stream to position the FileContext and
1225 // whether this offset is known to be a file offset.
1226 //
1227 // If this offset is known to be safe then go ahead and position the
1228 // file context. This handles the cases where the offset is the beginning
1229 // of the stream, the offset is from a previous search or this is the
1230 // initial query.
1231 //
1232
1233 if (KnownOffset) {
1234
1235 CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, DirentOffset );
1236
1237 //
1238 // Otherwise we walk through the directory from the beginning until
1239 // we reach the entry which contains this offset.
1240 //
1241
1242 } else {
1243
1244 LastDirentOffset = Fcb->StreamOffset;
1245 Found = TRUE;
1246
1247 CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset );
1248
1249 //
1250 // If the requested offset is prior to the beginning offset in the stream
1251 // then don't return the next entry.
1252 //
1253
1254 if (DirentOffset < LastDirentOffset) {
1255
1256 *ReturnNextEntry = FALSE;
1257
1258 //
1259 // Else look for the last entry which ends past the desired index.
1260 //
1261
1262 } else {
1263
1264 //
1265 // Keep walking through the directory until we run out of
1266 // entries or we find an entry which ends beyond the input
1267 // index value.
1268 //
1269
1270 do {
1271
1272 //
1273 // If we have passed the index value then exit.
1274 //
1275
1276 if (FileContext->InitialDirent->Dirent.DirentOffset > DirentOffset) {
1277
1278 Found = FALSE;
1279 break;
1280 }
1281
1282 //
1283 // Remember the current position in case we need to go back.
1284 //
1285
1286 LastDirentOffset = FileContext->InitialDirent->Dirent.DirentOffset;
1287
1288 //
1289 // Exit if the next entry is beyond the desired index value.
1290 //
1291
1292 if (LastDirentOffset + FileContext->InitialDirent->Dirent.DirentLength > DirentOffset) {
1293
1294 break;
1295 }
1296
1297 Found = CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext );
1298
1299 } while (Found);
1300
1301 //
1302 // If we didn't find the entry then go back to the last known entry.
1303 // This can happen if the index lies in the unused range at the
1304 // end of a sector.
1305 //
1306
1307 if (!Found) {
1308
1309 CdCleanupFileContext( IrpContext, FileContext );
1310 CdInitializeFileContext( IrpContext, FileContext );
1311
1312 CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset );
1313 }
1314 }
1315 }
1316
1317 //
1318 // Only update the dirent name if we will need it for some reason.
1319 // Don't update this name if we are returning the next entry and
1320 // the search string has a version component.
1321 //
1322
1323 FileContext->ShortName.FileName.Length = 0;
1324
1325 if (!(*ReturnNextEntry) ||
1326 (Ccb->SearchExpression.VersionString.Length == 0)) {
1327
1328 //
1329 // Update the name in the dirent into filename and version components.
1330 //
1331
1332 CdUpdateDirentName( IrpContext,
1333 &FileContext->InitialDirent->Dirent,
1334 FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
1335 }
1336
1337 //
1338 // Look at the flag in the IrpSp indicating whether to return just
1339 // one entry.
1340 //
1341
1342 *ReturnSingleEntry = FALSE;
1343
1344 if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) {
1345
1346 *ReturnSingleEntry = TRUE;
1347 }
1348
1349 return;
1350 }
1351
1352 \f
1353 //
1354 // Local support routine
1355 //
1356
1357 BOOLEAN
1358 CdEnumerateIndex (
1359 _In_ PIRP_CONTEXT IrpContext,
1360 _In_ PCCB Ccb,
1361 _Inout_ PFILE_ENUM_CONTEXT FileContext,
1362 _In_ BOOLEAN ReturnNextEntry
1363 )
1364
1365 /*++
1366
1367 Routine Description:
1368
1369 This routine is the worker routine for index enumeration. We are positioned
1370 at some dirent in the directory and will either return the first match
1371 at that point or look to the next entry. The Ccb contains details about
1372 the type of matching to do. If the user didn't specify a version in
1373 his search string then we only return the first version of a sequence
1374 of files with versions. We also don't return any associated files.
1375
1376 Arguments:
1377
1378 Ccb - Ccb for this directory handle.
1379
1380 FileContext - File context already positioned at some entry in the directory.
1381
1382 ReturnNextEntry - Indicates if we are returning this entry or should start
1383 with the next entry.
1384
1385 Return Value:
1386
1387 BOOLEAN - TRUE if next entry is found, FALSE otherwise.
1388
1389 --*/
1390
1391 {
1392 PDIRENT PreviousDirent = NULL;
1393 PDIRENT ThisDirent = &FileContext->InitialDirent->Dirent;
1394
1395 BOOLEAN Found = FALSE;
1396
1397 PAGED_CODE();
1398
1399 //
1400 // Loop until we find a match or exaust the directory.
1401 //
1402
1403 while (TRUE) {
1404
1405 //
1406 // Move to the next entry unless we want to consider the current
1407 // entry.
1408 //
1409
1410 if (ReturnNextEntry) {
1411
1412 if (!CdLookupNextInitialFileDirent( IrpContext, Ccb->Fcb, FileContext )) {
1413
1414 break;
1415 }
1416
1417 PreviousDirent = ThisDirent;
1418 ThisDirent = &FileContext->InitialDirent->Dirent;
1419
1420 CdUpdateDirentName( IrpContext, ThisDirent, FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
1421
1422 } else {
1423
1424 ReturnNextEntry = TRUE;
1425 }
1426
1427 //
1428 // Don't bother if we have a constant entry and are ignoring them.
1429 //
1430
1431 if (FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) &&
1432 FlagOn( Ccb->Flags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY )) {
1433
1434 continue;
1435 }
1436
1437 //
1438 // Look at the current entry if it is not an associated file
1439 // and the name doesn't match the previous file if the version
1440 // name is not part of the search.
1441 //
1442
1443 if (!FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) {
1444
1445 //
1446 // Check if this entry matches the previous entry except
1447 // for version number and whether we should return the
1448 // entry in that case. Go directly to the name comparison
1449 // if:
1450 //
1451 // There is no previous entry.
1452 // The search expression has a version component.
1453 // The name length doesn't match the length of the previous entry.
1454 // The base name strings don't match.
1455 //
1456
1457 if ((PreviousDirent == NULL) ||
1458 (Ccb->SearchExpression.VersionString.Length != 0) ||
1459 (PreviousDirent->CdCaseFileName.FileName.Length != ThisDirent->CdCaseFileName.FileName.Length) ||
1460 FlagOn( PreviousDirent->DirentFlags, CD_ATTRIBUTE_ASSOC ) ||
1461 !RtlEqualMemory( PreviousDirent->CdCaseFileName.FileName.Buffer,
1462 ThisDirent->CdCaseFileName.FileName.Buffer,
1463 ThisDirent->CdCaseFileName.FileName.Length )) {
1464
1465 //
1466 // If we match all names then return to our caller.
1467 //
1468
1469 if (FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {
1470
1471 FileContext->ShortName.FileName.Length = 0;
1472 Found = TRUE;
1473 break;
1474 }
1475
1476 //
1477 // Check if the long name matches the search expression.
1478 //
1479
1480 if (CdIsNameInExpression( IrpContext,
1481 &ThisDirent->CdCaseFileName,
1482 &Ccb->SearchExpression,
1483 Ccb->Flags,
1484 TRUE )) {
1485
1486 //
1487 // Let our caller know we found an entry.
1488 //
1489
1490 Found = TRUE;
1491 FileContext->ShortName.FileName.Length = 0;
1492 break;
1493 }
1494
1495 //
1496 // The long name didn't match so we need to check for a
1497 // possible short name match. There is no match if the
1498 // long name is 8dot3 or the search expression has a
1499 // version component. Special case the self and parent
1500 // entries.
1501 //
1502
1503 if ((Ccb->SearchExpression.VersionString.Length == 0) &&
1504 !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) &&
1505 !CdIs8dot3Name( IrpContext,
1506 ThisDirent->CdFileName.FileName )) {
1507
1508 CdGenerate8dot3Name( IrpContext,
1509 &ThisDirent->CdCaseFileName.FileName,
1510 ThisDirent->DirentOffset,
1511 FileContext->ShortName.FileName.Buffer,
1512 &FileContext->ShortName.FileName.Length );
1513
1514 //
1515 // Check if this name matches.
1516 //
1517
1518 if (CdIsNameInExpression( IrpContext,
1519 &FileContext->ShortName,
1520 &Ccb->SearchExpression,
1521 Ccb->Flags,
1522 FALSE )) {
1523
1524 //
1525 // Let our caller know we found an entry.
1526 //
1527
1528 Found = TRUE;
1529 break;
1530 }
1531 }
1532 }
1533 }
1534 }
1535
1536 //
1537 // If we found the entry then make sure we walk through all of the
1538 // file dirents.
1539 //
1540
1541 if (Found) {
1542
1543 CdLookupLastFileDirent( IrpContext, Ccb->Fcb, FileContext );
1544 }
1545
1546 return Found;
1547 }
1548
1549