xref: /reactos/drivers/filesystems/cdfs/create.c (revision 9393fc32)
1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Create.c
8 
9 Abstract:
10 
11     This module implements the File Create routine for Cdfs called by the
12     Fsd/Fsp dispatch routines.
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_CREATE)
24 
25 //
26 //  Local support routines
27 //
28 
29 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedCcb, _In_))
30 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedCcb, _In_opt_))
31 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedFileName, _In_))
32 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedFileName, _In_opt_))
33 NTSTATUS
34 CdNormalizeFileNames (
35     _Inout_ PIRP_CONTEXT IrpContext,
36     _In_ PVCB Vcb,
37     _In_ BOOLEAN OpenByFileId,
38     _In_ BOOLEAN IgnoreCase,
39     _In_ TYPE_OF_OPEN RelatedTypeOfOpen,
40     PCCB RelatedCcb,
41     PUNICODE_STRING RelatedFileName,
42     _Inout_ PUNICODE_STRING FileName,
43     _Inout_ PCD_NAME RemainingName
44     );
45 
46 _Requires_lock_held_(_Global_critical_region_)
47 _Acquires_exclusive_lock_((*CurrentFcb)->FcbNonpaged->FcbResource)
48 NTSTATUS
49 CdOpenByFileId (
50     _In_ PIRP_CONTEXT IrpContext,
51     _In_ PIO_STACK_LOCATION IrpSp,
52     _In_ PVCB Vcb,
53     _Inout_ PFCB *CurrentFcb
54     );
55 
56 _Requires_lock_held_(_Global_critical_region_)
57 NTSTATUS
58 CdOpenExistingFcb (
59     _In_ PIRP_CONTEXT IrpContext,
60     _In_ PIO_STACK_LOCATION IrpSp,
61     _Inout_ PFCB *CurrentFcb,
62     _In_ TYPE_OF_OPEN TypeOfOpen,
63     _In_ BOOLEAN IgnoreCase,
64     _In_opt_ PCCB RelatedCcb
65     );
66 
67 _Requires_lock_held_(_Global_critical_region_)
68 _Acquires_lock_((*CurrentFcb)->FcbNonpaged->FcbResource)
69 NTSTATUS
70 CdOpenDirectoryFromPathEntry (
71     _In_ PIRP_CONTEXT IrpContext,
72     _In_ PIO_STACK_LOCATION IrpSp,
73     _In_ PVCB Vcb,
74     _Inout_ PFCB *CurrentFcb,
75     _In_ PCD_NAME DirName,
76     _In_ BOOLEAN IgnoreCase,
77     _In_ BOOLEAN ShortNameMatch,
78     _In_ PPATH_ENTRY PathEntry,
79     _In_ BOOLEAN PerformUserOpen,
80     _In_opt_ PCCB RelatedCcb
81     );
82 
83 _Requires_lock_held_(_Global_critical_region_)
84 NTSTATUS
85 CdOpenFileFromFileContext (
86     _In_ PIRP_CONTEXT IrpContext,
87     _In_ PIO_STACK_LOCATION IrpSp,
88     _In_ PVCB Vcb,
89     _Inout_ PFCB *CurrentFcb,
90     _In_ PCD_NAME FileName,
91     _In_ BOOLEAN IgnoreCase,
92     _In_ BOOLEAN ShortNameMatch,
93     _In_ PFILE_ENUM_CONTEXT FileContext,
94     _In_opt_ PCCB RelatedCcb
95     );
96 
97 _Requires_lock_held_(_Global_critical_region_)
98 NTSTATUS
99 CdCompleteFcbOpen (
100     _In_ PIRP_CONTEXT IrpContext,
101     _In_ PIO_STACK_LOCATION IrpSp,
102     _In_ PVCB Vcb,
103     _Inout_ PFCB *CurrentFcb,
104     _In_ TYPE_OF_OPEN TypeOfOpen,
105     _In_ ULONG UserCcbFlags,
106     _In_ ACCESS_MASK DesiredAccess
107     );
108 
109 #ifdef ALLOC_PRAGMA
110 #pragma alloc_text(PAGE, CdCommonCreate)
111 #pragma alloc_text(PAGE, CdCompleteFcbOpen)
112 #pragma alloc_text(PAGE, CdNormalizeFileNames)
113 #pragma alloc_text(PAGE, CdOpenByFileId)
114 #pragma alloc_text(PAGE, CdOpenDirectoryFromPathEntry)
115 #pragma alloc_text(PAGE, CdOpenExistingFcb)
116 #pragma alloc_text(PAGE, CdOpenFileFromFileContext)
117 #endif
118 
119 
120 _Requires_lock_held_(_Global_critical_region_)
121 NTSTATUS
122 #ifdef _MSC_VER
123 #pragma prefast(suppress:26165, "Esp:1153")
124 #endif
125 CdCommonCreate (
126     _Inout_ PIRP_CONTEXT IrpContext,
127     _Inout_ PIRP Irp
128     )
129 
130 /*++
131 
132 Routine Description:
133 
134     This is the common routine for opening a file called by both the
135     Fsp and Fsd threads.
136 
137     The file can be opened either by name or by file Id either with or without
138     a relative name.  The file name field in the file object passed to this routine
139     contains either a unicode string or a 64 bit value which is the file Id.
140     If this is not a Joliet disk then we will convert the unicode name to
141     an Oem string in this routine.  If there is a related file object with
142     a name then we will already have converted that name to Oem.
143 
144     We will store the full name for the file in the file object on a successful
145     open.  We will allocate a larger buffer if necessary and combine the
146     related and file object names.  The only exception is the relative open
147     when the related file object is for an OpenByFileId file.  If we need to
148     allocate a buffer for a case insensitive name then we allocate it at
149     the tail of the buffer we will store into the file object.  The upcased
150     portion will begin immediately after the name defined by the FileName
151     in the file object.
152 
153     Once we have the full name in the file object we don't want to split the
154     name in the event of a retry.  We use a flag in the IrpContext to indicate
155     that the name has been split.
156 
157 Arguments:
158 
159     Irp - Supplies the Irp to process
160 
161 Return Value:
162 
163     NTSTATUS - This is the status from this open operation.
164 
165 --*/
166 
167 {
168     NTSTATUS Status = STATUS_SUCCESS;
169     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
170 
171     PFILE_OBJECT FileObject;
172 
173     COMPOUND_PATH_ENTRY CompoundPathEntry = {{0}};/* ReactOS Change: GCC "missing braces around initializer" */
174     BOOLEAN CleanupCompoundPathEntry = FALSE;
175 
176     FILE_ENUM_CONTEXT FileContext = {0};
177     BOOLEAN CleanupFileContext = FALSE;
178     BOOLEAN FoundEntry;
179 
180     PVCB Vcb;
181 
182     BOOLEAN OpenByFileId;
183     BOOLEAN IgnoreCase;
184     ULONG CreateDisposition;
185 
186     BOOLEAN ShortNameMatch;
187     ULONG ShortNameDirentOffset;
188 
189     BOOLEAN VolumeOpen = FALSE;
190 
191     //
192     //  We will be acquiring and releasing file Fcb's as we move down the
193     //  directory tree during opens.  At any time we need to know the deepest
194     //  point we have traversed down in the tree in case we need to cleanup
195     //  any structures created here.
196     //
197     //  CurrentFcb - represents this point.  If non-null it means we have
198     //      acquired it and need to release it in finally clause.
199     //
200     //  NextFcb - represents the NextFcb to walk to but haven't acquired yet.
201     //
202 
203     TYPE_OF_OPEN RelatedTypeOfOpen = UnopenedFileObject;
204     PFILE_OBJECT RelatedFileObject;
205     PCCB RelatedCcb = NULL;
206 
207     PFCB NextFcb;
208     PFCB CurrentFcb = NULL;
209 
210     //
211     //  During the open we need to combine the related file object name
212     //  with the remaining name.  We also may need to upcase the file name
213     //  in order to do a case-insensitive name comparison.  We also need
214     //  to restore the name in the file object in the event that we retry
215     //  the request.  We use the following string variables to manage the
216     //  name.  We will can put these strings into either Unicode or Ansi
217     //  form.
218     //
219     //  FileName - Pointer to name as currently stored in the file
220     //      object.  We store the full name into the file object early in
221     //      the open operation.
222     //
223     //  RelatedFileName - Pointer to the name in the related file object.
224     //
225     //  RemainingName - String containing remaining name to parse.
226     //
227     //  MatchingName - Address of name structure in FileContext which matched.
228     //      We need this to know whether we matched the long or short name.
229     //
230 
231     PUNICODE_STRING FileName;
232     PUNICODE_STRING RelatedFileName = NULL;
233 
234     CD_NAME RemainingName = {{0}};/* ReactOS Change: GCC "missing braces around initializer" */
235     CD_NAME FinalName;
236     PCD_NAME MatchingName = NULL;
237 
238     PAGED_CODE();
239 
240     //
241     //  If we were called with our file system device object instead of a
242     //  volume device object, just complete this request with STATUS_SUCCESS.
243     //
244 
245     if (IrpContext->Vcb == NULL) {
246 
247         CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
248         return STATUS_SUCCESS;
249     }
250 
251     //
252     //  Get create parameters from the Irp.
253     //
254 
255     OpenByFileId = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_BY_FILE_ID );
256     IgnoreCase = !BooleanFlagOn( IrpSp->Flags, SL_CASE_SENSITIVE );
257     CreateDisposition = (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff;
258 
259     //
260     //  Do some preliminary checks to make sure the operation is supported.
261     //  We fail in the following cases immediately.
262     //
263     //      - Open a paging file.
264     //      - Open a target directory.
265     //      - Open a file with Eas.
266     //      - Create a file.
267     //
268 
269     if (FlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE | SL_OPEN_TARGET_DIRECTORY) ||
270         (IrpSp->Parameters.Create.EaLength != 0) ||
271         (CreateDisposition == FILE_CREATE)) {
272 
273         CdCompleteRequest( IrpContext, Irp, STATUS_ACCESS_DENIED );
274         return STATUS_ACCESS_DENIED;
275     }
276 
277 #if (NTDDI_VERSION >= NTDDI_WIN7)
278     //
279     //  CDFS does not support FILE_OPEN_REQUIRING_OPLOCK
280     //
281 
282     if (FlagOn( IrpSp->Parameters.Create.Options, FILE_OPEN_REQUIRING_OPLOCK )) {
283 
284         CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
285         return STATUS_INVALID_PARAMETER;
286     }
287 #endif
288 
289     //
290     //  Copy the Vcb to a local.  Assume the starting directory is the root.
291     //
292 
293     Vcb = IrpContext->Vcb;
294     NextFcb = Vcb->RootIndexFcb;
295 
296     //
297     //  Reference our input parameters to make things easier
298     //
299 
300     FileObject = IrpSp->FileObject;
301     RelatedFileObject = NULL;
302 
303     FileName = &FileObject->FileName;
304 
305     //
306     //  Set up the file object's Vpb pointer in case anything happens.
307     //  This will allow us to get a reasonable pop-up.
308     //
309 
310     if ((FileObject->RelatedFileObject != NULL) && !OpenByFileId) {
311 
312         RelatedFileObject = FileObject->RelatedFileObject;
313         FileObject->Vpb = RelatedFileObject->Vpb;
314 
315         RelatedTypeOfOpen = CdDecodeFileObject( IrpContext, RelatedFileObject, &NextFcb, &RelatedCcb );
316 
317         //
318         //  Fail the request if this is not a user file object.
319         //
320 
321         if (RelatedTypeOfOpen < UserVolumeOpen) {
322 
323             CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
324             return STATUS_INVALID_PARAMETER;
325         }
326 
327         //
328         //  Remember the name in the related file object.
329         //
330 
331         RelatedFileName = &RelatedFileObject->FileName;
332     }
333 
334     //
335     //  If we haven't initialized the names then make sure the strings are valid.
336     //  If this an OpenByFileId then verify the file id buffer.
337     //
338     //  After this routine returns we know that the full name is in the
339     //  FileName buffer and the buffer will hold the upcased portion
340     //  of the name yet to parse immediately after the full name in the
341     //  buffer.  Any trailing backslash has been removed and the flag
342     //  in the IrpContext will indicate whether we removed the
343     //  backslash.
344     //
345 
346     Status = CdNormalizeFileNames( IrpContext,
347                                    Vcb,
348                                    OpenByFileId,
349                                    IgnoreCase,
350                                    RelatedTypeOfOpen,
351                                    RelatedCcb,
352                                    RelatedFileName,
353                                    FileName,
354                                    &RemainingName );
355 
356     //
357     //  Return the error code if not successful.
358     //
359 
360     if (!NT_SUCCESS( Status )) {
361 
362         CdCompleteRequest( IrpContext, Irp, Status );
363         return Status;
364     }
365 
366     //
367     //  We want to acquire the Vcb.  Exclusively for a volume open, shared otherwise.
368     //  The file name is empty for a volume open.
369     //
370 
371     if ((FileName->Length == 0) &&
372         (RelatedTypeOfOpen <= UserVolumeOpen) &&
373         !OpenByFileId) {
374 
375         VolumeOpen = TRUE;
376         CdAcquireVcbExclusive( IrpContext, Vcb, FALSE );
377 
378     } else {
379 
380         CdAcquireVcbShared( IrpContext, Vcb, FALSE );
381     }
382 
383     //
384     //  Use a try-finally to facilitate cleanup.
385     //
386 
387     _SEH2_TRY {
388 
389         //
390         //  Verify that the Vcb is not in an unusable condition.  This routine
391         //  will raise if not usable.
392         //
393 
394         CdVerifyVcb( IrpContext, Vcb );
395 
396         //
397         //  If the Vcb is locked then we cannot open another file
398         //
399 
400         if (FlagOn( Vcb->VcbState, VCB_STATE_LOCKED )) {
401 
402             try_return( Status = STATUS_ACCESS_DENIED );
403         }
404 
405         //
406         //  If we are opening this file by FileId then process this immediately
407         //  and exit.
408         //
409 
410         if (OpenByFileId) {
411 
412             //
413             //  We only allow Dasd opens of audio disks.  Fail this request at
414             //  this point.
415             //
416 
417             if (FlagOn( Vcb->VcbState, VCB_STATE_AUDIO_DISK )) {
418 
419                 try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
420             }
421 
422             //
423             //  The only create disposition we allow is OPEN.
424             //
425 
426             if ((CreateDisposition != FILE_OPEN) &&
427                 (CreateDisposition != FILE_OPEN_IF)) {
428 
429                 try_return( Status = STATUS_ACCESS_DENIED );
430             }
431 
432             //
433             //  Make sure we can wait for this request.
434             //
435 
436             if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
437 
438                 CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
439             }
440 
441             try_return( Status = CdOpenByFileId( IrpContext,
442                                                  IrpSp,
443                                                  Vcb,
444                                                  &CurrentFcb ));
445         }
446 
447         //
448         //  If we are opening this volume Dasd then process this immediately
449         //  and exit.
450         //
451 
452         if (VolumeOpen) {
453 
454             //
455             //  The only create disposition we allow is OPEN.
456             //
457 
458             if ((CreateDisposition != FILE_OPEN) &&
459                 (CreateDisposition != FILE_OPEN_IF)) {
460 
461                 try_return( Status = STATUS_ACCESS_DENIED );
462             }
463 
464             //
465             //  If they wanted to open a directory, surprise.
466             //
467 
468             if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
469 
470                 try_return( Status = STATUS_NOT_A_DIRECTORY );
471             }
472 
473             //
474             //  Acquire the Fcb first.
475             //
476 
477             CurrentFcb = Vcb->VolumeDasdFcb;
478             CdAcquireFcbExclusive( IrpContext, CurrentFcb, FALSE );
479 
480             try_return( Status = CdOpenExistingFcb( IrpContext,
481                                                     IrpSp,
482                                                     &CurrentFcb,
483                                                     UserVolumeOpen,
484                                                     FALSE,
485                                                     NULL ));
486         }
487 
488         //
489         //  At this point CurrentFcb points to the deepest Fcb for this open
490         //  in the tree.  Let's acquire this Fcb to keep it from being deleted
491         //  beneath us.
492         //
493 
494         CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
495         CurrentFcb = NextFcb;
496 
497         //
498         //  Do a prefix search if there is more of the name to parse.
499         //
500 
501         if (RemainingName.FileName.Length != 0) {
502 
503             //
504             //  Do the prefix search to find the longest matching name.
505             //
506 
507             CdFindPrefix( IrpContext,
508                           &CurrentFcb,
509                           &RemainingName.FileName,
510                           IgnoreCase );
511         }
512 
513         //
514         //  If the remaining name length is zero then we have found our
515         //  target.
516         //
517 
518         if (RemainingName.FileName.Length == 0) {
519 
520             //
521             //  If this is a file so verify the user didn't want to open
522             //  a directory.
523             //
524 
525             if (SafeNodeType( CurrentFcb ) == CDFS_NTC_FCB_DATA) {
526 
527                 if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
528                     FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
529 
530                     try_return( Status = STATUS_NOT_A_DIRECTORY );
531                 }
532 
533                 //
534                 //  The only create disposition we allow is OPEN.
535                 //
536 
537                 if ((CreateDisposition != FILE_OPEN) &&
538                     (CreateDisposition != FILE_OPEN_IF)) {
539 
540                     try_return( Status = STATUS_ACCESS_DENIED );
541                 }
542 
543                 try_return( Status = CdOpenExistingFcb( IrpContext,
544                                                         IrpSp,
545                                                         &CurrentFcb,
546                                                         UserFileOpen,
547                                                         IgnoreCase,
548                                                         RelatedCcb ));
549 
550             //
551             //  This is a directory.  Verify the user didn't want to open
552             //  as a file.
553             //
554 
555             } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
556 
557                 try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
558 
559             //
560             //  Open the file as a directory.
561             //
562 
563             } else {
564 
565                 //
566                 //  The only create disposition we allow is OPEN.
567                 //
568 
569                 if ((CreateDisposition != FILE_OPEN) &&
570                     (CreateDisposition != FILE_OPEN_IF)) {
571 
572                     try_return( Status = STATUS_ACCESS_DENIED );
573                 }
574 
575                 try_return( Status = CdOpenExistingFcb( IrpContext,
576                                                         IrpSp,
577                                                         &CurrentFcb,
578                                                         UserDirectoryOpen,
579                                                         IgnoreCase,
580                                                         RelatedCcb ));
581             }
582         }
583 
584         //
585         //  We have more work to do.  We have a starting Fcb which we own shared.
586         //  We also have the remaining name to parse.  Walk through the name
587         //  component by component looking for the full name.
588         //
589 
590         //
591         //  Our starting Fcb better be a directory.
592         //
593 
594         if (!FlagOn( CurrentFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
595 
596             try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
597         }
598 
599         //
600         //  If we can't wait then post this request.
601         //
602 
603         if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
604 
605             CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
606         }
607 
608         //
609         //  Make sure the final name has no version string.
610         //
611 
612         FinalName.VersionString.Length = 0;
613 
614         while (TRUE) {
615 
616             ShortNameMatch = FALSE;
617 
618             //
619             //  Split off the next component from the name.
620             //
621 
622             CdDissectName( IrpContext,
623                            &RemainingName.FileName,
624                            &FinalName.FileName );
625 
626             //
627             //  Go ahead and look this entry up in the path table.
628             //
629 
630             CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
631             CleanupCompoundPathEntry = TRUE;
632 
633             FoundEntry = CdFindPathEntry( IrpContext,
634                                           CurrentFcb,
635                                           &FinalName,
636                                           IgnoreCase,
637                                           &CompoundPathEntry );
638 
639             //
640             //  If we didn't find the entry then check if the current name
641             //  is a possible short name.
642             //
643 
644             if (!FoundEntry) {
645 
646                 ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &FinalName.FileName );
647 
648                 //
649                 //  If there is an embedded short name offset then look for the
650                 //  matching long name in the directory.
651                 //
652 
653                 if (ShortNameDirentOffset != MAXULONG) {
654 
655                     if (CleanupFileContext) {
656 
657                         CdCleanupFileContext( IrpContext, &FileContext );
658                     }
659 
660                     CdInitializeFileContext( IrpContext, &FileContext );
661                     CleanupFileContext = TRUE;
662 
663                     FoundEntry = CdFindFileByShortName( IrpContext,
664                                                         CurrentFcb,
665                                                         &FinalName,
666                                                         IgnoreCase,
667                                                         ShortNameDirentOffset,
668                                                         &FileContext );
669 
670                     //
671                     //  If we found an entry and it is a directory then look
672                     //  this up in the path table.
673                     //
674 
675                     if (FoundEntry) {
676 
677                         ShortNameMatch = TRUE;
678 
679                         if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags,
680                                     CD_ATTRIBUTE_DIRECTORY )) {
681 
682                             CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
683                             CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
684 
685                             FoundEntry = CdFindPathEntry( IrpContext,
686                                                           CurrentFcb,
687                                                           &FileContext.InitialDirent->Dirent.CdCaseFileName,
688                                                           IgnoreCase,
689                                                           &CompoundPathEntry );
690 
691                             //
692                             //  We better find this entry.
693                             //
694 
695                             if (!FoundEntry) {
696 
697                                 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
698                             }
699 
700                             //
701                             //  Upcase the name with the short name if case
702                             //  insensitive.
703                             //
704 
705                             if (IgnoreCase) {
706 
707                                 CdUpcaseName( IrpContext, &FinalName, &FinalName );
708                             }
709 
710                         //
711                         //  We found a matching file.  If we are at the last
712                         //  entry then break out of the loop and open the
713                         //  file below.  Otherwise we return an error.
714                         //
715 
716                         } else if (RemainingName.FileName.Length == 0) {
717 
718                             //
719                             //  Break out of the loop.  We will process the dirent
720                             //  below.
721                             //
722 
723                             MatchingName = &FileContext.ShortName;
724                             break;
725 
726                         } else {
727 
728                             try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
729                         }
730                     }
731                 }
732 
733                 //
734                 //  We didn't find the name in either the path table or as
735                 //  a short name in a directory.  If the remaining name
736                 //  length is zero then break out of the loop to search
737                 //  the directory.
738                 //
739 
740                 if (!FoundEntry) {
741 
742                     if (RemainingName.FileName.Length == 0) {
743 
744                         break;
745 
746                     //
747                     //  Otherwise this path could not be cracked.
748                     //
749 
750                     } else {
751 
752                         try_return( Status = STATUS_OBJECT_PATH_NOT_FOUND );
753                     }
754                 }
755             }
756 
757             //
758             //  If this is an ignore case open then copy the exact case
759             //  in the file object name.  If it was a short name match then
760             //  the name must be upcase already.
761             //
762 
763             if (IgnoreCase && !ShortNameMatch) {
764 
765                 RtlCopyMemory( FinalName.FileName.Buffer,
766                                CompoundPathEntry.PathEntry.CdDirName.FileName.Buffer,
767                                CompoundPathEntry.PathEntry.CdDirName.FileName.Length );
768             }
769 
770             //
771             //  If we have found the last component then open this as a directory
772             //  and return to our caller.
773             //
774 
775             if (RemainingName.FileName.Length == 0) {
776 
777                 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
778 
779                     try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
780                 }
781 
782                 //
783                 //  The only create disposition we allow is OPEN.
784                 //
785 
786                 if ((CreateDisposition != FILE_OPEN) &&
787                     (CreateDisposition != FILE_OPEN_IF)) {
788 
789                     try_return( Status = STATUS_ACCESS_DENIED );
790                 }
791 
792                 try_return( Status = CdOpenDirectoryFromPathEntry( IrpContext,
793                                                                    IrpSp,
794                                                                    Vcb,
795                                                                    &CurrentFcb,
796                                                                    &FinalName,
797                                                                    IgnoreCase,
798                                                                    ShortNameMatch,
799                                                                    &CompoundPathEntry.PathEntry,
800                                                                    TRUE,
801                                                                    RelatedCcb ));
802             }
803 
804             //
805             //  Otherwise open an Fcb for this intermediate index Fcb.
806             //
807 
808             CdOpenDirectoryFromPathEntry( IrpContext,
809                                           IrpSp,
810                                           Vcb,
811                                           &CurrentFcb,
812                                           &FinalName,
813                                           IgnoreCase,
814                                           ShortNameMatch,
815                                           &CompoundPathEntry.PathEntry,
816                                           FALSE,
817                                           NULL );
818 
819             CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
820             CleanupCompoundPathEntry = FALSE;
821         }
822 
823         //
824         //  We need to scan the current directory for a matching file name
825         //  if we don't already have one.
826         //
827 
828         if (!FoundEntry) {
829 
830             if (CleanupFileContext) {
831 
832                 CdCleanupFileContext( IrpContext, &FileContext );
833             }
834 
835             CdInitializeFileContext( IrpContext, &FileContext );
836             CleanupFileContext = TRUE;
837 
838             //
839             //  Split our search name into separate components.
840             //
841 
842             CdConvertNameToCdName( IrpContext, &FinalName );
843 
844             FoundEntry = CdFindFile( IrpContext,
845                                      CurrentFcb,
846                                      &FinalName,
847                                      IgnoreCase,
848                                      &FileContext,
849                                      &MatchingName );
850         }
851 
852         //
853         //  If we didn't find a match then check if the name is invalid to
854         //  determine which error code to return.
855         //
856 
857         if (!FoundEntry) {
858 
859             if ((CreateDisposition == FILE_OPEN) ||
860                 (CreateDisposition == FILE_OVERWRITE)) {
861 
862                 try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
863             }
864 
865             //
866             //  Any other operation return STATUS_ACCESS_DENIED.
867             //
868 
869             try_return( Status = STATUS_ACCESS_DENIED );
870         }
871 
872         //
873         //  If this is a directory then the disk is corrupt because it wasn't
874         //  in the Path Table.
875         //
876 
877         if (FlagOn( FileContext.InitialDirent->Dirent.Flags, CD_ATTRIBUTE_DIRECTORY )) {
878 
879             CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
880         }
881 
882         //
883         //  Make sure our opener didn't want a directory.
884         //
885 
886         if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH ) ||
887             FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
888 
889             try_return( Status = STATUS_NOT_A_DIRECTORY );
890         }
891 
892         //
893         //  The only create disposition we allow is OPEN.
894         //
895 
896         if ((CreateDisposition != FILE_OPEN) &&
897             (CreateDisposition != FILE_OPEN_IF)) {
898 
899             try_return( Status = STATUS_ACCESS_DENIED );
900         }
901 
902         //
903         //  If this is an ignore case open then copy the exact case
904         //  in the file object name.  Any version portion should
905         //  already be upcased.
906         //
907 
908         if (IgnoreCase) {
909 
910             RtlCopyMemory( FinalName.FileName.Buffer,
911                            MatchingName->FileName.Buffer,
912                            MatchingName->FileName.Length );
913         }
914 
915         //
916         //  Open the file using the file context.  We already have the
917         //  first and last dirents.
918         //
919 
920         try_return( Status = CdOpenFileFromFileContext( IrpContext,
921                                                         IrpSp,
922                                                         Vcb,
923                                                         &CurrentFcb,
924                                                         &FinalName,
925                                                         IgnoreCase,
926                                                         (BOOLEAN) (MatchingName == &FileContext.ShortName),
927                                                         &FileContext,
928                                                         RelatedCcb ));
929 
930     try_exit:  NOTHING;
931     } _SEH2_FINALLY {
932 
933         //
934         //  Cleanup the PathEntry if initialized.
935         //
936 
937         if (CleanupCompoundPathEntry) {
938 
939             CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
940         }
941 
942         //
943         //  Cleanup the FileContext if initialized.
944         //
945 
946         if (CleanupFileContext) {
947 
948             CdCleanupFileContext( IrpContext, &FileContext );
949         }
950 
951         //
952         //  The result of this open could be success, pending or some error
953         //  condition.
954         //
955 
956         if (_SEH2_AbnormalTermination()) {
957 
958 
959             //
960             //  In the error path we start by calling our teardown routine if we
961             //  have a CurrentFcb and its not the volume Dasd Fcb.
962             //
963 
964             if ((CurrentFcb != NULL) &&
965                 (CurrentFcb != Vcb->VolumeDasdFcb)) {
966 
967                 BOOLEAN RemovedFcb;
968 
969                 CdTeardownStructures( IrpContext, CurrentFcb, &RemovedFcb );
970 
971                 if (RemovedFcb) {
972 
973                     CurrentFcb = NULL;
974                 }
975             }
976 
977             //
978             //  No need to complete the request.
979             //
980 
981             IrpContext = NULL;
982             Irp = NULL;
983 
984         //
985         //  If we posted this request through the oplock package we need
986         //  to show that there is no reason to complete the request.
987         //
988 
989         } else if (Status == STATUS_PENDING) {
990 
991             IrpContext = NULL;
992             Irp = NULL;
993         }
994 
995         //
996         //  Release the Current Fcb if still acquired.
997         //
998 
999         if (CurrentFcb != NULL) {
1000             _Analysis_assume_lock_held_(CurrentFcb->FcbNonpaged->FcbResource);
1001             CdReleaseFcb( IrpContext, CurrentFcb );
1002         }
1003 
1004         //
1005         //  Release the Vcb.
1006         //
1007 
1008         CdReleaseVcb( IrpContext, Vcb );
1009 
1010         //
1011         //  Call our completion routine.  It will handle the case where either
1012         //  the Irp and/or IrpContext are gone.
1013         //
1014 
1015         CdCompleteRequest( IrpContext, Irp, Status );
1016     } _SEH2_END;
1017 
1018     return Status;
1019 }
1020 
1021 
1022 //
1023 //  Local support routine
1024 //
1025 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedCcb, _In_))
1026 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedCcb, _In_opt_))
1027 _When_(RelatedTypeOfOpen != UnopenedFileObject, _At_(RelatedFileName, _In_))
1028 _When_(RelatedTypeOfOpen == UnopenedFileObject, _At_(RelatedFileName, _In_opt_))
1029 NTSTATUS
1030 CdNormalizeFileNames (
1031     _Inout_ PIRP_CONTEXT IrpContext,
1032     _In_ PVCB Vcb,
1033     _In_ BOOLEAN OpenByFileId,
1034     _In_ BOOLEAN IgnoreCase,
1035     _In_ TYPE_OF_OPEN RelatedTypeOfOpen,
1036     PCCB RelatedCcb,
1037     PUNICODE_STRING RelatedFileName,
1038     _Inout_ PUNICODE_STRING FileName,
1039     _Inout_ PCD_NAME RemainingName
1040     )
1041 
1042 /*++
1043 
1044 Routine Description:
1045 
1046     This routine is called to store the full name and upcased name into the
1047     filename buffer.  We only upcase the portion yet to parse.  We also
1048     check for a trailing backslash and lead-in double backslashes.  This
1049     routine also verifies the mode of the related open against the name
1050     currently in the filename.
1051 
1052 Arguments:
1053 
1054     Vcb - Vcb for this volume.
1055 
1056     OpenByFileId - Indicates if the filename should be a 64 bit FileId.
1057 
1058     IgnoreCase - Indicates if this open is a case-insensitive operation.
1059 
1060     RelatedTypeOfOpen - Indicates the type of the related file object.
1061 
1062     RelatedCcb - Ccb for the related open.  Ignored if no relative open.
1063 
1064     RelatedFileName - FileName buffer for related open.  Ignored if no
1065         relative open.
1066 
1067     FileName - FileName to update in this routine.  The name should
1068         either be a 64-bit FileId or a Unicode string.
1069 
1070     RemainingName - Name with the remaining portion of the name.  This
1071         will begin after the related name and any separator.  For a
1072         non-relative open we also step over the initial separator.
1073 
1074 Return Value:
1075 
1076     NTSTATUS - STATUS_SUCCESS if the names are OK, appropriate error code
1077         otherwise.
1078 
1079 --*/
1080 
1081 {
1082     ULONG RemainingNameLength = 0;
1083     ULONG RelatedNameLength = 0;
1084     ULONG SeparatorLength = 0;
1085 
1086     ULONG BufferLength;
1087 
1088     UNICODE_STRING NewFileName;
1089 
1090     PAGED_CODE();
1091 
1092     //
1093     //  If this is the first pass then we need to build the full name and
1094     //  check for name compatibility.
1095     //
1096 
1097     if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME )) {
1098 
1099         //
1100         //  Deal with the regular file name case first.
1101         //
1102 
1103         if (!OpenByFileId) {
1104 
1105             //
1106             //  This is here because the Win32 layer can't avoid sending me double
1107             //  beginning backslashes.
1108             //
1109 
1110             if ((FileName->Length > sizeof( WCHAR )) &&
1111                 (FileName->Buffer[1] == L'\\') &&
1112                 (FileName->Buffer[0] == L'\\')) {
1113 
1114                 //
1115                 //  If there are still two beginning backslashes, the name is bogus.
1116                 //
1117 
1118                 if ((FileName->Length > 2 * sizeof( WCHAR )) &&
1119                     (FileName->Buffer[2] == L'\\')) {
1120 
1121                     return STATUS_OBJECT_NAME_INVALID;
1122                 }
1123 
1124                 //
1125                 //  Slide the name down in the buffer.
1126                 //
1127 
1128                 FileName->Length -= sizeof( WCHAR );
1129 
1130                 RtlMoveMemory( FileName->Buffer,
1131                                FileName->Buffer + 1,
1132                                FileName->Length );
1133             }
1134 
1135             //
1136             //  Check for a trailing backslash.  Don't strip off if only character
1137             //  in the full name or for relative opens where this is illegal.
1138             //
1139 
1140             if (((FileName->Length > sizeof( WCHAR)) ||
1141                  ((FileName->Length == sizeof( WCHAR )) && (RelatedTypeOfOpen == UserDirectoryOpen))) &&
1142                 (FileName->Buffer[ (FileName->Length/2) - 1 ] == L'\\')) {
1143 
1144                 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_TRAIL_BACKSLASH );
1145                 FileName->Length -= sizeof( WCHAR );
1146             }
1147 
1148             //
1149             //  Remember the length we need for this portion of the name.
1150             //
1151 
1152             RemainingNameLength = FileName->Length;
1153 
1154             //
1155             //  If this is a related file object then we verify the compatibility
1156             //  of the name in the file object with the relative file object.
1157             //
1158 
1159             if (RelatedTypeOfOpen != UnopenedFileObject) {
1160 
1161                 //
1162                 //  If the filename length was zero then it must be legal.
1163                 //  If there are characters then check with the related
1164                 //  type of open.
1165                 //
1166 
1167                 if (FileName->Length != 0) {
1168 
1169                     //
1170                     //  The name length must always be zero for a volume open.
1171                     //
1172 
1173                     if (RelatedTypeOfOpen <= UserVolumeOpen) {
1174 
1175                         return STATUS_INVALID_PARAMETER;
1176 
1177                     //
1178                     //  The remaining name cannot begin with a backslash.
1179                     //
1180 
1181                     } else if (FileName->Buffer[0] == L'\\' ) {
1182 
1183                         return STATUS_INVALID_PARAMETER;
1184 
1185                     //
1186                     //  If the related file is a user file then there
1187                     //  is no file with this path.
1188                     //
1189 
1190                     } else if (RelatedTypeOfOpen == UserFileOpen) {
1191 
1192                         return STATUS_OBJECT_PATH_NOT_FOUND;
1193                     }
1194                 }
1195 
1196                 //
1197                 //  Remember the length of the related name when building
1198                 //  the full name.  We leave the RelatedNameLength and
1199                 //  SeparatorLength at zero if the relative file is opened
1200                 //  by Id.
1201                 //
1202 
1203                 if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) {
1204 
1205                     //
1206                     //  Add a separator if the name length is non-zero
1207                     //  unless the relative Fcb is at the root.
1208                     //
1209 
1210                     if ((FileName->Length != 0) &&
1211                         (RelatedCcb->Fcb != Vcb->RootIndexFcb)) {
1212 
1213                         SeparatorLength = sizeof( WCHAR );
1214                     }
1215 
1216                     RelatedNameLength = RelatedFileName->Length;
1217                 }
1218 
1219             //
1220             //  The full name is already in the filename.  It must either
1221             //  be length 0 or begin with a backslash.
1222             //
1223 
1224             } else if (FileName->Length != 0) {
1225 
1226                 if (FileName->Buffer[0] != L'\\') {
1227 
1228                     return STATUS_INVALID_PARAMETER;
1229                 }
1230 
1231                 //
1232                 //  We will want to trim the leading backslash from the
1233                 //  remaining name we return.
1234                 //
1235 
1236                 RemainingNameLength -= sizeof( WCHAR );
1237                 SeparatorLength = sizeof( WCHAR );
1238             }
1239 
1240             //
1241             //  Now see if the buffer is large enough to hold the full name.
1242             //
1243 
1244             BufferLength = RelatedNameLength + SeparatorLength + RemainingNameLength;
1245 
1246             //
1247             //  Check for an overflow of the maximum filename size.
1248             //
1249 
1250             if (BufferLength > MAXUSHORT) {
1251 
1252                 return STATUS_INVALID_PARAMETER;
1253             }
1254 
1255             //
1256             //  Now see if we need to allocate a new buffer.
1257             //
1258 
1259             if (FileName->MaximumLength < BufferLength) {
1260 
1261                 NewFileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
1262                                                                BufferLength,
1263                                                                TAG_FILE_NAME );
1264 
1265                 NewFileName.MaximumLength = (USHORT) BufferLength;
1266 
1267             } else {
1268 
1269                 NewFileName.Buffer = FileName->Buffer;
1270                 NewFileName.MaximumLength = FileName->MaximumLength;
1271             }
1272 
1273             //
1274             //  If there is a related name then we need to slide the remaining bytes up and
1275             //  insert the related name.  Otherwise the name is in the correct position
1276             //  already.
1277             //
1278 
1279             if (RelatedNameLength != 0) {
1280 
1281                 //
1282                 //  Store the remaining name in its correct position.
1283                 //
1284 
1285                 if (RemainingNameLength != 0) {
1286 
1287                     RtlMoveMemory( Add2Ptr( NewFileName.Buffer, RelatedNameLength + SeparatorLength, PVOID ),
1288                                    FileName->Buffer,
1289                                    RemainingNameLength );
1290                 }
1291 
1292                 RtlCopyMemory( NewFileName.Buffer,
1293                                RelatedFileName->Buffer,
1294                                RelatedNameLength );
1295 
1296                 //
1297                 //  Add the separator if needed.
1298                 //
1299 
1300                 if (SeparatorLength != 0) {
1301 
1302                     *(Add2Ptr( NewFileName.Buffer, RelatedNameLength, PWCHAR )) = L'\\';
1303                 }
1304 
1305                 //
1306                 //  Update the filename value we got from the user.
1307                 //
1308 
1309                 if (NewFileName.Buffer != FileName->Buffer) {
1310 
1311                     if (FileName->Buffer != NULL) {
1312 
1313                         CdFreePool( &FileName->Buffer );
1314                     }
1315 
1316                     FileName->Buffer = NewFileName.Buffer;
1317                     FileName->MaximumLength = NewFileName.MaximumLength;
1318                 }
1319 
1320                 //
1321                 //  Copy the name length to the user's filename.
1322                 //
1323 
1324                 FileName->Length = (USHORT) (RelatedNameLength + SeparatorLength + RemainingNameLength);
1325             }
1326 
1327             //
1328             //  Now update the remaining name to parse.
1329             //
1330 
1331             RemainingName->FileName.MaximumLength =
1332             RemainingName->FileName.Length = (USHORT) RemainingNameLength;
1333             RemainingName->VersionString.Length = 0;
1334 
1335             RemainingName->FileName.Buffer = Add2Ptr( FileName->Buffer,
1336                                                       RelatedNameLength + SeparatorLength,
1337                                                       PWCHAR );
1338 
1339             //
1340             //  Upcase the name if necessary.
1341             //
1342 
1343             if (IgnoreCase && (RemainingNameLength != 0)) {
1344 
1345                 CdUpcaseName( IrpContext,
1346                               RemainingName,
1347                               RemainingName );
1348             }
1349 
1350             //
1351             //  Do a quick check to make sure there are no wildcards.
1352             //
1353 #ifdef _MSC_VER
1354 #pragma prefast(push)
1355 #pragma prefast(suppress:26000, "RemainingName->FileName.Buffer = FileName.Buffer + (RelatedNameLength + SeparatorLength); FileName.MaximumLength < (RelatedNameLength + SeparatorLength + RemainingNameLength).")
1356 #endif
1357             if (FsRtlDoesNameContainWildCards( &RemainingName->FileName )) {
1358 #ifdef _MSC_VER
1359 #pragma prefast(pop)
1360 #endif
1361 
1362                 return STATUS_OBJECT_NAME_INVALID;
1363             }
1364 
1365         //
1366         //  For the open by file Id case we verify the name really contains
1367         //  a 64 bit value.
1368         //
1369 
1370         } else {
1371 
1372             //
1373             //  Check for validity of the buffer.
1374             //
1375 
1376             if (FileName->Length != sizeof( FILE_ID )) {
1377 
1378                 return STATUS_INVALID_PARAMETER;
1379             }
1380         }
1381 
1382         SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FULL_NAME );
1383 
1384     //
1385     //  If we are in the retry path then the full name is already in the
1386     //  file object name.  If this is a case-sensitive operation then
1387     //  we need to upcase the name from the end of any related file name already stored
1388     //  there.
1389     //
1390 
1391     } else {
1392 
1393         //
1394         //  Assume there is no relative name.
1395         //
1396 
1397         RemainingName->FileName = *FileName;
1398         RemainingName->VersionString.Length = 0;
1399 
1400         //
1401         //  Nothing to do if the name length is zero.
1402         //
1403 
1404         if (RemainingName->FileName.Length != 0) {
1405 
1406             //
1407             //  If there is a relative name then we need to walk past it.
1408             //
1409 
1410             if (RelatedTypeOfOpen != UnopenedFileObject) {
1411 
1412                 //
1413                 //  Nothing to walk past if the RelatedCcb is opened by FileId.
1414                 //
1415 
1416 
1417                 if (!FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID )) {
1418 
1419                     //
1420                     //  Related file name is a proper prefix of the full name.
1421                     //  We step over the related name and if we are then
1422                     //  pointing at a separator character we step over that.
1423                     //
1424 
1425                     RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer,
1426                                                               RelatedFileName->Length,
1427                                                               PWCHAR );
1428 
1429                     RemainingName->FileName.Length -= RelatedFileName->Length;
1430                 }
1431             }
1432 
1433             //
1434             //  If we are pointing at a separator character then step past that.
1435             //
1436 
1437             if (RemainingName->FileName.Length != 0) {
1438 
1439                 if (*(RemainingName->FileName.Buffer) == L'\\') {
1440 
1441                     RemainingName->FileName.Buffer = Add2Ptr( RemainingName->FileName.Buffer,
1442                                                               sizeof( WCHAR ),
1443                                                               PWCHAR );
1444 
1445                     RemainingName->FileName.Length -= sizeof( WCHAR );
1446                 }
1447             }
1448         }
1449 
1450         //
1451         //  Upcase the name if necessary.
1452         //
1453 
1454         if (IgnoreCase && (RemainingName->FileName.Length != 0)) {
1455 
1456             CdUpcaseName( IrpContext,
1457                           RemainingName,
1458                           RemainingName );
1459         }
1460     }
1461 
1462 #ifdef _MSC_VER
1463 #pragma prefast(push)
1464 #pragma prefast(suppress:26030, "RemainingName->FileName.Buffer = FileName.Buffer + (RelatedNameLength + SeparatorLength); FileName.MaximumLength < (RelatedNameLength + SeparatorLength + RemainingNameLength).")
1465 #endif
1466     return STATUS_SUCCESS;
1467 #ifdef _MSC_VER
1468 #pragma prefast(pop)
1469 #endif
1470 }
1471 
1472 
1473 //
1474 //  Local support routine
1475 //
1476 
1477 _Requires_lock_held_(_Global_critical_region_)
1478 _Acquires_exclusive_lock_((*CurrentFcb)->FcbNonpaged->FcbResource)
1479 NTSTATUS
1480 CdOpenByFileId (
1481     _In_ PIRP_CONTEXT IrpContext,
1482     _In_ PIO_STACK_LOCATION IrpSp,
1483     _In_ PVCB Vcb,
1484     _Inout_ PFCB *CurrentFcb
1485     )
1486 
1487 /*++
1488 
1489 Routine Description:
1490 
1491     This routine is called to open a file by the FileId.  The file Id is in
1492     the FileObject name buffer and has been verified to be 64 bits.
1493 
1494     We extract the Id number and then check to see whether we are opening a
1495     file or directory and compare that with the create options.  If this
1496     generates no error then optimistically look up the Fcb in the Fcb Table.
1497 
1498     If we don't find the Fcb then we need to carefully verify there is a file
1499     at this offset.  First check whether the Parent Fcb is in the table.  If
1500     not then lookup the parent at the path table offset given by file ID.
1501 
1502     If found then build the Fcb from this entry and store the new Fcb in the
1503     tree.
1504 
1505     We know have the parent Fcb.  Do a directory scan to find the dirent at
1506     the given offset in this stream.  This must point to the first entry
1507     of a valid file.
1508 
1509     Finally we call our worker routine to complete the open on this Fcb.
1510 
1511 Arguments:
1512 
1513     IrpSp - Stack location within the create Irp.
1514 
1515     Vcb - Vcb for this volume.
1516 
1517     CurrentFcb - Address to store the Fcb for this open.  We only store the
1518         CurrentFcb here when we have acquired it so our caller knows to
1519         free or deallocate it.
1520 
1521 Return Value:
1522 
1523     NTSTATUS - Status indicating the result of the operation.
1524 
1525 --*/
1526 
1527 {
1528     NTSTATUS Status = STATUS_ACCESS_DENIED;
1529 
1530     BOOLEAN UnlockVcb = FALSE;
1531     BOOLEAN Found;
1532 
1533     ULONG StreamOffset;
1534 
1535     NODE_TYPE_CODE NodeTypeCode;
1536     TYPE_OF_OPEN TypeOfOpen;
1537 
1538     FILE_ENUM_CONTEXT FileContext;
1539     BOOLEAN CleanupFileContext = FALSE;
1540 
1541     COMPOUND_PATH_ENTRY CompoundPathEntry = {{0}};/* ReactOS Change: GCC "missing braces around initializer" */
1542     BOOLEAN CleanupCompoundPathEntry = FALSE;
1543 
1544     FILE_ID FileId;
1545     FILE_ID ParentFileId;
1546 
1547     PFCB NextFcb;
1548 
1549     PAGED_CODE();
1550 
1551     //
1552     //  Extract the FileId from the FileObject.
1553     //
1554 
1555     RtlCopyMemory( &FileId, IrpSp->FileObject->FileName.Buffer, sizeof( FILE_ID ));
1556 
1557     //
1558     //  Use a try-finally to facilitate cleanup.
1559     //
1560 
1561     _SEH2_TRY {
1562 
1563         //
1564         //  Go ahead and figure out the TypeOfOpen and NodeType.  We can
1565         //  get these from the input FileId.
1566         //
1567 
1568         if (CdFidIsDirectory( FileId )) {
1569 
1570             TypeOfOpen = UserDirectoryOpen;
1571             NodeTypeCode = CDFS_NTC_FCB_INDEX;
1572 
1573             //
1574             //  If the offset isn't zero then the file Id is bad.
1575             //
1576 
1577             if (CdQueryFidDirentOffset( FileId ) != 0) {
1578 
1579                 try_return( Status = STATUS_INVALID_PARAMETER );
1580             }
1581 
1582         } else {
1583 
1584             TypeOfOpen = UserFileOpen;
1585             NodeTypeCode = CDFS_NTC_FCB_DATA;
1586         }
1587 
1588         //
1589         //  Acquire the Vcb and check if there is already an Fcb.
1590         //  If not we will need to carefully verify the Fcb.
1591         //  We will post the request if we don't find the Fcb and this
1592         //  request can't wait.
1593         //
1594 
1595         CdLockVcb( IrpContext, Vcb );
1596         UnlockVcb = TRUE;
1597 
1598         NextFcb = CdLookupFcbTable( IrpContext, Vcb, FileId );
1599 
1600         if (NextFcb == NULL) {
1601 
1602             //
1603             //  Get the path table offset from the file id.
1604             //
1605 
1606             StreamOffset = CdQueryFidPathTableOffset( FileId );
1607 
1608             //
1609             //  Build the parent FileId for this and try looking it
1610             //  up in the PathTable.
1611             //
1612 
1613             CdSetFidDirentOffset( ParentFileId, 0 );
1614             CdSetFidPathTableOffset( ParentFileId, StreamOffset );
1615             CdFidSetDirectory( ParentFileId );
1616 
1617             NextFcb = CdLookupFcbTable( IrpContext, Vcb, ParentFileId );
1618 
1619             //
1620             //  If not present then walk through the PathTable to this point.
1621             //
1622 
1623             if (NextFcb == NULL) {
1624 
1625                 CdUnlockVcb( IrpContext, Vcb );
1626                 UnlockVcb = FALSE;
1627 
1628                 //
1629                 //  Check that the path table offset lies within the path
1630                 //  table.
1631                 //
1632 
1633                 if (StreamOffset > Vcb->PathTableFcb->FileSize.LowPart) {
1634 
1635                     try_return( Status = STATUS_INVALID_PARAMETER );
1636                 }
1637 
1638                 CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
1639                 CleanupCompoundPathEntry = TRUE;
1640 
1641                 //
1642                 //  Start at the first entry in the PathTable.
1643                 //
1644 
1645                 CdLookupPathEntry( IrpContext,
1646                                    Vcb->PathTableFcb->StreamOffset,
1647                                    1,
1648                                    TRUE,
1649                                    &CompoundPathEntry );
1650 
1651                 //
1652                 //  Continue looking until we have passed our target offset.
1653                 //
1654 
1655                 while (TRUE) {
1656 
1657                     //
1658                     //  Move to the next entry.
1659                     //
1660 
1661                     Found = CdLookupNextPathEntry( IrpContext,
1662                                                    &CompoundPathEntry.PathContext,
1663                                                    &CompoundPathEntry.PathEntry );
1664 
1665                     //
1666                     //  If we didn't find the entry or are beyond it then the
1667                     //  input Id is invalid.
1668                     //
1669 
1670                     if (!Found ||
1671                         (CompoundPathEntry.PathEntry.PathTableOffset > StreamOffset)) {
1672 
1673                         try_return( Status = STATUS_INVALID_PARAMETER );
1674                     }
1675                 }
1676 
1677                 //
1678                 //  If the FileId specified a directory then we have found
1679                 //  the entry.  Make sure our caller wanted to open a directory.
1680                 //
1681 
1682                 if ((TypeOfOpen == UserDirectoryOpen) &&
1683                     FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
1684 
1685                     try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
1686                 }
1687 
1688                 //
1689                 //  Lock the Vcb and create the Fcb if necessary.
1690                 //
1691 
1692                 CdLockVcb( IrpContext, Vcb );
1693                 UnlockVcb = TRUE;
1694 
1695                 NextFcb = CdCreateFcb( IrpContext, ParentFileId, NodeTypeCode, &Found );
1696 
1697                 //
1698                 //  It's possible that someone got in here ahead of us.
1699                 //
1700 
1701                 if (!Found) {
1702 
1703                     CdInitializeFcbFromPathEntry( IrpContext,
1704                                                   NextFcb,
1705                                                   NULL,
1706                                                   &CompoundPathEntry.PathEntry );
1707                 }
1708 
1709                 //
1710                 //  If the user wanted to open a directory then we have found
1711                 //  it.  Store this Fcb into the CurrentFcb and skip the
1712                 //  directory scan.
1713                 //
1714 
1715                 if (TypeOfOpen == UserDirectoryOpen) {
1716 
1717                     *CurrentFcb = NextFcb;
1718                     NextFcb = NULL;
1719                 }
1720             }
1721 
1722             //
1723             //  Perform the directory scan if we don't already have our target.
1724             //
1725 
1726             if (NextFcb != NULL) {
1727 
1728                 //
1729                 //  Acquire the parent.  We currently own the Vcb lock so
1730                 //  do this without waiting first.
1731                 //
1732 
1733                 if (!CdAcquireFcbExclusive( IrpContext,
1734                                             NextFcb,
1735                                             TRUE )) {
1736 
1737                     NextFcb->FcbReference += 1;
1738                     CdUnlockVcb( IrpContext, Vcb );
1739 
1740                     CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
1741 
1742                     CdLockVcb( IrpContext, Vcb );
1743                     NextFcb->FcbReference -= 1;
1744                     CdUnlockVcb( IrpContext, Vcb );
1745 
1746                 } else {
1747 
1748                     CdUnlockVcb( IrpContext, Vcb );
1749                 }
1750 
1751                 UnlockVcb = FALSE;
1752 
1753                 //
1754                 //  Set up the CurrentFcb pointers.  We know there was
1755                 //  no previous parent in this case.
1756                 //
1757 
1758                 *CurrentFcb = NextFcb;
1759 
1760                 //
1761                 //  Calculate the offset in the stream.
1762                 //
1763 
1764                 StreamOffset = CdQueryFidDirentOffset( FileId );
1765 
1766                 //
1767                 //  Create the stream file if it doesn't exist.  This will update
1768                 //  the Fcb with the size from the self entry.
1769                 //
1770 
1771                 CdVerifyOrCreateDirStreamFile( IrpContext, NextFcb);
1772 
1773                 //
1774                 //  If our offset is beyond the end of the directory then the
1775                 //  FileId is invalid.
1776                 //
1777 
1778                 if (StreamOffset > NextFcb->FileSize.LowPart) {
1779 
1780                     try_return( Status = STATUS_INVALID_PARAMETER );
1781                 }
1782 
1783                 //
1784                 //  Otherwise position ourselves at the self entry and walk
1785                 //  through dirent by dirent until this location is found.
1786                 //
1787 
1788                 CdInitializeFileContext( IrpContext, &FileContext );
1789                 CdLookupInitialFileDirent( IrpContext,
1790                                            NextFcb,
1791                                            &FileContext,
1792                                            NextFcb->StreamOffset );
1793 
1794                 CleanupFileContext = TRUE;
1795 
1796                 while (TRUE) {
1797 
1798                     //
1799                     //  Move to the first entry of the next file.
1800                     //
1801 
1802                     Found = CdLookupNextInitialFileDirent( IrpContext,
1803                                                            NextFcb,
1804                                                            &FileContext );
1805 
1806                     //
1807                     //  If we didn't find the entry or are beyond it then the
1808                     //  input Id is invalid.
1809                     //
1810 
1811                     if (!Found ||
1812                         (FileContext.InitialDirent->Dirent.DirentOffset > StreamOffset)) {
1813 
1814                         try_return( Status = STATUS_INVALID_PARAMETER );
1815                     }
1816                 }
1817 
1818                 //
1819                 //  This better not be a directory.  Directory FileIds must
1820                 //  refer to the self entry for directories.
1821                 //
1822 
1823                 if (FlagOn( FileContext.InitialDirent->Dirent.DirentFlags,
1824                             CD_ATTRIBUTE_DIRECTORY )) {
1825 
1826                     try_return( Status = STATUS_INVALID_PARAMETER );
1827                 }
1828 
1829                 //
1830                 //  Check that our caller wanted to open a file.
1831                 //
1832 
1833                 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
1834 
1835                     try_return( Status = STATUS_NOT_A_DIRECTORY );
1836                 }
1837 
1838                 //
1839                 //  Otherwise we want to collect all of the dirents for this file
1840                 //  and create an Fcb with this.
1841                 //
1842 
1843                 CdLookupLastFileDirent( IrpContext, NextFcb, &FileContext );
1844 
1845                 CdLockVcb( IrpContext, Vcb );
1846                 UnlockVcb = TRUE;
1847 
1848                 NextFcb = CdCreateFcb( IrpContext, FileId, NodeTypeCode, &Found );
1849 
1850                 //
1851                 //  It's possible that someone has since created this Fcb since we
1852                 //  first checked.  If so then can simply use this.  Otherwise
1853                 //  we need to initialize a new Fcb and attach it to our parent
1854                 //  and insert it into the Fcb Table.
1855                 //
1856 
1857                 if (!Found) {
1858 
1859                     CdInitializeFcbFromFileContext( IrpContext,
1860                                                     NextFcb,
1861                                                     *CurrentFcb,
1862                                                     &FileContext );
1863                 }
1864             }
1865 
1866         //
1867         //  We have the Fcb.  Check that the type of the file is compatible with
1868         //  the desired type of file to open.
1869         //
1870 
1871         } else {
1872 
1873             if (FlagOn( NextFcb->FileAttributes, FILE_ATTRIBUTE_DIRECTORY )) {
1874 
1875                 if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NON_DIRECTORY_FILE )) {
1876 
1877                     try_return( Status = STATUS_FILE_IS_A_DIRECTORY );
1878                 }
1879 
1880             } else if (FlagOn( IrpSp->Parameters.Create.Options, FILE_DIRECTORY_FILE )) {
1881 
1882                 try_return( Status = STATUS_NOT_A_DIRECTORY );
1883             }
1884         }
1885 
1886         //
1887         //  If we have a the previous Fcb and have inserted the next Fcb into
1888         //  the Fcb Table.  It is safe to release the current Fcb if present
1889         //  since it is referenced through the child Fcb.
1890         //
1891 
1892         if (*CurrentFcb != NULL) {
1893 
1894             CdReleaseFcb( IrpContext, *CurrentFcb );
1895         }
1896 
1897         //
1898         //  We now know the Fcb and currently hold the Vcb lock.
1899         //  Try to acquire this Fcb without waiting.  Otherwise we
1900         //  need to reference it, drop the Vcb, acquire the Fcb and
1901         //  then dereference the Fcb.
1902         //
1903 
1904         if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
1905 
1906             NextFcb->FcbReference += 1;
1907 
1908             CdUnlockVcb( IrpContext, Vcb );
1909 
1910             CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
1911 
1912             CdLockVcb( IrpContext, Vcb );
1913             NextFcb->FcbReference -= 1;
1914             CdUnlockVcb( IrpContext, Vcb );
1915 
1916         } else {
1917 
1918             CdUnlockVcb( IrpContext, Vcb );
1919         }
1920 
1921         UnlockVcb = FALSE;
1922 
1923         //
1924         //  Move to this Fcb.
1925         //
1926 
1927         *CurrentFcb = NextFcb;
1928 
1929         // Lock object is acquired using internal state
1930         _Analysis_suppress_lock_checking_(NextFcb->FcbNonpaged->FcbResource);
1931 
1932         //
1933         //  Check the requested access on this Fcb.
1934         //
1935 
1936         if (!CdIllegalFcbAccess( IrpContext,
1937                                  TypeOfOpen,
1938                                  IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
1939 
1940             //
1941             //  Call our worker routine to complete the open.
1942             //
1943 
1944             Status = CdCompleteFcbOpen( IrpContext,
1945                                         IrpSp,
1946                                         Vcb,
1947                                         CurrentFcb,
1948                                         TypeOfOpen,
1949                                         CCB_FLAG_OPEN_BY_ID,
1950                                         IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
1951 
1952         }
1953 
1954     try_exit:  NOTHING;
1955     } _SEH2_FINALLY {
1956 
1957         if (UnlockVcb) {
1958 
1959             CdUnlockVcb( IrpContext, Vcb );
1960         }
1961 
1962         if (CleanupFileContext) {
1963 
1964             CdCleanupFileContext( IrpContext, &FileContext );
1965         }
1966 
1967         if (CleanupCompoundPathEntry) {
1968 
1969             CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
1970         }
1971     } _SEH2_END;
1972 
1973     return Status;
1974 }
1975 
1976 
1977 //
1978 //  Local support routine
1979 //
1980 
1981 _Requires_lock_held_(_Global_critical_region_)
1982 NTSTATUS
1983 CdOpenExistingFcb (
1984     _In_ PIRP_CONTEXT IrpContext,
1985     _In_ PIO_STACK_LOCATION IrpSp,
1986     _Inout_ PFCB *CurrentFcb,
1987     _In_ TYPE_OF_OPEN TypeOfOpen,
1988     _In_ BOOLEAN IgnoreCase,
1989     _In_opt_ PCCB RelatedCcb
1990     )
1991 
1992 /*++
1993 
1994 Routine Description:
1995 
1996     This routine is called to open an Fcb which is already in the Fcb table.
1997     We will verify the access to the file and then call our worker routine
1998     to perform the final operations.
1999 
2000 Arguments:
2001 
2002     IrpSp - Pointer to the stack location for this open.
2003 
2004     CurrentFcb - Address of Fcb to open.  We will clear this if the Fcb
2005         is released here.
2006 
2007     TypeOfOpen - Indicates whether we are opening a file, directory or volume.
2008 
2009     IgnoreCase - Indicates if this open is case-insensitive.
2010 
2011     RelatedCcb - Ccb for related file object if relative open.  We use
2012         this when setting the Ccb flags for this open.  It will tell
2013         us whether the name currently in the file object is relative or
2014         absolute.
2015 
2016 Return Value:
2017 
2018     NTSTATUS - Status indicating the result of the operation.
2019 
2020 --*/
2021 
2022 {
2023     ULONG CcbFlags = 0;
2024 
2025     NTSTATUS Status = STATUS_ACCESS_DENIED;
2026 
2027     PAGED_CODE();
2028 
2029     //
2030     //  Check that the desired access is legal.
2031     //
2032 
2033     if (!CdIllegalFcbAccess( IrpContext,
2034                              TypeOfOpen,
2035                              IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
2036 
2037         //
2038         //  Set the Ignore case.
2039         //
2040 
2041         if (IgnoreCase) {
2042 
2043             SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
2044         }
2045 
2046         //
2047         //  Check the related Ccb to see if this was an OpenByFileId and
2048         //  whether there was a version.
2049         //
2050 
2051         if (ARGUMENT_PRESENT( RelatedCcb )) {
2052 
2053             SetFlag( CcbFlags, FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_WITH_VERSION ));
2054 
2055 
2056             if (FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
2057 
2058                 SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID );
2059             }
2060         }
2061 
2062         //
2063         //  Call our worker routine to complete the open.
2064         //
2065 
2066         Status = CdCompleteFcbOpen( IrpContext,
2067                                     IrpSp,
2068                                     (*CurrentFcb)->Vcb,
2069                                     CurrentFcb,
2070                                     TypeOfOpen,
2071                                     CcbFlags,
2072                                     IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
2073     }
2074 
2075     return Status;
2076 }
2077 
2078 
2079 //
2080 //  Local support routine
2081 //
2082 
2083 _Requires_lock_held_(_Global_critical_region_)
2084 _Acquires_lock_((*CurrentFcb)->FcbNonpaged->FcbResource)
2085 NTSTATUS
2086 CdOpenDirectoryFromPathEntry (
2087     _In_ PIRP_CONTEXT IrpContext,
2088     _In_ PIO_STACK_LOCATION IrpSp,
2089     _In_ PVCB Vcb,
2090     _Inout_ PFCB *CurrentFcb,
2091     _In_ PCD_NAME DirName,
2092     _In_ BOOLEAN IgnoreCase,
2093     _In_ BOOLEAN ShortNameMatch,
2094     _In_ PPATH_ENTRY PathEntry,
2095     _In_ BOOLEAN PerformUserOpen,
2096     _In_opt_ PCCB RelatedCcb
2097     )
2098 
2099 /*++
2100 
2101 Routine Description:
2102 
2103     This routine is called to open a directory where the directory was found
2104     in the path table.  This routine is called in the case where this is the
2105     file to open for the user and where this is an intermediate node in the
2106     full path to open.
2107 
2108     We first check that the desired access is legal for a directory.  Then we
2109     construct the FileId for this and do a check to see if it is the Fcb
2110     Table.  It is always possible that either it was created since or simply
2111     wasn't in the prefix table at the time of the prefix table search.
2112     Initialize the Fcb and store into the FcbTable if not present.
2113 
2114     Next we will add this to the prefix table of our parent if needed.
2115 
2116     Once we know that the new Fcb has been initialized then we move our pointer
2117     in the tree down to this position.
2118 
2119     This routine does not own the Vcb lock on entry.  We must be sure to release
2120     it on exit.
2121 
2122 Arguments:
2123 
2124     IrpSp - Stack location for this request.
2125 
2126     Vcb - Vcb for this volume.
2127 
2128     CurrentFcb - On input this is the parent of the Fcb to open.  On output we
2129         store the Fcb for the file being opened.
2130 
2131     DirName - This is always the exact name used to reach this file.
2132 
2133     IgnoreCase - Indicates the type of case match for the open.
2134 
2135     ShortNameMatch - Indicates if we are opening via the short name.
2136 
2137     PathEntry - Path entry for the entry found.
2138 
2139     PerformUserOpen - TRUE if we are to open this for a user, FALSE otherwise.
2140 
2141     RelatedCcb - RelatedCcb for relative file object used to make this open.
2142 
2143 Return Value:
2144 
2145     NTSTATUS - Status indicating the result of the operation.
2146 
2147 --*/
2148 
2149 {
2150     ULONG CcbFlags = 0;
2151     FILE_ID FileId;
2152 
2153     BOOLEAN UnlockVcb = FALSE;
2154     BOOLEAN FcbExisted;
2155 
2156     PFCB NextFcb;
2157     PFCB ParentFcb = NULL;
2158 
2159     NTSTATUS Status = STATUS_SUCCESS;
2160 
2161     PAGED_CODE();
2162 
2163     //
2164     //  Check for illegal access to this file.
2165     //
2166 
2167     if (PerformUserOpen &&
2168         CdIllegalFcbAccess( IrpContext,
2169                             UserDirectoryOpen,
2170                             IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
2171 
2172         return STATUS_ACCESS_DENIED;
2173     }
2174 
2175     //
2176     //  Use a try-finally to facilitate cleanup.
2177     //
2178 
2179     _SEH2_TRY {
2180 
2181         //
2182         //  Check the related Ccb to see if this was an OpenByFileId.
2183         //
2184 
2185         if (ARGUMENT_PRESENT( RelatedCcb ) &&
2186             FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
2187 
2188             CcbFlags = CCB_FLAG_OPEN_RELATIVE_BY_ID;
2189         }
2190 
2191         if (IgnoreCase) {
2192 
2193             SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
2194         }
2195 
2196         //
2197         //  Build the file Id for this file.
2198         //
2199 
2200         FileId.QuadPart = 0;
2201         CdSetFidPathTableOffset( FileId, PathEntry->PathTableOffset );
2202         CdFidSetDirectory( FileId );
2203 
2204         //
2205         //  Lock the Vcb so we can examine the Fcb Table.
2206         //
2207 
2208         CdLockVcb( IrpContext, Vcb );
2209         UnlockVcb = TRUE;
2210 
2211         //
2212         //  Get the Fcb for this directory.
2213         //
2214 
2215         NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_INDEX, &FcbExisted );
2216 
2217         //
2218         //  If the Fcb was created here then initialize from the values in the
2219         //  path table entry.
2220         //
2221 
2222         if (!FcbExisted) {
2223 
2224             CdInitializeFcbFromPathEntry( IrpContext, NextFcb, *CurrentFcb, PathEntry );
2225         }
2226 
2227         //
2228         //  Now try to acquire the new Fcb without waiting.  We will reference
2229         //  the Fcb and retry with wait if unsuccessful.
2230         //
2231 
2232         if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
2233 
2234             NextFcb->FcbReference += 1;
2235 
2236             CdUnlockVcb( IrpContext, Vcb );
2237 
2238             CdReleaseFcb( IrpContext, *CurrentFcb );
2239             CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
2240             CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE );
2241 
2242             CdLockVcb( IrpContext, Vcb );
2243             NextFcb->FcbReference -= 1;
2244             CdUnlockVcb( IrpContext, Vcb );
2245 
2246         } else {
2247 
2248             //
2249             //  Unlock the Vcb and move down to this new Fcb.  Remember that we still
2250             //  own the parent however.
2251             //
2252 
2253             CdUnlockVcb( IrpContext, Vcb );
2254         }
2255 
2256         UnlockVcb = FALSE;
2257 
2258         ParentFcb = *CurrentFcb;
2259         *CurrentFcb = NextFcb;
2260 
2261         // Lock object is acquired using internal state
2262         _Analysis_suppress_lock_checking_(NextFcb->FcbNonpaged->FcbResource);
2263 
2264         //
2265         //  Store this name into the prefix table for the parent.
2266         //
2267 
2268         if (ShortNameMatch) {
2269 
2270             //
2271             //  Make sure the exact case is always in the tree.
2272             //
2273 
2274             CdInsertPrefix( IrpContext,
2275                             NextFcb,
2276                             DirName,
2277                             FALSE,
2278                             TRUE,
2279                             ParentFcb );
2280 
2281             if (IgnoreCase) {
2282 
2283                 CdInsertPrefix( IrpContext,
2284                                 NextFcb,
2285                                 DirName,
2286                                 TRUE,
2287                                 TRUE,
2288                                 ParentFcb );
2289             }
2290 
2291         } else {
2292 
2293             //
2294             //  Make sure the exact case is always in the tree.
2295             //
2296 
2297             CdInsertPrefix( IrpContext,
2298                             NextFcb,
2299                             &PathEntry->CdDirName,
2300                             FALSE,
2301                             FALSE,
2302                             ParentFcb );
2303 
2304             if (IgnoreCase) {
2305 
2306                 CdInsertPrefix( IrpContext,
2307                                 NextFcb,
2308                                 &PathEntry->CdCaseDirName,
2309                                 TRUE,
2310                                 FALSE,
2311                                 ParentFcb );
2312             }
2313         }
2314 
2315         //
2316         //  Release the parent Fcb at this point.
2317         //
2318 
2319         CdReleaseFcb( IrpContext, ParentFcb );
2320         ParentFcb = NULL;
2321 
2322         //
2323         //  Call our worker routine to complete the open.
2324         //
2325 
2326         if (PerformUserOpen) {
2327 
2328             Status = CdCompleteFcbOpen( IrpContext,
2329                                         IrpSp,
2330                                         Vcb,
2331                                         CurrentFcb,
2332                                         UserDirectoryOpen,
2333                                         CcbFlags,
2334                                         IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
2335         }
2336 
2337     } _SEH2_FINALLY {
2338 
2339         //
2340         //  Unlock the Vcb if held.
2341         //
2342 
2343         if (UnlockVcb) {
2344 
2345             CdUnlockVcb( IrpContext, Vcb );
2346         }
2347 
2348         //
2349         //  Release the parent if held.
2350         //
2351 
2352         if (ParentFcb != NULL) {
2353 
2354             CdReleaseFcb( IrpContext, ParentFcb );
2355         }
2356     } _SEH2_END;
2357 
2358     return Status;
2359 }
2360 
2361 
2362 //
2363 //  Local support routine
2364 //
2365 
2366 _Requires_lock_held_(_Global_critical_region_)
2367 NTSTATUS
2368 CdOpenFileFromFileContext (
2369     _In_ PIRP_CONTEXT IrpContext,
2370     _In_ PIO_STACK_LOCATION IrpSp,
2371     _In_ PVCB Vcb,
2372     _Inout_ PFCB *CurrentFcb,
2373     _In_ PCD_NAME FileName,
2374     _In_ BOOLEAN IgnoreCase,
2375     _In_ BOOLEAN ShortNameMatch,
2376     _In_ PFILE_ENUM_CONTEXT FileContext,
2377     _In_opt_ PCCB RelatedCcb
2378     )
2379 
2380 /*++
2381 
2382 Routine Description:
2383 
2384     This routine is called to open a file where the file was found in a directory scan.
2385     This should only be for a file in the case since we will find the directories in the
2386     path table.
2387 
2388     We first check that the desired access is legal for this file.  Then we
2389     construct the FileId for this and do a check to see if it is the Fcb
2390     Table.  It is always possible that either it was created since or simply
2391     wasn't in the prefix table at the time of the prefix table search.
2392     Initialize the Fcb and store into the FcbTable if not present.
2393 
2394     Next we will add this to the prefix table of our parent if needed.
2395 
2396     Once we know that the new Fcb has been initialized then we move our pointer
2397     in the tree down to this position.
2398 
2399     This routine does not own the Vcb lock on entry.  We must be sure to release
2400     it on exit.
2401 
2402 Arguments:
2403 
2404     IrpSp - Stack location for this request.
2405 
2406     Vcb - Vcb for the current volume.
2407 
2408     CurrentFcb - On input this is the parent of the Fcb to open.  On output we
2409         store the Fcb for the file being opened.
2410 
2411     FileName - This is always the exact name used to reach this file.
2412 
2413     IgnoreCase - Indicates the type of case of CaseName above.
2414 
2415     ShortNameMatch - Indicates if we are opening via the short name.
2416 
2417     FileContext - This is the context used to find the file.
2418 
2419     RelatedCcb - RelatedCcb for relative file object used to make this open.
2420 
2421 Return Value:
2422 
2423     NTSTATUS - Status indicating the result of the operation.
2424 
2425 --*/
2426 
2427 {
2428     ULONG CcbFlags = 0;
2429     FILE_ID FileId;
2430 
2431     BOOLEAN UnlockVcb = FALSE;
2432     BOOLEAN FcbExisted;
2433 
2434     PFCB NextFcb;
2435     PFCB ParentFcb = NULL;
2436 
2437     NTSTATUS Status = STATUS_SUCCESS;
2438 
2439     PAGED_CODE();
2440 
2441     //
2442     //  Check for illegal access to this file.
2443     //
2444 
2445     if (CdIllegalFcbAccess( IrpContext,
2446                             UserFileOpen,
2447                             IrpSp->Parameters.Create.SecurityContext->DesiredAccess )) {
2448 
2449         return STATUS_ACCESS_DENIED;
2450     }
2451 
2452     //
2453     //  Use a try-finally to facilitate cleanup.
2454     //
2455 
2456     _SEH2_TRY {
2457 
2458         //
2459         //  Check if a version number was used to open this file.
2460         //
2461 
2462         if (FileName->VersionString.Length != 0) {
2463 
2464             SetFlag( CcbFlags, CCB_FLAG_OPEN_WITH_VERSION );
2465         }
2466 
2467         //
2468         //  Check the related Ccb to see if this was an OpenByFileId.
2469         //
2470 
2471         if (ARGUMENT_PRESENT( RelatedCcb ) &&
2472             FlagOn( RelatedCcb->Flags, CCB_FLAG_OPEN_BY_ID | CCB_FLAG_OPEN_RELATIVE_BY_ID )) {
2473 
2474             SetFlag( CcbFlags, CCB_FLAG_OPEN_RELATIVE_BY_ID );
2475         }
2476 
2477         if (IgnoreCase) {
2478 
2479             SetFlag( CcbFlags, CCB_FLAG_IGNORE_CASE );
2480         }
2481 
2482         //
2483         //  Build the file Id for this file.  We can use the path table offset from the
2484         //  parent and the directory offset from the dirent.
2485         //
2486 
2487         CdSetFidPathTableOffset( FileId, CdQueryFidPathTableOffset( (*CurrentFcb)->FileId ));
2488         CdSetFidDirentOffset( FileId, FileContext->InitialDirent->Dirent.DirentOffset );
2489 
2490         //
2491         //  Lock the Vcb so we can examine the Fcb Table.
2492         //
2493 
2494         CdLockVcb( IrpContext, Vcb );
2495         UnlockVcb = TRUE;
2496 
2497         //
2498         //  Get the Fcb for this file.
2499         //
2500 
2501         NextFcb = CdCreateFcb( IrpContext, FileId, CDFS_NTC_FCB_DATA, &FcbExisted );
2502 
2503         //
2504         //  If the Fcb was created here then initialize from the values in the
2505         //  dirent.
2506         //
2507 
2508         if (!FcbExisted) {
2509 
2510             CdInitializeFcbFromFileContext( IrpContext,
2511                                             NextFcb,
2512                                             *CurrentFcb,
2513                                             FileContext );
2514         }
2515 
2516         //
2517         //  Now try to acquire the new Fcb without waiting.  We will reference
2518         //  the Fcb and retry with wait if unsuccessful.
2519         //
2520 
2521         if (!CdAcquireFcbExclusive( IrpContext, NextFcb, TRUE )) {
2522 
2523             NextFcb->FcbReference += 1;
2524 
2525             CdUnlockVcb( IrpContext, Vcb );
2526 
2527             CdReleaseFcb( IrpContext, *CurrentFcb );
2528             CdAcquireFcbExclusive( IrpContext, NextFcb, FALSE );
2529             CdAcquireFcbExclusive( IrpContext, *CurrentFcb, FALSE );
2530 
2531             CdLockVcb( IrpContext, Vcb );
2532             NextFcb->FcbReference -= 1;
2533             CdUnlockVcb( IrpContext, Vcb );
2534 
2535         } else {
2536 
2537             //
2538             //  Unlock the Vcb and move down to this new Fcb.  Remember that we still
2539             //  own the parent however.
2540             //
2541 
2542             CdUnlockVcb( IrpContext, Vcb );
2543         }
2544 
2545         UnlockVcb = FALSE;
2546 
2547         ParentFcb = *CurrentFcb;
2548         *CurrentFcb = NextFcb;
2549 
2550         //
2551         //  Store this name into the prefix table for the parent.
2552         //
2553 
2554 
2555         if (ShortNameMatch) {
2556 
2557             //
2558             //  Make sure the exact case is always in the tree.
2559             //
2560 
2561             CdInsertPrefix( IrpContext,
2562                             NextFcb,
2563                             FileName,
2564                             FALSE,
2565                             TRUE,
2566                             ParentFcb );
2567 
2568             if (IgnoreCase) {
2569 
2570                 CdInsertPrefix( IrpContext,
2571                                 NextFcb,
2572                                 FileName,
2573                                 TRUE,
2574                                 TRUE,
2575                                 ParentFcb );
2576             }
2577 
2578         //
2579         //  Insert this into the prefix table if we found this without
2580         //  using a version string.
2581         //
2582 
2583         } else if (FileName->VersionString.Length == 0) {
2584 
2585             //
2586             //  Make sure the exact case is always in the tree.
2587             //
2588 
2589             CdInsertPrefix( IrpContext,
2590                             NextFcb,
2591                             &FileContext->InitialDirent->Dirent.CdFileName,
2592                             FALSE,
2593                             FALSE,
2594                             ParentFcb );
2595 
2596             if (IgnoreCase) {
2597 
2598                 CdInsertPrefix( IrpContext,
2599                                 NextFcb,
2600                                 &FileContext->InitialDirent->Dirent.CdCaseFileName,
2601                                 TRUE,
2602                                 FALSE,
2603                                 ParentFcb );
2604             }
2605         }
2606 
2607         //
2608         //  Release the parent Fcb at this point.
2609         //
2610 
2611         _Analysis_assume_same_lock_(ParentFcb->FcbNonpaged->FcbResource, NextFcb->FcbNonpaged->FcbResource);
2612         CdReleaseFcb( IrpContext, ParentFcb );
2613         ParentFcb = NULL;
2614 
2615         //
2616         //  Call our worker routine to complete the open.
2617         //
2618 
2619         Status = CdCompleteFcbOpen( IrpContext,
2620                                     IrpSp,
2621                                     Vcb,
2622                                     CurrentFcb,
2623                                     UserFileOpen,
2624                                     CcbFlags,
2625                                     IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
2626 
2627     } _SEH2_FINALLY {
2628 
2629         //
2630         //  Unlock the Vcb if held.
2631         //
2632 
2633         if (UnlockVcb) {
2634 
2635             CdUnlockVcb( IrpContext, Vcb );
2636         }
2637 
2638         //
2639         //  Release the parent if held.
2640         //
2641 
2642         if (ParentFcb != NULL) {
2643 
2644             CdReleaseFcb( IrpContext, ParentFcb );
2645         }
2646     } _SEH2_END;
2647 
2648     return Status;
2649 }
2650 
2651 
2652 //
2653 //  Local support routine
2654 //
2655 
2656 _Requires_lock_held_(_Global_critical_region_)
2657 NTSTATUS
2658 CdCompleteFcbOpen (
2659     _In_ PIRP_CONTEXT IrpContext,
2660     _In_ PIO_STACK_LOCATION IrpSp,
2661     _In_ PVCB Vcb,
2662     _Inout_ PFCB *CurrentFcb,
2663     _In_ TYPE_OF_OPEN TypeOfOpen,
2664     _In_ ULONG UserCcbFlags,
2665     _In_ ACCESS_MASK DesiredAccess
2666     )
2667 
2668 /*++
2669 
2670 Routine Description:
2671 
2672     This is the worker routine which takes an existing Fcb and completes
2673     the open.  We will do any necessary oplock checks and sharing checks.
2674     Finally we will create the Ccb and update the file object and any
2675     file object flags.
2676 
2677 Arguments:
2678 
2679     IrpSp - Stack location for the current request.
2680 
2681     Vcb - Vcb for the current volume.
2682 
2683     CurrentFcb - Address of pointer to Fcb to open.  We clear this field if
2684         we release the resource for this file.
2685 
2686     TypeOfOpen - Type of open for this request.
2687 
2688     UserCcbFlags - Flags to OR into the Ccb flags.
2689 
2690     DesiredAccess - Desired access for this open.
2691 
2692 Return Value:
2693 
2694     NTSTATUS - STATUS_SUCCESS if we complete this request, STATUS_PENDING if
2695         the oplock package takes the Irp or SHARING_VIOLATION if there is a
2696         sharing check conflict.
2697 
2698 --*/
2699 
2700 {
2701     NTSTATUS Status;
2702     NTSTATUS OplockStatus  = STATUS_SUCCESS;
2703     ULONG Information = FILE_OPENED;
2704 
2705     BOOLEAN LockVolume = FALSE;
2706 
2707     PFCB Fcb = *CurrentFcb;
2708     PCCB Ccb;
2709 
2710     PAGED_CODE();
2711 
2712     //
2713     //  Expand maximum allowed to something sensible for share access checking
2714     //
2715 
2716     if (MAXIMUM_ALLOWED == DesiredAccess)  {
2717 
2718         DesiredAccess = FILE_ALL_ACCESS & ~((TypeOfOpen != UserVolumeOpen ?
2719                                              (FILE_WRITE_ATTRIBUTES           |
2720                                               FILE_WRITE_DATA                 |
2721                                               FILE_WRITE_EA                   |
2722                                               FILE_ADD_FILE                   |
2723                                               FILE_ADD_SUBDIRECTORY           |
2724                                               FILE_APPEND_DATA) : 0)          |
2725                                             FILE_DELETE_CHILD                 |
2726                                             DELETE                            |
2727                                             WRITE_DAC );
2728     }
2729 
2730     //
2731     //  If this a volume open and the user wants to lock the volume then
2732     //  purge and lock the volume.
2733     //
2734 
2735     if ((TypeOfOpen <= UserVolumeOpen) &&
2736         !FlagOn( IrpSp->Parameters.Create.ShareAccess, FILE_SHARE_READ )) {
2737 
2738         //
2739         //  If there are open handles then fail this immediately.
2740         //
2741 
2742         if (Vcb->VcbCleanup != 0) {
2743 
2744             return STATUS_SHARING_VIOLATION;
2745         }
2746 
2747         //
2748         //  If we can't wait then force this to be posted.
2749         //
2750 
2751         if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {
2752 
2753             CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
2754         }
2755 
2756         LockVolume = TRUE;
2757 
2758         //
2759         //  Purge the volume and make sure all of the user references
2760         //  are gone.
2761         //
2762 
2763         Status = CdPurgeVolume( IrpContext, Vcb, FALSE );
2764 
2765         if (Status != STATUS_SUCCESS) {
2766 
2767             return Status;
2768         }
2769 
2770         //
2771         //  Now force all of the delayed close operations to go away.
2772         //
2773 
2774         CdFspClose( Vcb );
2775 
2776         if (Vcb->VcbUserReference > CDFS_RESIDUAL_USER_REFERENCE) {
2777 
2778             return STATUS_SHARING_VIOLATION;
2779         }
2780     }
2781 
2782     //
2783     //  If the Fcb already existed then we need to check the oplocks and
2784     //  the share access.
2785     //
2786 
2787     if (Fcb->FcbCleanup != 0) {
2788 
2789         //
2790         //  If this is a user file open then check whether there are any
2791         //  batch oplock.
2792         //
2793 
2794         if (TypeOfOpen == UserFileOpen) {
2795 
2796             //
2797             //  Store the address of the Fcb for a possible teardown into
2798             //  the IrpContext.  We will release this in the call to
2799             //  prepost the Irp.
2800             //
2801 
2802             IrpContext->TeardownFcb = CurrentFcb;
2803 
2804             if (FsRtlCurrentBatchOplock( CdGetFcbOplock(Fcb) )) {
2805 
2806                 //
2807                 //  We remember if a batch oplock break is underway for the
2808                 //  case where the sharing check fails.
2809                 //
2810 
2811                 Information = FILE_OPBATCH_BREAK_UNDERWAY;
2812 
2813                 OplockStatus = FsRtlCheckOplock( CdGetFcbOplock(Fcb),
2814                                                  IrpContext->Irp,
2815                                                  IrpContext,
2816                                                  (PVOID)CdOplockComplete,   /* ReactOS Change: GCC "assignment from incompatible pointer type" */
2817                                                  (PVOID)CdPrePostIrp );   /* ReactOS Change: GCC "assignment from incompatible pointer type" */
2818 
2819                 if (OplockStatus == STATUS_PENDING) {
2820 
2821                     return STATUS_PENDING;
2822                 }
2823             }
2824 
2825             //
2826             //  Check the share access before breaking any exclusive oplocks.
2827             //
2828 
2829             Status = IoCheckShareAccess( DesiredAccess,
2830                                          IrpSp->Parameters.Create.ShareAccess,
2831                                          IrpSp->FileObject,
2832                                          &Fcb->ShareAccess,
2833                                          FALSE );
2834 
2835             if (!NT_SUCCESS( Status )) {
2836 
2837                 return Status;
2838             }
2839 
2840             //
2841             //  Now check that we can continue based on the oplock state of the
2842             //  file.
2843             //
2844 
2845             OplockStatus = FsRtlCheckOplock( CdGetFcbOplock(Fcb),
2846                                              IrpContext->Irp,
2847                                              IrpContext,
2848                                              (PVOID)CdOplockComplete,/* ReactOS Change: GCC "assignment from incompatible pointer type" */
2849                                              (PVOID)CdPrePostIrp );/* ReactOS Change: GCC "assignment from incompatible pointer type" */
2850 
2851             if (OplockStatus == STATUS_PENDING) {
2852 
2853                 return STATUS_PENDING;
2854             }
2855 
2856             IrpContext->TeardownFcb = NULL;
2857 
2858         //
2859         //  Otherwise just do the sharing check.
2860         //
2861 
2862         } else {
2863 
2864             Status = IoCheckShareAccess( DesiredAccess,
2865                                          IrpSp->Parameters.Create.ShareAccess,
2866                                          IrpSp->FileObject,
2867                                          &Fcb->ShareAccess,
2868                                          FALSE );
2869 
2870             if (!NT_SUCCESS( Status )) {
2871 
2872                 return Status;
2873             }
2874         }
2875     }
2876 
2877     //
2878     //  Create the Ccb now.
2879     //
2880 
2881     Ccb = CdCreateCcb( IrpContext, Fcb, UserCcbFlags );
2882 
2883     //
2884     //  Update the share access.
2885     //
2886 
2887     if (Fcb->FcbCleanup == 0) {
2888 
2889         IoSetShareAccess( DesiredAccess,
2890                           IrpSp->Parameters.Create.ShareAccess,
2891                           IrpSp->FileObject,
2892                           &Fcb->ShareAccess );
2893 
2894     } else {
2895 
2896         IoUpdateShareAccess( IrpSp->FileObject, &Fcb->ShareAccess );
2897     }
2898 
2899     //
2900     //  Set the file object type.
2901     //
2902 
2903     CdSetFileObject( IrpContext, IrpSp->FileObject, TypeOfOpen, Fcb, Ccb );
2904 
2905     //
2906     //  Set the appropriate cache flags for a user file object.
2907     //
2908 
2909     if (TypeOfOpen == UserFileOpen) {
2910 
2911         if (FlagOn( IrpSp->Parameters.Create.Options, FILE_NO_INTERMEDIATE_BUFFERING )) {
2912 
2913             SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING );
2914 
2915         } else {
2916 
2917             SetFlag( IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED );
2918         }
2919     }
2920     else if (TypeOfOpen == UserVolumeOpen)  {
2921 
2922         SetFlag( IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING );
2923     }
2924 
2925     //
2926     //  Update the open and cleanup counts.  Check the fast io state here.
2927     //
2928 
2929     CdLockVcb( IrpContext, Vcb );
2930 
2931     CdIncrementCleanupCounts( IrpContext, Fcb );
2932     CdIncrementReferenceCounts( IrpContext, Fcb, 1, 1 );
2933 
2934     if (LockVolume) {
2935 
2936         Vcb->VolumeLockFileObject = IrpSp->FileObject;
2937         SetFlag( Vcb->VcbState, VCB_STATE_LOCKED );
2938     }
2939 
2940     CdUnlockVcb( IrpContext, Vcb );
2941 
2942     CdLockFcb( IrpContext, Fcb );
2943 
2944     if (TypeOfOpen == UserFileOpen) {
2945 
2946         Fcb->IsFastIoPossible = CdIsFastIoPossible( Fcb );
2947 
2948     } else {
2949 
2950         Fcb->IsFastIoPossible = FastIoIsNotPossible;
2951     }
2952 
2953     CdUnlockFcb( IrpContext, Fcb );
2954 
2955     //
2956     //  Show that we opened the file.
2957     //
2958 
2959     IrpContext->Irp->IoStatus.Information = Information;
2960 
2961     //
2962     //  Point to the section object pointer in the non-paged Fcb.
2963     //
2964 
2965     IrpSp->FileObject->SectionObjectPointer = &Fcb->FcbNonpaged->SegmentObject;
2966     return OplockStatus;
2967 }
2968 
2969 
2970 
2971 
2972 
2973