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