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