xref: /reactos/drivers/filesystems/cdfs/pathsup.c (revision 34593d93)
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