1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     FileInfo.c
8 
9 Abstract:
10 
11     This module implements the File Information routines for Fat called by
12     the dispatch driver.
13 
14 
15 --*/
16 
17 #include "fatprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (FAT_BUG_CHECK_FILEINFO)
24 
25 //
26 //  The local debug trace level
27 //
28 
29 #define Dbg                              (DEBUG_TRACE_FILEINFO)
30 
31 VOID
32 FatQueryBasicInfo (
33     IN PIRP_CONTEXT IrpContext,
34     IN PFCB Fcb,
35     IN PFILE_OBJECT FileObject,
36     IN OUT PFILE_BASIC_INFORMATION Buffer,
37     IN OUT PLONG Length
38     );
39 
40 _Requires_lock_held_(_Global_critical_region_)
41 VOID
42 FatQueryStandardInfo (
43     IN PIRP_CONTEXT IrpContext,
44     IN PFCB Fcb,
45     IN OUT PFILE_STANDARD_INFORMATION Buffer,
46     IN OUT PLONG Length
47     );
48 
49 VOID
50 FatQueryInternalInfo (
51     IN PIRP_CONTEXT IrpContext,
52     IN PFCB Fcb,
53     IN OUT PFILE_INTERNAL_INFORMATION Buffer,
54     IN OUT PLONG Length
55     );
56 
57 VOID
58 FatQueryEaInfo (
59     IN PIRP_CONTEXT IrpContext,
60     IN PFCB Fcb,
61     IN OUT PFILE_EA_INFORMATION Buffer,
62     IN OUT PLONG Length
63     );
64 
65 VOID
66 FatQueryPositionInfo (
67     IN PIRP_CONTEXT IrpContext,
68     IN PFILE_OBJECT FileObject,
69     IN OUT PFILE_POSITION_INFORMATION Buffer,
70     IN OUT PLONG Length
71     );
72 
73 _Requires_lock_held_(_Global_critical_region_)
74 VOID
75 FatQueryNameInfo (
76     IN PIRP_CONTEXT IrpContext,
77     IN PFCB Fcb,
78     IN PCCB Ccb,
79     IN BOOLEAN Normalized,
80     IN OUT PFILE_NAME_INFORMATION Buffer,
81     IN OUT PLONG Length
82     );
83 
84 VOID
85 FatQueryShortNameInfo (
86     IN PIRP_CONTEXT IrpContext,
87     IN PFCB Fcb,
88     IN OUT PFILE_NAME_INFORMATION Buffer,
89     IN OUT PLONG Length
90     );
91 
92 _Requires_lock_held_(_Global_critical_region_)
93 VOID
94 FatQueryNetworkInfo (
95     IN PIRP_CONTEXT IrpContext,
96     IN PFCB Fcb,
97     IN PFILE_OBJECT FileObject,
98     IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
99     IN OUT PLONG Length
100     );
101 
102 _Requires_lock_held_(_Global_critical_region_)
103 NTSTATUS
104 FatSetBasicInfo (
105     IN PIRP_CONTEXT IrpContext,
106     IN PIRP Irp,
107     IN PFCB Fcb,
108     IN PCCB Ccb
109     );
110 
111 _Requires_lock_held_(_Global_critical_region_)
112 NTSTATUS
113 FatSetDispositionInfo (
114     IN PIRP_CONTEXT IrpContext,
115     IN PIRP Irp,
116     IN PFILE_OBJECT FileObject,
117     IN PFCB Fcb
118     );
119 
120 NTSTATUS
121 FatSetRenameInfo (
122     IN PIRP_CONTEXT IrpContext,
123     IN PIRP Irp,
124     IN PVCB Vcb,
125     IN PFCB Fcb,
126     IN PCCB Ccb
127     );
128 
129 NTSTATUS
130 FatSetPositionInfo (
131     IN PIRP_CONTEXT IrpContext,
132     IN PIRP Irp,
133     IN PFILE_OBJECT FileObject
134     );
135 
136 _Requires_lock_held_(_Global_critical_region_)
137 NTSTATUS
138 FatSetAllocationInfo (
139     IN PIRP_CONTEXT IrpContext,
140     IN PIRP Irp,
141     IN PFCB Fcb,
142     IN PFILE_OBJECT FileObject
143     );
144 
145 _Requires_lock_held_(_Global_critical_region_)
146 NTSTATUS
147 FatSetEndOfFileInfo (
148     IN PIRP_CONTEXT IrpContext,
149     IN PIRP Irp,
150     IN PFILE_OBJECT FileObject,
151     IN PVCB Vcb,
152     IN PFCB Fcb
153     );
154 
155 _Requires_lock_held_(_Global_critical_region_)
156 NTSTATUS
157 FatSetValidDataLengthInfo (
158     IN PIRP_CONTEXT IrpContext,
159     IN PIRP Irp,
160     IN PFILE_OBJECT FileObject,
161     IN PFCB Fcb,
162     IN PCCB Ccb
163     );
164 
165 _Requires_lock_held_(_Global_critical_region_)
166 VOID
167 FatRenameEAs (
168     IN PIRP_CONTEXT IrpContext,
169     IN PFCB Fcb,
170     IN USHORT ExtendedAttributes,
171     IN POEM_STRING OldOemName
172     );
173 
174 #ifdef ALLOC_PRAGMA
175 #pragma alloc_text(PAGE, FatCommonQueryInformation)
176 #pragma alloc_text(PAGE, FatCommonSetInformation)
177 #pragma alloc_text(PAGE, FatFsdQueryInformation)
178 #pragma alloc_text(PAGE, FatFsdSetInformation)
179 #pragma alloc_text(PAGE, FatQueryBasicInfo)
180 #pragma alloc_text(PAGE, FatQueryEaInfo)
181 #pragma alloc_text(PAGE, FatQueryInternalInfo)
182 #pragma alloc_text(PAGE, FatQueryNameInfo)
183 #pragma alloc_text(PAGE, FatQueryNetworkInfo)
184 #pragma alloc_text(PAGE, FatQueryShortNameInfo)
185 #pragma alloc_text(PAGE, FatQueryPositionInfo)
186 #pragma alloc_text(PAGE, FatQueryStandardInfo)
187 #pragma alloc_text(PAGE, FatSetAllocationInfo)
188 #pragma alloc_text(PAGE, FatSetBasicInfo)
189 #pragma alloc_text(PAGE, FatSetDispositionInfo)
190 #pragma alloc_text(PAGE, FatSetEndOfFileInfo)
191 #pragma alloc_text(PAGE, FatSetValidDataLengthInfo)
192 #pragma alloc_text(PAGE, FatSetPositionInfo)
193 #pragma alloc_text(PAGE, FatSetRenameInfo)
194 #pragma alloc_text(PAGE, FatDeleteFile)
195 #pragma alloc_text(PAGE, FatRenameEAs)
196 #endif
197 
198 
199 _Function_class_(IRP_MJ_QUERY_INFORMATION)
_Function_class_(DRIVER_DISPATCH)200 _Function_class_(DRIVER_DISPATCH)
201 NTSTATUS
202 NTAPI
203 FatFsdQueryInformation (
204     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
205     _Inout_ PIRP Irp
206     )
207 
208 /*++
209 
210 Routine Description:
211 
212     This routine implements the Fsd part of the NtQueryInformationFile API
213     call.
214 
215 Arguments:
216 
217     VolumeDeviceObject - Supplies the volume device object where the file
218         being queried exists.
219 
220     Irp - Supplies the Irp being processed.
221 
222 Return Value:
223 
224     NTSTATUS - The FSD status for the Irp.
225 
226 --*/
227 
228 {
229     NTSTATUS Status;
230     PIRP_CONTEXT IrpContext = NULL;
231 
232     BOOLEAN TopLevel;
233 
234     PAGED_CODE();
235 
236     DebugTrace(+1, Dbg, "FatFsdQueryInformation\n", 0);
237 
238     //
239     //  Call the common query routine, with blocking allowed if synchronous
240     //
241 
242     FsRtlEnterFileSystem();
243 
244     TopLevel = FatIsIrpTopLevel( Irp );
245 
246     _SEH2_TRY {
247 
248         IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
249 
250         Status = FatCommonQueryInformation( IrpContext, Irp );
251 
252     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
253 
254         //
255         //  We had some trouble trying to perform the requested
256         //  operation, so we'll abort the I/O request with
257         //  the error status that we get back from the
258         //  execption code
259         //
260 
261         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
262     } _SEH2_END;
263 
264     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
265 
266     FsRtlExitFileSystem();
267 
268     //
269     //  And return to our caller
270     //
271 
272     DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status);
273 
274     UNREFERENCED_PARAMETER( VolumeDeviceObject );
275 
276     return Status;
277 }
278 
279 
280 _Function_class_(IRP_MJ_SET_INFORMATION)
_Function_class_(DRIVER_DISPATCH)281 _Function_class_(DRIVER_DISPATCH)
282 NTSTATUS
283 NTAPI
284 FatFsdSetInformation (
285     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
286     _Inout_ PIRP Irp
287     )
288 
289 /*++
290 
291 Routine Description:
292 
293     This routine implements the FSD part of the NtSetInformationFile API
294     call.
295 
296 Arguments:
297 
298     VolumeDeviceObject - Supplies the volume device object where the file
299         being set exists.
300 
301     Irp - Supplies the Irp being processed.
302 
303 Return Value:
304 
305     NTSTATUS - The FSD status for the Irp.
306 
307 --*/
308 
309 {
310     NTSTATUS Status;
311     PIRP_CONTEXT IrpContext = NULL;
312 
313     BOOLEAN TopLevel;
314 
315     PAGED_CODE();
316 
317     DebugTrace(+1, Dbg, "FatFsdSetInformation\n", 0);
318 
319     //
320     //  Call the common set routine, with blocking allowed if synchronous
321     //
322 
323     FsRtlEnterFileSystem();
324 
325     TopLevel = FatIsIrpTopLevel( Irp );
326 
327     _SEH2_TRY {
328 
329         IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
330 
331         Status = FatCommonSetInformation( IrpContext, Irp );
332 
333     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
334 
335         //
336         //  We had some trouble trying to perform the requested
337         //  operation, so we'll abort the I/O request with
338         //  the error status that we get back from the
339         //  execption code
340         //
341 
342         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
343     } _SEH2_END;
344 
345     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
346 
347     FsRtlExitFileSystem();
348 
349     //
350     //  And return to our caller
351     //
352 
353     DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status);
354 
355     UNREFERENCED_PARAMETER( VolumeDeviceObject );
356 
357     return Status;
358 }
359 
360 
_Requires_lock_held_(_Global_critical_region_)361 _Requires_lock_held_(_Global_critical_region_)
362 NTSTATUS
363 FatCommonQueryInformation (
364     IN PIRP_CONTEXT IrpContext,
365     IN PIRP Irp
366     )
367 
368 /*++
369 
370 Routine Description:
371 
372     This is the common routine for querying file information called by both
373     the fsd and fsp threads.
374 
375 Arguments:
376 
377     Irp - Supplies the Irp being processed
378 
379 Return Value:
380 
381     NTSTATUS - The return status for the operation
382 
383 --*/
384 
385 {
386     NTSTATUS Status;
387 
388     PIO_STACK_LOCATION IrpSp;
389 
390     PFILE_OBJECT FileObject;
391 
392     LONG Length;
393     FILE_INFORMATION_CLASS FileInformationClass;
394     PVOID Buffer;
395 
396     TYPE_OF_OPEN TypeOfOpen;
397     PVCB Vcb;
398     PFCB Fcb;
399     PCCB Ccb;
400 
401     BOOLEAN FcbAcquired = FALSE;
402     BOOLEAN VcbAcquired = FALSE;
403 
404     PFILE_ALL_INFORMATION AllInfo;
405 
406     PAGED_CODE();
407 
408     //
409     //  Get the current stack location
410     //
411 
412     IrpSp = IoGetCurrentIrpStackLocation( Irp );
413 
414     FileObject = IrpSp->FileObject;
415 
416     DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0);
417     DebugTrace( 0, Dbg, "Irp                    = %p\n", Irp);
418     DebugTrace( 0, Dbg, "->Length               = %08lx\n", IrpSp->Parameters.QueryFile.Length);
419     DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass);
420     DebugTrace( 0, Dbg, "->Buffer               = %p\n", Irp->AssociatedIrp.SystemBuffer);
421 
422     //
423     //  Reference our input parameters to make things easier
424     //
425 
426     Length = (LONG)IrpSp->Parameters.QueryFile.Length;
427     FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass;
428     Buffer = Irp->AssociatedIrp.SystemBuffer;
429 
430     //
431     //  Decode the file object
432     //
433 
434     TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
435 
436     Status = STATUS_SUCCESS;
437 
438     _SEH2_TRY {
439 
440         //
441         //  Case on the type of open we're dealing with
442         //
443 
444         switch (TypeOfOpen) {
445 
446         case UserVolumeOpen:
447 
448             //
449             //  We cannot query the user volume open.
450             //
451 
452             Status = STATUS_INVALID_PARAMETER;
453             break;
454 
455         case UserFileOpen:
456         case UserDirectoryOpen:
457         case DirectoryFile:
458 
459 
460             //
461             //  NameInfo requires synchronization with deletion in order to perform
462             //  the full filename query.  A lighter-weight way to do this would be per
463             //  directory as the full name is built up and since the multiple Fcb
464             //  lockorder is bottom up, this is conceivable.  At this time, though,
465             //  this change is safer.
466             //
467 
468             if (FileInformationClass == FileNameInformation ||
469 #if (NTDDI_VERSION >= NTDDI_VISTA)
470                 FileInformationClass == FileNormalizedNameInformation ||
471 #endif
472                 FileInformationClass == FileAllInformation ) {
473 
474                 if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
475 
476                     DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
477 
478                     Status = FatFsdPostRequest( IrpContext, Irp );
479                     IrpContext = NULL;
480                     Irp = NULL;
481 
482                     try_return( Status );
483                 }
484 
485                 VcbAcquired = TRUE;
486             }
487 
488             //
489             //  Acquire shared access to the fcb, except for a paging file
490             //  in order to avoid deadlocks with Mm.
491             //
492             //  The "removable" check was added specifically for ReadyBoost,
493             //  which opens its cache file on a removable device as a paging file and
494             //  relies on the file system to validate its mapping information after a
495             //  power transition.
496             //
497 
498             if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) ||
499                 FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
500 
501                 if (!FatAcquireSharedFcb( IrpContext, Fcb )) {
502 
503                     DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
504 
505                     Status = FatFsdPostRequest( IrpContext, Irp );
506                     IrpContext = NULL;
507                     Irp = NULL;
508 
509                     try_return( Status );
510                 }
511 
512                 FcbAcquired = TRUE;
513             }
514 
515             //
516             //  Make sure the Fcb is in a usable condition.  This
517             //  will raise an error condition if the fcb is unusable
518             //
519 
520             FatVerifyFcb( IrpContext, Fcb );
521 
522             //
523             //  Based on the information class we'll do different
524             //  actions.  Each of hte procedures that we're calling fills
525             //  up the output buffer, if possible.  They will raise the
526             //  status STATUS_BUFFER_OVERFLOW for an insufficient buffer.
527             //  This is considered a somewhat unusual case and is handled
528             //  more cleanly with the exception mechanism rather than
529             //  testing a return status value for each call.
530             //
531 
532             switch (FileInformationClass) {
533 
534             case FileAllInformation:
535 
536                 //
537                 //  For the all information class we'll typecast a local
538                 //  pointer to the output buffer and then call the
539                 //  individual routines to fill in the buffer.
540                 //
541 
542                 AllInfo = Buffer;
543                 Length -= (sizeof(FILE_ACCESS_INFORMATION)
544                            + sizeof(FILE_MODE_INFORMATION)
545                            + sizeof(FILE_ALIGNMENT_INFORMATION));
546 
547                 FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length );
548                 FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length );
549                 FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length );
550                 FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length );
551                 FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length );
552                 FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, &AllInfo->NameInformation, &Length );
553 
554                 break;
555 
556             case FileBasicInformation:
557 
558                 FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
559                 break;
560 
561             case FileStandardInformation:
562 
563                 FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length );
564                 break;
565 
566             case FileInternalInformation:
567 
568                 FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length );
569                 break;
570 
571             case FileEaInformation:
572 
573                 FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length );
574                 break;
575 
576             case FilePositionInformation:
577 
578                 FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length );
579                 break;
580 
581             case FileNameInformation:
582 
583                 FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, Buffer, &Length );
584                 break;
585 
586 #if (NTDDI_VERSION >= NTDDI_VISTA)
587             case FileNormalizedNameInformation:
588 
589                 FatQueryNameInfo( IrpContext, Fcb, Ccb, TRUE, Buffer, &Length );
590                 break;
591 #endif
592 
593             case FileAlternateNameInformation:
594 
595                 FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length );
596                 break;
597 
598             case FileNetworkOpenInformation:
599 
600                 FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
601                 break;
602 
603             default:
604 
605                 Status = STATUS_INVALID_PARAMETER;
606                 break;
607             }
608 
609             break;
610 
611         default:
612 
613             KdPrintEx((DPFLTR_FASTFAT_ID,
614                        DPFLTR_INFO_LEVEL,
615                        "FATQueryFile, Illegal TypeOfOpen = %08lx\n",
616                        TypeOfOpen));
617 
618             Status = STATUS_INVALID_PARAMETER;
619             break;
620         }
621 
622         //
623         //  If we overflowed the buffer, set the length to 0 and change the
624         //  status to STATUS_BUFFER_OVERFLOW.
625         //
626 
627         if ( Length < 0 ) {
628 
629             Status = STATUS_BUFFER_OVERFLOW;
630 
631             Length = 0;
632         }
633 
634         //
635         //  Set the information field to the number of bytes actually filled in
636         //  and then complete the request
637         //
638 
639         Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length;
640 
641     try_exit: NOTHING;
642     } _SEH2_FINALLY {
643 
644         DebugUnwind( FatCommonQueryInformation );
645 
646         if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
647         if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
648 
649         if (!_SEH2_AbnormalTermination()) {
650 
651             FatCompleteRequest( IrpContext, Irp, Status );
652         }
653 
654         DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status);
655     } _SEH2_END;
656 
657     return Status;
658 }
659 
660 
_Requires_lock_held_(_Global_critical_region_)661 _Requires_lock_held_(_Global_critical_region_)
662 NTSTATUS
663 FatCommonSetInformation (
664     IN PIRP_CONTEXT IrpContext,
665     IN PIRP Irp
666     )
667 
668 /*++
669 
670 Routine Description:
671 
672     This is the common routine for setting file information called by both
673     the fsd and fsp threads.
674 
675 Arguments:
676 
677     Irp - Supplies the Irp being processed
678 
679 Return Value:
680 
681     NTSTATUS - The return status for the operation
682 
683 --*/
684 
685 {
686     NTSTATUS Status = STATUS_SUCCESS;
687 
688     PIO_STACK_LOCATION IrpSp;
689 
690     PFILE_OBJECT FileObject;
691     FILE_INFORMATION_CLASS FileInformationClass;
692 
693     TYPE_OF_OPEN TypeOfOpen;
694     PVCB Vcb;
695     PFCB Fcb;
696     PCCB Ccb;
697 
698     BOOLEAN VcbAcquired = FALSE;
699     BOOLEAN FcbAcquired = FALSE;
700 
701     PAGED_CODE();
702 
703     //
704     //  Get the current stack location
705     //
706 
707     IrpSp = IoGetCurrentIrpStackLocation( Irp );
708 
709     DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0);
710     DebugTrace( 0, Dbg, "Irp                    = %p\n", Irp);
711     DebugTrace( 0, Dbg, "->Length               = %08lx\n", IrpSp->Parameters.SetFile.Length);
712     DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass);
713     DebugTrace( 0, Dbg, "->FileObject           = %p\n", IrpSp->Parameters.SetFile.FileObject);
714     DebugTrace( 0, Dbg, "->ReplaceIfExists      = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
715     DebugTrace( 0, Dbg, "->Buffer               = %p\n", Irp->AssociatedIrp.SystemBuffer);
716 
717     //
718     //  Reference our input parameters to make things easier
719     //
720 
721     FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
722     FileObject = IrpSp->FileObject;
723 
724     //
725     //  Decode the file object
726     //
727 
728     TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
729 
730     _SEH2_TRY {
731 
732         //
733         //  Case on the type of open we're dealing with
734         //
735 
736         switch (TypeOfOpen) {
737 
738         case UserVolumeOpen:
739 
740             //
741             //  We cannot query the user volume open.
742             //
743 
744             try_return( Status = STATUS_INVALID_PARAMETER );
745             break;
746 
747         case UserFileOpen:
748 
749             if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
750                 ((FileInformationClass == FileEndOfFileInformation) ||
751                  (FileInformationClass == FileAllocationInformation) ||
752                  (FileInformationClass == FileValidDataLengthInformation))) {
753 
754                 //
755                 //  We check whether we can proceed
756                 //  based on the state of the file oplocks.
757                 //
758 
759                 Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
760                                            Irp,
761                                            IrpContext,
762                                            NULL,
763                                            NULL );
764 
765                 if (Status != STATUS_SUCCESS) {
766 
767                     try_return( Status );
768                 }
769 
770                 //
771                 //  Set the flag indicating if Fast I/O is possible
772                 //
773 
774                 Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
775             }
776             break;
777 
778         case UserDirectoryOpen:
779 
780             break;
781 
782         default:
783 
784             try_return( Status = STATUS_INVALID_PARAMETER );
785         }
786 
787         //
788         //  We can only do a set on a nonroot dcb, so we do the test
789         //  and then fall through to the user file open code.
790         //
791 
792         if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
793 
794             if (FileInformationClass == FileDispositionInformation) {
795 
796                 try_return( Status = STATUS_CANNOT_DELETE );
797             }
798 
799             try_return( Status = STATUS_INVALID_PARAMETER );
800         }
801 
802         //
803         //  In the following two cases, we cannot have creates occuring
804         //  while we are here, so acquire the volume exclusive.
805         //
806 
807         if ((FileInformationClass == FileDispositionInformation) ||
808             (FileInformationClass == FileRenameInformation)) {
809 
810             if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
811 
812                 DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
813 
814                 Status = FatFsdPostRequest( IrpContext, Irp );
815                 Irp = NULL;
816                 IrpContext = NULL;
817 
818                 try_return( Status );
819             }
820 
821             VcbAcquired = TRUE;
822 
823             //
824             //  Make sure we haven't been called recursively by a filter inside an existing
825             //  create request.
826             //
827 
828             if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS)) {
829 
830 #ifdef _MSC_VER
831 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
832 #endif
833                 FatBugCheck( 0, 0, 0);
834             }
835         }
836 
837         //
838         //  Acquire exclusive access to the Fcb,  We use exclusive
839         //  because it is probable that one of the subroutines
840         //  that we call will need to monkey with file allocation,
841         //  create/delete extra fcbs.  So we're willing to pay the
842         //  cost of exclusive Fcb access.
843         //
844         //  Note that we do not acquire the resource for paging file
845         //  operations in order to avoid deadlock with Mm.
846         //
847         //  The "removable" check was added specifically for ReadyBoost,
848         //  which opens its cache file on a removable device as a paging file and
849         //  relies on the file system to validate its mapping information after a
850         //  power transition.
851         //
852 
853         if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) ||
854             FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
855 
856             if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) {
857 
858                 DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
859 
860                 Status = FatFsdPostRequest( IrpContext, Irp );
861                 Irp = NULL;
862                 IrpContext = NULL;
863 
864                 try_return( Status );
865             }
866 
867             FcbAcquired = TRUE;
868         }
869 
870         Status = STATUS_SUCCESS;
871 
872         //
873         //  Make sure the Fcb is in a usable condition.  This
874         //  will raise an error condition if the fcb is unusable
875         //
876 
877         FatVerifyFcb( IrpContext, Fcb );
878 
879         //
880         //  Now that we've acquired the file, do an oplock check if the operation
881         //  so warrants.
882         //
883 
884         if (FatIsFileOplockable( Fcb )&&
885             ((FileInformationClass == FileRenameInformation) ||
886              ((FileInformationClass == FileDispositionInformation) &&
887               ((PFILE_DISPOSITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer)->DeleteFile))) {
888 
889             Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
890                                        Irp,
891                                        IrpContext,
892                                        FatOplockComplete,
893                                        NULL );
894 
895             //
896             //  Set the flag indicating if Fast I/O is possible
897             //
898 
899             Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
900 
901             //
902             //  If STATUS_PENDING is returned it means the oplock
903             //  package has the Irp.  Don't complete the request here.
904             //
905 
906             if (Status == STATUS_PENDING) {
907 
908                 Irp = NULL;
909                 IrpContext = NULL;
910             }
911 
912             if (!NT_SUCCESS( Status ) ||
913                 (Status == STATUS_PENDING)) {
914 
915                 try_return( Status );
916             }
917         }
918 
919         //
920         //  Based on the information class we'll do different
921         //  actions.  Each of the procedures that we're calling will either
922         //  complete the request of send the request off to the fsp
923         //  to do the work.
924         //
925 
926         switch (FileInformationClass) {
927 
928         case FileBasicInformation:
929 
930             Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb );
931             break;
932 
933         case FileDispositionInformation:
934 
935             //
936             //  If this is on deferred flush media, we have to be able to wait.
937             //
938 
939             if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
940                  !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {
941 
942                 Status = FatFsdPostRequest( IrpContext, Irp );
943                 Irp = NULL;
944                 IrpContext = NULL;
945 
946             } else {
947 
948                 Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb );
949             }
950 
951             break;
952 
953         case FileRenameInformation:
954 
955             //
956             //  We proceed with this operation only if we can wait
957             //
958 
959             if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
960 
961                 Status = FatFsdPostRequest( IrpContext, Irp );
962                 Irp = NULL;
963                 IrpContext = NULL;
964 
965             } else {
966 
967                 Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb );
968 
969                 //
970                 //  If STATUS_PENDING is returned it means the oplock
971                 //  package has the Irp.  Don't complete the request here.
972                 //
973 
974                 if (Status == STATUS_PENDING) {
975                     Irp = NULL;
976                     IrpContext = NULL;
977                 }
978             }
979 
980             break;
981 
982         case FilePositionInformation:
983 
984             Status = FatSetPositionInfo( IrpContext, Irp, FileObject );
985             break;
986 
987         case FileLinkInformation:
988 
989             Status = STATUS_INVALID_DEVICE_REQUEST;
990             break;
991 
992         case FileAllocationInformation:
993 
994             Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject );
995             break;
996 
997         case FileEndOfFileInformation:
998 
999             Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb );
1000             break;
1001 
1002         case FileValidDataLengthInformation:
1003 
1004             Status = FatSetValidDataLengthInfo( IrpContext, Irp, FileObject, Fcb, Ccb );
1005             break;
1006 
1007         default:
1008 
1009             Status = STATUS_INVALID_PARAMETER;
1010             break;
1011         }
1012 
1013         if ( IrpContext != NULL ) {
1014 
1015             FatUnpinRepinnedBcbs( IrpContext );
1016         }
1017 
1018     try_exit: NOTHING;
1019     } _SEH2_FINALLY {
1020 
1021         DebugUnwind( FatCommonSetInformation );
1022 
1023         if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
1024 
1025         if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
1026 
1027         if (!_SEH2_AbnormalTermination()) {
1028 
1029             FatCompleteRequest( IrpContext, Irp, Status );
1030         }
1031 
1032         DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status);
1033     } _SEH2_END;
1034 
1035     return Status;
1036 }
1037 
1038 
1039 //
1040 //  Internal Support Routine
1041 //
1042 
1043 VOID
FatQueryBasicInfo(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb,IN PFILE_OBJECT FileObject,IN OUT PFILE_BASIC_INFORMATION Buffer,IN OUT PLONG Length)1044 FatQueryBasicInfo (
1045     IN PIRP_CONTEXT IrpContext,
1046     IN PFCB Fcb,
1047     IN PFILE_OBJECT FileObject,
1048     IN OUT PFILE_BASIC_INFORMATION Buffer,
1049     IN OUT PLONG Length
1050     )
1051 
1052 /*++
1053  Description:
1054 
1055     This routine performs the query basic information function for fat.
1056 
1057 Arguments:
1058 
1059     Fcb - Supplies the Fcb being queried, it has been verified
1060 
1061     FileObject - Supplies the flag bit that indicates the file was modified.
1062 
1063     Buffer - Supplies a pointer to the buffer where the information is to
1064         be returned
1065 
1066     Length - Supplies the length of the buffer in bytes, and receives the
1067         remaining bytes free in the buffer upon return.
1068 
1069 Return Value:
1070 
1071     None
1072 
1073 --*/
1074 
1075 {
1076     PAGED_CODE();
1077 
1078     UNREFERENCED_PARAMETER( FileObject );
1079     UNREFERENCED_PARAMETER( IrpContext );
1080 
1081     DebugTrace(+1, Dbg, "FatQueryBasicInfo...\n", 0);
1082 
1083     //
1084     //  Zero out the output buffer, and set it to indicate that
1085     //  the query is a normal file.  Later we might overwrite the
1086     //  attribute.
1087     //
1088 
1089     RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );
1090 
1091     //
1092     //  Extract the data and fill in the non zero fields of the output
1093     //  buffer
1094     //
1095 
1096     if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
1097 
1098         //
1099         //  We have to munge a lie on the fly.  Every time we have to
1100         //  use 1/1/80 we need to convert to GMT since the TZ may have
1101         //  changed on us.
1102         //
1103 
1104         ExLocalTimeToSystemTime( &FatJanOne1980,
1105                                  &Buffer->LastWriteTime );
1106         Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
1107 
1108     } else {
1109 
1110         Buffer->LastWriteTime = Fcb->LastWriteTime;
1111         Buffer->CreationTime = Fcb->CreationTime;
1112         Buffer->LastAccessTime = Fcb->LastAccessTime;
1113     }
1114 
1115     Buffer->FileAttributes = Fcb->DirentFatFlags;
1116 
1117 
1118     //
1119     //  If the temporary flag is set, then set it in the buffer.
1120     //
1121 
1122     if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
1123 
1124         SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
1125     }
1126 
1127     //
1128     //  If no attributes were set, set the normal bit.
1129     //
1130 
1131     if (Buffer->FileAttributes == 0) {
1132 
1133         Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
1134     }
1135 
1136     //
1137     //  Update the length and status output variables
1138     //
1139 
1140     *Length -= sizeof( FILE_BASIC_INFORMATION );
1141 
1142     DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1143 
1144     DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0);
1145 
1146     return;
1147 }
1148 
1149 
1150 //
1151 //  Internal Support Routine
1152 //
1153 
_Requires_lock_held_(_Global_critical_region_)1154 _Requires_lock_held_(_Global_critical_region_)
1155 VOID
1156 FatQueryStandardInfo (
1157     IN PIRP_CONTEXT IrpContext,
1158     IN PFCB Fcb,
1159     IN OUT PFILE_STANDARD_INFORMATION Buffer,
1160     IN OUT PLONG Length
1161     )
1162 
1163 /*++
1164 
1165 Routine Description:
1166 
1167     This routine performs the query standard information function for fat.
1168 
1169 Arguments:
1170 
1171     Fcb - Supplies the Fcb being queried, it has been verified
1172 
1173     Buffer - Supplies a pointer to the buffer where the information is to
1174         be returned
1175 
1176     Length - Supplies the length of the buffer in bytes, and receives the
1177         remaining bytes free in the buffer upon return.
1178 
1179 Return Value:
1180 
1181     None
1182 
1183 --*/
1184 
1185 {
1186     PAGED_CODE();
1187 
1188     DebugTrace(+1, Dbg, "FatQueryStandardInfo...\n", 0);
1189 
1190     //
1191     //  Zero out the output buffer, and fill in the number of links
1192     //  and the delete pending flag.
1193     //
1194 
1195     RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );
1196 
1197     Buffer->NumberOfLinks = 1;
1198     Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
1199 
1200     //
1201     //  Case on whether this is a file or a directory, and extract
1202     //  the information and fill in the fcb/dcb specific parts
1203     //  of the output buffer
1204     //
1205 
1206     if (NodeType(Fcb) == FAT_NTC_FCB) {
1207 
1208         if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
1209 
1210             FatLookupFileAllocationSize( IrpContext, Fcb );
1211         }
1212 
1213         Buffer->AllocationSize = Fcb->Header.AllocationSize;
1214         Buffer->EndOfFile = Fcb->Header.FileSize;
1215 
1216         Buffer->Directory = FALSE;
1217 
1218     } else {
1219 
1220         Buffer->Directory = TRUE;
1221     }
1222 
1223     //
1224     //  Update the length and status output variables
1225     //
1226 
1227     *Length -= sizeof( FILE_STANDARD_INFORMATION );
1228 
1229     DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1230 
1231     DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0);
1232 
1233     return;
1234 }
1235 
1236 
1237 //
1238 //  Internal Support Routine
1239 //
1240 
1241 VOID
FatQueryInternalInfo(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb,IN OUT PFILE_INTERNAL_INFORMATION Buffer,IN OUT PLONG Length)1242 FatQueryInternalInfo (
1243     IN PIRP_CONTEXT IrpContext,
1244     IN PFCB Fcb,
1245     IN OUT PFILE_INTERNAL_INFORMATION Buffer,
1246     IN OUT PLONG Length
1247     )
1248 
1249 /*++
1250 
1251 Routine Description:
1252 
1253     This routine performs the query internal information function for fat.
1254 
1255 Arguments:
1256 
1257     Fcb - Supplies the Fcb being queried, it has been verified
1258 
1259     Buffer - Supplies a pointer to the buffer where the information is to
1260         be returned
1261 
1262     Length - Supplies the length of the buffer in bytes, and receives the
1263         remaining bytes free in the buffer upon return.
1264 
1265 Return Value:
1266 
1267     None
1268 
1269 --*/
1270 
1271 {
1272     PAGED_CODE();
1273 
1274     UNREFERENCED_PARAMETER( IrpContext );
1275 
1276     DebugTrace(+1, Dbg, "FatQueryInternalInfo...\n", 0);
1277 
1278     _SEH2_TRY {
1279 
1280         Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb );
1281 
1282         //
1283         //  Update the length and status output variables
1284         //
1285 
1286         *Length -= sizeof( FILE_INTERNAL_INFORMATION );
1287 
1288     } _SEH2_FINALLY {
1289 
1290         DebugUnwind( FatQueryInternalInfo );
1291 
1292         DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1293 
1294         DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0);
1295     } _SEH2_END;
1296 
1297     return;
1298 }
1299 
1300 
1301 //
1302 //  Internal Support Routine
1303 //
1304 
1305 VOID
FatQueryEaInfo(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb,IN OUT PFILE_EA_INFORMATION Buffer,IN OUT PLONG Length)1306 FatQueryEaInfo (
1307     IN PIRP_CONTEXT IrpContext,
1308     IN PFCB Fcb,
1309     IN OUT PFILE_EA_INFORMATION Buffer,
1310     IN OUT PLONG Length
1311     )
1312 
1313 /*++
1314 
1315 Routine Description:
1316 
1317     This routine performs the query Ea information function for fat.
1318 
1319 Arguments:
1320 
1321     Fcb - Supplies the Fcb being queried, it has been verified
1322 
1323     Buffer - Supplies a pointer to the buffer where the information is to
1324         be returned
1325 
1326     Length - Supplies the length of the buffer in bytes, and receives the
1327         remaining bytes free in the buffer upon return.
1328 
1329 Return Value:
1330 
1331     None
1332 
1333 --*/
1334 
1335 {
1336     PBCB Bcb;
1337 
1338     PAGED_CODE();
1339 
1340     UNREFERENCED_PARAMETER( Fcb );
1341     UNREFERENCED_PARAMETER( IrpContext );
1342 
1343     DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0);
1344 
1345     Bcb = NULL;
1346 
1347     _SEH2_TRY {
1348 
1349         //
1350         //  Zero out the output buffer
1351         //
1352 
1353         RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) );
1354 
1355 #if 0
1356         //
1357         //  The Root dcb does not have any EAs so don't look for any.  Fat32
1358         //  doesn't have any, either.
1359         //
1360 
1361         if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB &&
1362              !FatIsFat32( Fcb->Vcb )) {
1363 
1364             PDIRENT Dirent;
1365 
1366             //
1367             //  Try to get the dirent for this file.
1368             //
1369 
1370             FatGetDirentFromFcbOrDcb( IrpContext,
1371                                       Fcb,
1372                                       &Dirent,
1373                                       &Bcb );
1374 
1375             if (Dirent != NULL) {
1376 
1377                 //
1378                 //  Get a the size needed to store the full eas for the file.
1379                 //
1380 
1381                 FatGetEaLength( IrpContext,
1382                                 Fcb->Vcb,
1383                                 Dirent,
1384                                 &Buffer->EaSize );
1385             }
1386         }
1387 #endif
1388 
1389         //
1390         //  Update the length and status output variables
1391         //
1392 
1393         *Length -= sizeof( FILE_EA_INFORMATION );
1394 
1395     } _SEH2_FINALLY {
1396 
1397         DebugUnwind( FatQueryEaInfo );
1398 
1399         //
1400         //  Unpin the dirent if pinned.
1401         //
1402 
1403         FatUnpinBcb( IrpContext, Bcb );
1404 
1405         DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1406 
1407         DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0);
1408     } _SEH2_END;
1409 }
1410 
1411 
1412 //
1413 //  Internal Support Routine
1414 //
1415 
1416 VOID
FatQueryPositionInfo(IN PIRP_CONTEXT IrpContext,IN PFILE_OBJECT FileObject,IN OUT PFILE_POSITION_INFORMATION Buffer,IN OUT PLONG Length)1417 FatQueryPositionInfo (
1418     IN PIRP_CONTEXT IrpContext,
1419     IN PFILE_OBJECT FileObject,
1420     IN OUT PFILE_POSITION_INFORMATION Buffer,
1421     IN OUT PLONG Length
1422     )
1423 
1424 /*++
1425 
1426 Routine Description:
1427 
1428     This routine performs the query position information function for fat.
1429 
1430 Arguments:
1431 
1432     FileObject - Supplies the File object being queried
1433 
1434     Buffer - Supplies a pointer to the buffer where the information is to
1435         be returned
1436 
1437     Length - Supplies the length of the buffer in bytes, and receives the
1438         remaining bytes free in the buffer upon return.
1439 
1440 Return Value:
1441 
1442     None
1443 
1444 --*/
1445 
1446 {
1447     PAGED_CODE();
1448 
1449     DebugTrace(+1, Dbg, "FatQueryPositionInfo...\n", 0);
1450 
1451     //
1452     //  Get the current position found in the file object.
1453     //
1454 
1455     Buffer->CurrentByteOffset = FileObject->CurrentByteOffset;
1456 
1457     //
1458     //  Update the length and status output variables
1459     //
1460 
1461     *Length -= sizeof( FILE_POSITION_INFORMATION );
1462 
1463     DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1464 
1465     DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0);
1466 
1467     UNREFERENCED_PARAMETER( IrpContext );
1468 
1469     return;
1470 }
1471 
1472 
1473 //
1474 //  Internal Support Routine
1475 //
1476 
_Requires_lock_held_(_Global_critical_region_)1477 _Requires_lock_held_(_Global_critical_region_)
1478 VOID
1479 FatQueryNameInfo (
1480     IN PIRP_CONTEXT IrpContext,
1481     IN PFCB Fcb,
1482     IN PCCB Ccb,
1483     IN BOOLEAN Normalized,
1484     IN OUT PFILE_NAME_INFORMATION Buffer,
1485     IN OUT PLONG Length
1486     )
1487 
1488 /*++
1489 
1490 Routine Description:
1491 
1492     This routine performs the query name information function for fat.
1493 
1494 Arguments:
1495 
1496     Fcb - Supplies the Fcb being queried, it has been verified
1497 
1498     Ccb - Supplies the Ccb for the context of the user open
1499 
1500     Normalized - if true the caller wants a normalized name (w/out short names).
1501         This means we're servicing a FileNormalizedNameInformation query.
1502 
1503     Buffer - Supplies a pointer to the buffer where the information is to
1504         be returned
1505 
1506     Length - Supplies the length of the buffer in bytes, and receives the
1507         remaining bytes free in the buffer upon return.
1508 
1509 Return Value:
1510 
1511     None
1512 
1513 --*/
1514 
1515 {
1516     ULONG BytesToCopy;
1517     LONG TrimLength;
1518     BOOLEAN Overflow = FALSE;
1519 
1520     PAGED_CODE();
1521 
1522     DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
1523 
1524     //
1525     //  Convert the name to UNICODE
1526     //
1527 
1528     *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
1529 
1530     //
1531     //  Use the full filename to build the path up.  If we wanted to be
1532     //  slick in the future, we'd just build the path directly into the
1533     //  return buffer and avoid constructing the full filename, but since
1534     //  the full filename winds up being required so often lets not
1535     //  over optimize this case yet.
1536     //
1537 
1538     if (Fcb->FullFileName.Buffer == NULL) {
1539 
1540         FatSetFullFileNameInFcb( IrpContext, Fcb );
1541     }
1542 
1543     //
1544     //  Here is where it gets a smidge tricky.  FinalNameLength is the length
1545     //  of the LFN element if it exists, and since the long name is always used
1546     //  to build FullFileName, we have two cases:
1547     //
1548     //  1) short name: use FinalNameLength to tear off the path from FullFileName
1549     //      and append the UNICODE converted short name.
1550     //  2) long name: just use FullFileName
1551     //
1552     //  We bias to the name the user thinks they opened by.  This winds
1553     //  up fixing some oddball tunneling cases where intermediate filters
1554     //  translate operations like delete into renames - this lets them
1555     //  do the operation in the context of the name the user was using.
1556     //
1557     //  It also matches what NTFS does, and so we have the definition of
1558     //  correct behavior.
1559     //
1560 
1561     //
1562     //
1563     //  Assume there is no long name and we are just going to use
1564     //  FullFileName.
1565     //
1566 
1567     TrimLength = 0;
1568 
1569     //
1570     //  If a LongName exists, the caller isn't asking for the normalized name,
1571     //  and the original open was by the short name then set TrimLength to point
1572     //  to the place where the short name goes.
1573     //
1574     //  Note: The Ccb can be NULL.  The lazy writer calls to get the name of
1575     //  a DirectoryOpen FILE_OBJECT that it wants to display in the lost
1576     //  delayed write popup.  Handle this case by just using the FileFullName.
1577     //
1578 
1579     if (!Normalized &&
1580         (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL)) {
1581 
1582         if ((Ccb != NULL) &&
1583             FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) {
1584 
1585             TrimLength = Fcb->FinalNameLength;
1586         }
1587     }
1588 
1589     if (*Length < Fcb->FullFileName.Length - TrimLength) {
1590 
1591         BytesToCopy = *Length;
1592         Overflow = TRUE;
1593 
1594     } else {
1595 
1596         BytesToCopy = Fcb->FullFileName.Length - TrimLength;
1597         *Length -= BytesToCopy;
1598     }
1599 
1600     RtlCopyMemory( &Buffer->FileName[0],
1601                    Fcb->FullFileName.Buffer,
1602                    BytesToCopy );
1603 
1604     //
1605     //  Note that this is just the amount of name we've copied so far.  It'll
1606     //  either be all of it (long) or the path element including the \ (short).
1607     //
1608 
1609     Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength;
1610 
1611     //
1612     //  If we trimmed off the name element, this is the short name case.  Pick
1613     //  up the UNICODE conversion and append it.
1614     //
1615 
1616     if (TrimLength != 0) {
1617 
1618         UNICODE_STRING ShortName;
1619         WCHAR ShortNameBuffer[12];
1620         NTSTATUS Status;
1621 
1622         //
1623         //  Convert the short name to UNICODE and figure out how much
1624         //  of it can fit.  Again, we always bump the returned length
1625         //  to indicate how much is available even if we can't return it.
1626         //
1627 
1628         ShortName.Length = 0;
1629         ShortName.MaximumLength = sizeof(ShortNameBuffer);
1630         ShortName.Buffer = ShortNameBuffer;
1631 
1632 #ifdef _MSC_VER
1633 #pragma prefast( suppress:28931, "needed for debug build" )
1634 #endif
1635         Status = RtlOemStringToCountedUnicodeString( &ShortName,
1636                                                      &Fcb->ShortName.Name.Oem,
1637                                                      FALSE );
1638 
1639         NT_ASSERT( Status == STATUS_SUCCESS );
1640 
1641         if (!Overflow) {
1642 
1643             if (*Length < ShortName.Length) {
1644 
1645                 BytesToCopy = *Length;
1646                 Overflow = TRUE;
1647 
1648             } else {
1649 
1650                 BytesToCopy = ShortName.Length;
1651                 *Length -= BytesToCopy;
1652             }
1653 
1654             RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength,
1655                            ShortName.Buffer,
1656                            BytesToCopy );
1657         }
1658 
1659         Buffer->FileNameLength += ShortName.Length;
1660     }
1661 
1662     if (Overflow) {
1663 
1664         *Length = -1;
1665     }
1666 
1667     //
1668     //  Return to caller
1669     //
1670 
1671     DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1672 
1673     DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
1674 
1675     UNREFERENCED_PARAMETER( IrpContext );
1676 
1677     return;
1678 }
1679 
1680 
1681 //
1682 //  Internal Support Routine
1683 //
1684 
1685 VOID
FatQueryShortNameInfo(IN PIRP_CONTEXT IrpContext,IN PFCB Fcb,IN OUT PFILE_NAME_INFORMATION Buffer,IN OUT PLONG Length)1686 FatQueryShortNameInfo (
1687     IN PIRP_CONTEXT IrpContext,
1688     IN PFCB Fcb,
1689     IN OUT PFILE_NAME_INFORMATION Buffer,
1690     IN OUT PLONG Length
1691     )
1692 
1693 /*++
1694 
1695 Routine Description:
1696 
1697     This routine queries the short name of the file.
1698 
1699 Arguments:
1700 
1701     Fcb - Supplies the Fcb being queried, it has been verified
1702 
1703     Buffer - Supplies a pointer to the buffer where the information is to
1704         be returned
1705 
1706     Length - Supplies the length of the buffer in bytes, and receives the
1707         remaining bytes free in the buffer upon return.
1708 
1709 Return Value:
1710 
1711     None
1712 
1713 --*/
1714 
1715 {
1716     NTSTATUS Status;
1717 
1718     ULONG BytesToCopy;
1719     WCHAR ShortNameBuffer[12];
1720     UNICODE_STRING ShortName;
1721 
1722     PAGED_CODE();
1723 
1724     DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
1725 
1726     //
1727     //  Convert the name to UNICODE
1728     //
1729 
1730     ShortName.Length = 0;
1731     ShortName.MaximumLength = sizeof(ShortNameBuffer);
1732     ShortName.Buffer = ShortNameBuffer;
1733 
1734     *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
1735 
1736 #ifdef _MSC_VER
1737 #pragma prefast( suppress:28931, "needed for debug build" )
1738 #endif
1739     Status = RtlOemStringToCountedUnicodeString( &ShortName,
1740                                                  &Fcb->ShortName.Name.Oem,
1741                                                  FALSE );
1742 
1743     NT_ASSERT( Status == STATUS_SUCCESS );
1744 
1745     //
1746     //  If we overflow, set *Length to -1 as a flag.
1747     //
1748 
1749     if (*Length < ShortName.Length) {
1750 
1751         BytesToCopy = *Length;
1752         *Length = -1;
1753 
1754     } else {
1755 
1756         BytesToCopy = ShortName.Length;
1757         *Length -= ShortName.Length;
1758     }
1759 
1760     RtlCopyMemory( &Buffer->FileName[0],
1761                    &ShortName.Buffer[0],
1762                    BytesToCopy );
1763 
1764     Buffer->FileNameLength = ShortName.Length;
1765 
1766     //
1767     //  Return to caller
1768     //
1769 
1770     DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1771 
1772     DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
1773 
1774     UNREFERENCED_PARAMETER( IrpContext );
1775 
1776     return;
1777 }
1778 
1779 
1780 //
1781 //  Internal Support Routine
1782 //
1783 
_Requires_lock_held_(_Global_critical_region_)1784 _Requires_lock_held_(_Global_critical_region_)
1785 VOID
1786 FatQueryNetworkInfo (
1787     IN PIRP_CONTEXT IrpContext,
1788     IN PFCB Fcb,
1789     IN PFILE_OBJECT FileObject,
1790     IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
1791     IN OUT PLONG Length
1792     )
1793 
1794 /*++
1795  Description:
1796 
1797     This routine performs the query network open information function for fat.
1798 
1799 Arguments:
1800 
1801     Fcb - Supplies the Fcb being queried, it has been verified
1802 
1803     FileObject - Supplies the flag bit that indicates the file was modified.
1804 
1805     Buffer - Supplies a pointer to the buffer where the information is to
1806         be returned
1807 
1808     Length - Supplies the length of the buffer in bytes, and receives the
1809         remaining bytes free in the buffer upon return.
1810 
1811 Return Value:
1812 
1813     None
1814 
1815 --*/
1816 
1817 {
1818     PAGED_CODE();
1819 
1820     UNREFERENCED_PARAMETER( FileObject );
1821 
1822     DebugTrace(+1, Dbg, "FatQueryNetworkInfo...\n", 0);
1823 
1824     //
1825     //  Zero out the output buffer, and set it to indicate that
1826     //  the query is a normal file.  Later we might overwrite the
1827     //  attribute.
1828     //
1829 
1830     RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) );
1831 
1832     //
1833     //  Extract the data and fill in the non zero fields of the output
1834     //  buffer
1835     //
1836 
1837     if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
1838 
1839         //
1840         //  We have to munge a lie on the fly.  Every time we have to
1841         //  use 1/1/80 we need to convert to GMT since the TZ may have
1842         //  changed on us.
1843         //
1844 
1845         ExLocalTimeToSystemTime( &FatJanOne1980,
1846                                  &Buffer->LastWriteTime );
1847         Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
1848 
1849     } else {
1850 
1851         Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart;
1852         Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart;
1853         Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart;
1854     }
1855 
1856     Buffer->FileAttributes = Fcb->DirentFatFlags;
1857 
1858 
1859     //
1860     //  If the temporary flag is set, then set it in the buffer.
1861     //
1862 
1863     if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
1864 
1865         SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
1866     }
1867 
1868     //
1869     //  If no attributes were set, set the normal bit.
1870     //
1871 
1872     if (Buffer->FileAttributes == 0) {
1873 
1874         Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
1875     }
1876     //
1877     //  Case on whether this is a file or a directory, and extract
1878     //  the information and fill in the fcb/dcb specific parts
1879     //  of the output buffer
1880     //
1881 
1882     if (NodeType(Fcb) == FAT_NTC_FCB) {
1883 
1884         if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
1885 
1886             FatLookupFileAllocationSize( IrpContext, Fcb );
1887         }
1888 
1889         Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart;
1890         Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart;
1891     }
1892 
1893     //
1894     //  Update the length and status output variables
1895     //
1896 
1897     *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION );
1898 
1899     DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
1900 
1901     DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0);
1902 
1903     return;
1904 }
1905 
1906 
1907 //
1908 //  Internal Support routine
1909 //
1910 
_Requires_lock_held_(_Global_critical_region_)1911 _Requires_lock_held_(_Global_critical_region_)
1912 NTSTATUS
1913 FatSetBasicInfo (
1914     IN PIRP_CONTEXT IrpContext,
1915     IN PIRP Irp,
1916     IN PFCB Fcb,
1917     IN PCCB Ccb
1918     )
1919 
1920 /*++
1921 
1922 Routine Description:
1923 
1924     This routine performs the set basic information for fat.  It either
1925     completes the request or enqueues it off to the fsp.
1926 
1927 Arguments:
1928 
1929     Irp - Supplies the irp being processed
1930 
1931     Fcb - Supplies the Fcb or Dcb being processed, already known not to
1932         be the root dcb
1933 
1934     Ccb - Supplies the flag bit that control updating the last modify
1935         time on cleanup.
1936 
1937 Return Value:
1938 
1939     NTSTATUS - The result of this operation if it completes without
1940                an exception.
1941 
1942 --*/
1943 
1944 {
1945     NTSTATUS Status;
1946 
1947     PFILE_BASIC_INFORMATION Buffer;
1948 
1949     PDIRENT Dirent;
1950     PBCB DirentBcb;
1951 
1952     FAT_TIME_STAMP CreationTime = {0};
1953     UCHAR CreationMSec = 0;
1954     FAT_TIME_STAMP LastWriteTime = {0};
1955     FAT_TIME_STAMP LastAccessTime = {0};
1956     FAT_DATE LastAccessDate = {0};
1957     UCHAR Attributes;
1958 
1959     BOOLEAN ModifyCreation = FALSE;
1960     BOOLEAN ModifyLastWrite = FALSE;
1961     BOOLEAN ModifyLastAccess = FALSE;
1962 
1963 #if (NTDDI_VERSION >= NTDDI_WIN8)
1964     BOOLEAN ModifiedAttributes = FALSE;
1965 #endif
1966 
1967     LARGE_INTEGER LargeCreationTime = {0};
1968     LARGE_INTEGER LargeLastWriteTime = {0};
1969     LARGE_INTEGER LargeLastAccessTime = {0};
1970 
1971     ULONG NotifyFilter = 0;
1972 
1973     PAGED_CODE();
1974 
1975     DebugTrace(+1, Dbg, "FatSetBasicInfo...\n", 0);
1976 
1977     Buffer = Irp->AssociatedIrp.SystemBuffer;
1978 
1979     //
1980     //  If the user is specifying -1 for a field, that means
1981     //  we should leave that field unchanged, even if we might
1982     //  have otherwise set it ourselves.  We'll set the Ccb flag
1983     //  saying that the user set the field so that we
1984     //  don't do our default updating.
1985     //
1986     //  We set the field to 0 then so we know not to actually
1987     //  set the field to the user-specified (and in this case,
1988     //  illegal) value.
1989     //
1990 
1991     if (Buffer->LastWriteTime.QuadPart == -1) {
1992 
1993         SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
1994         Buffer->LastWriteTime.QuadPart = 0;
1995     }
1996 
1997     if (Buffer->LastAccessTime.QuadPart == -1) {
1998 
1999         SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
2000         Buffer->LastAccessTime.QuadPart = 0;
2001     }
2002 
2003     if (Buffer->CreationTime.QuadPart == -1) {
2004 
2005         SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
2006         Buffer->CreationTime.QuadPart = 0;
2007     }
2008 
2009     DirentBcb = NULL;
2010 
2011     Status = STATUS_SUCCESS;
2012 
2013     _SEH2_TRY {
2014 
2015         LARGE_INTEGER FatLocalDecThirtyOne1979;
2016         LARGE_INTEGER FatLocalJanOne1980;
2017 
2018         ExLocalTimeToSystemTime( &FatDecThirtyOne1979,
2019                                  &FatLocalDecThirtyOne1979 );
2020 
2021         ExLocalTimeToSystemTime( &FatJanOne1980,
2022                                  &FatLocalJanOne1980 );
2023 
2024         //
2025         //  Get a pointer to the dirent
2026         //
2027 
2028         NT_ASSERT( Fcb->FcbCondition == FcbGood );
2029 
2030         FatGetDirentFromFcbOrDcb( IrpContext,
2031                                   Fcb,
2032                                   FALSE,
2033                                   &Dirent,
2034                                   &DirentBcb );
2035         //
2036         //  Check if the user specified a non-zero creation time
2037         //
2038 
2039         if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) {
2040 
2041             LargeCreationTime = Buffer->CreationTime;
2042 
2043             //
2044             //  Convert the Nt time to a Fat time
2045             //
2046 
2047             if ( !FatNtTimeToFatTime( IrpContext,
2048                                       &LargeCreationTime,
2049                                       FALSE,
2050                                       &CreationTime,
2051                                       &CreationMSec )) {
2052 
2053                 //
2054                 //  Special case the value 12/31/79 and treat this as 1/1/80.
2055                 //  This '79 value can happen because of time zone issues.
2056                 //
2057 
2058                 if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
2059                     (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
2060 
2061                     CreationTime = FatTimeJanOne1980;
2062                     LargeCreationTime = FatLocalJanOne1980;
2063 
2064                 } else {
2065 
2066                     DebugTrace(0, Dbg, "Invalid CreationTime\n", 0);
2067                     try_return( Status = STATUS_INVALID_PARAMETER );
2068                 }
2069 
2070                 //
2071                 //  Don't worry about CreationMSec
2072                 //
2073 
2074                 CreationMSec = 0;
2075             }
2076 
2077             ModifyCreation = TRUE;
2078         }
2079 
2080         //
2081         //  Check if the user specified a non-zero last access time
2082         //
2083 
2084         if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) {
2085 
2086             LargeLastAccessTime = Buffer->LastAccessTime;
2087 
2088             //
2089             //  Convert the Nt time to a Fat time
2090             //
2091 
2092             if ( !FatNtTimeToFatTime( IrpContext,
2093                                       &LargeLastAccessTime,
2094                                       TRUE,
2095                                       &LastAccessTime,
2096                                       NULL )) {
2097 
2098                 //
2099                 //  Special case the value 12/31/79 and treat this as 1/1/80.
2100                 //  This '79 value can happen because of time zone issues.
2101                 //
2102 
2103                 if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
2104                     (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
2105 
2106                     LastAccessTime = FatTimeJanOne1980;
2107                     LargeLastAccessTime = FatLocalJanOne1980;
2108 
2109                 } else {
2110 
2111                     DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0);
2112                     try_return( Status = STATUS_INVALID_PARAMETER );
2113                 }
2114             }
2115 
2116             LastAccessDate = LastAccessTime.Date;
2117             ModifyLastAccess = TRUE;
2118         }
2119 
2120         //
2121         //  Check if the user specified a non-zero last write time
2122         //
2123 
2124         if (Buffer->LastWriteTime.QuadPart != 0) {
2125 
2126             //
2127             //  First do a quick check here if the this time is the same
2128             //  time as LastAccessTime.
2129             //
2130 
2131             if (ModifyLastAccess &&
2132                 (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) {
2133 
2134                 ModifyLastWrite = TRUE;
2135                 LastWriteTime = LastAccessTime;
2136                 LargeLastWriteTime = LargeLastAccessTime;
2137 
2138             } else {
2139 
2140                 LargeLastWriteTime = Buffer->LastWriteTime;
2141 
2142                 //
2143                 //  Convert the Nt time to a Fat time
2144                 //
2145 
2146                 if ( !FatNtTimeToFatTime( IrpContext,
2147                                           &LargeLastWriteTime,
2148                                           TRUE,
2149                                           &LastWriteTime,
2150                                           NULL )) {
2151 
2152 
2153                     //
2154                     //  Special case the value 12/31/79 and treat this as 1/1/80.
2155                     //  This '79 value can happen because of time zone issues.
2156                     //
2157 
2158                     if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
2159                         (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
2160 
2161                         LastWriteTime = FatTimeJanOne1980;
2162                         LargeLastWriteTime = FatLocalJanOne1980;
2163 
2164                     } else {
2165 
2166                         DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0);
2167                         try_return( Status = STATUS_INVALID_PARAMETER );
2168                     }
2169                 }
2170 
2171                 ModifyLastWrite = TRUE;
2172             }
2173         }
2174 
2175 
2176         //
2177         //  Check if the user specified a non zero file attributes byte
2178         //
2179 
2180         if (Buffer->FileAttributes != 0) {
2181 
2182             //
2183             //  Only permit the attributes that FAT understands.  The rest are silently
2184             //  dropped on the floor.
2185             //
2186 
2187             Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY |
2188                                                            FILE_ATTRIBUTE_HIDDEN |
2189                                                            FILE_ATTRIBUTE_SYSTEM |
2190                                                            FILE_ATTRIBUTE_DIRECTORY |
2191                                                            FILE_ATTRIBUTE_ARCHIVE));
2192 
2193             //
2194             //  Make sure that for a file the directory bit is not set
2195             //  and that for a directory the bit is set.
2196             //
2197 
2198             if (NodeType(Fcb) == FAT_NTC_FCB) {
2199 
2200                 if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {
2201 
2202                     DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0);
2203                     try_return( Status = STATUS_INVALID_PARAMETER );
2204                 }
2205 
2206             } else {
2207 
2208                 Attributes |= FAT_DIRENT_ATTR_DIRECTORY;
2209             }
2210 
2211             //
2212             //  Mark the FcbState temporary flag correctly.
2213             //
2214 
2215             if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) {
2216 
2217                 //
2218                 //  Don't allow the temporary bit to be set on directories.
2219                 //
2220 
2221                 if (NodeType(Fcb) == FAT_NTC_DCB) {
2222 
2223                     DebugTrace(0, Dbg, "No temporary directories\n", 0);
2224                     try_return( Status = STATUS_INVALID_PARAMETER );
2225                 }
2226 
2227                 SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
2228 
2229                 SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
2230                          FO_TEMPORARY_FILE );
2231 
2232             } else {
2233 
2234                 ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
2235 
2236                 ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
2237                            FO_TEMPORARY_FILE );
2238             }
2239 
2240             //
2241             //  Now, only proceed if the requested file attributes are different
2242             //  than the existing attributes.
2243             //
2244 
2245             if (Dirent->Attributes != Attributes) {
2246 
2247                 //
2248                 //  Set the new attributes byte, and mark the bcb dirty
2249                 //
2250 
2251                 Fcb->DirentFatFlags = Attributes;
2252 
2253                 Dirent->Attributes = Attributes;
2254 
2255                 NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
2256 
2257 #if (NTDDI_VERSION >= NTDDI_WIN8)
2258                 ModifiedAttributes = TRUE;
2259 #endif
2260             }
2261         }
2262 
2263         if ( ModifyCreation ) {
2264 
2265             //
2266             //  Set the new last write time in the dirent, and mark
2267             //  the bcb dirty
2268             //
2269 
2270             Fcb->CreationTime = LargeCreationTime;
2271             Dirent->CreationTime = CreationTime;
2272             Dirent->CreationMSec = CreationMSec;
2273 
2274 
2275             NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
2276             //
2277             //  Now we have to round the time in the Fcb up to the
2278             //  nearest tem msec.
2279             //
2280 
2281             Fcb->CreationTime.QuadPart =
2282 
2283                 ((Fcb->CreationTime.QuadPart + AlmostTenMSec) /
2284                  TenMSec) * TenMSec;
2285 
2286             //
2287             //  Now because the user just set the creation time we
2288             //  better not set the creation time on close
2289             //
2290 
2291             SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
2292         }
2293 
2294         if ( ModifyLastAccess ) {
2295 
2296             //
2297             //  Set the new last write time in the dirent, and mark
2298             //  the bcb dirty
2299             //
2300 
2301             Fcb->LastAccessTime = LargeLastAccessTime;
2302             Dirent->LastAccessDate = LastAccessDate;
2303 
2304             NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
2305 
2306             //
2307             //  Now we have to truncate the time in the Fcb down to the
2308             //  current day.  This has to be in LocalTime though, so first
2309             //  convert to local, trunacate, then set back to GMT.
2310             //
2311 
2312             ExSystemTimeToLocalTime( &Fcb->LastAccessTime,
2313                                      &Fcb->LastAccessTime );
2314 
2315             Fcb->LastAccessTime.QuadPart =
2316 
2317                 (Fcb->LastAccessTime.QuadPart /
2318                  FatOneDay.QuadPart) * FatOneDay.QuadPart;
2319 
2320             ExLocalTimeToSystemTime( &Fcb->LastAccessTime,
2321                                      &Fcb->LastAccessTime );
2322 
2323             //
2324             //  Now because the user just set the last access time we
2325             //  better not set the last access time on close
2326             //
2327 
2328             SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
2329         }
2330 
2331         if ( ModifyLastWrite ) {
2332 
2333             //
2334             //  Set the new last write time in the dirent, and mark
2335             //  the bcb dirty
2336             //
2337 
2338             Fcb->LastWriteTime = LargeLastWriteTime;
2339             Dirent->LastWriteTime = LastWriteTime;
2340 
2341             NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
2342 
2343             //
2344             //  Now we have to round the time in the Fcb up to the
2345             //  nearest two seconds.
2346             //
2347 
2348             Fcb->LastWriteTime.QuadPart =
2349 
2350                 ((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) /
2351                  TwoSeconds) * TwoSeconds;
2352 
2353             //
2354             //  Now because the user just set the last write time we
2355             //  better not set the last write time on close
2356             //
2357 
2358             SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
2359         }
2360 
2361         //
2362         //  If we modified any of the values, we report this to the notify
2363         //  package.
2364         //
2365         //  We also take this opportunity to set the current file size and
2366         //  first cluster in the Dirent in order to support a server hack.
2367         //
2368 
2369         if (NotifyFilter != 0) {
2370 
2371             if (NodeType(Fcb) == FAT_NTC_FCB) {
2372 
2373                 Dirent->FileSize = Fcb->Header.FileSize.LowPart;
2374 
2375 
2376                 Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile;
2377 
2378                 if (FatIsFat32(Fcb->Vcb)) {
2379 
2380                     Dirent->FirstClusterOfFileHi =
2381                             (USHORT)(Fcb->FirstClusterOfFile >> 16);
2382                 }
2383             }
2384 
2385             FatNotifyReportChange( IrpContext,
2386                                    Fcb->Vcb,
2387                                    Fcb,
2388                                    NotifyFilter,
2389                                    FILE_ACTION_MODIFIED );
2390 
2391             FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
2392         }
2393 
2394 #if (NTDDI_VERSION >= NTDDI_WIN8)
2395         //
2396         //  If last-access, last-write, or any attribute bits changed, break
2397         //  parent directory oplock.
2398         //
2399 
2400         if ((Fcb->ParentDcb != NULL) &&
2401             (ModifyLastAccess ||
2402              ModifyLastWrite ||
2403              ModifiedAttributes)) {
2404 
2405             FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb),
2406                                 IrpContext->OriginatingIrp,
2407                                 OPLOCK_FLAG_PARENT_OBJECT,
2408                                 NULL,
2409                                 NULL,
2410                                 NULL );
2411         }
2412 #endif
2413 
2414     try_exit: NOTHING;
2415     } _SEH2_FINALLY {
2416 
2417         DebugUnwind( FatSetBasicInfo );
2418 
2419         FatUnpinBcb( IrpContext, DirentBcb );
2420 
2421         DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status);
2422     } _SEH2_END;
2423 
2424     return Status;
2425 }
2426 
2427 //
2428 //  Internal Support Routine
2429 //
2430 
_Requires_lock_held_(_Global_critical_region_)2431 _Requires_lock_held_(_Global_critical_region_)
2432 NTSTATUS
2433 FatSetDispositionInfo (
2434     IN PIRP_CONTEXT IrpContext,
2435     IN PIRP Irp,
2436     IN PFILE_OBJECT FileObject,
2437     IN PFCB Fcb
2438     )
2439 
2440 /*++
2441 
2442 Routine Description:
2443 
2444     This routine performs the set disposition information for fat.  It either
2445     completes the request or enqueues it off to the fsp.
2446 
2447 Arguments:
2448 
2449     Irp - Supplies the irp being processed
2450 
2451     FileObject - Supplies the file object being processed
2452 
2453     Fcb - Supplies the Fcb or Dcb being processed, already known not to
2454         be the root dcb
2455 
2456 Return Value:
2457 
2458     NTSTATUS - The result of this operation if it completes without
2459                an exception.
2460 
2461 --*/
2462 
2463 {
2464     PFILE_DISPOSITION_INFORMATION Buffer;
2465     PBCB Bcb;
2466     PDIRENT Dirent;
2467 
2468     PAGED_CODE();
2469 
2470     DebugTrace(+1, Dbg, "FatSetDispositionInfo...\n", 0);
2471 
2472     Buffer = Irp->AssociatedIrp.SystemBuffer;
2473 
2474     //
2475     //  Check if the user wants to delete the file or not delete
2476     //  the file
2477     //
2478 
2479     if (Buffer->DeleteFile) {
2480 
2481         //
2482         //  Check if the file is marked read only
2483         //
2484 
2485         if (FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY)) {
2486 
2487             DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0);
2488 
2489             return STATUS_CANNOT_DELETE;
2490         }
2491 
2492         //
2493         //  Make sure there is no process mapping this file as an image.
2494         //
2495 
2496         if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
2497                                   MmFlushForDelete )) {
2498 
2499             DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0);
2500 
2501             return STATUS_CANNOT_DELETE;
2502         }
2503 
2504         //
2505         //  Check if this is a dcb and if so then only allow
2506         //  the request if the directory is empty.
2507         //
2508 
2509         if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
2510 
2511             DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0);
2512 
2513             return STATUS_CANNOT_DELETE;
2514         }
2515 
2516         if (NodeType(Fcb) == FAT_NTC_DCB) {
2517 
2518             DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0);
2519 
2520             //
2521             //  Check if the directory is empty
2522             //
2523 
2524             if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) {
2525 
2526                 DebugTrace(-1, Dbg, "Directory is not empty\n", 0);
2527 
2528                 return STATUS_DIRECTORY_NOT_EMPTY;
2529             }
2530         }
2531 
2532         //
2533         //  If this is a floppy, touch the volume so to verify that it
2534         //  is not write protected.
2535         //
2536 
2537         if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {
2538 
2539             PVCB Vcb;
2540             PBCB LocalBcb = NULL;
2541             UCHAR *LocalBuffer;
2542             UCHAR TmpChar;
2543             ULONG BytesToMap;
2544 
2545             IO_STATUS_BLOCK Iosb;
2546 
2547             Vcb = Fcb->Vcb;
2548 
2549             BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ?
2550                          FatReservedBytes(&Vcb->Bpb) +
2551                          FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE;
2552 
2553             FatReadVolumeFile( IrpContext,
2554                                Vcb,
2555                                0,
2556                                BytesToMap,
2557                                &LocalBcb,
2558                                (PVOID *)&LocalBuffer );
2559 
2560             _SEH2_TRY {
2561 
2562                 if (!CcPinMappedData( Vcb->VirtualVolumeFile,
2563                                       &FatLargeZero,
2564                                       BytesToMap,
2565                                       BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
2566                                       &LocalBcb )) {
2567 
2568                     //
2569                     // Could not pin the data without waiting (cache miss).
2570                     //
2571 
2572                     FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
2573                 }
2574 
2575                 //
2576                 //  Make Mm, myself, and Cc think the byte is dirty, and then
2577                 //  force a writethrough.
2578                 //
2579 
2580                 LocalBuffer += FatReservedBytes(&Vcb->Bpb);
2581 
2582                 TmpChar = LocalBuffer[0];
2583                 LocalBuffer[0] = TmpChar;
2584 
2585                 FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb,
2586                                 FatReservedBytes( &Vcb->Bpb ),
2587                                 FatReservedBytes( &Vcb->Bpb ),
2588                                 Vcb->Bpb.BytesPerSector );
2589 
2590             } _SEH2_FINALLY {
2591 
2592                 if (_SEH2_AbnormalTermination() && (LocalBcb != NULL)) {
2593 
2594                     FatUnpinBcb( IrpContext, LocalBcb );
2595                 }
2596             } _SEH2_END;
2597 
2598             CcRepinBcb( LocalBcb );
2599             CcSetDirtyPinnedData( LocalBcb, NULL );
2600             CcUnpinData( LocalBcb );
2601             DbgDoit( NT_ASSERT( IrpContext->PinCount ));
2602             DbgDoit( IrpContext->PinCount -= 1 );
2603             CcUnpinRepinnedBcb( LocalBcb, TRUE, &Iosb );
2604 
2605             //
2606             //  If this was not successful, raise the status.
2607             //
2608 
2609             if ( !NT_SUCCESS(Iosb.Status) ) {
2610 
2611                 FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status );
2612             }
2613 
2614         } else {
2615 
2616             //
2617             //  Just set a Bcb dirty here.  The above code was only there to
2618             //  detect a write protected floppy, while the below code works
2619             //  for any write protected media and only takes a hit when the
2620             //  volume in clean.
2621             //
2622 
2623             FatGetDirentFromFcbOrDcb( IrpContext,
2624                                       Fcb,
2625                                       FALSE,
2626                                       &Dirent,
2627                                       &Bcb );
2628 
2629             //
2630             //  This has to work for the usual reasons (we verified the Fcb within
2631             //  volume synch).
2632             //
2633 
2634             _SEH2_TRY {
2635 
2636                 FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE );
2637 
2638             } _SEH2_FINALLY {
2639 
2640                 FatUnpinBcb( IrpContext, Bcb );
2641             } _SEH2_END;
2642         }
2643 
2644         //
2645         //  At this point either we have a file or an empty directory
2646         //  so we know the delete can proceed.
2647         //
2648 
2649         SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
2650         FileObject->DeletePending = TRUE;
2651 
2652         //
2653         //  If this is a directory then report this delete pending to
2654         //  the dir notify package.
2655         //
2656 
2657         if (NodeType(Fcb) == FAT_NTC_DCB) {
2658 
2659             FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync,
2660                                             &Fcb->Vcb->DirNotifyList,
2661                                             FileObject->FsContext,
2662                                             NULL,
2663                                             FALSE,
2664                                             FALSE,
2665                                             0,
2666                                             NULL,
2667                                             NULL,
2668                                             NULL );
2669         }
2670     } else {
2671 
2672         //
2673         //  The user doesn't want to delete the file so clear
2674         //  the delete on close bit
2675         //
2676 
2677         DebugTrace(0, Dbg, "User want to not delete file\n", 0);
2678 
2679         ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
2680         FileObject->DeletePending = FALSE;
2681     }
2682 
2683     DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0);
2684 
2685     return STATUS_SUCCESS;
2686 }
2687 
2688 
2689 //
2690 //  Internal Support Routine
2691 //
2692 
2693 NTSTATUS
FatSetRenameInfo(IN PIRP_CONTEXT IrpContext,IN PIRP Irp,IN PVCB Vcb,IN PFCB Fcb,IN PCCB Ccb)2694 FatSetRenameInfo (
2695     IN PIRP_CONTEXT IrpContext,
2696     IN PIRP Irp,
2697     IN PVCB Vcb,
2698     IN PFCB Fcb,
2699     IN PCCB Ccb
2700     )
2701 
2702 /*++
2703 
2704 Routine Description:
2705 
2706     This routine performs the set name information for fat.  It either
2707     completes the request or enqueues it off to the fsp.
2708 
2709 Arguments:
2710 
2711     Irp - Supplies the irp being processed
2712 
2713     Vcb - Supplies the Vcb being processed
2714 
2715     Fcb - Supplies the Fcb or Dcb being processed, already known not to
2716         be the root dcb
2717 
2718     Ccb - Supplies the Ccb corresponding to the handle opening the source
2719         file
2720 
2721 Return Value:
2722 
2723     NTSTATUS - The result of this operation if it completes without
2724                an exception.
2725 
2726 --*/
2727 
2728 {
2729     BOOLEAN AllLowerComponent;
2730     BOOLEAN AllLowerExtension;
2731     BOOLEAN CaseOnlyRename;
2732     BOOLEAN ContinueWithRename;
2733     BOOLEAN CreateLfn = FALSE;
2734     BOOLEAN DeleteSourceDirent;
2735     BOOLEAN DeleteTarget;
2736     BOOLEAN NewDirentFromPool;
2737     BOOLEAN RenamedAcrossDirectories;
2738     BOOLEAN ReplaceIfExists;
2739 
2740     CCB LocalCcb;
2741     PCCB SourceCcb;
2742 
2743     DIRENT Dirent;
2744 
2745     NTSTATUS Status = STATUS_SUCCESS;
2746 
2747     OEM_STRING OldOemName;
2748     OEM_STRING NewOemName;
2749     UCHAR OemNameBuffer[24*2];
2750 
2751     PBCB DotDotBcb;
2752     PBCB NewDirentBcb;
2753     PBCB OldDirentBcb;
2754     PBCB SecondPageBcb;
2755     PBCB TargetDirentBcb;
2756 
2757     PDCB TargetDcb = NULL;
2758     PDCB OldParentDcb;
2759 
2760     PDIRENT DotDotDirent = NULL;
2761     PDIRENT FirstPageDirent = NULL;
2762     PDIRENT NewDirent = NULL;
2763     PDIRENT OldDirent = NULL;
2764     PDIRENT SecondPageDirent = NULL;
2765     PDIRENT ShortDirent = NULL;
2766     PDIRENT TargetDirent = NULL;
2767 
2768     PFCB TempFcb;
2769 
2770     PFILE_OBJECT TargetFileObject;
2771     PFILE_OBJECT FileObject;
2772 
2773     PIO_STACK_LOCATION IrpSp;
2774 
2775     PLIST_ENTRY Links;
2776 
2777     ULONG BytesInFirstPage = 0;
2778     ULONG DirentsInFirstPage = 0;
2779     ULONG DirentsRequired = 0;
2780     ULONG NewOffset = 0;
2781     ULONG NotifyAction = 0;
2782     ULONG SecondPageOffset = 0;
2783     ULONG ShortDirentOffset = 0;
2784     ULONG TargetDirentOffset = 0;
2785     ULONG TargetLfnOffset = 0;
2786 
2787     // NewName comes from the IRP buffer or the TargetFileObject, so we can't
2788     // go around modifying it. Instead we modify NewNameCopy.
2789     UNICODE_STRING NewName;
2790 
2791     // NB: these five UNICODE_STRINGS are allocated
2792     // from one chopped up pool allocation called UnicodeBuffer.
2793     UNICODE_STRING NewNameCopy;
2794     UNICODE_STRING NewUpcasedName;
2795     UNICODE_STRING OldName;
2796     UNICODE_STRING OldUpcasedName;
2797     UNICODE_STRING TargetLfn;
2798     PWCHAR UnicodeBuffer;
2799 
2800     UNICODE_STRING TargetOrigLfn = {0};
2801 
2802     UNICODE_STRING UniTunneledShortName;
2803     WCHAR UniTunneledShortNameBuffer[12];
2804     UNICODE_STRING UniTunneledLongName;
2805     WCHAR UniTunneledLongNameBuffer[26];
2806 
2807     LARGE_INTEGER TunneledCreationTime;
2808     ULONG TunneledDataSize;
2809     BOOLEAN HaveTunneledInformation = FALSE;
2810     BOOLEAN UsingTunneledLfn = FALSE;
2811 
2812     BOOLEAN InvalidateFcbOnRaise = FALSE;
2813 
2814     PFILE_OBJECT DirectoryFileObject = NULL;
2815     ULONG Flags = 0;
2816 
2817     PAGED_CODE();
2818 
2819     DebugTrace(+1, Dbg, "FatSetRenameInfo...\n", 0);
2820 
2821     //
2822     //  P H A S E  0: Initialize some variables.
2823     //
2824 
2825     CaseOnlyRename = FALSE;
2826     ContinueWithRename = FALSE;
2827     DeleteSourceDirent = FALSE;
2828     DeleteTarget = FALSE;
2829     NewDirentFromPool = FALSE;
2830     RenamedAcrossDirectories = FALSE;
2831 
2832     DotDotBcb = NULL;
2833     NewDirentBcb = NULL;
2834     OldDirentBcb = NULL;
2835     SecondPageBcb = NULL;
2836     TargetDirentBcb = NULL;
2837 
2838     NewOemName.Length = 0;
2839     NewOemName.MaximumLength = 24;
2840     NewOemName.Buffer = (PCHAR)&OemNameBuffer[0];
2841 
2842     OldOemName.Length = 0;
2843     OldOemName.MaximumLength = 24;
2844     OldOemName.Buffer = (PCHAR)&OemNameBuffer[24];
2845 
2846     UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool,
2847                                               5 * MAX_LFN_CHARACTERS * sizeof(WCHAR),
2848                                               TAG_FILENAME_BUFFER );
2849 
2850     NewUpcasedName.Length = 0;
2851     NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
2852     NewUpcasedName.Buffer = &UnicodeBuffer[0];
2853 
2854     OldName.Length = 0;
2855     OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
2856     OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS];
2857 
2858     OldUpcasedName.Length = 0;
2859     OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
2860     OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2];
2861 
2862     TargetLfn.Length = 0;
2863     TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
2864     TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3];
2865 
2866     NewNameCopy.Length = 0;
2867     NewNameCopy.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
2868     NewNameCopy.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 4];
2869 
2870     UniTunneledShortName.Length = 0;
2871     UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer);
2872     UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0];
2873 
2874     UniTunneledLongName.Length = 0;
2875     UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer);
2876     UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0];
2877 
2878     //
2879     //  Remember the name in case we have to modify the name
2880     //  value in the ea.
2881     //
2882 
2883     RtlCopyMemory( OldOemName.Buffer,
2884                    Fcb->ShortName.Name.Oem.Buffer,
2885                    OldOemName.Length );
2886 
2887     //
2888     //  Get the current stack location
2889     //
2890 
2891     IrpSp = IoGetCurrentIrpStackLocation( Irp );
2892 
2893     //
2894     //  Extract information from the Irp to make our life easier
2895     //
2896 
2897     FileObject = IrpSp->FileObject;
2898     SourceCcb = FileObject->FsContext2;
2899     TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
2900     ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;
2901 
2902     RtlZeroMemory( &LocalCcb, sizeof(CCB) );
2903 
2904     //
2905     //  P H A S E  1:
2906     //
2907     //  Test if rename is legal.  Only small side-effects are not undone.
2908     //
2909 
2910     _SEH2_TRY {
2911 
2912         //
2913         //  Can't rename the root directory
2914         //
2915 
2916         if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {
2917 
2918             try_return( Status = STATUS_INVALID_PARAMETER );
2919         }
2920 
2921         //
2922         //  Check that we were not given a dcb with open handles beneath
2923         //  it.  If there are only UncleanCount == 0 Fcbs beneath us, then
2924         //  remove them from the prefix table, and they will just close
2925         //  and go away naturally.
2926         //
2927 
2928         if (NodeType(Fcb) == FAT_NTC_DCB) {
2929 
2930             PFCB BatchOplockFcb;
2931             ULONG BatchOplockCount;
2932 
2933             //
2934             //  Loop until there are no batch oplocks in the subtree below
2935             //  this directory.
2936             //
2937 
2938             while (TRUE) {
2939 
2940                 BatchOplockFcb = NULL;
2941                 BatchOplockCount = 0;
2942 
2943                 //
2944                 //  First look for any UncleanCount != 0 Fcbs, and fail if we
2945                 //  find any.
2946                 //
2947 
2948                 for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
2949                       TempFcb != Fcb;
2950                       TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
2951 
2952                      if ( TempFcb->UncleanCount != 0 ) {
2953 
2954                          //
2955                          // If there is a batch oplock on this file then
2956                          // increment our count and remember the Fcb if
2957                          // this is the first.
2958                          //
2959 
2960                          if (FatIsFileOplockable( TempFcb ) &&
2961                              (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) )
2962 #if (NTDDI_VERSION >= NTDDI_WIN7)
2963                               ||
2964                               FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) )
2965 #endif
2966                               )) {
2967 
2968                              BatchOplockCount += 1;
2969                              if ( BatchOplockFcb == NULL ) {
2970 
2971                                  BatchOplockFcb = TempFcb;
2972                              }
2973 
2974                          } else {
2975 
2976                             try_return( Status = STATUS_ACCESS_DENIED );
2977                          }
2978                      }
2979                 }
2980 
2981                 //
2982                 //  If this is not the first pass for rename and the number
2983                 //  of batch oplocks has not decreased then give up.
2984                 //
2985 
2986                 if ( BatchOplockFcb != NULL ) {
2987 
2988                     if ( (Irp->IoStatus.Information != 0) &&
2989                          (BatchOplockCount >= Irp->IoStatus.Information) ) {
2990 
2991                         try_return( Status = STATUS_ACCESS_DENIED );
2992                     }
2993 
2994                     //
2995                     //  Try to break this batch oplock.
2996                     //
2997 
2998                     Irp->IoStatus.Information = BatchOplockCount;
2999                     Status = FsRtlCheckOplock( FatGetFcbOplock(BatchOplockFcb),
3000                                                Irp,
3001                                                IrpContext,
3002                                                FatOplockComplete,
3003                                                NULL );
3004 
3005                     //
3006                     //  If the oplock was already broken then look for more
3007                     //  batch oplocks.
3008                     //
3009 
3010                     if (Status == STATUS_SUCCESS) {
3011 
3012                         continue;
3013                     }
3014 
3015                     //
3016                     //  Otherwise the oplock package will post or complete the
3017                     //  request.
3018                     //
3019 
3020                     try_return( Status = STATUS_PENDING );
3021                 }
3022 
3023                 break;
3024             }
3025 
3026             //
3027             //  Now try to get as many of these file object, and thus Fcbs
3028             //  to go away as possible, flushing first, of course.
3029             //
3030 
3031             FatPurgeReferencedFileObjects( IrpContext, Fcb, Flush );
3032 
3033             //
3034             //  OK, so there are no UncleanCount != 0, Fcbs.  Infact, there
3035             //  shouldn't really be any Fcbs left at all, except obstinate
3036             //  ones from user mapped sections .... Remove the full file name
3037             //  and exact case lfn.
3038             //
3039 
3040             for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
3041                   TempFcb != Fcb;
3042                   TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
3043 
3044                 FatAcquireExclusiveFcb( IrpContext, TempFcb );
3045 
3046                 if (TempFcb->FullFileName.Buffer != NULL) {
3047 
3048                     ExFreePool( TempFcb->FullFileName.Buffer );
3049                     TempFcb->FullFileName.Buffer = NULL;
3050                 }
3051 
3052                 FatReleaseFcb( IrpContext, TempFcb );
3053             }
3054         }
3055 
3056         //
3057         //  Check if this is a simple rename or a fully-qualified rename
3058         //  In both cases we need to figure out what the TargetDcb, and
3059         //  NewName are.
3060         //
3061 
3062         if (TargetFileObject == NULL) {
3063 
3064             //
3065             //  In the case of a simple rename the target dcb is the
3066             //  same as the source file's parent dcb, and the new file name
3067             //  is taken from the system buffer
3068             //
3069 
3070             PFILE_RENAME_INFORMATION Buffer;
3071 
3072             Buffer = Irp->AssociatedIrp.SystemBuffer;
3073 
3074             TargetDcb = Fcb->ParentDcb;
3075 
3076             NewName.Length = (USHORT) Buffer->FileNameLength;
3077             NewName.Buffer = (PWSTR) &Buffer->FileName;
3078 
3079             //
3080             //  Make sure the name is of legal length.
3081             //
3082 
3083             if (NewName.Length > 255*sizeof(WCHAR)) {
3084 
3085                 try_return( Status = STATUS_OBJECT_NAME_INVALID );
3086             }
3087 
3088             RtlCopyUnicodeString(&NewNameCopy,&NewName);
3089 
3090         } else {
3091 
3092             //
3093             //  For a fully-qualified rename the target dcb is taken from
3094             //  the target file object, which must be on the same vcb as
3095             //  the source.
3096             //
3097 
3098             PVCB TargetVcb;
3099             PCCB TargetCcb;
3100 
3101             if ((FatDecodeFileObject( TargetFileObject,
3102                                       &TargetVcb,
3103                                       &TargetDcb,
3104                                       &TargetCcb ) != UserDirectoryOpen) ||
3105                 (TargetVcb != Vcb)) {
3106 
3107                 try_return( Status = STATUS_INVALID_PARAMETER );
3108             }
3109 
3110             //
3111             //  This name is by definition legal.
3112             //
3113 
3114             NewName = *((PUNICODE_STRING)&TargetFileObject->FileName);
3115 
3116             RtlCopyUnicodeString(&NewNameCopy,&NewName);
3117 
3118         }
3119 
3120         //
3121         //  We will need an upcased version of the unicode name and the
3122         //  old name as well.
3123         //
3124 
3125         Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE );
3126 
3127         if (!NT_SUCCESS(Status)) {
3128 
3129             try_return( Status );
3130         }
3131 
3132         FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName );
3133 
3134         Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE );
3135 
3136         if (!NT_SUCCESS(Status)) {
3137             try_return(Status);
3138         }
3139 
3140         //
3141         //  Check if the current name and new name are equal, and the
3142         //  DCBs are equal.  If they are then our work is already done.
3143         //
3144 
3145         if (TargetDcb == Fcb->ParentDcb) {
3146 
3147             //
3148             //  OK, now if we found something then check if it was an exact
3149             //  match or just a case match.  If it was an exact match, then
3150             //  we can bail here.
3151             //
3152 
3153             if (FsRtlAreNamesEqual( &NewName,
3154                                     &OldName,
3155                                     FALSE,
3156                                     NULL )) {
3157 
3158                  try_return( Status = STATUS_SUCCESS );
3159             }
3160 
3161             //
3162             //  Check now for a case only rename.
3163             //
3164 
3165 
3166             if (FsRtlAreNamesEqual( &NewUpcasedName,
3167                                     &OldUpcasedName,
3168                                     FALSE,
3169                                     NULL )) {
3170 
3171                  CaseOnlyRename = TRUE;
3172             }
3173 
3174         } else {
3175 
3176             RenamedAcrossDirectories = TRUE;
3177         }
3178 
3179         //
3180         //  Upcase the name and convert it to the Oem code page.
3181         //
3182         //  If the new UNICODE name is already more than 12 characters,
3183         //  then we know the Oem name will not be valid
3184         //
3185 
3186         if (NewName.Length <= 12*sizeof(WCHAR)) {
3187 
3188             FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName );
3189 
3190             //
3191             //  If the name is not valid 8.3, zero the length.
3192             //
3193 
3194             if (FatSpaceInName( IrpContext, &NewName ) ||
3195                 !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) {
3196 
3197                 NewOemName.Length = 0;
3198             }
3199 
3200         } else {
3201 
3202             NewOemName.Length = 0;
3203         }
3204 
3205         //
3206         //  Look in the tunnel cache for names and timestamps to restore
3207         //
3208 
3209         TunneledDataSize = sizeof(LARGE_INTEGER);
3210         HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel,
3211                                                           FatDirectoryKey(TargetDcb),
3212                                                           &NewName,
3213                                                           &UniTunneledShortName,
3214                                                           &UniTunneledLongName,
3215                                                           &TunneledDataSize,
3216                                                           &TunneledCreationTime );
3217         NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));
3218 
3219 
3220         //
3221         //  Now we need to determine how many dirents this new name will
3222         //  require.
3223         //
3224 
3225         if ((NewOemName.Length == 0) ||
3226             (FatEvaluateNameCase( IrpContext,
3227                                   &NewNameCopy,
3228                                   &AllLowerComponent,
3229                                   &AllLowerExtension,
3230                                   &CreateLfn ),
3231              CreateLfn)) {
3232 
3233             DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewNameCopy) + 1;
3234 
3235         } else {
3236 
3237             //
3238             //  The user-given name is a short name, but we might still have
3239             //  a tunneled long name we want to use. See if we can.
3240             //
3241 
3242             if (UniTunneledLongName.Length &&
3243                 !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) {
3244 
3245                 UsingTunneledLfn = CreateLfn = TRUE;
3246                 DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1;
3247 
3248             } else {
3249 
3250                 //
3251                 //  This really is a simple dirent.  Note that the two AllLower BOOLEANs
3252                 //  are correctly set now.
3253                 //
3254 
3255                 DirentsRequired = 1;
3256 
3257 
3258             }
3259         }
3260 
3261         //
3262         //  Do some extra checks here if we are not in Chicago mode.
3263         //
3264 
3265         if (!FatData.ChicagoMode) {
3266 
3267             //
3268             //  If the name was not 8.3 valid, fail the rename.
3269             //
3270 
3271             if (NewOemName.Length == 0) {
3272 
3273                 try_return( Status = STATUS_OBJECT_NAME_INVALID );
3274             }
3275 
3276             //
3277             //  Don't use the magic bits.
3278             //
3279 
3280             AllLowerComponent = FALSE;
3281             AllLowerExtension = FALSE;
3282             CreateLfn = FALSE;
3283             UsingTunneledLfn = FALSE;
3284         }
3285 
3286         if (!CaseOnlyRename) {
3287 
3288             //
3289             //  Check if the new name already exists, wait is known to be
3290             //  true.
3291             //
3292 
3293             if (NewOemName.Length != 0) {
3294 
3295                 FatStringTo8dot3( IrpContext,
3296                                   NewOemName,
3297                                   &LocalCcb.OemQueryTemplate.Constant );
3298 
3299             } else {
3300 
3301                 SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
3302             }
3303 
3304             LocalCcb.UnicodeQueryTemplate = NewUpcasedName;
3305             LocalCcb.ContainsWildCards = FALSE;
3306 
3307             Flags = 0;
3308 
3309 
3310             FatLocateDirent( IrpContext,
3311                              TargetDcb,
3312                              &LocalCcb,
3313                              0,
3314                              &Flags,
3315                              &TargetDirent,
3316                              &TargetDirentBcb,
3317                              (PVBO)&TargetDirentOffset,
3318                              NULL,
3319                              &TargetLfn,
3320                              &TargetOrigLfn );
3321 
3322             if (TargetDirent != NULL) {
3323 
3324 
3325                 //
3326                 //  The name already exists, check if the user wants
3327                 //  to overwrite the name, and has access to do the overwrite
3328                 //  We cannot overwrite a directory.
3329                 //
3330 
3331                 if ((!ReplaceIfExists) ||
3332                     (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) ||
3333                     (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) {
3334 
3335                     try_return( Status = STATUS_OBJECT_NAME_COLLISION );
3336                 }
3337 
3338                 //
3339                 //  Check that the file has no open user handles, if it does
3340                 //  then we will deny access.  We do the check by searching
3341                 //  down the list of fcbs opened under our parent Dcb, and making
3342                 //  sure none of the maching Fcbs have a non-zero unclean count or
3343                 //  outstanding image sections.
3344                 //
3345 
3346                 for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
3347                      Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) {
3348 
3349                     TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
3350 
3351                     //
3352                     //  Advance now.  The image section flush may cause the final
3353                     //  close, which will recursively happen underneath of us here.
3354                     //  It would be unfortunate if we looked through free memory.
3355                     //
3356 
3357                     Links = Links->Flink;
3358 
3359                     if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) &&
3360                         ((TempFcb->UncleanCount != 0) ||
3361                          !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers,
3362                                                MmFlushForDelete))) {
3363 
3364                         //
3365                         //  If there are batch oplocks on this file then break the
3366                         //  oplocks before failing the rename.
3367                         //
3368 
3369                         Status = STATUS_ACCESS_DENIED;
3370 
3371                         if (FatIsFileOplockable( TempFcb ) &&
3372                             (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) )
3373 #if (NTDDI_VERSION >= NTDDI_WIN7)
3374                              ||
3375                              FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) )
3376 #endif
3377                              )) {
3378 
3379                             //
3380                             //  Do all of our cleanup now since the IrpContext
3381                             //  could go away when this request is posted.
3382                             //
3383 
3384                             FatUnpinBcb( IrpContext, TargetDirentBcb );
3385 
3386                             Status = FsRtlCheckOplock( FatGetFcbOplock(TempFcb),
3387                                                        Irp,
3388                                                        IrpContext,
3389                                                        FatOplockComplete,
3390                                                        NULL );
3391 
3392                             if (Status != STATUS_PENDING) {
3393 
3394                                 Status = STATUS_ACCESS_DENIED;
3395                             }
3396                         }
3397 
3398                         try_return( NOTHING );
3399                     }
3400                 }
3401 
3402                 //
3403                 //  OK, this target is toast.  Remember the Lfn offset.
3404                 //
3405 
3406                 TargetLfnOffset = TargetDirentOffset -
3407                                   FAT_LFN_DIRENTS_NEEDED(&TargetOrigLfn) *
3408                                   sizeof(DIRENT);
3409 
3410                 DeleteTarget = TRUE;
3411             }
3412         }
3413 
3414 
3415         //
3416         //  If we will need more dirents than we have, allocate them now.
3417         //
3418 
3419         if ((TargetDcb != Fcb->ParentDcb) ||
3420             (DirentsRequired !=
3421              (Fcb->DirentOffsetWithinDirectory -
3422               Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) {
3423 
3424             //
3425             //  Get some new allocation
3426             //
3427 
3428             NewOffset = FatCreateNewDirent( IrpContext,
3429                                             TargetDcb,
3430                                             DirentsRequired,
3431                                             FALSE );
3432 
3433             DeleteSourceDirent = TRUE;
3434 
3435         } else {
3436 
3437             NewOffset = Fcb->LfnOffsetWithinDirectory;
3438         }
3439 
3440         ContinueWithRename = TRUE;
3441 
3442     try_exit: NOTHING;
3443 
3444     } _SEH2_FINALLY {
3445 
3446         if (!ContinueWithRename) {
3447 
3448             //
3449             //  Undo everything from above.
3450             //
3451 
3452             ExFreePool( UnicodeBuffer );
3453             FatUnpinBcb( IrpContext, TargetDirentBcb );
3454 
3455         }
3456     } _SEH2_END;
3457 
3458     //
3459     //  Now, if we are already done, return here.
3460     //
3461 
3462     if (!ContinueWithRename) {
3463 
3464         return Status;
3465     }
3466 
3467     //
3468     //  P H A S E  2: Actually perform the rename.
3469     //
3470 
3471     _SEH2_TRY {
3472 
3473         //
3474         //  Report the fact that we are going to remove this entry.
3475         //  If we renamed within the same directory and the new name for the
3476         //  file did not previously exist, we report this as a rename old
3477         //  name.  Otherwise this is a removed file.
3478         //
3479 
3480         if (!RenamedAcrossDirectories && !DeleteTarget) {
3481 
3482             NotifyAction = FILE_ACTION_RENAMED_OLD_NAME;
3483 
3484         } else {
3485 
3486             NotifyAction = FILE_ACTION_REMOVED;
3487         }
3488 
3489         FatNotifyReportChange( IrpContext,
3490                                Vcb,
3491                                Fcb,
3492                                ((NodeType( Fcb ) == FAT_NTC_FCB)
3493                                 ? FILE_NOTIFY_CHANGE_FILE_NAME
3494                                 : FILE_NOTIFY_CHANGE_DIR_NAME ),
3495                                NotifyAction );
3496 
3497         _SEH2_TRY {
3498 
3499             //
3500             //  Capture a copy of the source dirent.
3501             //
3502 
3503             FatGetDirentFromFcbOrDcb( IrpContext, Fcb, FALSE, &OldDirent, &OldDirentBcb );
3504 
3505             Dirent = *OldDirent;
3506 
3507             //
3508             //  Tunnel the source Fcb - the names are disappearing regardless of
3509             //  whether the dirent allocation physically changed
3510             //
3511 
3512             FatTunnelFcbOrDcb( Fcb, SourceCcb );
3513 
3514             //
3515             //  From here until very nearly the end of the operation, if we raise there
3516             //  is no reasonable way to suppose we'd be able to undo the damage.  Not
3517             //  being a transactional filesystem, FAT is at the mercy of a lot of things
3518             //  (as the astute reader has no doubt realized by now).
3519             //
3520 
3521             InvalidateFcbOnRaise = TRUE;
3522 
3523             //
3524             //  Delete our current dirent(s) if we got a new one.
3525             //
3526 
3527             if (DeleteSourceDirent) {
3528 
3529                 FatDeleteDirent( IrpContext, Fcb, NULL, FALSE );
3530             }
3531 
3532             //
3533             //  Delete a target conflict if we were meant to.
3534             //
3535 
3536             if (DeleteTarget) {
3537 
3538                 FatDeleteFile( IrpContext,
3539                                TargetDcb,
3540                                TargetLfnOffset,
3541                                TargetDirentOffset,
3542                                TargetDirent,
3543                                &TargetLfn );
3544             }
3545 
3546             //
3547             //  We need to evaluate any short names required.  If there were any
3548             //  conflicts in existing short names, they would have been deleted above.
3549             //
3550             //  It isn't neccesary to worry about the UsingTunneledLfn case. Since we
3551             //  actually already know whether CreateLfn will be set either NewName is
3552             //  an Lfn and !UsingTunneledLfn is implied or NewName is a short name and
3553             //  we can handle that externally.
3554             //
3555 
3556             FatSelectNames( IrpContext,
3557                             TargetDcb,
3558                             &NewOemName,
3559                             &NewNameCopy,
3560                             &NewOemName,
3561                             (HaveTunneledInformation ? &UniTunneledShortName : NULL),
3562                             &AllLowerComponent,
3563                             &AllLowerExtension,
3564                             &CreateLfn );
3565 
3566             if (!CreateLfn && UsingTunneledLfn) {
3567 
3568                 CreateLfn = TRUE;
3569                 RtlCopyUnicodeString( &NewNameCopy, &UniTunneledLongName );
3570 
3571                 //
3572                 //  Short names are always upcase if an LFN exists
3573                 //
3574 
3575                 AllLowerComponent = FALSE;
3576                 AllLowerExtension = FALSE;
3577             }
3578 
3579             //
3580             //  OK, now setup the new dirent(s) for the new name.
3581             //
3582 
3583             FatPrepareWriteDirectoryFile( IrpContext,
3584                                           TargetDcb,
3585                                           NewOffset,
3586                                           sizeof(DIRENT),
3587                                           &NewDirentBcb,
3588 #ifndef __REACTOS__
3589                                           &NewDirent,
3590 #else
3591                                           (PVOID *)&NewDirent,
3592 #endif
3593                                           FALSE,
3594                                           TRUE,
3595                                           &Status );
3596 
3597             NT_ASSERT( NT_SUCCESS( Status ) );
3598 
3599             //
3600             //  Deal with the special case of an LFN + Dirent structure crossing
3601             //  a page boundry.
3602             //
3603 
3604             if ((NewOffset / PAGE_SIZE) !=
3605                 ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {
3606 
3607                 SecondPageOffset = (NewOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
3608 
3609                 BytesInFirstPage = SecondPageOffset - NewOffset;
3610 
3611                 DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);
3612 
3613                 FatPrepareWriteDirectoryFile( IrpContext,
3614                                               TargetDcb,
3615                                               SecondPageOffset,
3616                                               sizeof(DIRENT),
3617                                               &SecondPageBcb,
3618 #ifndef __REACTOS__
3619                                               &SecondPageDirent,
3620 #else
3621                                               (PVOID *)&SecondPageDirent,
3622 #endif
3623                                               FALSE,
3624                                               TRUE,
3625                                               &Status );
3626 
3627                 NT_ASSERT( NT_SUCCESS( Status ) );
3628 
3629                 FirstPageDirent = NewDirent;
3630 
3631                 NewDirent = FsRtlAllocatePoolWithTag( PagedPool,
3632                                                       DirentsRequired * sizeof(DIRENT),
3633                                                       TAG_DIRENT );
3634 
3635                 NewDirentFromPool = TRUE;
3636             }
3637 
3638             //
3639             //  Bump up Dirent and DirentOffset
3640             //
3641 
3642             ShortDirent = NewDirent + DirentsRequired - 1;
3643             ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT);
3644 
3645             //
3646             //  Fill in the fields of the dirent.
3647             //
3648 
3649             *ShortDirent = Dirent;
3650 
3651             FatConstructDirent( IrpContext,
3652                                 ShortDirent,
3653                                 &NewOemName,
3654                                 AllLowerComponent,
3655                                 AllLowerExtension,
3656                                 CreateLfn ? &NewNameCopy : NULL,
3657                                 Dirent.Attributes,
3658                                 FALSE,
3659                                 (HaveTunneledInformation ? &TunneledCreationTime : NULL) );
3660 
3661             if (HaveTunneledInformation) {
3662 
3663                 //
3664                 //  Need to go in and fix the timestamps in the FCB. Note that we can't use
3665                 //  the TunneledCreationTime since the conversions may have failed.
3666                 //
3667 
3668                 Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec);
3669                 Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0);
3670                 Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate);
3671             }
3672 
3673 
3674             //
3675             //  If the dirent crossed pages, split the contents of the
3676             //  temporary pool between the two pages.
3677             //
3678 
3679             if (NewDirentFromPool) {
3680 
3681                 RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage );
3682 
3683                 RtlCopyMemory( SecondPageDirent,
3684                                NewDirent + DirentsInFirstPage,
3685                                DirentsRequired*sizeof(DIRENT) - BytesInFirstPage );
3686 
3687                 ShortDirent = SecondPageDirent +
3688                               (DirentsRequired - DirentsInFirstPage) - 1;
3689             }
3690 
3691             Dirent = *ShortDirent;
3692 
3693         } _SEH2_FINALLY {
3694 
3695             //
3696             //  Remove the entry from the splay table, and then remove the
3697             //  full file name and exact case lfn. It is important that we
3698             //  always remove the name from the prefix table regardless of
3699             //  other errors.
3700             //
3701 
3702             FatRemoveNames( IrpContext, Fcb );
3703 
3704             if (Fcb->FullFileName.Buffer != NULL) {
3705 
3706                 ExFreePool( Fcb->FullFileName.Buffer );
3707                 Fcb->FullFileName.Buffer = NULL;
3708             }
3709 
3710             if (Fcb->ExactCaseLongName.Buffer) {
3711 
3712                 ExFreePool( Fcb->ExactCaseLongName.Buffer );
3713                 Fcb->ExactCaseLongName.Buffer = NULL;
3714             }
3715 
3716             FatUnpinBcb( IrpContext, OldDirentBcb );
3717             FatUnpinBcb( IrpContext, TargetDirentBcb );
3718             FatUnpinBcb( IrpContext, NewDirentBcb );
3719             FatUnpinBcb( IrpContext, SecondPageBcb );
3720         } _SEH2_END;
3721 
3722         //
3723         //  Now we need to update the location of the file's directory
3724         //  offset and move the fcb from its current parent dcb to
3725         //  the target dcb.
3726         //
3727 
3728         Fcb->LfnOffsetWithinDirectory = NewOffset;
3729         Fcb->DirentOffsetWithinDirectory = ShortDirentOffset;
3730 
3731         RemoveEntryList( &Fcb->ParentDcbLinks );
3732 
3733         //
3734         //  There is a deep reason we put files on the tail, others on the head,
3735         //  which is to allow us to easily enumerate all child directories before
3736         //  child files. This is important to let us maintain whole-volume lockorder
3737         //  via BottomUp enumeration.
3738         //
3739 
3740         if (NodeType(Fcb) == FAT_NTC_FCB) {
3741 
3742             InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
3743                             &Fcb->ParentDcbLinks );
3744 
3745         } else {
3746 
3747             InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
3748                             &Fcb->ParentDcbLinks );
3749         }
3750 
3751         OldParentDcb = Fcb->ParentDcb;
3752         Fcb->ParentDcb = TargetDcb;
3753 
3754 #if (NTDDI_VERSION >= NTDDI_WIN8)
3755         //
3756         //  Break parent directory oplock on the old parent.  Directory oplock
3757         //  breaks are always advisory, so we will never block/get STATUS_PENDING
3758         //  here.
3759         //
3760 
3761         FsRtlCheckOplockEx( FatGetFcbOplock(OldParentDcb),
3762                             IrpContext->OriginatingIrp,
3763                             OPLOCK_FLAG_PARENT_OBJECT,
3764                             NULL,
3765                             NULL,
3766                             NULL );
3767 #endif
3768 
3769         //
3770         //  If we renamed across directories, some cleanup is now in order.
3771         //
3772 
3773         if (RenamedAcrossDirectories) {
3774 
3775 #if (NTDDI_VERSION >= NTDDI_WIN8)
3776             //
3777             //  Break parent directory oplock on the new parent.  Directory oplock
3778             //  breaks are always advisory, so we will never block/get STATUS_PENDING
3779             //  here.
3780             //
3781 
3782             FsRtlCheckOplockEx( FatGetFcbOplock(TargetDcb),
3783                                 IrpContext->OriginatingIrp,
3784                                 OPLOCK_FLAG_PARENT_OBJECT,
3785                                 NULL,
3786                                 NULL,
3787                                 NULL );
3788 #endif
3789 
3790             //
3791             //  See if we need to uninitialize the cachemap for the source directory.
3792             //  Do this now in case we get unlucky and raise trying to finalize the
3793             //  operation.
3794             //
3795 
3796             if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) &&
3797                 (OldParentDcb->OpenCount == 0) &&
3798                 (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) {
3799 
3800                 NT_ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB );
3801 
3802                 DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile;
3803 
3804                 OldParentDcb->Specific.Dcb.DirectoryFile = NULL;
3805             }
3806 
3807             //
3808             //  If we move a directory across directories, we have to change
3809             //  the cluster number in its .. entry
3810             //
3811 
3812             if (NodeType(Fcb) == FAT_NTC_DCB) {
3813 
3814                 FatPrepareWriteDirectoryFile( IrpContext,
3815                                               Fcb,
3816                                               sizeof(DIRENT),
3817                                               sizeof(DIRENT),
3818                                               &DotDotBcb,
3819 #ifndef __REACTOS__
3820                                               &DotDotDirent,
3821 #else
3822                                               (PVOID *)&DotDotDirent,
3823 #endif
3824                                               FALSE,
3825                                               TRUE,
3826                                               &Status );
3827 
3828                 NT_ASSERT( NT_SUCCESS( Status ) );
3829 
3830                 DotDotDirent->FirstClusterOfFile = (USHORT)
3831                     ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ?
3832                       0 : TargetDcb->FirstClusterOfFile);
3833 
3834                 if (FatIsFat32( Vcb )) {
3835 
3836                     DotDotDirent->FirstClusterOfFileHi = (USHORT)
3837                     ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ?
3838                       0 : (TargetDcb->FirstClusterOfFile >> 16));
3839                 }
3840             }
3841         }
3842 
3843         //
3844         //  Now we need to setup the splay table and the name within
3845         //  the fcb.  Free the old short name at this point.
3846         //
3847 
3848         ExFreePool( Fcb->ShortName.Name.Oem.Buffer );
3849         Fcb->ShortName.Name.Oem.Buffer = NULL;
3850 
3851 
3852         FatConstructNamesInFcb( IrpContext,
3853                                 Fcb,
3854                                 &Dirent,
3855                                 CreateLfn ? &NewName : NULL );
3856 
3857         FatSetFullNameInFcb( IrpContext, Fcb, &NewName );
3858 
3859         //
3860         //  The rest of the actions taken are not related to correctness of
3861         //  the in-memory structures, so we shouldn't toast the Fcb if we
3862         //  raise from here to the end.
3863         //
3864 
3865         InvalidateFcbOnRaise = FALSE;
3866 
3867         //
3868         //  If a file, set the file as modified so that the archive bit
3869         //  is set.  We prevent this from adjusting the write time by
3870         //  indicating the user flag in the ccb.
3871         //
3872 
3873         if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) {
3874 
3875             SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
3876             SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
3877         }
3878 
3879         //
3880         //  We have three cases to report.
3881         //
3882         //      1.  If we overwrote an existing file, we report this as
3883         //          a modified file.
3884         //
3885         //      2.  If we renamed to a new directory, then we added a file.
3886         //
3887         //      3.  If we renamed in the same directory, then we report the
3888         //          the renamednewname.
3889         //
3890 
3891         if (DeleteTarget) {
3892 
3893             FatNotifyReportChange( IrpContext,
3894                                    Vcb,
3895                                    Fcb,
3896                                    FILE_NOTIFY_CHANGE_ATTRIBUTES
3897                                    | FILE_NOTIFY_CHANGE_SIZE
3898                                    | FILE_NOTIFY_CHANGE_LAST_WRITE
3899                                    | FILE_NOTIFY_CHANGE_LAST_ACCESS
3900                                    | FILE_NOTIFY_CHANGE_CREATION
3901                                    | FILE_NOTIFY_CHANGE_EA,
3902                                    FILE_ACTION_MODIFIED );
3903 
3904         } else if (RenamedAcrossDirectories) {
3905 
3906             FatNotifyReportChange( IrpContext,
3907                                    Vcb,
3908                                    Fcb,
3909                                    ((NodeType( Fcb ) == FAT_NTC_FCB)
3910                                     ? FILE_NOTIFY_CHANGE_FILE_NAME
3911                                     : FILE_NOTIFY_CHANGE_DIR_NAME ),
3912                                    FILE_ACTION_ADDED );
3913 
3914         } else {
3915 
3916             FatNotifyReportChange( IrpContext,
3917                                    Vcb,
3918                                    Fcb,
3919                                    ((NodeType( Fcb ) == FAT_NTC_FCB)
3920                                     ? FILE_NOTIFY_CHANGE_FILE_NAME
3921                                     : FILE_NOTIFY_CHANGE_DIR_NAME ),
3922                                    FILE_ACTION_RENAMED_NEW_NAME );
3923         }
3924 
3925         //
3926         //  We need to update the file name in the dirent.  This value
3927         //  is never used elsewhere, so we don't concern ourselves
3928         //  with any error we may encounter.  We let chkdsk fix the
3929         //  disk at some later time.
3930         //
3931 
3932         if (!FatIsFat32(Vcb) &&
3933             Dirent.ExtendedAttributes != 0) {
3934 
3935             FatRenameEAs( IrpContext,
3936                           Fcb,
3937                           Dirent.ExtendedAttributes,
3938                           &OldOemName );
3939         }
3940 
3941         FatUnpinBcb( IrpContext, DotDotBcb );
3942 
3943         FatUnpinRepinnedBcbs( IrpContext );
3944 
3945         //
3946         //  Set our final status
3947         //
3948 
3949         Status = STATUS_SUCCESS;
3950 
3951     } _SEH2_FINALLY {
3952 
3953         DebugUnwind( FatSetRenameInfo );
3954 
3955         ExFreePool( UnicodeBuffer );
3956 
3957         if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) {
3958 
3959             //
3960             //  Free pool if the buffer was grown on tunneling lookup
3961             //
3962 
3963             ExFreePool(UniTunneledLongName.Buffer);
3964         }
3965 
3966         if (NewDirentFromPool) {
3967 
3968             ExFreePool( NewDirent );
3969         }
3970 
3971         FatUnpinBcb( IrpContext, TargetDirentBcb );
3972         FatUnpinBcb( IrpContext, DotDotBcb );
3973 
3974         //
3975         //  Uninitialize the cachemap for the source directory if we need to.
3976         //
3977 
3978         if (DirectoryFileObject) {
3979 
3980             DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
3981 
3982             CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
3983 
3984             ObDereferenceObject( DirectoryFileObject );
3985         }
3986 
3987         //
3988         //  If this was an abnormal termination, then we are in trouble.
3989         //  Should the operation have been in a sensitive state there is
3990         //  nothing we can do but invalidate the Fcb.
3991         //
3992 
3993         if (_SEH2_AbnormalTermination() && InvalidateFcbOnRaise) {
3994 
3995             Fcb->FcbCondition = FcbBad;
3996         }
3997 
3998         DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status);
3999     } _SEH2_END;
4000 
4001     return Status;
4002 }
4003 
4004 
4005 //
4006 //  Internal Support Routine
4007 //
4008 
4009 NTSTATUS
FatSetPositionInfo(IN PIRP_CONTEXT IrpContext,IN PIRP Irp,IN PFILE_OBJECT FileObject)4010 FatSetPositionInfo (
4011     IN PIRP_CONTEXT IrpContext,
4012     IN PIRP Irp,
4013     IN PFILE_OBJECT FileObject
4014     )
4015 
4016 /*++
4017 
4018 Routine Description:
4019 
4020     This routine performs the set position information for fat.  It either
4021     completes the request or enqueues it off to the fsp.
4022 
4023 Arguments:
4024 
4025     Irp - Supplies the irp being processed
4026 
4027     FileObject - Supplies the file object being processed
4028 
4029 Return Value:
4030 
4031     NTSTATUS - The result of this operation if it completes without
4032                an exception.
4033 
4034 --*/
4035 
4036 {
4037     PFILE_POSITION_INFORMATION Buffer;
4038 
4039     PAGED_CODE();
4040 
4041     DebugTrace(+1, Dbg, "FatSetPositionInfo...\n", 0);
4042 
4043     Buffer = Irp->AssociatedIrp.SystemBuffer;
4044 
4045     //
4046     //  Check if the file does not use intermediate buffering.  If it
4047     //  does not use intermediate buffering then the new position we're
4048     //  supplied must be aligned properly for the device
4049     //
4050 
4051     if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {
4052 
4053         PDEVICE_OBJECT DeviceObject;
4054 
4055         DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject;
4056 
4057         if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) {
4058 
4059             DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0);
4060             DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER);
4061 
4062             return STATUS_INVALID_PARAMETER;
4063         }
4064     }
4065 
4066     //
4067     //  The input parameter is fine so set the current byte offset and
4068     //  complete the request
4069     //
4070 
4071     DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset);
4072 
4073     FileObject->CurrentByteOffset = Buffer->CurrentByteOffset;
4074 
4075     DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS);
4076 
4077     UNREFERENCED_PARAMETER( IrpContext );
4078 
4079     return STATUS_SUCCESS;
4080 }
4081 
4082 
4083 //
4084 //  Internal Support Routine
4085 //
4086 
_Requires_lock_held_(_Global_critical_region_)4087 _Requires_lock_held_(_Global_critical_region_)
4088 NTSTATUS
4089 FatSetAllocationInfo (
4090     IN PIRP_CONTEXT IrpContext,
4091     IN PIRP Irp,
4092     IN PFCB Fcb,
4093     IN PFILE_OBJECT FileObject
4094     )
4095 
4096 /*++
4097 
4098 Routine Description:
4099 
4100     This routine performs the set Allocation information for fat.  It either
4101     completes the request or enqueues it off to the fsp.
4102 
4103 Arguments:
4104 
4105     Irp - Supplies the irp being processed
4106 
4107     Fcb - Supplies the Fcb or Dcb being processed, already known not to
4108         be the root dcb
4109 
4110     FileObject - Supplies the FileObject being processed, already known not to
4111         be the root dcb
4112 
4113 Return Value:
4114 
4115     NTSTATUS - The result of this operation if it completes without
4116                an exception.
4117 
4118 --*/
4119 
4120 {
4121     NTSTATUS Status = STATUS_SUCCESS;
4122     PFILE_ALLOCATION_INFORMATION Buffer;
4123     ULONG NewAllocationSize = 0;
4124     ULONG HeaderSize = 0;
4125 
4126     BOOLEAN FileSizeTruncated = FALSE;
4127     BOOLEAN CacheMapInitialized = FALSE;
4128     BOOLEAN ResourceAcquired = FALSE;
4129     ULONG OriginalFileSize = 0;
4130     ULONG OriginalValidDataLength = 0;
4131     ULONG OriginalValidDataToDisk = 0;
4132 
4133     PAGED_CODE();
4134 
4135     Buffer = Irp->AssociatedIrp.SystemBuffer;
4136 
4137     NewAllocationSize = Buffer->AllocationSize.LowPart;
4138 
4139     DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize);
4140 
4141     //
4142     //  Allocation is only allowed on a file and not a directory
4143     //
4144 
4145     if (NodeType(Fcb) == FAT_NTC_DCB) {
4146 
4147         DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0);
4148 
4149         return STATUS_INVALID_DEVICE_REQUEST;
4150     }
4151 
4152     //
4153     //  Check that the new file allocation is legal
4154     //
4155 
4156 
4157     if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0)) {
4158 
4159         DebugTrace(-1, Dbg, "Illegal allocation size\n", 0);
4160 
4161         return STATUS_DISK_FULL;
4162     }
4163 
4164 
4165     //
4166     //  If we haven't yet looked up the correct AllocationSize, do so.
4167     //
4168 
4169     if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
4170 
4171         FatLookupFileAllocationSize( IrpContext, Fcb );
4172     }
4173 
4174     //
4175     //  This is kinda gross, but if the file is not cached, but there is
4176     //  a data section, we have to cache the file to avoid a bunch of
4177     //  extra work.
4178     //
4179 
4180     if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
4181         (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
4182         !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
4183 
4184         NT_ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
4185 
4186         //
4187         //  Now initialize the cache map.
4188         //
4189 
4190         FatInitializeCacheMap( FileObject,
4191                                (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
4192                                FALSE,
4193                                &FatData.CacheManagerCallbacks,
4194                                Fcb );
4195 
4196         CacheMapInitialized = TRUE;
4197     }
4198 
4199     //
4200     //  Now mark the fact that the file needs to be truncated on close
4201     //
4202 
4203     Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
4204 
4205     //
4206     //  Now mark that the time on the dirent needs to be updated on close.
4207     //
4208 
4209     SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
4210 
4211     _SEH2_TRY {
4212 
4213         //
4214         //  Increase or decrease the allocation size.
4215         //
4216 
4217         if (NewAllocationSize+HeaderSize > Fcb->Header.AllocationSize.LowPart) {
4218 
4219             FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize+HeaderSize);
4220 
4221         } else {
4222 
4223             //
4224             //  Check here if we will be decreasing file size and synchonize with
4225             //  paging IO.
4226             //
4227 
4228             if ( Fcb->Header.FileSize.LowPart > NewAllocationSize+HeaderSize ) {
4229 
4230                 //
4231                 //  The way Sections for DataScan are created and used, an AV's
4232                 //  memory-mapping can come into being after we check it's safe
4233                 //  to truncate a file while continuing to hold  the file here.
4234                 //  This leads to data corruption because Purge eventually fails
4235                 //  (during call to Cc to set file sizes) and stale data continues
4236                 //  to live in the cache/memory.
4237                 //
4238 
4239                 if (Fcb->PurgeFailureModeEnableCount != 0) {
4240 
4241                     try_return( Status = STATUS_PURGE_FAILED );
4242                 }
4243 
4244                 //
4245                 //  Before we actually truncate, check to see if the purge
4246                 //  is going to fail.
4247                 //
4248 
4249                 if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
4250                                            &Buffer->AllocationSize )) {
4251 
4252                     try_return( Status = STATUS_USER_MAPPED_FILE );
4253                 }
4254 
4255                 FileSizeTruncated = TRUE;
4256 
4257                 OriginalFileSize = Fcb->Header.FileSize.LowPart;
4258                 OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart;
4259                 OriginalValidDataToDisk = Fcb->ValidDataToDisk;
4260 
4261                 (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
4262                 ResourceAcquired = TRUE;
4263 
4264                 Fcb->Header.FileSize.LowPart = NewAllocationSize;
4265 
4266                 //
4267                 //  If we reduced the file size to less than the ValidDataLength,
4268                 //  adjust the VDL.  Likewise ValidDataToDisk.
4269                 //
4270 
4271                 if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) {
4272 
4273                     Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart;
4274                 }
4275                 if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) {
4276 
4277                     Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart;
4278                 }
4279 
4280             }
4281 
4282             //
4283             //  Now that File Size is down, actually do the truncate.
4284             //
4285 
4286             FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize+HeaderSize );
4287 
4288             //
4289             //  Now check if we needed to decrease the file size accordingly.
4290             //
4291 
4292             if ( FileSizeTruncated ) {
4293 
4294                 //
4295                 //  Tell the cache manager we reduced the file size.
4296                 //  The call is unconditional, because MM always wants to know.
4297                 //
4298 
4299 #if DBG
4300                 _SEH2_TRY {
4301 #endif
4302 
4303                     CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
4304 
4305 #if DBG
4306                 } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
4307 
4308                       NOTHING;
4309                 } _SEH2_END;
4310 #endif
4311 
4312                 NT_ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess );
4313 
4314                 //
4315                 //  There is no going back from this. If we run into problems updating
4316                 //  the dirent we will have to live with the consequences. Not sending
4317                 //  the notifies is likewise pretty benign compared to failing the entire
4318                 //  operation and trying to back out everything, which could fail for the
4319                 //  same reasons.
4320                 //
4321                 //  If you want a transacted filesystem, use NTFS ...
4322                 //
4323 
4324                 FileSizeTruncated = FALSE;
4325 
4326                 FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
4327 
4328                 //
4329                 //  Report that we just reduced the file size.
4330                 //
4331 
4332                 FatNotifyReportChange( IrpContext,
4333                                        Fcb->Vcb,
4334                                        Fcb,
4335                                        FILE_NOTIFY_CHANGE_SIZE,
4336                                        FILE_ACTION_MODIFIED );
4337             }
4338         }
4339 
4340     try_exit: NOTHING;
4341 
4342     } _SEH2_FINALLY {
4343 
4344         if ( _SEH2_AbnormalTermination() && FileSizeTruncated ) {
4345 
4346             Fcb->Header.FileSize.LowPart = OriginalFileSize;
4347             Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength;
4348             Fcb->ValidDataToDisk = OriginalValidDataToDisk;
4349 
4350             //
4351             //  Make sure Cc knows the right filesize.
4352             //
4353 
4354             if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
4355 
4356                 *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
4357             }
4358 
4359             NT_ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart );
4360         }
4361 
4362         if (CacheMapInitialized) {
4363 
4364             CcUninitializeCacheMap( FileObject, NULL, NULL );
4365         }
4366 
4367         if (ResourceAcquired) {
4368 
4369             ExReleaseResourceLite( Fcb->Header.PagingIoResource );
4370 
4371         }
4372 
4373     } _SEH2_END;
4374 
4375     DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS);
4376 
4377     return Status;
4378 }
4379 
4380 
4381 //
4382 //  Internal Support Routine
4383 //
4384 
_Requires_lock_held_(_Global_critical_region_)4385 _Requires_lock_held_(_Global_critical_region_)
4386 NTSTATUS
4387 FatSetEndOfFileInfo (
4388     IN PIRP_CONTEXT IrpContext,
4389     IN PIRP Irp,
4390     IN PFILE_OBJECT FileObject,
4391     IN PVCB Vcb,
4392     IN PFCB Fcb
4393     )
4394 
4395 /*++
4396 
4397 Routine Description:
4398 
4399     This routine performs the set End of File information for fat.  It either
4400     completes the request or enqueues it off to the fsp.
4401 
4402 Arguments:
4403 
4404     Irp - Supplies the irp being processed
4405 
4406     FileObject - Supplies the file object being processed
4407 
4408     Vcb - Supplies the Vcb being processed
4409 
4410     Fcb - Supplies the Fcb or Dcb being processed, already known not to
4411         be the root dcb
4412 
4413 Return Value:
4414 
4415     NTSTATUS - The result of this operation if it completes without
4416                an exception.
4417 
4418 --*/
4419 
4420 {
4421     NTSTATUS Status = STATUS_SUCCESS;
4422 
4423     PFILE_END_OF_FILE_INFORMATION Buffer;
4424 
4425     ULONG NewFileSize = 0;
4426     ULONG InitialFileSize = 0;
4427     ULONG InitialValidDataLength = 0;
4428     ULONG InitialValidDataToDisk = 0;
4429 
4430     BOOLEAN CacheMapInitialized = FALSE;
4431     BOOLEAN UnwindFileSizes = FALSE;
4432     BOOLEAN ResourceAcquired = FALSE;
4433 
4434 
4435     PAGED_CODE();
4436 
4437     DebugTrace(+1, Dbg, "FatSetEndOfFileInfo...\n", 0);
4438 
4439     Buffer = Irp->AssociatedIrp.SystemBuffer;
4440 
4441     _SEH2_TRY {
4442 
4443         //
4444         //  File Size changes are only allowed on a file and not a directory
4445         //
4446 
4447         if (NodeType(Fcb) != FAT_NTC_FCB) {
4448 
4449             DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0);
4450 
4451             try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
4452         }
4453 
4454 
4455         //
4456         //  Check that the new file size is legal
4457         //
4458 
4459 
4460         if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0)) {
4461 
4462             DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
4463 
4464             try_return( Status = STATUS_DISK_FULL );
4465         }
4466 
4467         NewFileSize = Buffer->EndOfFile.LowPart;
4468 
4469 
4470         //
4471         //  If we haven't yet looked up the correct AllocationSize, do so.
4472         //
4473 
4474         if ((Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT)  &&
4475             (Fcb->FcbCondition == FcbGood)) {
4476 
4477             FatLookupFileAllocationSize( IrpContext, Fcb );
4478         }
4479 
4480         //
4481         //  This is kinda gross, but if the file is not cached, but there is
4482         //  a data section, we have to cache the file to avoid a bunch of
4483         //  extra work.
4484         //
4485 
4486         if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
4487             (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
4488             !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
4489 
4490             if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ))  {
4491 
4492                 //
4493                 //  This IRP has raced (and lost) with a close (=>cleanup)
4494                 //  on the same fileobject.  We don't want to reinitialise the
4495                 //  cachemap here now because we'll leak it (unless we do so &
4496                 //  then tear it down again here,  which is too much of a change at
4497                 //  this stage).   So we'll just say the file is closed - which
4498                 //  is arguably the right thing to do anyway,  since a caller
4499                 //  racing operations in this way is broken.  The only stumbling
4500                 //  block is possibly filters - do they operate on cleaned
4501                 //  up fileobjects?
4502                 //
4503 
4504                 FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED);
4505             }
4506 
4507             //
4508             //  Now initialize the cache map.
4509             //
4510 
4511             FatInitializeCacheMap( FileObject,
4512                                    (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
4513                                    FALSE,
4514                                    &FatData.CacheManagerCallbacks,
4515                                    Fcb );
4516 
4517             CacheMapInitialized = TRUE;
4518         }
4519 
4520         //
4521         //  Do a special case here for the lazy write of file sizes.
4522         //
4523 
4524         if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) {
4525 
4526             //
4527             //  Only attempt this if the file hasn't been "deleted on close" and
4528             //  this is a good FCB.
4529             //
4530 
4531             if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) {
4532 
4533                 PDIRENT Dirent = NULL;
4534                 PBCB DirentBcb;
4535 
4536 
4537                 //
4538                 //  Never have the dirent filesize larger than the fcb filesize
4539                 //
4540 
4541                 if (NewFileSize >= Fcb->Header.FileSize.LowPart) {
4542 
4543                     NewFileSize = Fcb->Header.FileSize.LowPart;
4544                 }
4545 
4546                 //
4547                 //  Make sure we don't set anything higher than the alloc size.
4548                 //
4549 
4550 
4551                 NT_ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart );
4552 
4553 
4554                 //
4555                 //  Only advance the file size, never reduce it with this call
4556                 //
4557 
4558                 FatGetDirentFromFcbOrDcb( IrpContext,
4559                                           Fcb,
4560                                           FALSE,
4561                                           &Dirent,
4562                                           &DirentBcb );
4563                 _SEH2_TRY {
4564 
4565 
4566                     if ( NewFileSize > Dirent->FileSize ) {
4567                         Dirent->FileSize = NewFileSize;
4568 
4569                         FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
4570 
4571                         //
4572                         //  Report that we just changed the file size.
4573                         //
4574 
4575                         FatNotifyReportChange( IrpContext,
4576                                                Vcb,
4577                                                Fcb,
4578                                                FILE_NOTIFY_CHANGE_SIZE,
4579                                                FILE_ACTION_MODIFIED );
4580 
4581                     }
4582 
4583                 } _SEH2_FINALLY {
4584 
4585                     FatUnpinBcb( IrpContext, DirentBcb );
4586                 } _SEH2_END;
4587 
4588             } else {
4589 
4590                 DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0);
4591             }
4592 
4593             try_return( Status = STATUS_SUCCESS );
4594         }
4595 
4596         //
4597         //  Check if the new file size is greater than the current
4598         //  allocation size.  If it is then we need to increase the
4599         //  allocation size.
4600         //
4601 
4602 
4603         if ( (NewFileSize) > Fcb->Header.AllocationSize.LowPart ) {
4604 
4605             //
4606             //  Change the file allocation
4607             //
4608 
4609             FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize );
4610         }
4611 
4612 
4613         //
4614         //  At this point we have enough allocation for the file.
4615         //  So check if we are really changing the file size
4616         //
4617 
4618         if (Fcb->Header.FileSize.LowPart != NewFileSize) {
4619 
4620             if ( NewFileSize < Fcb->Header.FileSize.LowPart ) {
4621 
4622                 //
4623                 //  The way Sections for DataScan are created and used, an AV's
4624                 //  memory-mapping can come into being after we check it's safe
4625                 //  to truncate a file while continuing to hold  the file here.
4626                 //  This leads to data corruption because Purge eventually fails
4627                 //  (during call to Cc to set file sizes) and stale data continues
4628                 //  to live in the cache/memory.
4629                 //
4630 
4631                 if (Fcb->PurgeFailureModeEnableCount != 0) {
4632 
4633                     try_return( Status = STATUS_PURGE_FAILED );
4634                 }
4635 
4636                 //
4637                 //  Before we actually truncate, check to see if the purge
4638                 //  is going to fail.
4639                 //
4640 
4641                 if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
4642                                            &Buffer->EndOfFile )) {
4643 
4644                     try_return( Status = STATUS_USER_MAPPED_FILE );
4645                 }
4646 
4647                 //
4648                 //  This call is unconditional, because MM always wants to know.
4649                 //  Also serialize here with paging io since we are truncating
4650                 //  the file size.
4651                 //
4652 
4653                 ResourceAcquired =
4654                     ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
4655             }
4656 
4657             //
4658             //  Set the new file size
4659             //
4660 
4661             InitialFileSize = Fcb->Header.FileSize.LowPart;
4662             InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart;
4663             InitialValidDataToDisk = Fcb->ValidDataToDisk;
4664             UnwindFileSizes = TRUE;
4665 
4666             Fcb->Header.FileSize.LowPart = NewFileSize;
4667 
4668             //
4669             //  If we reduced the file size to less than the ValidDataLength,
4670             //  adjust the VDL.  Likewise ValidDataToDisk.
4671             //
4672 
4673             if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) {
4674 
4675                 Fcb->Header.ValidDataLength.LowPart = NewFileSize;
4676             }
4677 
4678             if (Fcb->ValidDataToDisk > NewFileSize) {
4679 
4680                 Fcb->ValidDataToDisk = NewFileSize;
4681             }
4682 
4683             DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize);
4684 
4685             //
4686             //  We must now update the cache mapping (benign if not cached).
4687             //
4688 
4689             CcSetFileSizes( FileObject,
4690                             (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
4691 
4692             FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
4693 
4694             //
4695             //  Report that we just changed the file size.
4696             //
4697 
4698             FatNotifyReportChange( IrpContext,
4699                                    Vcb,
4700                                    Fcb,
4701                                    FILE_NOTIFY_CHANGE_SIZE,
4702                                    FILE_ACTION_MODIFIED );
4703 
4704             //
4705             //  Mark the fact that the file will need to checked for
4706             //  truncation on cleanup.
4707             //
4708 
4709             SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE );
4710         }
4711 
4712         //
4713         //  Set this handle as having modified the file
4714         //
4715 
4716         FileObject->Flags |= FO_FILE_MODIFIED;
4717 
4718         //
4719         //  Set our return status to success
4720         //
4721 
4722         Status = STATUS_SUCCESS;
4723 
4724     try_exit: NOTHING;
4725 
4726         FatUnpinRepinnedBcbs( IrpContext );
4727 
4728     } _SEH2_FINALLY {
4729 
4730         DebugUnwind( FatSetEndOfFileInfo );
4731 
4732         if (_SEH2_AbnormalTermination() && UnwindFileSizes) {
4733 
4734             Fcb->Header.FileSize.LowPart = InitialFileSize;
4735             Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength;
4736             Fcb->ValidDataToDisk = InitialValidDataToDisk;
4737 
4738             if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
4739 
4740                 *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
4741             }
4742 
4743             //
4744             //  WinSE bug #307418 "Occasional data corruption when
4745             //  standby/resume while copying files to removable FAT
4746             //  formatted media".
4747             //  On system suspend FatUnpinRepinnedBcbs() can fail
4748             //  because the underlying drive is already marked with DO_VERIFY
4749             //  flag. FatUnpinRepinnedBcbs() will raise in this case and
4750             //  the file size changes will be un-rolled in FCB but the change
4751             //  to Dirent file still can make it to the disk since its BCB
4752             //  will not be purged by FatUnpinRepinnedBcbs(). In this case
4753             //  we'll also try to un-roll the change to Dirent to keep
4754             //  in-memory and on-disk metadata in sync.
4755             //
4756 
4757             FatSetFileSizeInDirentNoRaise( IrpContext, Fcb, NULL );
4758 
4759         }
4760 
4761         if (CacheMapInitialized) {
4762 
4763             CcUninitializeCacheMap( FileObject, NULL, NULL );
4764         }
4765 
4766         if ( ResourceAcquired ) {
4767 
4768             ExReleaseResourceLite( Fcb->Header.PagingIoResource );
4769         }
4770 
4771         DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status);
4772     } _SEH2_END;
4773 
4774     return Status;
4775 }
4776 
4777 
4778 //
4779 //  Internal Support Routine
4780 //
4781 
_Requires_lock_held_(_Global_critical_region_)4782 _Requires_lock_held_(_Global_critical_region_)
4783 NTSTATUS
4784 FatSetValidDataLengthInfo (
4785     IN PIRP_CONTEXT IrpContext,
4786     IN PIRP Irp,
4787     IN PFILE_OBJECT FileObject,
4788     IN PFCB Fcb,
4789     IN PCCB Ccb
4790     )
4791 
4792 /*++
4793 
4794 Routine Description:
4795 
4796     This routine performs the set valid data length information for fat.  It either
4797     completes the request or enqueues it off to the fsp.
4798 
4799 Arguments:
4800 
4801     Irp - Supplies the irp being processed
4802 
4803     FileObject - Supplies the file object being processed
4804 
4805     Fcb - Supplies the Fcb or Dcb being processed, already known not to
4806         be the root dcb
4807 
4808     Ccb - Supplies the Ccb corresponding to the handle opening the source
4809         file
4810 
4811 Return Value:
4812 
4813     NTSTATUS - The result of this operation if it completes without
4814                an exception.
4815 
4816 --*/
4817 
4818 {
4819     NTSTATUS Status = STATUS_SUCCESS;
4820 
4821     PFILE_VALID_DATA_LENGTH_INFORMATION Buffer;
4822 
4823     ULONG NewValidDataLength;
4824     BOOLEAN ResourceAcquired = FALSE;
4825 
4826     PAGED_CODE();
4827 
4828     UNREFERENCED_PARAMETER( IrpContext );
4829 
4830     DebugTrace(+1, Dbg, "FatSetValidDataLengthInfo...\n", 0);
4831 
4832     Buffer = Irp->AssociatedIrp.SystemBuffer;
4833 
4834     _SEH2_TRY {
4835 
4836         //
4837         //  User must have manage volume privilege to explicitly tweak the VDL
4838         //
4839 
4840         if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
4841 
4842             try_return( Status = STATUS_INVALID_PARAMETER );
4843         }
4844 
4845         //
4846         //  Valid data length changes are only allowed on a file and not a directory
4847         //
4848 
4849         if (NodeType(Fcb) != FAT_NTC_FCB) {
4850 
4851             DebugTrace(0, Dbg, "Cannot change VDL of a directory\n", 0);
4852 
4853             try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
4854         }
4855 
4856         //
4857         //  Check that the new file size is legal
4858         //
4859 
4860 
4861         if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->ValidDataLength, 0)) {
4862 
4863             DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
4864 
4865             try_return( Status = STATUS_DISK_FULL );
4866         }
4867 
4868 
4869         NewValidDataLength = Buffer->ValidDataLength.LowPart;
4870 
4871         //
4872         //  VDL can only move forward
4873         //
4874 
4875         if ((NewValidDataLength < Fcb->Header.ValidDataLength.LowPart) ||
4876             (NewValidDataLength > Fcb->Header.FileSize.LowPart)) {
4877 
4878             try_return( Status = STATUS_INVALID_PARAMETER );
4879         }
4880 
4881         //
4882         //  We can't change the VDL without being able to purge. This should stay
4883         //  constant since we own everything exclusive
4884         //
4885 
4886         if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
4887                                    &Buffer->ValidDataLength )) {
4888 
4889             try_return( Status = STATUS_USER_MAPPED_FILE );
4890         }
4891 
4892         //
4893         //  Flush old data out and purge the cache so we can see new data.
4894         //
4895 
4896         if (FileObject->SectionObjectPointer->DataSectionObject != NULL) {
4897 
4898             ResourceAcquired =
4899                 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
4900 
4901             CcFlushCache( FileObject->SectionObjectPointer,
4902                           NULL,
4903                           0,
4904                           &Irp->IoStatus );
4905 
4906             if (!NT_SUCCESS( Irp->IoStatus.Status )) {
4907 
4908                 try_return( Irp->IoStatus.Status );
4909             }
4910 
4911             CcPurgeCacheSection( FileObject->SectionObjectPointer,
4912                                  NULL,
4913                                  0,
4914                                  FALSE );
4915         }
4916 
4917         //
4918         //  Set the new ValidDataLength, Likewise ValidDataToDisk.
4919         //
4920 
4921         Fcb->Header.ValidDataLength.LowPart = NewValidDataLength;
4922         Fcb->ValidDataToDisk = NewValidDataLength;
4923 
4924         DebugTrace(0, Dbg, "New VDL is 0x%08lx.\n", NewValidDataLength);
4925 
4926         //
4927         //  We must now update the cache mapping.
4928         //
4929 
4930         if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
4931 
4932             CcSetFileSizes( FileObject,
4933                             (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
4934         }
4935 
4936         //
4937         //  Set this handle as having modified the file
4938         //
4939 
4940         FileObject->Flags |= FO_FILE_MODIFIED;
4941 
4942         //
4943         //  Set our return status to success
4944         //
4945 
4946         Status = STATUS_SUCCESS;
4947 
4948     try_exit: NOTHING;
4949 
4950     } _SEH2_FINALLY {
4951 
4952         DebugUnwind( FatSetValidDataLengthInfo );
4953 
4954         if (ResourceAcquired) {
4955 
4956             ExReleaseResourceLite( Fcb->Header.PagingIoResource );
4957         }
4958 
4959         DebugTrace(-1, Dbg, "FatSetValidDataLengthInfo -> %08lx\n", Status);
4960     } _SEH2_END;
4961 
4962     return Status;
4963 }
4964 
4965 
4966 
4967 //
4968 //  Internal Support Routine
4969 //
4970 
_Requires_lock_held_(_Global_critical_region_)4971 _Requires_lock_held_(_Global_critical_region_)
4972 VOID
4973 FatRenameEAs (
4974     IN PIRP_CONTEXT IrpContext,
4975     IN PFCB Fcb,
4976     IN USHORT ExtendedAttributes,
4977     IN POEM_STRING OldOemName
4978     )
4979 {
4980     BOOLEAN LockedEaFcb = FALSE;
4981 
4982     PBCB EaBcb = NULL;
4983     PDIRENT EaDirent;
4984     EA_RANGE EaSetRange;
4985     PEA_SET_HEADER EaSetHeader;
4986 
4987     PVCB Vcb;
4988 
4989     PAGED_CODE();
4990 
4991     RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
4992 
4993     Vcb = Fcb->Vcb;
4994 
4995     _SEH2_TRY {
4996 
4997         //
4998         //  Use a try-except to catch any errors.
4999         //
5000 
5001         _SEH2_TRY {
5002 
5003 
5004             //
5005             //  Try to get the Ea file object.  Return FALSE on failure.
5006             //
5007 
5008             FatGetEaFile( IrpContext,
5009                           Vcb,
5010                           &EaDirent,
5011                           &EaBcb,
5012                           FALSE,
5013                           FALSE );
5014 
5015             LockedEaFcb = TRUE;
5016 
5017             //
5018             //  If we didn't get the file because it doesn't exist, then the
5019             //  disk is corrupted.  We do nothing here.
5020             //
5021 
5022             if (Vcb->VirtualEaFile != NULL) {
5023 
5024                 //
5025                 //  Try to pin down the Ea set header for the index in the
5026                 //  dirent.  If the operation doesn't complete, return FALSE
5027                 //  from this routine.
5028                 //
5029 
5030                 FatReadEaSet( IrpContext,
5031                               Vcb,
5032                               ExtendedAttributes,
5033                               OldOemName,
5034                               FALSE,
5035                               &EaSetRange );
5036 
5037                 EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
5038 
5039                 //
5040                 //  We now have the Ea set header for this file.  We simply
5041                 //  overwrite the owning file name.
5042                 //
5043 
5044                 RtlZeroMemory( EaSetHeader->OwnerFileName, 14 );
5045 
5046                 RtlCopyMemory( EaSetHeader->OwnerFileName,
5047                                Fcb->ShortName.Name.Oem.Buffer,
5048                                Fcb->ShortName.Name.Oem.Length );
5049 
5050                 FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
5051                 FatUnpinEaRange( IrpContext, &EaSetRange );
5052 
5053                 CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
5054             }
5055 
5056         } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
5057 
5058             //
5059             //  We catch all exceptions that Fat catches, but don't do
5060             //  anything with them.
5061             //
5062         } _SEH2_END;
5063 
5064     } _SEH2_FINALLY {
5065 
5066         //
5067         //  Unpin the EaDirent and the EaSetHeader if pinned.
5068         //
5069 
5070         FatUnpinBcb( IrpContext, EaBcb );
5071         FatUnpinEaRange( IrpContext, &EaSetRange );
5072 
5073         //
5074         //  Release the Fcb for the Ea file if locked.
5075         //
5076 
5077         if (LockedEaFcb) {
5078 
5079             FatReleaseFcb( IrpContext, Vcb->EaFcb );
5080         }
5081     } _SEH2_END;
5082 
5083     return;
5084 }
5085 
_Requires_lock_held_(_Global_critical_region_)5086 _Requires_lock_held_(_Global_critical_region_)
5087 VOID
5088 FatDeleteFile (
5089     IN PIRP_CONTEXT IrpContext,
5090     IN PDCB TargetDcb,
5091     IN ULONG LfnOffset,
5092     IN ULONG DirentOffset,
5093     IN PDIRENT Dirent,
5094     IN PUNICODE_STRING Lfn
5095     )
5096 {
5097     PFCB Fcb;
5098     PLIST_ENTRY Links;
5099 
5100     PAGED_CODE();
5101 
5102     //
5103     //  We can do the replace by removing the other Fcb(s) from
5104     //  the prefix table.
5105     //
5106 
5107     for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
5108          Links != &TargetDcb->Specific.Dcb.ParentDcbQueue;
5109          Links = Links->Flink) {
5110 
5111         Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
5112 
5113         if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) &&
5114             (Fcb->DirentOffsetWithinDirectory == DirentOffset)) {
5115 
5116             NT_ASSERT( NodeType(Fcb) == FAT_NTC_FCB );
5117             NT_ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset );
5118 
5119             if ( Fcb->UncleanCount != 0 ) {
5120 
5121 #ifdef _MSC_VER
5122 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
5123 #endif
5124                 FatBugCheck(0,0,0);
5125 
5126             } else {
5127 
5128                 PERESOURCE Resource;
5129 
5130                 //
5131                 //  Make this fcb "appear" deleted, synchronizing with
5132                 //  paging IO.
5133                 //
5134 
5135                 FatRemoveNames( IrpContext, Fcb );
5136 
5137                 Resource = Fcb->Header.PagingIoResource;
5138 
5139                 (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE );
5140 
5141                 SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);
5142 
5143                 Fcb->ValidDataToDisk = 0;
5144                 Fcb->Header.FileSize.QuadPart =
5145                 Fcb->Header.ValidDataLength.QuadPart = 0;
5146 
5147                 Fcb->FirstClusterOfFile = 0;
5148 
5149                 ExReleaseResourceLite( Resource );
5150             }
5151         }
5152     }
5153 
5154     //
5155     //  The file is not currently opened so we can delete the file
5156     //  that is being overwritten.  To do the operation we dummy
5157     //  up an fcb, truncate allocation, delete the fcb, and delete
5158     //  the dirent.
5159     //
5160 
5161     Fcb = FatCreateFcb( IrpContext,
5162                         TargetDcb->Vcb,
5163                         TargetDcb,
5164                         LfnOffset,
5165                         DirentOffset,
5166                         Dirent,
5167                         Lfn,
5168                         NULL,
5169                         FALSE,
5170                         FALSE );
5171 
5172     Fcb->Header.FileSize.LowPart = 0;
5173 
5174     _SEH2_TRY {
5175 
5176         FatTruncateFileAllocation( IrpContext, Fcb, 0 );
5177 
5178         FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );
5179 
5180     } _SEH2_FINALLY {
5181 
5182         FatDeleteFcb( IrpContext, &Fcb );
5183     } _SEH2_END;
5184 }
5185 
5186 
5187