1 /*++
2
3 Copyright (c) 1989-2000 Microsoft Corporation
4
5 Module Name:
6
7 PathSup.c
8
9 Abstract:
10
11 This module implements the Path Table support routines for Cdfs.
12
13 The path table on a CDROM is a condensed summary of the entire
14 directory structure. It is stored on a number of contiguous sectors
15 on the disk. Each directory on the disk has an entry in the path
16 table. The entries are aligned on USHORT boundaries and MAY span
17 sector boundaries. The entries are stored as a breadth-first search.
18
19 The first entry in the table contains the entry for the root. The
20 next entries will consist of the contents of the root directory. The
21 next entries will consist of the all the directories at the next level
22 of the tree. The children of a given directory will be grouped together.
23
24 The directories are assigned ordinal numbers based on their position in
25 the path table. The root dirctory is assigned ordinal value 1.
26
27 Path table sectors:
28
29 Ordinal 1 2 3 4 5 6
30 +-----------+
31 | Spanning |
32 | Sectors |
33 +----------------------------+ +------------------------+
34 | | | | | | | | |
35 DirName | \ | a | b |c| | c | d | e |
36 | | | | | | | | |
37 Parent #| 1 | 1 | 1 | | | 2 | 2 | 3 |
38 +----------------------------+ +------------------------+
39
40 Directory Tree:
41
42 \ (root)
43
44 / \
45 / \
46 a b
47
48 / \ \
49 / \ \
50 c d e
51
52 Path Table Entries:
53
54 - Position scan at known offset in the path table. Path Entry at
55 this offset must exist and is known to be valid. Used when
56 scanning for the children of a given directory.
57
58 - Position scan at known offset in the path table. Path Entry is
59 known to start at this location but the bounds must be checked
60 for validity.
61
62 - Move to next path entry in the table.
63
64 - Update a common path entry structure with the details of the
65 on-disk structure. This is used to smooth out the differences
66 in the on-disk structures.
67
68 - Update the filename in the in-memory path entry with the bytes
69 off the disk. For Joliet disks we will have
70 to convert to little endian. We assume that directories
71 don't have version numbers.
72
73
74 --*/
75
76 #include "cdprocs.h"
77
78 //
79 // The Bug check file id for this module
80 //
81
82 #define BugCheckFileId (CDFS_BUG_CHECK_PATHSUP)
83
84 //
85 // Local macros
86 //
87
88 //
89 // PRAW_PATH_ENTRY
90 // CdRawPathEntry (
91 // _In_ PIRP_CONTEXT IrpContext,
92 // _In_ PPATH_ENUM_CONTEXT PathContext
93 // );
94 //
95
96 #define CdRawPathEntry(IC, PC) \
97 Add2Ptr( (PC)->Data, (PC)->DataOffset, PRAW_PATH_ENTRY )
98
99 //
100 // Local support routines
101 //
102
103 VOID
104 CdMapPathTableBlock (
105 _In_ PIRP_CONTEXT IrpContext,
106 _In_ PFCB Fcb,
107 _In_ LONGLONG BaseOffset,
108 _Inout_ PPATH_ENUM_CONTEXT PathContext
109 );
110
111 _Success_(return != FALSE)
112 BOOLEAN
113 CdUpdatePathEntryFromRawPathEntry (
114 _In_ PIRP_CONTEXT IrpContext,
115 _In_ ULONG Ordinal,
116 _In_ BOOLEAN VerifyBounds,
117 _In_ PPATH_ENUM_CONTEXT PathContext,
118 _Out_ PPATH_ENTRY PathEntry
119 );
120
121 #ifdef ALLOC_PRAGMA
122 #pragma alloc_text(PAGE, CdFindPathEntry)
123 #pragma alloc_text(PAGE, CdLookupPathEntry)
124 #pragma alloc_text(PAGE, CdLookupNextPathEntry)
125 #pragma alloc_text(PAGE, CdMapPathTableBlock)
126 #pragma alloc_text(PAGE, CdUpdatePathEntryFromRawPathEntry)
127 #pragma alloc_text(PAGE, CdUpdatePathEntryName)
128 #endif
129
130
131 VOID
CdLookupPathEntry(_In_ PIRP_CONTEXT IrpContext,_In_ ULONG PathEntryOffset,_In_ ULONG Ordinal,_In_ BOOLEAN VerifyBounds,_Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry)132 CdLookupPathEntry (
133 _In_ PIRP_CONTEXT IrpContext,
134 _In_ ULONG PathEntryOffset,
135 _In_ ULONG Ordinal,
136 _In_ BOOLEAN VerifyBounds,
137 _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry
138 )
139
140 /*++
141
142 Routine Description:
143
144 This routine is called to initiate a walk through a path table. We are
145 looking for a path table entry at location PathEntryOffset.
146
147 Arguments:
148
149 PathEntryOffset - This is our target point in the Path Table. We know that
150 a path entry must begin at this point although we may have to verify
151 the bounds.
152
153 Ordinal - Ordinal number for the directory at the PathEntryOffset above.
154
155 VerifyBounds - Indicates whether we need to check the validity of
156 this entry.
157
158 CompoundPathEntry - PathEnumeration context and in-memory path entry. This
159 has been initialized outside of this call.
160
161 Return Value:
162
163 None.
164
165 --*/
166
167 {
168 PPATH_ENUM_CONTEXT PathContext = &CompoundPathEntry->PathContext;
169 LONGLONG CurrentBaseOffset;
170
171 PAGED_CODE();
172
173 //
174 // Compute the starting base and starting path table offset.
175 //
176
177 CurrentBaseOffset = SectorTruncate( PathEntryOffset );
178
179 //
180 // Map the next block in the Path Table.
181 //
182
183 CdMapPathTableBlock( IrpContext,
184 IrpContext->Vcb->PathTableFcb,
185 CurrentBaseOffset,
186 PathContext );
187
188 //
189 // Set up our current offset into the Path Context.
190 //
191
192 PathContext->DataOffset = PathEntryOffset - PathContext->BaseOffset;
193
194 //
195 // Update the in-memory structure for this path entry.
196 //
197
198 (VOID) CdUpdatePathEntryFromRawPathEntry( IrpContext,
199 Ordinal,
200 VerifyBounds,
201 &CompoundPathEntry->PathContext,
202 &CompoundPathEntry->PathEntry );
203 }
204
205
206 BOOLEAN
CdLookupNextPathEntry(_In_ PIRP_CONTEXT IrpContext,_Inout_ PPATH_ENUM_CONTEXT PathContext,_Inout_ PPATH_ENTRY PathEntry)207 CdLookupNextPathEntry (
208 _In_ PIRP_CONTEXT IrpContext,
209 _Inout_ PPATH_ENUM_CONTEXT PathContext,
210 _Inout_ PPATH_ENTRY PathEntry
211 )
212
213 /*++
214
215 Routine Description:
216
217 This routine is called to move to the next path table entry. We know
218 the offset and the length of the current entry. We start by computing
219 the offset of the next entry and determine if it is contained in the
220 table. Then we check to see if we need to move to the next sector in
221 the path table. We always map two sectors at a time so we don't
222 have to deal with any path entries which span sectors. We move to
223 the next sector if we are in the second sector of the current mapped
224 data block.
225
226 We look up the next entry and update the path entry structure with
227 the values out of the raw sector but don't update the CdName structure.
228
229 Arguments:
230
231 PathContext - Enumeration context for this scan of the path table.
232
233 PathEntry - In-memory representation of the on-disk path table entry.
234
235 Return Value:
236
237 BOOLEAN - TRUE if another entry is found, FALSE otherwise.
238 This routine may raise on error.
239
240 --*/
241
242 {
243 LONGLONG CurrentBaseOffset;
244
245 PAGED_CODE();
246
247 //
248 // Get the offset of the next path entry within the current
249 // data block.
250 //
251
252 PathContext->DataOffset += PathEntry->PathEntryLength;
253
254 //
255 // If we are in the last data block then check if we are beyond the
256 // end of the file.
257 //
258
259 if (PathContext->LastDataBlock) {
260
261 if (PathContext->DataOffset >= PathContext->DataLength) {
262
263 return FALSE;
264 }
265
266 //
267 // If we are not in the last data block of the path table and
268 // this offset is in the second sector then move to the next
269 // data block.
270 //
271
272 } else if (PathContext->DataOffset >= SECTOR_SIZE) {
273
274 CurrentBaseOffset = PathContext->BaseOffset + SECTOR_SIZE;
275
276 CdMapPathTableBlock( IrpContext,
277 IrpContext->Vcb->PathTableFcb,
278 CurrentBaseOffset,
279 PathContext );
280
281 //
282 // Set up our current offset into the Path Context.
283 //
284
285 PathContext->DataOffset -= SECTOR_SIZE;
286 }
287
288 //
289 // Now update the path entry with the values from the on-disk
290 // structure.
291 //
292
293 return CdUpdatePathEntryFromRawPathEntry( IrpContext,
294 PathEntry->Ordinal + 1,
295 TRUE,
296 PathContext,
297 PathEntry );
298 }
299
300 _Success_(return != FALSE)
301 BOOLEAN
CdFindPathEntry(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB ParentFcb,_In_ PCD_NAME DirName,_In_ BOOLEAN IgnoreCase,_Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry)302 CdFindPathEntry (
303 _In_ PIRP_CONTEXT IrpContext,
304 _In_ PFCB ParentFcb,
305 _In_ PCD_NAME DirName,
306 _In_ BOOLEAN IgnoreCase,
307 _Inout_ PCOMPOUND_PATH_ENTRY CompoundPathEntry
308 )
309
310 /*++
311
312 Routine Description:
313
314 This routine will walk through the path table looking for a matching entry for DirName
315 among the child directories of the ParentFcb.
316
317 Arguments:
318
319 ParentFcb - This is the directory we are examining. We know the ordinal and path table
320 offset for this directory in the path table. If this is the first scan for this
321 Fcb we will update the first child offset for this directory in the path table.
322
323 DirName - This is the name we are searching for. This name will not contain wildcard
324 characters. The name will also not have a version string.
325
326 IgnoreCase - Indicates if this search is exact or ignore case.
327
328 CompoundPathEntry - Complete path table enumeration structure. We will have initialized
329 it for the search on entry. This will be positioned at the matching name if found.
330
331 Return Value:
332
333 BOOLEAN - TRUE if matching entry found, FALSE otherwise.
334
335 --*/
336
337 {
338 BOOLEAN Found = FALSE;
339 BOOLEAN UpdateChildOffset = TRUE;
340
341 ULONG StartingOffset;
342 ULONG StartingOrdinal;
343
344 PAGED_CODE();
345
346 //
347 // Position ourselves at either the first child or at the directory itself.
348 // Lock the Fcb to get this value and remember whether to update with the first
349 // child.
350 //
351
352 StartingOffset = CdQueryFidPathTableOffset( ParentFcb->FileId );
353 StartingOrdinal = ParentFcb->Ordinal;
354
355 //
356 // ISO 9660 9.4.4 restricts the backpointer from child to parent in a
357 // pathtable entry to 16bits. Although we internally store ordinals
358 // as 32bit values, it is impossible to search for the children of a
359 // directory whose ordinal value is greater than MAXUSHORT. Media that
360 // could induce such a search is illegal.
361 //
362 // Note that it is not illegal to have more than MAXUSHORT directories.
363 //
364
365 if (ParentFcb->Ordinal > MAXUSHORT) {
366
367 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
368 }
369
370 CdLockFcb( IrpContext, ParentFcb );
371
372 if (ParentFcb->ChildPathTableOffset != 0) {
373
374 StartingOffset = ParentFcb->ChildPathTableOffset;
375 StartingOrdinal = ParentFcb->ChildOrdinal;
376 UpdateChildOffset = FALSE;
377
378 } else if (ParentFcb == ParentFcb->Vcb->RootIndexFcb) {
379
380 UpdateChildOffset = FALSE;
381 }
382
383 CdUnlockFcb( IrpContext, ParentFcb );
384
385 CdLookupPathEntry( IrpContext, StartingOffset, StartingOrdinal, FALSE, CompoundPathEntry );
386
387 //
388 // Loop until we find a match or are beyond the children for this directory.
389 //
390
391 do {
392
393 //
394 // If we are beyond this directory then return FALSE.
395 //
396
397 if (CompoundPathEntry->PathEntry.ParentOrdinal > ParentFcb->Ordinal) {
398
399 //
400 // Update the Fcb with the offsets for the children in the path table.
401 //
402
403 if (UpdateChildOffset) {
404
405 CdLockFcb( IrpContext, ParentFcb );
406
407 ParentFcb->ChildPathTableOffset = StartingOffset;
408 ParentFcb->ChildOrdinal = StartingOrdinal;
409
410 CdUnlockFcb( IrpContext, ParentFcb );
411 }
412
413 break;
414 }
415
416 //
417 // If we are within the children of this directory then check for a match.
418 //
419
420 if (CompoundPathEntry->PathEntry.ParentOrdinal == ParentFcb->Ordinal) {
421
422 //
423 // Update the child offset if not yet done.
424 //
425
426 if (UpdateChildOffset) {
427
428 CdLockFcb( IrpContext, ParentFcb );
429
430 ParentFcb->ChildPathTableOffset = CompoundPathEntry->PathEntry.PathTableOffset;
431 ParentFcb->ChildOrdinal = CompoundPathEntry->PathEntry.Ordinal;
432
433 CdUnlockFcb( IrpContext, ParentFcb );
434
435 UpdateChildOffset = FALSE;
436 }
437
438 //
439 // Update the name in the path entry.
440 //
441
442 CdUpdatePathEntryName( IrpContext, &CompoundPathEntry->PathEntry, IgnoreCase );
443
444 //
445 // Now compare the names for an exact match.
446 //
447
448 if (CdIsNameInExpression( IrpContext,
449 &CompoundPathEntry->PathEntry.CdCaseDirName,
450 DirName,
451 0,
452 FALSE )) {
453
454 //
455 // Let our caller know we have a match.
456 //
457
458 Found = TRUE;
459 break;
460 }
461 }
462
463 //
464 // Go to the next entry in the path table. Remember the current position
465 // in the event we update the Fcb.
466 //
467
468 StartingOffset = CompoundPathEntry->PathEntry.PathTableOffset;
469 StartingOrdinal = CompoundPathEntry->PathEntry.Ordinal;
470
471 } while (CdLookupNextPathEntry( IrpContext,
472 &CompoundPathEntry->PathContext,
473 &CompoundPathEntry->PathEntry ));
474
475 return Found;
476 }
477
478
479 //
480 // Local support routine
481 //
482
483 VOID
CdMapPathTableBlock(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ LONGLONG BaseOffset,_Inout_ PPATH_ENUM_CONTEXT PathContext)484 CdMapPathTableBlock (
485 _In_ PIRP_CONTEXT IrpContext,
486 _In_ PFCB Fcb,
487 _In_ LONGLONG BaseOffset,
488 _Inout_ PPATH_ENUM_CONTEXT PathContext
489 )
490
491 /*++
492
493 Routine Description:
494
495 This routine is called to map (or allocate and copy) the next
496 data block in the path table. We check if the next block will
497 span a view boundary and allocate an auxilary buffer in that case.
498
499 Arguments:
500
501 Fcb - This is the Fcb for the Path Table.
502
503 BaseOffset - Offset of the first sector to map. This will be on a
504 sector boundary.
505
506 PathContext - Enumeration context to update in this routine.
507
508 Return Value:
509
510 None.
511
512 --*/
513
514 {
515 ULONG CurrentLength;
516 ULONG SectorSize;
517 ULONG DataOffset;
518 ULONG PassCount;
519 PVOID Sector;
520
521 PAGED_CODE();
522
523 UNREFERENCED_PARAMETER( IrpContext );
524
525 //
526 // Map the new block and set the enumeration context to this
527 // point. Allocate an auxilary buffer if necessary.
528 //
529
530 CurrentLength = 2 * SECTOR_SIZE;
531
532 if (CurrentLength >= (ULONG) (Fcb->FileSize.QuadPart - BaseOffset)) {
533
534 CurrentLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset);
535
536 //
537 // We know this is the last data block for this
538 // path table.
539 //
540
541 PathContext->LastDataBlock = TRUE;
542 }
543
544 //
545 // Set context values.
546 //
547
548 PathContext->BaseOffset = (ULONG) BaseOffset;
549 PathContext->DataLength = CurrentLength;
550
551 //
552 // Drop the previous sector's mapping
553 //
554
555 CdUnpinData( IrpContext, &PathContext->Bcb );
556
557 //
558 // Check if spanning a view section. The following must
559 // be true before we take this step.
560 //
561 // Data length is more than one sector.
562 // Starting offset must be one sector before the
563 // cache manager VACB boundary.
564 //
565
566 if ((CurrentLength > SECTOR_SIZE) &&
567 (FlagOn( ((ULONG) BaseOffset), VACB_MAPPING_MASK ) == LAST_VACB_SECTOR_OFFSET )) {
568
569 //
570 // Map each sector individually and store into an auxilary
571 // buffer.
572 //
573
574 SectorSize = SECTOR_SIZE;
575 DataOffset = 0;
576 PassCount = 2;
577
578 PathContext->Data = FsRtlAllocatePoolWithTag( CdPagedPool,
579 CurrentLength,
580 TAG_SPANNING_PATH_TABLE );
581 PathContext->AllocatedData = TRUE;
582
583 while (PassCount--) {
584
585 CcMapData( Fcb->FileObject,
586 (PLARGE_INTEGER) &BaseOffset,
587 SectorSize,
588 TRUE,
589 &PathContext->Bcb,
590 &Sector );
591
592 RtlCopyMemory( Add2Ptr( PathContext->Data, DataOffset, PVOID ),
593 Sector,
594 SectorSize );
595
596 CdUnpinData( IrpContext, &PathContext->Bcb );
597
598 BaseOffset += SECTOR_SIZE;
599 SectorSize = CurrentLength - SECTOR_SIZE;
600 DataOffset = SECTOR_SIZE;
601 }
602
603 //
604 // Otherwise we can just map the data into the cache.
605 //
606
607 } else {
608
609 //
610 // There is a slight chance that we have allocated an
611 // auxilary buffer on the previous sector.
612 //
613
614 if (PathContext->AllocatedData) {
615
616 CdFreePool( &PathContext->Data );
617 PathContext->AllocatedData = FALSE;
618 }
619
620 CcMapData( Fcb->FileObject,
621 (PLARGE_INTEGER) &BaseOffset,
622 CurrentLength,
623 TRUE,
624 &PathContext->Bcb,
625 &PathContext->Data );
626 }
627
628 return;
629 }
630
631
632 //
633 // Local support routine
634 //
635 _Success_(return != FALSE)
636 BOOLEAN
CdUpdatePathEntryFromRawPathEntry(_In_ PIRP_CONTEXT IrpContext,_In_ ULONG Ordinal,_In_ BOOLEAN VerifyBounds,_In_ PPATH_ENUM_CONTEXT PathContext,_Out_ PPATH_ENTRY PathEntry)637 CdUpdatePathEntryFromRawPathEntry (
638 _In_ PIRP_CONTEXT IrpContext,
639 _In_ ULONG Ordinal,
640 _In_ BOOLEAN VerifyBounds,
641 _In_ PPATH_ENUM_CONTEXT PathContext,
642 _Out_ PPATH_ENTRY PathEntry
643 )
644
645 /*++
646
647 Routine Description:
648
649 This routine is called to update the in-memory Path Entry from the on-disk
650 path entry. We also do a careful check of the bounds if requested and we
651 are in the last data block of the path table.
652
653 Arguments:
654
655 Ordinal - Ordinal number for this directory.
656
657 VerifyBounds - Check that the current raw Path Entry actually fits
658 within the data block.
659
660 PathContext - Current path table enumeration context.
661
662 PathEntry - Pointer to the in-memory path entry structure.
663
664 Return Value:
665
666 TRUE if updated ok,
667 FALSE if we've hit the end of the pathtable - zero name length && PT size is a multiple
668 of blocksize. This is a workaround for some Video CDs. Win 9x works around this.
669
670 This routine may raise.
671
672 --*/
673
674 {
675 PRAW_PATH_ENTRY RawPathEntry = CdRawPathEntry( IrpContext, PathContext );
676 ULONG RemainingDataLength;
677
678 PAGED_CODE();
679
680 //
681 // Check for a name length of zero. This is the first byte of the record,
682 // and there must be at least one byte remaining in the buffer else we
683 // wouldn't be here (caller would have spotted buffer end).
684 //
685
686 PathEntry->DirNameLen = CdRawPathIdLen( IrpContext, RawPathEntry );
687
688 if (0 == PathEntry->DirNameLen) {
689
690 //
691 // If we are in the last block, and the path table size (ie last block) is a
692 // multiple of block size, then we will consider this the end of the path table
693 // rather than raising an error. Workaround for NTI Cd Maker video CDs which
694 // round path table length to blocksize multiple. In all other cases we consider
695 // a zero length name to be corruption.
696 //
697
698 if ( PathContext->LastDataBlock &&
699 (0 == BlockOffset( IrpContext->Vcb, PathContext->DataLength))) {
700
701 return FALSE;
702 }
703
704 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
705 }
706
707 //
708 // Check if we should verify the path entry. If we are not in the last
709 // data block then there is nothing to check.
710 //
711
712 if (PathContext->LastDataBlock && VerifyBounds) {
713
714 //
715 // Quick check to see if the maximum size is still available. This
716 // will handle most cases and we don't need to access any of the
717 // fields.
718 //
719
720 RemainingDataLength = PathContext->DataLength - PathContext->DataOffset;
721
722 if (RemainingDataLength < sizeof( RAW_PATH_ENTRY )) {
723
724 //
725 // Make sure the remaining bytes hold the path table entries.
726 // Do the following checks.
727 //
728 // - A minimal path table entry will fit (and then check)
729 // - This path table entry (with dir name) will fit.
730 //
731
732 if ((RemainingDataLength < MIN_RAW_PATH_ENTRY_LEN) ||
733 (RemainingDataLength < (ULONG) (CdRawPathIdLen( IrpContext, RawPathEntry ) + MIN_RAW_PATH_ENTRY_LEN - 1))) {
734
735 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
736 }
737 }
738 }
739
740 //
741 // The ordinal number of this directory is passed in.
742 // Compute the path table offset of this entry.
743 //
744
745 PathEntry->Ordinal = Ordinal;
746 PathEntry->PathTableOffset = PathContext->BaseOffset + PathContext->DataOffset;
747
748 //
749 // We know we can safely access all of the fields of the raw path table at
750 // this point.
751
752 //
753 // Bias the disk offset by the number of logical blocks
754 //
755
756 CopyUchar4( &PathEntry->DiskOffset, CdRawPathLoc( IrpContext, RawPathEntry ));
757
758 PathEntry->DiskOffset += CdRawPathXar( IrpContext, RawPathEntry );
759
760 CopyUchar2( &PathEntry->ParentOrdinal, &RawPathEntry->ParentNum );
761
762 PathEntry->PathEntryLength = PathEntry->DirNameLen + MIN_RAW_PATH_ENTRY_LEN - 1;
763
764 //
765 // Align the path entry length on a ushort boundary.
766 //
767
768 PathEntry->PathEntryLength = WordAlign( PathEntry->PathEntryLength );
769
770 PathEntry->DirName = (PCHAR)RawPathEntry->DirId;
771
772 return TRUE;
773 }
774
775
776 //
777 // Local support routine
778 //
779
780 VOID
CdUpdatePathEntryName(_In_ PIRP_CONTEXT IrpContext,_Inout_ PPATH_ENTRY PathEntry,_In_ BOOLEAN IgnoreCase)781 CdUpdatePathEntryName (
782 _In_ PIRP_CONTEXT IrpContext,
783 _Inout_ PPATH_ENTRY PathEntry,
784 _In_ BOOLEAN IgnoreCase
785 )
786
787 /*++
788
789 Routine Description:
790
791 This routine will store the directory name into the CdName in the
792 path entry. If this is a Joliet name then we will make sure we have
793 an allocated buffer and need to convert from big endian to little
794 endian. We also correctly update the case name. If this operation is ignore
795 case then we need an auxilary buffer for the name.
796
797 For an Ansi disk we can use the name from the disk for the exact case. We only
798 need to allocate a buffer for the ignore case name. The on-disk representation of
799 a Unicode name is useless for us. In this case we will need a name buffer for
800 both names. We store a buffer in the PathEntry which can hold two 8.3 unicode
801 names. This means we will almost never need to allocate a buffer in the Ansi case
802 (we only need one buffer and already have 48 characters).
803
804 Arguments:
805
806 PathEntry - Pointer to a path entry structure. We have already updated
807 this path entry with the values from the raw path entry.
808
809 Return Value:
810
811 None.
812
813 --*/
814
815 {
816 ULONG Length;
817 NTSTATUS Status;
818
819 PAGED_CODE();
820
821 //
822 // Check if this is a self entry. We use a fixed string for this.
823 //
824 // Self-Entry - Length is 1, value is 0.
825 //
826
827 if ((*PathEntry->DirName == 0) &&
828 (PathEntry->DirNameLen == 1)) {
829
830 //
831 // There should be no allocated buffers.
832 //
833
834 NT_ASSERT( !FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ));
835
836 //
837 // Now use one of the hard coded directory names.
838 //
839
840 PathEntry->CdDirName.FileName = CdUnicodeDirectoryNames[0];
841
842 //
843 // Show that there is no version number.
844 //
845
846 PathEntry->CdDirName.VersionString.Length = 0;
847
848 //
849 // The case name is identical.
850 //
851
852 PathEntry->CdCaseDirName = PathEntry->CdDirName;
853
854 //
855 // Return now.
856 //
857
858 return;
859 }
860
861 //
862 // Compute how large a buffer we will need. If this is an ignore
863 // case operation then we will want a double size buffer. If the disk is not
864 // a Joliet disk then we might need two bytes for each byte in the name.
865 //
866
867 Length = PathEntry->DirNameLen;
868
869 if (IgnoreCase) {
870
871 Length *= 2;
872 }
873
874 if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) {
875
876 Length *= sizeof( WCHAR );
877 }
878
879 //
880 // Now decide if we need to allocate a new buffer. We will if
881 // this name won't fit in the embedded name buffer and it is
882 // larger than the current allocated buffer. We always use the
883 // allocated buffer if present.
884 //
885 // If we haven't allocated a buffer then use the embedded buffer if the data
886 // will fit. This is the typical case.
887 //
888
889 if (!FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER ) &&
890 (Length <= sizeof( PathEntry->NameBuffer ))) {
891
892 PathEntry->CdDirName.FileName.MaximumLength = sizeof( PathEntry->NameBuffer );
893 PathEntry->CdDirName.FileName.Buffer = PathEntry->NameBuffer;
894
895 } else {
896
897 //
898 // We need to use an allocated buffer. Check if the current buffer
899 // is large enough.
900 //
901
902 if (Length > PathEntry->CdDirName.FileName.MaximumLength) {
903
904 //
905 // Free any allocated buffer.
906 //
907
908 if (FlagOn( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER )) {
909
910 CdFreePool( &PathEntry->CdDirName.FileName.Buffer );
911 ClearFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER );
912 }
913
914 PathEntry->CdDirName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
915 Length,
916 TAG_PATH_ENTRY_NAME );
917
918 SetFlag( PathEntry->Flags, PATH_ENTRY_FLAG_ALLOC_BUFFER );
919
920 PathEntry->CdDirName.FileName.MaximumLength = (USHORT) Length;
921 }
922 }
923
924 //
925 // We now have a buffer for the name. We need to either convert the on-disk bigendian
926 // to little endian or covert the name to Unicode.
927 //
928
929 if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) {
930
931 Status = RtlOemToUnicodeN( PathEntry->CdDirName.FileName.Buffer,
932 PathEntry->CdDirName.FileName.MaximumLength,
933 &Length,
934 PathEntry->DirName,
935 PathEntry->DirNameLen );
936
937 NT_ASSERT( Status == STATUS_SUCCESS );
938 __analysis_assert( Status == STATUS_SUCCESS );
939 PathEntry->CdDirName.FileName.Length = (USHORT) Length;
940
941 } else {
942
943 //
944 // Convert this string to little endian.
945 //
946
947 CdConvertBigToLittleEndian( IrpContext,
948 PathEntry->DirName,
949 PathEntry->DirNameLen,
950 (PCHAR) PathEntry->CdDirName.FileName.Buffer );
951
952 PathEntry->CdDirName.FileName.Length = (USHORT) PathEntry->DirNameLen;
953 }
954
955 //
956 // There is no version string.
957 //
958
959 PathEntry->CdDirName.VersionString.Length =
960 PathEntry->CdCaseDirName.VersionString.Length = 0;
961
962 //
963 // If the name string ends with a period then knock off the last
964 // character.
965 //
966
967 if (PathEntry->CdDirName.FileName.Buffer[(PathEntry->CdDirName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') {
968
969 //
970 // Shrink the filename length.
971 //
972
973 PathEntry->CdDirName.FileName.Length -= sizeof( WCHAR );
974 }
975
976 //
977 // Update the case name buffer if necessary. If this is an exact case
978 // operation then just copy the exact case string.
979 //
980
981 if (IgnoreCase) {
982
983 PathEntry->CdCaseDirName.FileName.Buffer = Add2Ptr( PathEntry->CdDirName.FileName.Buffer,
984 PathEntry->CdDirName.FileName.MaximumLength / 2,
985 PWCHAR);
986
987 PathEntry->CdCaseDirName.FileName.MaximumLength = PathEntry->CdDirName.FileName.MaximumLength / 2;
988
989 CdUpcaseName( IrpContext,
990 &PathEntry->CdDirName,
991 &PathEntry->CdCaseDirName );
992
993 } else {
994
995 PathEntry->CdCaseDirName = PathEntry->CdDirName;
996 }
997
998 return;
999 }
1000
1001
1002