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