[CDFS_NEW] Re-apply many of the reactos-specific changes and fix build.
[reactos.git] / drivers / filesystems / cdfs_new / create.c
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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