3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the File Information routines for Fat called by
20 // The Bug check file id for this module
23 #define BugCheckFileId (FAT_BUG_CHECK_FILEINFO)
26 // The local debug trace level
29 #define Dbg (DEBUG_TRACE_FILEINFO)
33 IN PIRP_CONTEXT IrpContext
,
35 IN PFILE_OBJECT FileObject
,
36 IN OUT PFILE_BASIC_INFORMATION Buffer
,
41 FatQueryStandardInfo (
42 IN PIRP_CONTEXT IrpContext
,
44 IN OUT PFILE_STANDARD_INFORMATION Buffer
,
49 FatQueryInternalInfo (
50 IN PIRP_CONTEXT IrpContext
,
52 IN OUT PFILE_INTERNAL_INFORMATION Buffer
,
58 IN PIRP_CONTEXT IrpContext
,
60 IN OUT PFILE_EA_INFORMATION Buffer
,
65 FatQueryPositionInfo (
66 IN PIRP_CONTEXT IrpContext
,
67 IN PFILE_OBJECT FileObject
,
68 IN OUT PFILE_POSITION_INFORMATION Buffer
,
74 IN PIRP_CONTEXT IrpContext
,
77 IN OUT PFILE_NAME_INFORMATION Buffer
,
82 FatQueryShortNameInfo (
83 IN PIRP_CONTEXT IrpContext
,
85 IN OUT PFILE_NAME_INFORMATION Buffer
,
91 IN PIRP_CONTEXT IrpContext
,
93 IN PFILE_OBJECT FileObject
,
94 IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer
,
100 IN PIRP_CONTEXT IrpContext
,
107 FatSetDispositionInfo (
108 IN PIRP_CONTEXT IrpContext
,
110 IN PFILE_OBJECT FileObject
,
116 IN PIRP_CONTEXT IrpContext
,
125 IN PIRP_CONTEXT IrpContext
,
127 IN PFILE_OBJECT FileObject
131 FatSetAllocationInfo (
132 IN PIRP_CONTEXT IrpContext
,
135 IN PFILE_OBJECT FileObject
139 FatSetEndOfFileInfo (
140 IN PIRP_CONTEXT IrpContext
,
142 IN PFILE_OBJECT FileObject
,
149 IN PIRP_CONTEXT IrpContext
,
152 IN ULONG DirentOffset
,
154 IN PUNICODE_STRING Lfn
159 IN PIRP_CONTEXT IrpContext
,
161 IN USHORT ExtendedAttributes
,
162 IN POEM_STRING OldOemName
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)
191 FatFsdQueryInformation (
192 IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject
,
200 This routine implements the Fsd part of the NtQueryInformationFile API
205 VolumeDeviceObject - Supplies the volume device object where the file
206 being queried exists.
208 Irp - Supplies the Irp being processed.
212 NTSTATUS - The FSD status for the Irp.
218 PIRP_CONTEXT IrpContext
= NULL
;
222 DebugTrace(+1, Dbg
, "FatFsdQueryInformation\n", 0);
225 // Call the common query routine, with blocking allowed if synchronous
228 FsRtlEnterFileSystem();
230 TopLevel
= FatIsIrpTopLevel( Irp
);
234 IrpContext
= FatCreateIrpContext( Irp
, CanFsdWait( Irp
) );
236 Status
= FatCommonQueryInformation( IrpContext
, Irp
);
238 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
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
247 Status
= FatProcessException( IrpContext
, Irp
, _SEH2_GetExceptionCode() );
250 if (TopLevel
) { IoSetTopLevelIrp( NULL
); }
252 FsRtlExitFileSystem();
255 // And return to our caller
258 DebugTrace(-1, Dbg
, "FatFsdQueryInformation -> %08lx\n", Status
);
260 UNREFERENCED_PARAMETER( VolumeDeviceObject
);
268 FatFsdSetInformation (
269 IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject
,
277 This routine implements the FSD part of the NtSetInformationFile API
282 VolumeDeviceObject - Supplies the volume device object where the file
285 Irp - Supplies the Irp being processed.
289 NTSTATUS - The FSD status for the Irp.
295 PIRP_CONTEXT IrpContext
= NULL
;
299 DebugTrace(+1, Dbg
, "FatFsdSetInformation\n", 0);
302 // Call the common set routine, with blocking allowed if synchronous
305 FsRtlEnterFileSystem();
307 TopLevel
= FatIsIrpTopLevel( Irp
);
311 IrpContext
= FatCreateIrpContext( Irp
, CanFsdWait( Irp
) );
313 Status
= FatCommonSetInformation( IrpContext
, Irp
);
315 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
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
324 Status
= FatProcessException( IrpContext
, Irp
, _SEH2_GetExceptionCode() );
327 if (TopLevel
) { IoSetTopLevelIrp( NULL
); }
329 FsRtlExitFileSystem();
332 // And return to our caller
335 DebugTrace(-1, Dbg
, "FatFsdSetInformation -> %08lx\n", Status
);
337 UNREFERENCED_PARAMETER( VolumeDeviceObject
);
344 FatCommonQueryInformation (
345 IN PIRP_CONTEXT IrpContext
,
353 This is the common routine for querying file information called by both
354 the fsd and fsp threads.
358 Irp - Supplies the Irp being processed
362 NTSTATUS - The return status for the operation
369 PIO_STACK_LOCATION IrpSp
;
371 PFILE_OBJECT FileObject
;
374 FILE_INFORMATION_CLASS FileInformationClass
;
377 TYPE_OF_OPEN TypeOfOpen
;
384 PFILE_ALL_INFORMATION AllInfo
;
387 // Get the current stack location
390 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
392 FileObject
= IrpSp
->FileObject
;
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
);
401 // Reference our input parameters to make things easier
404 Length
= (LONG
)IrpSp
->Parameters
.QueryFile
.Length
;
405 FileInformationClass
= IrpSp
->Parameters
.QueryFile
.FileInformationClass
;
406 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
409 // Decode the file object
412 TypeOfOpen
= FatDecodeFileObject( FileObject
, &Vcb
, &Fcb
, &Ccb
);
415 Status
= STATUS_SUCCESS
;
420 // Case on the type of open we're dealing with
423 switch (TypeOfOpen
) {
428 // We cannot query the user volume open.
431 Status
= STATUS_INVALID_PARAMETER
;
436 case UserDirectoryOpen
:
441 // Acquire shared access to the fcb, except for a paging file
442 // in order to avoid deadlocks with Mm.
445 if (!FlagOn( Fcb
->FcbState
, FCB_STATE_PAGING_FILE
)) {
447 if (!FatAcquireSharedFcb( IrpContext
, Fcb
)) {
449 DebugTrace(0, Dbg
, "Cannot acquire Fcb\n", 0);
451 Status
= FatFsdPostRequest( IrpContext
, Irp
);
455 try_return( Status
);
462 // Make sure the Fcb is in a usable condition. This
463 // will raise an error condition if the fcb is unusable
466 FatVerifyFcb( IrpContext
, Fcb
);
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.
478 switch (FileInformationClass
) {
480 case FileAllInformation
:
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.
489 Length
-= (sizeof(FILE_ACCESS_INFORMATION
)
490 + sizeof(FILE_MODE_INFORMATION
)
491 + sizeof(FILE_ALIGNMENT_INFORMATION
));
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
);
502 case FileBasicInformation
:
504 FatQueryBasicInfo( IrpContext
, Fcb
, FileObject
, Buffer
, &Length
);
507 case FileStandardInformation
:
509 FatQueryStandardInfo( IrpContext
, Fcb
, Buffer
, &Length
);
512 case FileInternalInformation
:
514 FatQueryInternalInfo( IrpContext
, Fcb
, Buffer
, &Length
);
517 case FileEaInformation
:
519 FatQueryEaInfo( IrpContext
, Fcb
, Buffer
, &Length
);
522 case FilePositionInformation
:
524 FatQueryPositionInfo( IrpContext
, FileObject
, Buffer
, &Length
);
527 case FileNameInformation
:
529 FatQueryNameInfo( IrpContext
, Fcb
, Ccb
, Buffer
, &Length
);
532 case FileAlternateNameInformation
:
534 FatQueryShortNameInfo( IrpContext
, Fcb
, Buffer
, &Length
);
537 case FileNetworkOpenInformation
:
539 FatQueryNetworkInfo( IrpContext
, Fcb
, FileObject
, Buffer
, &Length
);
544 Status
= STATUS_INVALID_PARAMETER
;
552 KdPrintEx((DPFLTR_FASTFAT_ID
,
554 "FATQueryFile, Illegal TypeOfOpen = %08lx\n",
557 Status
= STATUS_INVALID_PARAMETER
;
562 // If we overflowed the buffer, set the length to 0 and change the
563 // status to STATUS_BUFFER_OVERFLOW.
568 Status
= STATUS_BUFFER_OVERFLOW
;
574 // Set the information field to the number of bytes actually filled in
575 // and then complete the request
578 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.QueryFile
.Length
- Length
;
583 DebugUnwind( FatCommonQueryInformation
);
585 if (FcbAcquired
) { FatReleaseFcb( IrpContext
, Fcb
); }
587 if (!_SEH2_AbnormalTermination()) {
589 FatCompleteRequest( IrpContext
, Irp
, Status
);
592 DebugTrace(-1, Dbg
, "FatCommonQueryInformation -> %08lx\n", Status
);
600 FatCommonSetInformation (
601 IN PIRP_CONTEXT IrpContext
,
609 This is the common routine for setting file information called by both
610 the fsd and fsp threads.
614 Irp - Supplies the Irp being processed
618 NTSTATUS - The return status for the operation
625 PIO_STACK_LOCATION IrpSp
;
627 PFILE_OBJECT FileObject
;
628 FILE_INFORMATION_CLASS FileInformationClass
;
630 TYPE_OF_OPEN TypeOfOpen
;
635 BOOLEAN VcbAcquired
= FALSE
;
636 BOOLEAN FcbAcquired
= FALSE
;
639 // Get the current stack location
642 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
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
);
653 // Reference our input parameters to make things easier
656 FileInformationClass
= IrpSp
->Parameters
.SetFile
.FileInformationClass
;
657 FileObject
= IrpSp
->FileObject
;
660 // Decode the file object
663 TypeOfOpen
= FatDecodeFileObject( FileObject
, &Vcb
, &Fcb
, &Ccb
);
668 // Case on the type of open we're dealing with
671 switch (TypeOfOpen
) {
676 // We cannot query the user volume open.
679 try_return( Status
= STATUS_INVALID_PARAMETER
);
684 if (!FlagOn( Fcb
->FcbState
, FCB_STATE_PAGING_FILE
) &&
685 ((FileInformationClass
== FileEndOfFileInformation
) ||
686 (FileInformationClass
== FileAllocationInformation
))) {
689 // We check whether we can proceed
690 // based on the state of the file oplocks.
693 Status
= FsRtlCheckOplock( &Fcb
->Specific
.Fcb
.Oplock
,
699 if (Status
!= STATUS_SUCCESS
) {
701 try_return( Status
);
705 // Set the flag indicating if Fast I/O is possible
708 Fcb
->Header
.IsFastIoPossible
= FatIsFastIoPossible( Fcb
);
712 case UserDirectoryOpen
:
718 try_return( Status
= STATUS_INVALID_PARAMETER
);
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.
726 if (NodeType(Fcb
) == FAT_NTC_ROOT_DCB
) {
728 if (FileInformationClass
== FileDispositionInformation
) {
730 try_return( Status
= STATUS_CANNOT_DELETE
);
733 try_return( Status
= STATUS_INVALID_PARAMETER
);
737 // In the following two cases, we cannot have creates occuring
738 // while we are here, so acquire the volume exclusive.
741 if ((FileInformationClass
== FileDispositionInformation
) ||
742 (FileInformationClass
== FileRenameInformation
)) {
744 if (!FatAcquireExclusiveVcb( IrpContext
, Vcb
)) {
746 DebugTrace(0, Dbg
, "Cannot acquire Vcb\n", 0);
748 Status
= FatFsdPostRequest( IrpContext
, Irp
);
752 try_return( Status
);
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.
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.
772 // Note that we do not acquire the resource for paging file
773 // operations in order to avoid deadlock with Mm.
776 if (!FlagOn( Fcb
->FcbState
, FCB_STATE_PAGING_FILE
)) {
778 if (!FatAcquireExclusiveFcb( IrpContext
, Fcb
)) {
780 DebugTrace(0, Dbg
, "Cannot acquire Fcb\n", 0);
782 Status
= FatFsdPostRequest( IrpContext
, Irp
);
786 try_return( Status
);
792 Status
= STATUS_SUCCESS
;
795 // Make sure the Fcb is in a usable condition. This
796 // will raise an error condition if the fcb is unusable
799 FatVerifyFcb( IrpContext
, Fcb
);
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
808 switch (FileInformationClass
) {
810 case FileBasicInformation
:
812 Status
= FatSetBasicInfo( IrpContext
, Irp
, Fcb
, Ccb
);
815 case FileDispositionInformation
:
818 // If this is on deferred flush media, we have to be able to wait.
821 if ( FlagOn(Vcb
->VcbState
, VCB_STATE_FLAG_DEFERRED_FLUSH
) &&
822 !FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) ) {
824 Status
= FatFsdPostRequest( IrpContext
, Irp
);
830 Status
= FatSetDispositionInfo( IrpContext
, Irp
, FileObject
, Fcb
);
835 case FileRenameInformation
:
838 // We proceed with this operation only if we can wait
841 if (!FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
)) {
843 Status
= FatFsdPostRequest( IrpContext
, Irp
);
849 Status
= FatSetRenameInfo( IrpContext
, Irp
, Vcb
, Fcb
, Ccb
);
852 // If STATUS_PENDING is returned it means the oplock
853 // package has the Irp. Don't complete the request here.
856 if (Status
== STATUS_PENDING
) {
864 case FilePositionInformation
:
866 Status
= FatSetPositionInfo( IrpContext
, Irp
, FileObject
);
869 case FileLinkInformation
:
871 Status
= STATUS_INVALID_DEVICE_REQUEST
;
874 case FileAllocationInformation
:
876 Status
= FatSetAllocationInfo( IrpContext
, Irp
, Fcb
, FileObject
);
879 case FileEndOfFileInformation
:
881 Status
= FatSetEndOfFileInfo( IrpContext
, Irp
, FileObject
, Vcb
, Fcb
);
886 Status
= STATUS_INVALID_PARAMETER
;
890 if ( IrpContext
!= NULL
) {
892 FatUnpinRepinnedBcbs( IrpContext
);
898 DebugUnwind( FatCommonSetInformation
);
900 if (FcbAcquired
) { FatReleaseFcb( IrpContext
, Fcb
); }
901 if (VcbAcquired
) { FatReleaseVcb( IrpContext
, Vcb
); }
903 if (!_SEH2_AbnormalTermination()) {
905 FatCompleteRequest( IrpContext
, Irp
, Status
);
908 DebugTrace(-1, Dbg
, "FatCommonSetInformation -> %08lx\n", Status
);
916 // Internal Support Routine
921 IN PIRP_CONTEXT IrpContext
,
923 IN PFILE_OBJECT FileObject
,
924 IN OUT PFILE_BASIC_INFORMATION Buffer
,
931 This routine performs the query basic information function for fat.
935 Fcb - Supplies the Fcb being queried, it has been verified
937 FileObject - Supplies the flag bit that indicates the file was modified.
939 Buffer - Supplies a pointer to the buffer where the information is to
942 Length - Supplies the length of the buffer in bytes, and receives the
943 remaining bytes free in the buffer upon return.
952 DebugTrace(+1, Dbg
, "FatQueryBasicInfo...\n", 0);
955 // Zero out the output buffer, and set it to indicate that
956 // the query is a normal file. Later we might overwrite the
960 RtlZeroMemory( Buffer
, sizeof(FILE_BASIC_INFORMATION
) );
963 // Extract the data and fill in the non zero fields of the output
967 if (Fcb
->Header
.NodeTypeCode
== FAT_NTC_ROOT_DCB
) {
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
975 ExLocalTimeToSystemTime( &FatJanOne1980
,
976 &Buffer
->LastWriteTime
);
977 Buffer
->CreationTime
= Buffer
->LastAccessTime
= Buffer
->LastWriteTime
;
981 Buffer
->LastWriteTime
= Fcb
->LastWriteTime
;
982 Buffer
->CreationTime
= Fcb
->CreationTime
;
983 Buffer
->LastAccessTime
= Fcb
->LastAccessTime
;
986 Buffer
->FileAttributes
= Fcb
->DirentFatFlags
;
989 // If the temporary flag is set, then set it in the buffer.
992 if (FlagOn( Fcb
->FcbState
, FCB_STATE_TEMPORARY
)) {
994 SetFlag( Buffer
->FileAttributes
, FILE_ATTRIBUTE_TEMPORARY
);
998 // If no attributes were set, set the normal bit.
1001 if (Buffer
->FileAttributes
== 0) {
1003 Buffer
->FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
1007 // Update the length and status output variables
1010 *Length
-= sizeof( FILE_BASIC_INFORMATION
);
1012 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1014 DebugTrace(-1, Dbg
, "FatQueryBasicInfo -> VOID\n", 0);
1021 // Internal Support Routine
1025 FatQueryStandardInfo (
1026 IN PIRP_CONTEXT IrpContext
,
1028 IN OUT PFILE_STANDARD_INFORMATION Buffer
,
1034 Routine Description:
1036 This routine performs the query standard information function for fat.
1040 Fcb - Supplies the Fcb being queried, it has been verified
1042 Buffer - Supplies a pointer to the buffer where the information is to
1045 Length - Supplies the length of the buffer in bytes, and receives the
1046 remaining bytes free in the buffer upon return.
1055 DebugTrace(+1, Dbg
, "FatQueryStandardInfo...\n", 0);
1058 // Zero out the output buffer, and fill in the number of links
1059 // and the delete pending flag.
1062 RtlZeroMemory( Buffer
, sizeof(FILE_STANDARD_INFORMATION
) );
1064 Buffer
->NumberOfLinks
= 1;
1065 Buffer
->DeletePending
= BooleanFlagOn( Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
);
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
1073 if (NodeType(Fcb
) == FAT_NTC_FCB
) {
1075 if (Fcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
1077 FatLookupFileAllocationSize( IrpContext
, Fcb
);
1080 Buffer
->AllocationSize
= Fcb
->Header
.AllocationSize
;
1081 Buffer
->EndOfFile
= Fcb
->Header
.FileSize
;
1083 Buffer
->Directory
= FALSE
;
1087 Buffer
->Directory
= TRUE
;
1091 // Update the length and status output variables
1094 *Length
-= sizeof( FILE_STANDARD_INFORMATION
);
1096 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1098 DebugTrace(-1, Dbg
, "FatQueryStandardInfo -> VOID\n", 0);
1105 // Internal Support Routine
1109 FatQueryInternalInfo (
1110 IN PIRP_CONTEXT IrpContext
,
1112 IN OUT PFILE_INTERNAL_INFORMATION Buffer
,
1118 Routine Description:
1120 This routine performs the query internal information function for fat.
1124 Fcb - Supplies the Fcb being queried, it has been verified
1126 Buffer - Supplies a pointer to the buffer where the information is to
1129 Length - Supplies the length of the buffer in bytes, and receives the
1130 remaining bytes free in the buffer upon return.
1139 DebugTrace(+1, Dbg
, "FatQueryInternalInfo...\n", 0);
1143 Buffer
->IndexNumber
.QuadPart
= FatGenerateFileIdFromFcb( Fcb
);
1146 // Update the length and status output variables
1149 *Length
-= sizeof( FILE_INTERNAL_INFORMATION
);
1153 DebugUnwind( FatQueryInternalInfo
);
1155 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1157 DebugTrace(-1, Dbg
, "FatQueryInternalInfo -> VOID\n", 0);
1165 // Internal Support Routine
1170 IN PIRP_CONTEXT IrpContext
,
1172 IN OUT PFILE_EA_INFORMATION Buffer
,
1178 Routine Description:
1180 This routine performs the query Ea information function for fat.
1184 Fcb - Supplies the Fcb being queried, it has been verified
1186 Buffer - Supplies a pointer to the buffer where the information is to
1189 Length - Supplies the length of the buffer in bytes, and receives the
1190 remaining bytes free in the buffer upon return.
1201 DebugTrace(+1, Dbg
, "FatQueryEaInfo...\n", 0);
1208 // Zero out the output buffer
1211 RtlZeroMemory( Buffer
, sizeof(FILE_EA_INFORMATION
) );
1214 // The Root dcb does not have any EAs so don't look for any. Fat32
1215 // doesn't have any, either.
1218 if ( NodeType( Fcb
) != FAT_NTC_ROOT_DCB
&&
1219 !FatIsFat32( Fcb
->Vcb
)) {
1224 // Try to get the dirent for this file.
1227 FatGetDirentFromFcbOrDcb( IrpContext
,
1232 if (Dirent
!= NULL
) {
1235 // Get a the size needed to store the full eas for the file.
1238 FatGetEaLength( IrpContext
,
1246 // Update the length and status output variables
1249 *Length
-= sizeof( FILE_EA_INFORMATION
);
1253 DebugUnwind( FatQueryEaInfo
);
1256 // Unpin the dirent if pinned.
1259 FatUnpinBcb( IrpContext
, Bcb
);
1261 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1263 DebugTrace(-1, Dbg
, "FatQueryEaInfo -> VOID\n", 0);
1271 // Internal Support Routine
1275 FatQueryPositionInfo (
1276 IN PIRP_CONTEXT IrpContext
,
1277 IN PFILE_OBJECT FileObject
,
1278 IN OUT PFILE_POSITION_INFORMATION Buffer
,
1284 Routine Description:
1286 This routine performs the query position information function for fat.
1290 FileObject - Supplies the File object being queried
1292 Buffer - Supplies a pointer to the buffer where the information is to
1295 Length - Supplies the length of the buffer in bytes, and receives the
1296 remaining bytes free in the buffer upon return.
1305 DebugTrace(+1, Dbg
, "FatQueryPositionInfo...\n", 0);
1308 // Get the current position found in the file object.
1311 Buffer
->CurrentByteOffset
= FileObject
->CurrentByteOffset
;
1314 // Update the length and status output variables
1317 *Length
-= sizeof( FILE_POSITION_INFORMATION
);
1319 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1321 DebugTrace(-1, Dbg
, "FatQueryPositionInfo -> VOID\n", 0);
1323 UNREFERENCED_PARAMETER( IrpContext
);
1330 // Internal Support Routine
1335 IN PIRP_CONTEXT IrpContext
,
1338 IN OUT PFILE_NAME_INFORMATION Buffer
,
1344 Routine Description:
1346 This routine performs the query name information function for fat.
1350 Fcb - Supplies the Fcb being queried, it has been verified
1352 Ccb - Supplies the Ccb for the context of the user open
1354 Buffer - Supplies a pointer to the buffer where the information is to
1357 Length - Supplies the length of the buffer in bytes, and receives the
1358 remaining bytes free in the buffer upon return.
1369 BOOLEAN Overflow
= FALSE
;
1371 DebugTrace(+1, Dbg
, "FatQueryNameInfo...\n", 0);
1374 // Convert the name to UNICODE
1377 *Length
-= FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
[0]);
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.
1387 if (Fcb
->FullFileName
.Buffer
== NULL
) {
1389 FatSetFullFileNameInFcb( IrpContext
, Fcb
);
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:
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
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.
1406 // It also matches what NTFS does, and so we have the definition of
1407 // correct behavior.
1412 // Assume there is no long name and we are just going to use
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.
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.
1428 if (Fcb
->LongName
.Unicode
.Name
.Unicode
.Buffer
!= NULL
) {
1430 if ((Ccb
!= NULL
) && FlagOn(Ccb
->Flags
, CCB_FLAG_OPENED_BY_SHORTNAME
)) {
1432 TrimLength
= Fcb
->FinalNameLength
;
1438 if (*Length
< Fcb
->FullFileName
.Length
- TrimLength
) {
1440 BytesToCopy
= *Length
;
1445 BytesToCopy
= Fcb
->FullFileName
.Length
- TrimLength
;
1446 *Length
-= BytesToCopy
;
1449 RtlCopyMemory( &Buffer
->FileName
[0],
1450 Fcb
->FullFileName
.Buffer
,
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).
1458 Buffer
->FileNameLength
= Fcb
->FullFileName
.Length
- TrimLength
;
1461 // If we trimmed off the name element, this is the short name case. Pick
1462 // up the UNICODE conversion and append it.
1465 if (TrimLength
!= 0) {
1467 UNICODE_STRING ShortName
;
1468 WCHAR ShortNameBuffer
[12];
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.
1477 ShortName
.Length
= 0;
1478 ShortName
.MaximumLength
= sizeof(ShortNameBuffer
);
1479 ShortName
.Buffer
= ShortNameBuffer
;
1481 Status
= RtlOemStringToCountedUnicodeString( &ShortName
,
1482 &Fcb
->ShortName
.Name
.Oem
,
1485 ASSERT( Status
== STATUS_SUCCESS
);
1489 if (*Length
< ShortName
.Length
) {
1491 BytesToCopy
= *Length
;
1496 BytesToCopy
= ShortName
.Length
;
1497 *Length
-= BytesToCopy
;
1500 RtlCopyMemory( (PUCHAR
)&Buffer
->FileName
[0] + Buffer
->FileNameLength
,
1505 Buffer
->FileNameLength
+= ShortName
.Length
;
1517 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1519 DebugTrace(-1, Dbg
, "FatQueryNameInfo -> VOID\n", 0);
1521 UNREFERENCED_PARAMETER( IrpContext
);
1528 // Internal Support Routine
1532 FatQueryShortNameInfo (
1533 IN PIRP_CONTEXT IrpContext
,
1535 IN OUT PFILE_NAME_INFORMATION Buffer
,
1541 Routine Description:
1543 This routine queries the short name of the file.
1547 Fcb - Supplies the Fcb being queried, it has been verified
1549 Buffer - Supplies a pointer to the buffer where the information is to
1552 Length - Supplies the length of the buffer in bytes, and receives the
1553 remaining bytes free in the buffer upon return.
1565 WCHAR ShortNameBuffer
[12];
1566 UNICODE_STRING ShortName
;
1568 DebugTrace(+1, Dbg
, "FatQueryNameInfo...\n", 0);
1571 // Convert the name to UNICODE
1574 ShortName
.Length
= 0;
1575 ShortName
.MaximumLength
= sizeof(ShortNameBuffer
);
1576 ShortName
.Buffer
= ShortNameBuffer
;
1578 *Length
-= FIELD_OFFSET(FILE_NAME_INFORMATION
, FileName
[0]);
1580 Status
= RtlOemStringToCountedUnicodeString( &ShortName
,
1581 &Fcb
->ShortName
.Name
.Oem
,
1584 ASSERT( Status
== STATUS_SUCCESS
);
1587 // If we overflow, set *Length to -1 as a flag.
1590 if (*Length
< ShortName
.Length
) {
1592 BytesToCopy
= *Length
;
1597 BytesToCopy
= ShortName
.Length
;
1598 *Length
-= ShortName
.Length
;
1601 RtlCopyMemory( &Buffer
->FileName
[0],
1602 &ShortName
.Buffer
[0],
1605 Buffer
->FileNameLength
= ShortName
.Length
;
1611 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1613 DebugTrace(-1, Dbg
, "FatQueryNameInfo -> VOID\n", 0);
1615 UNREFERENCED_PARAMETER( IrpContext
);
1622 // Internal Support Routine
1626 FatQueryNetworkInfo (
1627 IN PIRP_CONTEXT IrpContext
,
1629 IN PFILE_OBJECT FileObject
,
1630 IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer
,
1637 This routine performs the query network open information function for fat.
1641 Fcb - Supplies the Fcb being queried, it has been verified
1643 FileObject - Supplies the flag bit that indicates the file was modified.
1645 Buffer - Supplies a pointer to the buffer where the information is to
1648 Length - Supplies the length of the buffer in bytes, and receives the
1649 remaining bytes free in the buffer upon return.
1658 DebugTrace(+1, Dbg
, "FatQueryNetworkInfo...\n", 0);
1661 // Zero out the output buffer, and set it to indicate that
1662 // the query is a normal file. Later we might overwrite the
1666 RtlZeroMemory( Buffer
, sizeof(FILE_NETWORK_OPEN_INFORMATION
) );
1669 // Extract the data and fill in the non zero fields of the output
1673 if (Fcb
->Header
.NodeTypeCode
== FAT_NTC_ROOT_DCB
) {
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
1681 ExLocalTimeToSystemTime( &FatJanOne1980
,
1682 &Buffer
->LastWriteTime
);
1683 Buffer
->CreationTime
= Buffer
->LastAccessTime
= Buffer
->LastWriteTime
;
1687 Buffer
->LastWriteTime
.QuadPart
= Fcb
->LastWriteTime
.QuadPart
;
1688 Buffer
->CreationTime
.QuadPart
= Fcb
->CreationTime
.QuadPart
;
1689 Buffer
->LastAccessTime
.QuadPart
= Fcb
->LastAccessTime
.QuadPart
;
1692 Buffer
->FileAttributes
= Fcb
->DirentFatFlags
;
1695 // If the temporary flag is set, then set it in the buffer.
1698 if (FlagOn( Fcb
->FcbState
, FCB_STATE_TEMPORARY
)) {
1700 SetFlag( Buffer
->FileAttributes
, FILE_ATTRIBUTE_TEMPORARY
);
1704 // If no attributes were set, set the normal bit.
1707 if (Buffer
->FileAttributes
== 0) {
1709 Buffer
->FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
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
1717 if (NodeType(Fcb
) == FAT_NTC_FCB
) {
1719 if (Fcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
1721 FatLookupFileAllocationSize( IrpContext
, Fcb
);
1724 Buffer
->AllocationSize
.QuadPart
= Fcb
->Header
.AllocationSize
.QuadPart
;
1725 Buffer
->EndOfFile
.QuadPart
= Fcb
->Header
.FileSize
.QuadPart
;
1729 // Update the length and status output variables
1732 *Length
-= sizeof( FILE_NETWORK_OPEN_INFORMATION
);
1734 DebugTrace( 0, Dbg
, "*Length = %08lx\n", *Length
);
1736 DebugTrace(-1, Dbg
, "FatQueryNetworkInfo -> VOID\n", 0);
1743 // Internal Support routine
1748 IN PIRP_CONTEXT IrpContext
,
1756 Routine Description:
1758 This routine performs the set basic information for fat. It either
1759 completes the request or enqueues it off to the fsp.
1763 Irp - Supplies the irp being processed
1765 Fcb - Supplies the Fcb or Dcb being processed, already known not to
1768 Ccb - Supplies the flag bit that control updating the last modify
1773 NTSTATUS - The result of this operation if it completes without
1781 PFILE_BASIC_INFORMATION Buffer
;
1786 FAT_TIME_STAMP CreationTime
;
1788 FAT_TIME_STAMP LastWriteTime
;
1789 FAT_TIME_STAMP LastAccessTime
;
1790 FAT_DATE LastAccessDate
;
1793 BOOLEAN ModifyCreation
= FALSE
;
1794 BOOLEAN ModifyLastWrite
= FALSE
;
1795 BOOLEAN ModifyLastAccess
= FALSE
;
1797 LARGE_INTEGER LargeCreationTime
;
1798 LARGE_INTEGER LargeLastWriteTime
;
1799 LARGE_INTEGER LargeLastAccessTime
;
1802 ULONG NotifyFilter
= 0;
1804 DebugTrace(+1, Dbg
, "FatSetBasicInfo...\n", 0);
1806 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
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.
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,
1820 if (Buffer
->LastWriteTime
.QuadPart
== -1) {
1822 SetFlag( Ccb
->Flags
, CCB_FLAG_USER_SET_LAST_WRITE
);
1823 Buffer
->LastWriteTime
.QuadPart
= 0;
1826 if (Buffer
->LastAccessTime
.QuadPart
== -1) {
1828 SetFlag( Ccb
->Flags
, CCB_FLAG_USER_SET_LAST_ACCESS
);
1829 Buffer
->LastAccessTime
.QuadPart
= 0;
1832 if (Buffer
->CreationTime
.QuadPart
== -1) {
1834 SetFlag( Ccb
->Flags
, CCB_FLAG_USER_SET_CREATION
);
1835 Buffer
->CreationTime
.QuadPart
= 0;
1840 Status
= STATUS_SUCCESS
;
1844 LARGE_INTEGER FatLocalDecThirtyOne1979
;
1845 LARGE_INTEGER FatLocalJanOne1980
;
1847 ExLocalTimeToSystemTime( &FatDecThirtyOne1979
,
1848 &FatLocalDecThirtyOne1979
);
1850 ExLocalTimeToSystemTime( &FatJanOne1980
,
1851 &FatLocalJanOne1980
);
1854 // Get a pointer to the dirent
1857 ASSERT( Fcb
->FcbCondition
== FcbGood
);
1859 FatGetDirentFromFcbOrDcb( IrpContext
,
1864 ASSERT( Dirent
&& DirentBcb
);
1867 // Check if the user specified a non-zero creation time
1870 if (FatData
.ChicagoMode
&& (Buffer
->CreationTime
.QuadPart
!= 0)) {
1872 LargeCreationTime
= Buffer
->CreationTime
;
1875 // Convert the Nt time to a Fat time
1878 if ( !FatNtTimeToFatTime( IrpContext
,
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.
1889 if ((LargeCreationTime
.QuadPart
>= FatLocalDecThirtyOne1979
.QuadPart
) &&
1890 (LargeCreationTime
.QuadPart
< FatLocalJanOne1980
.QuadPart
)) {
1892 CreationTime
= FatTimeJanOne1980
;
1893 LargeCreationTime
= FatLocalJanOne1980
;
1897 DebugTrace(0, Dbg
, "Invalid CreationTime\n", 0);
1898 try_return( Status
= STATUS_INVALID_PARAMETER
);
1902 // Don't worry about CreationMSec
1908 ModifyCreation
= TRUE
;
1912 // Check if the user specified a non-zero last access time
1915 if (FatData
.ChicagoMode
&& (Buffer
->LastAccessTime
.QuadPart
!= 0)) {
1917 LargeLastAccessTime
= Buffer
->LastAccessTime
;
1920 // Convert the Nt time to a Fat time
1923 if ( !FatNtTimeToFatTime( IrpContext
,
1924 &LargeLastAccessTime
,
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.
1934 if ((LargeLastAccessTime
.QuadPart
>= FatLocalDecThirtyOne1979
.QuadPart
) &&
1935 (LargeLastAccessTime
.QuadPart
< FatLocalJanOne1980
.QuadPart
)) {
1937 LastAccessTime
= FatTimeJanOne1980
;
1938 LargeLastAccessTime
= FatLocalJanOne1980
;
1942 DebugTrace(0, Dbg
, "Invalid LastAccessTime\n", 0);
1943 try_return( Status
= STATUS_INVALID_PARAMETER
);
1947 LastAccessDate
= LastAccessTime
.Date
;
1948 ModifyLastAccess
= TRUE
;
1952 // Check if the user specified a non-zero last write time
1955 if (Buffer
->LastWriteTime
.QuadPart
!= 0) {
1958 // First do a quick check here if the this time is the same
1959 // time as LastAccessTime.
1962 if (ModifyLastAccess
&&
1963 (Buffer
->LastWriteTime
.QuadPart
== Buffer
->LastAccessTime
.QuadPart
)) {
1965 ModifyLastWrite
= TRUE
;
1966 LastWriteTime
= LastAccessTime
;
1967 LargeLastWriteTime
= LargeLastAccessTime
;
1971 LargeLastWriteTime
= Buffer
->LastWriteTime
;
1974 // Convert the Nt time to a Fat time
1977 if ( !FatNtTimeToFatTime( IrpContext
,
1978 &LargeLastWriteTime
,
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.
1989 if ((LargeLastWriteTime
.QuadPart
>= FatLocalDecThirtyOne1979
.QuadPart
) &&
1990 (LargeLastWriteTime
.QuadPart
< FatLocalJanOne1980
.QuadPart
)) {
1992 LastWriteTime
= FatTimeJanOne1980
;
1993 LargeLastWriteTime
= FatLocalJanOne1980
;
1997 DebugTrace(0, Dbg
, "Invalid LastWriteTime\n", 0);
1998 try_return( Status
= STATUS_INVALID_PARAMETER
);
2002 ModifyLastWrite
= TRUE
;
2008 // Check if the user specified a non zero file attributes byte
2011 if (Buffer
->FileAttributes
!= 0) {
2014 // Only permit the attributes that FAT understands. The rest are silently
2015 // dropped on the floor.
2018 Attributes
= (UCHAR
)(Buffer
->FileAttributes
& (FILE_ATTRIBUTE_READONLY
|
2019 FILE_ATTRIBUTE_HIDDEN
|
2020 FILE_ATTRIBUTE_SYSTEM
|
2021 FILE_ATTRIBUTE_DIRECTORY
|
2022 FILE_ATTRIBUTE_ARCHIVE
));
2025 // Make sure that for a file the directory bit is not set
2026 // and that for a directory the bit is set.
2029 if (NodeType(Fcb
) == FAT_NTC_FCB
) {
2031 if (FlagOn(Buffer
->FileAttributes
, FILE_ATTRIBUTE_DIRECTORY
)) {
2033 DebugTrace(0, Dbg
, "Attempt to set dir attribute on file\n", 0);
2034 try_return( Status
= STATUS_INVALID_PARAMETER
);
2039 Attributes
|= FAT_DIRENT_ATTR_DIRECTORY
;
2043 // Mark the FcbState temporary flag correctly.
2046 if (FlagOn(Buffer
->FileAttributes
, FILE_ATTRIBUTE_TEMPORARY
)) {
2049 // Don't allow the temporary bit to be set on directories.
2052 if (NodeType(Fcb
) == FAT_NTC_DCB
) {
2054 DebugTrace(0, Dbg
, "No temporary directories\n", 0);
2055 try_return( Status
= STATUS_INVALID_PARAMETER
);
2058 SetFlag( Fcb
->FcbState
, FCB_STATE_TEMPORARY
);
2060 SetFlag( IoGetCurrentIrpStackLocation(Irp
)->FileObject
->Flags
,
2061 FO_TEMPORARY_FILE
);
2065 ClearFlag( Fcb
->FcbState
, FCB_STATE_TEMPORARY
);
2067 ClearFlag( IoGetCurrentIrpStackLocation(Irp
)->FileObject
->Flags
,
2068 FO_TEMPORARY_FILE
);
2072 // Set the new attributes byte, and mark the bcb dirty
2075 Fcb
->DirentFatFlags
= Attributes
;
2077 Dirent
->Attributes
= Attributes
;
2079 NotifyFilter
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
2082 if ( ModifyCreation
) {
2085 // Set the new last write time in the dirent, and mark
2089 Fcb
->CreationTime
= LargeCreationTime
;
2090 Dirent
->CreationTime
= CreationTime
;
2091 Dirent
->CreationMSec
= CreationMSec
;
2094 NotifyFilter
|= FILE_NOTIFY_CHANGE_CREATION
;
2096 // Now we have to round the time in the Fcb up to the
2097 // nearest tem msec.
2100 Fcb
->CreationTime
.QuadPart
=
2102 ((Fcb
->CreationTime
.QuadPart
+ AlmostTenMSec
) /
2106 // Now because the user just set the creation time we
2107 // better not set the creation time on close
2110 SetFlag( Ccb
->Flags
, CCB_FLAG_USER_SET_CREATION
);
2113 if ( ModifyLastAccess
) {
2116 // Set the new last write time in the dirent, and mark
2120 Fcb
->LastAccessTime
= LargeLastAccessTime
;
2121 Dirent
->LastAccessDate
= LastAccessDate
;
2123 NotifyFilter
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
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.
2131 ExSystemTimeToLocalTime( &Fcb
->LastAccessTime
,
2132 &Fcb
->LastAccessTime
);
2134 Fcb
->LastAccessTime
.QuadPart
=
2136 (Fcb
->LastAccessTime
.QuadPart
/
2137 FatOneDay
.QuadPart
) * FatOneDay
.QuadPart
;
2139 ExLocalTimeToSystemTime( &Fcb
->LastAccessTime
,
2140 &Fcb
->LastAccessTime
);
2143 // Now because the user just set the last access time we
2144 // better not set the last access time on close
2147 SetFlag( Ccb
->Flags
, CCB_FLAG_USER_SET_LAST_ACCESS
);
2150 if ( ModifyLastWrite
) {
2153 // Set the new last write time in the dirent, and mark
2157 Fcb
->LastWriteTime
= LargeLastWriteTime
;
2158 Dirent
->LastWriteTime
= LastWriteTime
;
2160 NotifyFilter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
2163 // Now we have to round the time in the Fcb up to the
2164 // nearest two seconds.
2167 Fcb
->LastWriteTime
.QuadPart
=
2169 ((Fcb
->LastWriteTime
.QuadPart
+ AlmostTwoSeconds
) /
2170 TwoSeconds
) * TwoSeconds
;
2173 // Now because the user just set the last write time we
2174 // better not set the last write time on close
2177 SetFlag( Ccb
->Flags
, CCB_FLAG_USER_SET_LAST_WRITE
);
2181 // If we modified any of the values, we report this to the notify
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.
2188 if (NotifyFilter
!= 0) {
2190 if (NodeType(Fcb
) == FAT_NTC_FCB
) {
2192 Dirent
->FileSize
= Fcb
->Header
.FileSize
.LowPart
;
2194 Dirent
->FirstClusterOfFile
= (USHORT
)Fcb
->FirstClusterOfFile
;
2196 if (FatIsFat32(Fcb
->Vcb
)) {
2198 Dirent
->FirstClusterOfFileHi
=
2199 (USHORT
)(Fcb
->FirstClusterOfFile
>> 16);
2203 FatNotifyReportChange( IrpContext
,
2207 FILE_ACTION_MODIFIED
);
2209 FatSetDirtyBcb( IrpContext
, DirentBcb
, Fcb
->Vcb
, TRUE
);
2215 DebugUnwind( FatSetBasicInfo
);
2217 FatUnpinBcb( IrpContext
, DirentBcb
);
2219 DebugTrace(-1, Dbg
, "FatSetBasicInfo -> %08lx\n", Status
);
2226 // Internal Support Routine
2230 FatSetDispositionInfo (
2231 IN PIRP_CONTEXT IrpContext
,
2233 IN PFILE_OBJECT FileObject
,
2239 Routine Description:
2241 This routine performs the set disposition information for fat. It either
2242 completes the request or enqueues it off to the fsp.
2246 Irp - Supplies the irp being processed
2248 FileObject - Supplies the file object being processed
2250 Fcb - Supplies the Fcb or Dcb being processed, already known not to
2255 NTSTATUS - The result of this operation if it completes without
2261 PFILE_DISPOSITION_INFORMATION Buffer
;
2265 DebugTrace(+1, Dbg
, "FatSetDispositionInfo...\n", 0);
2267 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
2270 // Check if the user wants to delete the file or not delete
2274 if (Buffer
->DeleteFile
) {
2277 // Check if the file is marked read only
2280 if (FlagOn(Fcb
->DirentFatFlags
, FAT_DIRENT_ATTR_READ_ONLY
)) {
2282 DebugTrace(-1, Dbg
, "Cannot delete readonly file\n", 0);
2284 return STATUS_CANNOT_DELETE
;
2288 // Make sure there is no process mapping this file as an image.
2291 if (!MmFlushImageSection( &Fcb
->NonPaged
->SectionObjectPointers
,
2292 MmFlushForDelete
)) {
2294 DebugTrace(-1, Dbg
, "Cannot delete user mapped image\n", 0);
2296 return STATUS_CANNOT_DELETE
;
2300 // Check if this is a dcb and if so then only allow
2301 // the request if the directory is empty.
2304 if (NodeType(Fcb
) == FAT_NTC_ROOT_DCB
) {
2306 DebugTrace(-1, Dbg
, "Cannot delete root Directory\n", 0);
2308 return STATUS_CANNOT_DELETE
;
2311 if (NodeType(Fcb
) == FAT_NTC_DCB
) {
2313 DebugTrace(-1, Dbg
, "User wants to delete a directory\n", 0);
2316 // Check if the directory is empty
2319 if ( !FatIsDirectoryEmpty(IrpContext
, Fcb
) ) {
2321 DebugTrace(-1, Dbg
, "Directory is not empty\n", 0);
2323 return STATUS_DIRECTORY_NOT_EMPTY
;
2328 // If this is a floppy, touch the volume so to verify that it
2329 // is not write protected.
2332 if ( FlagOn(Fcb
->Vcb
->Vpb
->RealDevice
->Characteristics
, FILE_FLOPPY_DISKETTE
)) {
2340 IO_STATUS_BLOCK Iosb
;
2344 BytesToMap
= Vcb
->AllocationSupport
.FatIndexBitSize
== 12 ?
2345 FatReservedBytes(&Vcb
->Bpb
) +
2346 FatBytesPerFat(&Vcb
->Bpb
):PAGE_SIZE
;
2348 FatReadVolumeFile( IrpContext
,
2357 if (!CcPinMappedData( Vcb
->VirtualVolumeFile
,
2360 BooleanFlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
),
2364 // Could not pin the data without waiting (cache miss).
2367 FatRaiseStatus( IrpContext
, STATUS_CANT_WAIT
);
2371 // Make Mm, myself, and Cc think the byte is dirty, and then
2372 // force a writethrough.
2375 Buffer
+= FatReservedBytes(&Vcb
->Bpb
);
2377 TmpChar
= Buffer
[0];
2378 Buffer
[0] = TmpChar
;
2380 FatAddMcbEntry( Vcb
, &Vcb
->DirtyFatMcb
,
2381 FatReservedBytes( &Vcb
->Bpb
),
2382 FatReservedBytes( &Vcb
->Bpb
),
2383 Vcb
->Bpb
.BytesPerSector
);
2387 if (_SEH2_AbnormalTermination() && (Bcb
!= NULL
)) {
2389 FatUnpinBcb( IrpContext
, Bcb
);
2394 CcSetDirtyPinnedData( Bcb
, NULL
);
2396 DbgDoit( ASSERT( IrpContext
->PinCount
));
2397 DbgDoit( IrpContext
->PinCount
-= 1 );
2398 CcUnpinRepinnedBcb( Bcb
, TRUE
, &Iosb
);
2401 // If this was not successful, raise the status.
2404 if ( !NT_SUCCESS(Iosb
.Status
) ) {
2406 FatNormalizeAndRaiseStatus( IrpContext
, Iosb
.Status
);
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
2418 FatGetDirentFromFcbOrDcb( IrpContext
,
2424 // This has to work for the usual reasons (we verified the Fcb within
2428 ASSERT( Bcb
!= NULL
);
2432 FatSetDirtyBcb( IrpContext
, Bcb
, Fcb
->Vcb
, TRUE
);
2436 FatUnpinBcb( IrpContext
, Bcb
);
2441 // At this point either we have a file or an empty directory
2442 // so we know the delete can proceed.
2445 SetFlag( Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
);
2446 FileObject
->DeletePending
= TRUE
;
2449 // If this is a directory then report this delete pending to
2450 // the dir notify package.
2453 if (NodeType(Fcb
) == FAT_NTC_DCB
) {
2455 FsRtlNotifyFullChangeDirectory( Fcb
->Vcb
->NotifySync
,
2456 &Fcb
->Vcb
->DirNotifyList
,
2457 FileObject
->FsContext
,
2469 // The user doesn't want to delete the file so clear
2470 // the delete on close bit
2473 DebugTrace(0, Dbg
, "User want to not delete file\n", 0);
2475 ClearFlag( Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
);
2476 FileObject
->DeletePending
= FALSE
;
2479 DebugTrace(-1, Dbg
, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0);
2481 return STATUS_SUCCESS
;
2486 // Internal Support Routine
2491 IN PIRP_CONTEXT IrpContext
,
2500 Routine Description:
2502 This routine performs the set name information for fat. It either
2503 completes the request or enqueues it off to the fsp.
2507 Irp - Supplies the irp being processed
2509 Vcb - Supplies the Vcb being processed
2511 Fcb - Supplies the Fcb or Dcb being processed, already known not to
2514 Ccb - Supplies the Ccb corresponding to the handle opening the source
2519 NTSTATUS - The result of this operation if it completes without
2525 BOOLEAN AllLowerComponent
;
2526 BOOLEAN AllLowerExtension
;
2527 BOOLEAN CaseOnlyRename
;
2528 BOOLEAN ContinueWithRename
;
2530 BOOLEAN DeleteSourceDirent
;
2531 BOOLEAN DeleteTarget
;
2532 BOOLEAN NewDirentFromPool
;
2533 BOOLEAN RenamedAcrossDirectories
;
2534 BOOLEAN ReplaceIfExists
;
2539 DIRENT SourceDirent
;
2543 OEM_STRING OldOemName
;
2544 OEM_STRING NewOemName
;
2545 UCHAR OemNameBuffer
[24*2];
2551 PBCB TargetDirentBcb
;
2556 PDIRENT DotDotDirent
;
2557 PDIRENT FirstPageDirent
;
2560 PDIRENT SecondPageDirent
;
2561 PDIRENT ShortDirent
;
2562 PDIRENT TargetDirent
;
2566 PFILE_OBJECT TargetFileObject
;
2567 PFILE_OBJECT FileObject
;
2569 PIO_STACK_LOCATION IrpSp
;
2573 ULONG BytesInFirstPage
;
2574 ULONG DirentsInFirstPage
;
2575 ULONG DirentsRequired
;
2578 ULONG SecondPageOffset
;
2579 ULONG ShortDirentOffset
;
2580 ULONG TargetDirentOffset
;
2581 ULONG TargetLfnOffset
;
2583 UNICODE_STRING NewName
;
2584 UNICODE_STRING NewUpcasedName
;
2585 UNICODE_STRING OldName
;
2586 UNICODE_STRING OldUpcasedName
;
2587 UNICODE_STRING TargetLfn
;
2589 PWCHAR UnicodeBuffer
;
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
;
2600 BOOLEAN InvalidateFcbOnRaise
= FALSE
;
2602 DebugTrace(+1, Dbg
, "FatSetRenameInfo...\n", 0);
2605 // P H A S E 0: Initialize some variables.
2608 CaseOnlyRename
= FALSE
;
2609 ContinueWithRename
= FALSE
;
2610 DeleteSourceDirent
= FALSE
;
2611 DeleteTarget
= FALSE
;
2612 NewDirentFromPool
= FALSE
;
2613 RenamedAcrossDirectories
= FALSE
;
2616 NewDirentBcb
= NULL
;
2617 OldDirentBcb
= NULL
;
2618 SecondPageBcb
= NULL
;
2619 TargetDirentBcb
= NULL
;
2621 NewOemName
.Length
= 0;
2622 NewOemName
.MaximumLength
= 24;
2624 NewOemName
.Buffer
= &OemNameBuffer
[0];
2626 NewOemName
.Buffer
= (PCHAR
)&OemNameBuffer
[0];
2629 OldOemName
.Length
= 0;
2630 OldOemName
.MaximumLength
= 24;
2632 OldOemName
.Buffer
= &OemNameBuffer
[24];
2634 OldOemName
.Buffer
= (PCHAR
)&OemNameBuffer
[24];
2637 UnicodeBuffer
= FsRtlAllocatePoolWithTag( PagedPool
,
2638 4 * MAX_LFN_CHARACTERS
* sizeof(WCHAR
),
2639 TAG_FILENAME_BUFFER
);
2641 NewUpcasedName
.Length
= 0;
2642 NewUpcasedName
.MaximumLength
= MAX_LFN_CHARACTERS
* sizeof(WCHAR
);
2643 NewUpcasedName
.Buffer
= &UnicodeBuffer
[0];
2646 OldName
.MaximumLength
= MAX_LFN_CHARACTERS
* sizeof(WCHAR
);
2647 OldName
.Buffer
= &UnicodeBuffer
[MAX_LFN_CHARACTERS
];
2649 OldUpcasedName
.Length
= 0;
2650 OldUpcasedName
.MaximumLength
= MAX_LFN_CHARACTERS
* sizeof(WCHAR
);
2651 OldUpcasedName
.Buffer
= &UnicodeBuffer
[MAX_LFN_CHARACTERS
* 2];
2653 TargetLfn
.Length
= 0;
2654 TargetLfn
.MaximumLength
= MAX_LFN_CHARACTERS
* sizeof(WCHAR
);
2655 TargetLfn
.Buffer
= &UnicodeBuffer
[MAX_LFN_CHARACTERS
* 3];
2657 UniTunneledShortName
.Length
= 0;
2658 UniTunneledShortName
.MaximumLength
= sizeof(UniTunneledShortNameBuffer
);
2659 UniTunneledShortName
.Buffer
= &UniTunneledShortNameBuffer
[0];
2661 UniTunneledLongName
.Length
= 0;
2662 UniTunneledLongName
.MaximumLength
= sizeof(UniTunneledLongNameBuffer
);
2663 UniTunneledLongName
.Buffer
= &UniTunneledLongNameBuffer
[0];
2666 // Remember the name in case we have to modify the name
2670 RtlCopyMemory( OldOemName
.Buffer
,
2671 Fcb
->ShortName
.Name
.Oem
.Buffer
,
2672 OldOemName
.Length
);
2675 // Get the current stack location
2678 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
2681 // Extract information from the Irp to make our life easier
2684 FileObject
= IrpSp
->FileObject
;
2685 SourceCcb
= FileObject
->FsContext2
;
2686 TargetFileObject
= IrpSp
->Parameters
.SetFile
.FileObject
;
2687 ReplaceIfExists
= IrpSp
->Parameters
.SetFile
.ReplaceIfExists
;
2689 RtlZeroMemory( &LocalCcb
, sizeof(CCB
) );
2694 // Test if rename is legal. Only small side-effects are not undone.
2700 // Can't rename the root directory
2703 if ( NodeType(Fcb
) == FAT_NTC_ROOT_DCB
) {
2705 try_return( Status
= STATUS_INVALID_PARAMETER
);
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.
2715 if (NodeType(Fcb
) == FAT_NTC_DCB
) {
2717 PFCB BatchOplockFcb
;
2718 ULONG BatchOplockCount
;
2721 // Loop until there are no batch oplocks in the subtree below
2727 BatchOplockFcb
= NULL
;
2728 BatchOplockCount
= 0;
2731 // First look for any UncleanCount != 0 Fcbs, and fail if we
2735 for ( TempFcb
= FatGetNextFcbBottomUp(IrpContext
, NULL
, Fcb
);
2737 TempFcb
= FatGetNextFcbBottomUp(IrpContext
, TempFcb
, Fcb
) ) {
2739 if ( TempFcb
->UncleanCount
!= 0 ) {
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.
2747 if ( (NodeType(TempFcb
) == FAT_NTC_FCB
) &&
2748 FsRtlCurrentBatchOplock( &TempFcb
->Specific
.Fcb
.Oplock
) ) {
2750 BatchOplockCount
+= 1;
2751 if ( BatchOplockFcb
== NULL
) {
2753 BatchOplockFcb
= TempFcb
;
2758 try_return( Status
= STATUS_ACCESS_DENIED
);
2764 // If this is not the first pass for rename and the number
2765 // of batch oplocks has not decreased then give up.
2768 if ( BatchOplockFcb
!= NULL
) {
2770 if ( (Irp
->IoStatus
.Information
!= 0) &&
2771 (BatchOplockCount
>= Irp
->IoStatus
.Information
) ) {
2773 try_return( Status
= STATUS_ACCESS_DENIED
);
2777 // Try to break this batch oplock.
2780 Irp
->IoStatus
.Information
= BatchOplockCount
;
2781 Status
= FsRtlCheckOplock( &BatchOplockFcb
->Specific
.Fcb
.Oplock
,
2788 // If the oplock was already broken then look for more
2792 if (Status
== STATUS_SUCCESS
) {
2798 // Otherwise the oplock package will post or complete the
2802 try_return( Status
= STATUS_PENDING
);
2809 // Now try to get as many of these file object, and thus Fcbs
2810 // to go away as possible, flushing first, of course.
2813 FatPurgeReferencedFileObjects( IrpContext
, Fcb
, TRUE
);
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.
2824 for ( TempFcb
= FatGetNextFcbBottomUp(IrpContext
, NULL
, Fcb
);
2826 TempFcb
= FatGetNextFcbBottomUp(IrpContext
, TempFcb
, Fcb
) ) {
2828 FatRemoveNames( IrpContext
, TempFcb
);
2830 SetFlag( TempFcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
);
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
2840 if (TargetFileObject
== NULL
) {
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
2848 PFILE_RENAME_INFORMATION Buffer
;
2850 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
2852 TargetDcb
= Fcb
->ParentDcb
;
2854 NewName
.Length
= (USHORT
) Buffer
->FileNameLength
;
2855 NewName
.Buffer
= (PWSTR
) &Buffer
->FileName
;
2858 // Make sure the name is of legal length.
2861 if (NewName
.Length
>= 255*sizeof(WCHAR
)) {
2863 try_return( Status
= STATUS_OBJECT_NAME_INVALID
);
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
2877 if ((FatDecodeFileObject( TargetFileObject
,
2880 &TargetCcb
) != UserDirectoryOpen
) ||
2881 (TargetVcb
!= Vcb
)) {
2883 try_return( Status
= STATUS_INVALID_PARAMETER
);
2887 // This name is by definition legal.
2890 NewName
= *((PUNICODE_STRING
)&TargetFileObject
->FileName
);
2894 // We will need an upcased version of the unicode name and the
2895 // old name as well.
2898 Status
= RtlUpcaseUnicodeString( &NewUpcasedName
, &NewName
, FALSE
);
2900 if (!NT_SUCCESS(Status
)) {
2902 try_return( Status
);
2905 FatGetUnicodeNameFromFcb( IrpContext
, Fcb
, &OldName
);
2907 Status
= RtlUpcaseUnicodeString( &OldUpcasedName
, &OldName
, FALSE
);
2909 if (!NT_SUCCESS(Status
)) {
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.
2918 if (TargetDcb
== Fcb
->ParentDcb
) {
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.
2926 if (FsRtlAreNamesEqual( &NewName
,
2931 try_return( Status
= STATUS_SUCCESS
);
2935 // Check now for a case only rename.
2939 if (FsRtlAreNamesEqual( &NewUpcasedName
,
2944 CaseOnlyRename
= TRUE
;
2949 RenamedAcrossDirectories
= TRUE
;
2953 // Upcase the name and convert it to the Oem code page.
2955 // If the new UNICODE name is already more than 12 characters,
2956 // then we know the Oem name will not be valid
2959 if (NewName
.Length
<= 12*sizeof(WCHAR
)) {
2961 FatUnicodeToUpcaseOem( IrpContext
, &NewOemName
, &NewName
);
2964 // If the name is not valid 8.3, zero the length.
2967 if (FatSpaceInName( IrpContext
, &NewName
) ||
2968 !FatIsNameShortOemValid( IrpContext
, NewOemName
, FALSE
, FALSE
, FALSE
)) {
2970 NewOemName
.Length
= 0;
2975 NewOemName
.Length
= 0;
2979 // Look in the tunnel cache for names and timestamps to restore
2982 TunneledDataSize
= sizeof(LARGE_INTEGER
);
2983 HaveTunneledInformation
= FsRtlFindInTunnelCache( &Vcb
->Tunnel
,
2984 FatDirectoryKey(TargetDcb
),
2986 &UniTunneledShortName
,
2987 &UniTunneledLongName
,
2989 &TunneledCreationTime
);
2990 ASSERT(TunneledDataSize
== sizeof(LARGE_INTEGER
));
2993 // Now we need to determine how many dirents this new name will
2997 if ((NewOemName
.Length
== 0) ||
2998 (FatEvaluateNameCase( IrpContext
,
3005 DirentsRequired
= FAT_LFN_DIRENTS_NEEDED(&NewName
) + 1;
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.
3014 if (UniTunneledLongName
.Length
&&
3015 !FatLfnDirentExists(IrpContext
, TargetDcb
, &UniTunneledLongName
, &TargetLfn
)) {
3017 UsingTunneledLfn
= CreateLfn
= TRUE
;
3018 DirentsRequired
= FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName
) + 1;
3023 // This really is a simple dirent. Note that the two AllLower BOOLEANs
3024 // are correctly set now.
3027 DirentsRequired
= 1;
3032 // Do some extra checks here if we are not in Chicago mode.
3035 if (!FatData
.ChicagoMode
) {
3038 // If the name was not 8.3 valid, fail the rename.
3041 if (NewOemName
.Length
== 0) {
3043 try_return( Status
= STATUS_OBJECT_NAME_INVALID
);
3047 // Don't use the magic bits.
3050 AllLowerComponent
= FALSE
;
3051 AllLowerExtension
= FALSE
;
3053 UsingTunneledLfn
= FALSE
;
3056 if (!CaseOnlyRename
) {
3059 // Check if the new name already exists, wait is known to be
3063 if (NewOemName
.Length
!= 0) {
3065 FatStringTo8dot3( IrpContext
,
3067 &LocalCcb
.OemQueryTemplate
.Constant
);
3071 SetFlag( LocalCcb
.Flags
, CCB_FLAG_SKIP_SHORT_NAME_COMPARE
);
3074 LocalCcb
.UnicodeQueryTemplate
= NewUpcasedName
;
3075 LocalCcb
.ContainsWildCards
= FALSE
;
3077 FatLocateDirent( IrpContext
,
3084 &TargetDirentOffset
,
3086 (PVBO
)&TargetDirentOffset
,
3091 if (TargetDirent
!= NULL
) {
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.
3099 if ((!ReplaceIfExists
) ||
3100 (FlagOn(TargetDirent
->Attributes
, FAT_DIRENT_ATTR_DIRECTORY
)) ||
3101 (FlagOn(TargetDirent
->Attributes
, FAT_DIRENT_ATTR_READ_ONLY
))) {
3103 try_return( Status
= STATUS_OBJECT_NAME_COLLISION
);
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.
3114 for (Links
= TargetDcb
->Specific
.Dcb
.ParentDcbQueue
.Flink
;
3115 Links
!= &TargetDcb
->Specific
.Dcb
.ParentDcbQueue
; ) {
3117 TempFcb
= CONTAINING_RECORD( Links
, FCB
, ParentDcbLinks
);
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.
3125 Links
= Links
->Flink
;
3127 if ((TempFcb
->DirentOffsetWithinDirectory
== TargetDirentOffset
) &&
3128 ((TempFcb
->UncleanCount
!= 0) ||
3129 !MmFlushImageSection( &TempFcb
->NonPaged
->SectionObjectPointers
,
3130 MmFlushForDelete
))) {
3133 // If there are batch oplocks on this file then break the
3134 // oplocks before failing the rename.
3137 Status
= STATUS_ACCESS_DENIED
;
3139 if ((NodeType(TempFcb
) == FAT_NTC_FCB
) &&
3140 FsRtlCurrentBatchOplock( &TempFcb
->Specific
.Fcb
.Oplock
)) {
3143 // Do all of our cleanup now since the IrpContext
3144 // could go away when this request is posted.
3147 FatUnpinBcb( IrpContext
, TargetDirentBcb
);
3149 Status
= FsRtlCheckOplock( &TempFcb
->Specific
.Fcb
.Oplock
,
3155 if (Status
!= STATUS_PENDING
) {
3157 Status
= STATUS_ACCESS_DENIED
;
3161 try_return( NOTHING
);
3166 // OK, this target is toast. Remember the Lfn offset.
3169 TargetLfnOffset
= TargetDirentOffset
-
3170 FAT_LFN_DIRENTS_NEEDED(&TargetLfn
) *
3173 DeleteTarget
= TRUE
;
3178 // If we will need more dirents than we have, allocate them now.
3181 if ((TargetDcb
!= Fcb
->ParentDcb
) ||
3183 (Fcb
->DirentOffsetWithinDirectory
-
3184 Fcb
->LfnOffsetWithinDirectory
) / sizeof(DIRENT
) + 1)) {
3187 // Get some new allocation
3190 NewOffset
= FatCreateNewDirent( IrpContext
,
3194 DeleteSourceDirent
= TRUE
;
3198 NewOffset
= Fcb
->LfnOffsetWithinDirectory
;
3201 ContinueWithRename
= TRUE
;
3207 if (!ContinueWithRename
) {
3210 // Undo everything from above.
3213 ExFreePool( UnicodeBuffer
);
3214 FatUnpinBcb( IrpContext
, TargetDirentBcb
);
3219 // Now, if we are already done, return here.
3222 if (!ContinueWithRename
) {
3228 // P H A S E 2: Actually perform the rename.
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.
3240 if (!RenamedAcrossDirectories
&& !DeleteTarget
) {
3242 NotifyAction
= FILE_ACTION_RENAMED_OLD_NAME
;
3246 NotifyAction
= FILE_ACTION_REMOVED
;
3249 FatNotifyReportChange( IrpContext
,
3252 ((NodeType( Fcb
) == FAT_NTC_FCB
)
3253 ? FILE_NOTIFY_CHANGE_FILE_NAME
3254 : FILE_NOTIFY_CHANGE_DIR_NAME
),
3258 // Capture a copy of the source dirent.
3261 FatGetDirentFromFcbOrDcb( IrpContext
, Fcb
, &OldDirent
, &OldDirentBcb
);
3262 SourceDirent
= *OldDirent
;
3267 // Tunnel the source Fcb - the names are disappearing regardless of
3268 // whether the dirent allocation physically changed
3271 FatTunnelFcbOrDcb( Fcb
, SourceCcb
);
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).
3280 InvalidateFcbOnRaise
= TRUE
;
3283 // Delete our current dirent(s) if we got a new one.
3286 if (DeleteSourceDirent
) {
3288 FatDeleteDirent( IrpContext
, Fcb
, NULL
, FALSE
);
3292 // Delete a target conflict if we were meant to.
3297 FatDeleteFile( IrpContext
,
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.
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.
3315 FatSelectNames( IrpContext
,
3320 (HaveTunneledInformation
? &UniTunneledShortName
: NULL
),
3325 if (!CreateLfn
&& UsingTunneledLfn
) {
3328 NewName
= UniTunneledLongName
;
3331 // Short names are always upcase if an LFN exists
3334 AllLowerComponent
= FALSE
;
3335 AllLowerExtension
= FALSE
;
3339 // OK, now setup the new dirent(s) for the new name.
3342 FatPrepareWriteDirectoryFile( IrpContext
,
3350 (PVOID
*)&NewDirent
,
3356 ASSERT( NT_SUCCESS( Status
) );
3359 // Deal with the special case of an LFN + Dirent structure crossing
3363 if ((NewOffset
/ PAGE_SIZE
) !=
3364 ((NewOffset
+ (DirentsRequired
- 1) * sizeof(DIRENT
)) / PAGE_SIZE
)) {
3366 SecondPageOffset
= (NewOffset
& ~(PAGE_SIZE
- 1)) + PAGE_SIZE
;
3368 BytesInFirstPage
= SecondPageOffset
- NewOffset
;
3370 DirentsInFirstPage
= BytesInFirstPage
/ sizeof(DIRENT
);
3372 FatPrepareWriteDirectoryFile( IrpContext
,
3380 (PVOID
*)&SecondPageDirent
,
3386 ASSERT( NT_SUCCESS( Status
) );
3388 FirstPageDirent
= NewDirent
;
3390 NewDirent
= FsRtlAllocatePoolWithTag( PagedPool
,
3391 DirentsRequired
* sizeof(DIRENT
),
3394 NewDirentFromPool
= TRUE
;
3398 // Bump up Dirent and DirentOffset
3401 ShortDirent
= NewDirent
+ DirentsRequired
- 1;
3402 ShortDirentOffset
= NewOffset
+ (DirentsRequired
- 1) * sizeof(DIRENT
);
3405 // Fill in the fields of the dirent.
3408 *ShortDirent
= SourceDirent
;
3410 FatConstructDirent( IrpContext
,
3415 CreateLfn
? &NewName
: NULL
,
3416 SourceDirent
.Attributes
,
3418 (HaveTunneledInformation
? &TunneledCreationTime
: NULL
) );
3420 if (HaveTunneledInformation
) {
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.
3427 Fcb
->CreationTime
= FatFatTimeToNtTime(IrpContext
, ShortDirent
->CreationTime
, ShortDirent
->CreationMSec
);
3428 Fcb
->LastWriteTime
= FatFatTimeToNtTime(IrpContext
, ShortDirent
->LastWriteTime
, 0);
3429 Fcb
->LastAccessTime
= FatFatDateToNtTime(IrpContext
, ShortDirent
->LastAccessDate
);
3433 // If the dirent crossed pages, split the contents of the
3434 // temporary pool between the two pages.
3437 if (NewDirentFromPool
) {
3439 RtlCopyMemory( FirstPageDirent
, NewDirent
, BytesInFirstPage
);
3441 RtlCopyMemory( SecondPageDirent
,
3442 NewDirent
+ DirentsInFirstPage
,
3443 DirentsRequired
*sizeof(DIRENT
) - BytesInFirstPage
);
3445 ShortDirent
= SecondPageDirent
+
3446 (DirentsRequired
- DirentsInFirstPage
) - 1;
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
3458 FatRemoveNames( IrpContext
, Fcb
);
3460 if (Fcb
->FullFileName
.Buffer
!= NULL
) {
3462 ExFreePool( Fcb
->FullFileName
.Buffer
);
3463 Fcb
->FullFileName
.Buffer
= NULL
;
3466 if (Fcb
->ExactCaseLongName
.Buffer
) {
3468 ExFreePool( Fcb
->ExactCaseLongName
.Buffer
);
3469 Fcb
->ExactCaseLongName
.Buffer
= NULL
;
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
3479 Fcb
->LfnOffsetWithinDirectory
= NewOffset
;
3480 Fcb
->DirentOffsetWithinDirectory
= ShortDirentOffset
;
3482 RemoveEntryList( &Fcb
->ParentDcbLinks
);
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.
3491 if (NodeType(Fcb
) == FAT_NTC_FCB
) {
3493 InsertTailList( &TargetDcb
->Specific
.Dcb
.ParentDcbQueue
,
3494 &Fcb
->ParentDcbLinks
);
3498 InsertHeadList( &TargetDcb
->Specific
.Dcb
.ParentDcbQueue
,
3499 &Fcb
->ParentDcbLinks
);
3502 OldParentDcb
= Fcb
->ParentDcb
;
3503 Fcb
->ParentDcb
= TargetDcb
;
3506 // If we renamed across directories, some cleanup is now in order.
3509 if (RenamedAcrossDirectories
) {
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
3517 if (IsListEmpty(&OldParentDcb
->Specific
.Dcb
.ParentDcbQueue
) &&
3518 (OldParentDcb
->OpenCount
== 0) &&
3519 (OldParentDcb
->Specific
.Dcb
.DirectoryFile
!= NULL
)) {
3521 PFILE_OBJECT DirectoryFileObject
;
3523 ASSERT( NodeType(OldParentDcb
) == FAT_NTC_DCB
);
3525 DirectoryFileObject
= OldParentDcb
->Specific
.Dcb
.DirectoryFile
;
3527 DebugTrace(0, Dbg
, "Uninitialize our parent Stream Cache Map\n", 0);
3529 CcUninitializeCacheMap( DirectoryFileObject
, NULL
, NULL
);
3531 OldParentDcb
->Specific
.Dcb
.DirectoryFile
= NULL
;
3533 ObDereferenceObject( DirectoryFileObject
);
3537 // If we move a directory across directories, we have to change
3538 // the cluster number in its .. entry
3541 if (NodeType(Fcb
) == FAT_NTC_DCB
) {
3543 FatPrepareWriteDirectoryFile( IrpContext
,
3551 (PVOID
*)&DotDotDirent
,
3557 ASSERT( NT_SUCCESS( Status
) );
3559 DotDotDirent
->FirstClusterOfFile
= (USHORT
)
3560 ( NodeType(TargetDcb
) == FAT_NTC_ROOT_DCB
?
3561 0 : TargetDcb
->FirstClusterOfFile
);
3563 if (FatIsFat32( Vcb
)) {
3565 DotDotDirent
->FirstClusterOfFileHi
= (USHORT
)
3566 ( NodeType( TargetDcb
) == FAT_NTC_ROOT_DCB
?
3567 0 : (TargetDcb
->FirstClusterOfFile
>> 16));
3573 // Now we need to setup the splay table and the name within
3574 // the fcb. Free the old short name at this point.
3577 ExFreePool( Fcb
->ShortName
.Name
.Oem
.Buffer
);
3578 Fcb
->ShortName
.Name
.Oem
.Buffer
= NULL
;
3580 FatConstructNamesInFcb( IrpContext
,
3583 CreateLfn
? &NewName
: NULL
);
3585 FatSetFullNameInFcb( IrpContext
, Fcb
, &NewName
);
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.
3593 InvalidateFcbOnRaise
= FALSE
;
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.
3601 if (Fcb
->Header
.NodeTypeCode
== FAT_NTC_FCB
) {
3603 SetFlag( FileObject
->Flags
, FO_FILE_MODIFIED
);
3604 SetFlag( Ccb
->Flags
, CCB_FLAG_USER_SET_LAST_WRITE
);
3608 // We have three cases to report.
3610 // 1. If we overwrote an existing file, we report this as
3613 // 2. If we renamed to a new directory, then we added a file.
3615 // 3. If we renamed in the same directory, then we report the
3616 // the renamednewname.
3621 FatNotifyReportChange( IrpContext
,
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
);
3632 } else if (RenamedAcrossDirectories
) {
3634 FatNotifyReportChange( IrpContext
,
3637 ((NodeType( Fcb
) == FAT_NTC_FCB
)
3638 ? FILE_NOTIFY_CHANGE_FILE_NAME
3639 : FILE_NOTIFY_CHANGE_DIR_NAME
),
3640 FILE_ACTION_ADDED
);
3644 FatNotifyReportChange( IrpContext
,
3647 ((NodeType( Fcb
) == FAT_NTC_FCB
)
3648 ? FILE_NOTIFY_CHANGE_FILE_NAME
3649 : FILE_NOTIFY_CHANGE_DIR_NAME
),
3650 FILE_ACTION_RENAMED_NEW_NAME
);
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.
3660 if (!FatIsFat32(Vcb
) &&
3661 ShortDirent
->ExtendedAttributes
!= 0) {
3663 FatRenameEAs( IrpContext
,
3665 ShortDirent
->ExtendedAttributes
,
3670 // Set our final status
3673 Status
= STATUS_SUCCESS
;
3677 DebugUnwind( FatSetRenameInfo
);
3679 ExFreePool( UnicodeBuffer
);
3681 if (UniTunneledLongName
.Buffer
!= UniTunneledLongNameBuffer
) {
3684 // Free pool if the buffer was grown on tunneling lookup
3687 ExFreePool(UniTunneledLongName
.Buffer
);
3690 FatUnpinBcb( IrpContext
, OldDirentBcb
);
3691 FatUnpinBcb( IrpContext
, TargetDirentBcb
);
3692 FatUnpinBcb( IrpContext
, NewDirentBcb
);
3693 FatUnpinBcb( IrpContext
, SecondPageBcb
);
3694 FatUnpinBcb( IrpContext
, DotDotBcb
);
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.
3703 if (_SEH2_AbnormalTermination() && InvalidateFcbOnRaise
) {
3705 Fcb
->FcbCondition
= FcbBad
;
3708 DebugTrace(-1, Dbg
, "FatSetRenameInfo -> %08lx\n", Status
);
3716 // Internal Support Routine
3720 FatSetPositionInfo (
3721 IN PIRP_CONTEXT IrpContext
,
3723 IN PFILE_OBJECT FileObject
3728 Routine Description:
3730 This routine performs the set position information for fat. It either
3731 completes the request or enqueues it off to the fsp.
3735 Irp - Supplies the irp being processed
3737 FileObject - Supplies the file object being processed
3741 NTSTATUS - The result of this operation if it completes without
3747 PFILE_POSITION_INFORMATION Buffer
;
3749 DebugTrace(+1, Dbg
, "FatSetPositionInfo...\n", 0);
3751 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
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
3759 if (FlagOn( FileObject
->Flags
, FO_NO_INTERMEDIATE_BUFFERING
)) {
3761 PDEVICE_OBJECT DeviceObject
;
3763 DeviceObject
= IoGetCurrentIrpStackLocation( Irp
)->DeviceObject
;
3765 if ((Buffer
->CurrentByteOffset
.LowPart
& DeviceObject
->AlignmentRequirement
) != 0) {
3767 DebugTrace(0, Dbg
, "Cannot set position due to aligment conflict\n", 0);
3768 DebugTrace(-1, Dbg
, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER
);
3770 return STATUS_INVALID_PARAMETER
;
3775 // The input parameter is fine so set the current byte offset and
3776 // complete the request
3779 DebugTrace(0, Dbg
, "Set the new position to %08lx\n", Buffer
->CurrentByteOffset
);
3781 FileObject
->CurrentByteOffset
= Buffer
->CurrentByteOffset
;
3783 DebugTrace(-1, Dbg
, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS
);
3785 UNREFERENCED_PARAMETER( IrpContext
);
3787 return STATUS_SUCCESS
;
3792 // Internal Support Routine
3796 FatSetAllocationInfo (
3797 IN PIRP_CONTEXT IrpContext
,
3800 IN PFILE_OBJECT FileObject
3805 Routine Description:
3807 This routine performs the set Allocation information for fat. It either
3808 completes the request or enqueues it off to the fsp.
3812 Irp - Supplies the irp being processed
3814 Fcb - Supplies the Fcb or Dcb being processed, already known not to
3817 FileObject - Supplies the FileObject being processed, already known not to
3822 NTSTATUS - The result of this operation if it completes without
3828 NTSTATUS Status
= STATUS_SUCCESS
;
3829 PFILE_ALLOCATION_INFORMATION Buffer
;
3830 ULONG NewAllocationSize
;
3832 BOOLEAN FileSizeTruncated
= FALSE
;
3833 BOOLEAN CacheMapInitialized
= FALSE
;
3834 BOOLEAN ResourceAcquired
= FALSE
;
3835 ULONG OriginalFileSize
;
3836 ULONG OriginalValidDataLength
;
3837 ULONG OriginalValidDataToDisk
;
3839 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
3841 NewAllocationSize
= Buffer
->AllocationSize
.LowPart
;
3843 DebugTrace(+1, Dbg
, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize
);
3846 // Allocation is only allowed on a file and not a directory
3849 if (NodeType(Fcb
) == FAT_NTC_DCB
) {
3851 DebugTrace(-1, Dbg
, "Cannot change allocation of a directory\n", 0);
3853 return STATUS_INVALID_DEVICE_REQUEST
;
3857 // Check that the new file allocation is legal
3860 if (!FatIsIoRangeValid( Fcb
->Vcb
, Buffer
->AllocationSize
, 0 )) {
3862 DebugTrace(-1, Dbg
, "Illegal allocation size\n", 0);
3864 return STATUS_DISK_FULL
;
3868 // If we haven't yet looked up the correct AllocationSize, do so.
3871 if (Fcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
3873 FatLookupFileAllocationSize( IrpContext
, Fcb
);
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
3882 if ((FileObject
->SectionObjectPointer
->DataSectionObject
!= NULL
) &&
3883 (FileObject
->SectionObjectPointer
->SharedCacheMap
== NULL
) &&
3884 !FlagOn(Irp
->Flags
, IRP_PAGING_IO
)) {
3886 ASSERT( !FlagOn( FileObject
->Flags
, FO_CLEANUP_COMPLETE
) );
3889 // Now initialize the cache map.
3892 CcInitializeCacheMap( FileObject
,
3893 (PCC_FILE_SIZES
)&Fcb
->Header
.AllocationSize
,
3895 &FatData
.CacheManagerCallbacks
,
3898 CacheMapInitialized
= TRUE
;
3902 // Now mark the fact that the file needs to be truncated on close
3905 Fcb
->FcbState
|= FCB_STATE_TRUNCATE_ON_CLOSE
;
3908 // Now mark that the time on the dirent needs to be updated on close.
3911 SetFlag( FileObject
->Flags
, FO_FILE_MODIFIED
);
3916 // Increase or decrease the allocation size.
3919 if (NewAllocationSize
> Fcb
->Header
.AllocationSize
.LowPart
) {
3921 FatAddFileAllocation( IrpContext
, Fcb
, FileObject
, NewAllocationSize
);
3926 // Check here if we will be decreasing file size and synchonize with
3930 if ( Fcb
->Header
.FileSize
.LowPart
> NewAllocationSize
) {
3933 // Before we actually truncate, check to see if the purge
3934 // is going to fail.
3937 if (!MmCanFileBeTruncated( FileObject
->SectionObjectPointer
,
3938 &Buffer
->AllocationSize
)) {
3940 try_return( Status
= STATUS_USER_MAPPED_FILE
);
3943 FileSizeTruncated
= TRUE
;
3945 OriginalFileSize
= Fcb
->Header
.FileSize
.LowPart
;
3946 OriginalValidDataLength
= Fcb
->Header
.ValidDataLength
.LowPart
;
3947 OriginalValidDataToDisk
= Fcb
->ValidDataToDisk
;
3949 (VOID
)ExAcquireResourceExclusiveLite( Fcb
->Header
.PagingIoResource
, TRUE
);
3950 ResourceAcquired
= TRUE
;
3952 Fcb
->Header
.FileSize
.LowPart
= NewAllocationSize
;
3955 // If we reduced the file size to less than the ValidDataLength,
3956 // adjust the VDL. Likewise ValidDataToDisk.
3959 if (Fcb
->Header
.ValidDataLength
.LowPart
> Fcb
->Header
.FileSize
.LowPart
) {
3961 Fcb
->Header
.ValidDataLength
.LowPart
= Fcb
->Header
.FileSize
.LowPart
;
3963 if (Fcb
->ValidDataToDisk
> Fcb
->Header
.FileSize
.LowPart
) {
3965 Fcb
->ValidDataToDisk
= Fcb
->Header
.FileSize
.LowPart
;
3971 // Now that File Size is down, actually do the truncate.
3974 FatTruncateFileAllocation( IrpContext
, Fcb
, NewAllocationSize
);
3977 // Now check if we needed to decrease the file size accordingly.
3980 if ( FileSizeTruncated
) {
3983 // Tell the cache manager we reduced the file size.
3984 // The call is unconditional, because MM always wants to know.
3991 CcSetFileSizes( FileObject
, (PCC_FILE_SIZES
)&Fcb
->Header
.AllocationSize
);
3994 } _SEH2_EXCEPT(FatBugCheckExceptionFilter( _SEH2_GetExceptionInformation() )) {
4000 ASSERT( FileObject
->DeleteAccess
|| FileObject
->WriteAccess
);
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
4009 // If you want a transacted filesystem, use NTFS ...
4012 FileSizeTruncated
= FALSE
;
4014 FatSetFileSizeInDirent( IrpContext
, Fcb
, NULL
);
4017 // Report that we just reduced the file size.
4020 FatNotifyReportChange( IrpContext
,
4023 FILE_NOTIFY_CHANGE_SIZE
,
4024 FILE_ACTION_MODIFIED
);
4032 if ( _SEH2_AbnormalTermination() && FileSizeTruncated
) {
4034 Fcb
->Header
.FileSize
.LowPart
= OriginalFileSize
;
4035 Fcb
->Header
.ValidDataLength
.LowPart
= OriginalValidDataLength
;
4036 Fcb
->ValidDataToDisk
= OriginalValidDataToDisk
;
4039 // Make sure Cc knows the right filesize.
4042 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
) {
4044 *CcGetFileSizePointer(FileObject
) = Fcb
->Header
.FileSize
;
4047 ASSERT( Fcb
->Header
.FileSize
.LowPart
<= Fcb
->Header
.AllocationSize
.LowPart
);
4050 if (CacheMapInitialized
) {
4052 CcUninitializeCacheMap( FileObject
, NULL
, NULL
);
4055 if (ResourceAcquired
) {
4057 ExReleaseResourceLite( Fcb
->Header
.PagingIoResource
);
4063 DebugTrace(-1, Dbg
, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS
);
4070 // Internal Support Routine
4074 FatSetEndOfFileInfo (
4075 IN PIRP_CONTEXT IrpContext
,
4077 IN PFILE_OBJECT FileObject
,
4084 Routine Description:
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.
4091 Irp - Supplies the irp being processed
4093 FileObject - Supplies the file object being processed
4095 Vcb - Supplies the Vcb being processed
4097 Fcb - Supplies the Fcb or Dcb being processed, already known not to
4102 NTSTATUS - The result of this operation if it completes without
4110 PFILE_END_OF_FILE_INFORMATION Buffer
;
4113 ULONG InitialFileSize
;
4114 ULONG InitialValidDataLength
;
4115 ULONG InitialValidDataToDisk
;
4117 BOOLEAN CacheMapInitialized
= FALSE
;
4118 BOOLEAN UnwindFileSizes
= FALSE
;
4119 BOOLEAN ResourceAcquired
= FALSE
;
4121 DebugTrace(+1, Dbg
, "FatSetEndOfFileInfo...\n", 0);
4123 Buffer
= Irp
->AssociatedIrp
.SystemBuffer
;
4128 // File Size changes are only allowed on a file and not a directory
4131 if (NodeType(Fcb
) != FAT_NTC_FCB
) {
4133 DebugTrace(0, Dbg
, "Cannot change size of a directory\n", 0);
4135 try_return( Status
= STATUS_INVALID_DEVICE_REQUEST
);
4139 // Check that the new file size is legal
4142 if (!FatIsIoRangeValid( Fcb
->Vcb
, Buffer
->EndOfFile
, 0 )) {
4144 DebugTrace(0, Dbg
, "Illegal allocation size\n", 0);
4146 try_return( Status
= STATUS_DISK_FULL
);
4149 NewFileSize
= Buffer
->EndOfFile
.LowPart
;
4152 // If we haven't yet looked up the correct AllocationSize, do so.
4155 if (Fcb
->Header
.AllocationSize
.QuadPart
== FCB_LOOKUP_ALLOCATIONSIZE_HINT
) {
4157 FatLookupFileAllocationSize( IrpContext
, Fcb
);
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
4166 if ((FileObject
->SectionObjectPointer
->DataSectionObject
!= NULL
) &&
4167 (FileObject
->SectionObjectPointer
->SharedCacheMap
== NULL
) &&
4168 !FlagOn(Irp
->Flags
, IRP_PAGING_IO
)) {
4170 if (FlagOn( FileObject
->Flags
, FO_CLEANUP_COMPLETE
)) {
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
4184 FatRaiseStatus( IrpContext
, STATUS_FILE_CLOSED
);
4188 // Now initialize the cache map.
4191 CcInitializeCacheMap( FileObject
,
4192 (PCC_FILE_SIZES
)&Fcb
->Header
.AllocationSize
,
4194 &FatData
.CacheManagerCallbacks
,
4197 CacheMapInitialized
= TRUE
;
4201 // Do a special case here for the lazy write of file sizes.
4204 if (IoGetCurrentIrpStackLocation(Irp
)->Parameters
.SetFile
.AdvanceOnly
) {
4207 // Only attempt this if the file hasn't been "deleted on close" and
4208 // this is a good FCB.
4211 if (!IsFileDeleted( IrpContext
, Fcb
) && (Fcb
->FcbCondition
== FcbGood
)) {
4217 // Never have the dirent filesize larger than the fcb filesize
4220 if (NewFileSize
>= Fcb
->Header
.FileSize
.LowPart
) {
4222 NewFileSize
= Fcb
->Header
.FileSize
.LowPart
;
4226 // Make sure we don't set anything higher than the alloc size.
4229 ASSERT( NewFileSize
<= Fcb
->Header
.AllocationSize
.LowPart
);
4232 // Only advance the file size, never reduce it with this call
4235 FatGetDirentFromFcbOrDcb( IrpContext
,
4240 ASSERT( Dirent
&& DirentBcb
);
4244 if ( NewFileSize
> Dirent
->FileSize
) {
4246 Dirent
->FileSize
= NewFileSize
;
4248 FatSetDirtyBcb( IrpContext
, DirentBcb
, Fcb
->Vcb
, TRUE
);
4251 // Report that we just changed the file size.
4254 FatNotifyReportChange( IrpContext
,
4257 FILE_NOTIFY_CHANGE_SIZE
,
4258 FILE_ACTION_MODIFIED
);
4263 FatUnpinBcb( IrpContext
, DirentBcb
);
4268 DebugTrace(0, Dbg
, "Cannot set size on deleted file.\n", 0);
4271 try_return( Status
= STATUS_SUCCESS
);
4275 // Check if the new file size is greater than the current
4276 // allocation size. If it is then we need to increase the
4280 if ( NewFileSize
> Fcb
->Header
.AllocationSize
.LowPart
) {
4283 // Change the file allocation
4286 FatAddFileAllocation( IrpContext
, Fcb
, FileObject
, NewFileSize
);
4290 // At this point we have enough allocation for the file.
4291 // So check if we are really changing the file size
4294 if (Fcb
->Header
.FileSize
.LowPart
!= NewFileSize
) {
4296 if ( NewFileSize
< Fcb
->Header
.FileSize
.LowPart
) {
4299 // Before we actually truncate, check to see if the purge
4300 // is going to fail.
4303 if (!MmCanFileBeTruncated( FileObject
->SectionObjectPointer
,
4304 &Buffer
->EndOfFile
)) {
4306 try_return( Status
= STATUS_USER_MAPPED_FILE
);
4310 // This call is unconditional, because MM always wants to know.
4311 // Also serialize here with paging io since we are truncating
4316 ExAcquireResourceExclusiveLite( Fcb
->Header
.PagingIoResource
, TRUE
);
4320 // Set the new file size
4323 InitialFileSize
= Fcb
->Header
.FileSize
.LowPart
;
4324 InitialValidDataLength
= Fcb
->Header
.ValidDataLength
.LowPart
;
4325 InitialValidDataToDisk
= Fcb
->ValidDataToDisk
;
4326 UnwindFileSizes
= TRUE
;
4328 Fcb
->Header
.FileSize
.LowPart
= NewFileSize
;
4331 // If we reduced the file size to less than the ValidDataLength,
4332 // adjust the VDL. Likewise ValidDataToDisk.
4335 if (Fcb
->Header
.ValidDataLength
.LowPart
> NewFileSize
) {
4337 Fcb
->Header
.ValidDataLength
.LowPart
= NewFileSize
;
4340 if (Fcb
->ValidDataToDisk
> NewFileSize
) {
4342 Fcb
->ValidDataToDisk
= NewFileSize
;
4345 DebugTrace(0, Dbg
, "New file size is 0x%08lx.\n", NewFileSize
);
4348 // We must now update the cache mapping (benign if not cached).
4351 CcSetFileSizes( FileObject
,
4352 (PCC_FILE_SIZES
)&Fcb
->Header
.AllocationSize
);
4354 FatSetFileSizeInDirent( IrpContext
, Fcb
, NULL
);
4357 // Report that we just changed the file size.
4360 FatNotifyReportChange( IrpContext
,
4363 FILE_NOTIFY_CHANGE_SIZE
,
4364 FILE_ACTION_MODIFIED
);
4367 // Mark the fact that the file will need to checked for
4368 // truncation on cleanup.
4371 SetFlag( Fcb
->FcbState
, FCB_STATE_TRUNCATE_ON_CLOSE
);
4375 // Set this handle as having modified the file
4378 FileObject
->Flags
|= FO_FILE_MODIFIED
;
4381 // Set our return status to success
4384 Status
= STATUS_SUCCESS
;
4388 FatUnpinRepinnedBcbs( IrpContext
);
4392 DebugUnwind( FatSetEndOfFileInfo
);
4394 if (_SEH2_AbnormalTermination() && UnwindFileSizes
) {
4396 Fcb
->Header
.FileSize
.LowPart
= InitialFileSize
;
4397 Fcb
->Header
.ValidDataLength
.LowPart
= InitialValidDataLength
;
4398 Fcb
->ValidDataToDisk
= InitialValidDataToDisk
;
4400 if (FileObject
->SectionObjectPointer
->SharedCacheMap
!= NULL
) {
4402 *CcGetFileSizePointer(FileObject
) = Fcb
->Header
.FileSize
;
4406 if (CacheMapInitialized
) {
4408 CcUninitializeCacheMap( FileObject
, NULL
, NULL
);
4411 if ( ResourceAcquired
) {
4413 ExReleaseResourceLite( Fcb
->Header
.PagingIoResource
);
4416 DebugTrace(-1, Dbg
, "FatSetEndOfFileInfo -> %08lx\n", Status
);
4424 // Internal Support Routine
4429 IN PIRP_CONTEXT IrpContext
,
4432 IN ULONG DirentOffset
,
4434 IN PUNICODE_STRING Lfn
4441 // We can do the replace by removing the other Fcb(s) from
4442 // the prefix table.
4445 for (Links
= TargetDcb
->Specific
.Dcb
.ParentDcbQueue
.Flink
;
4446 Links
!= &TargetDcb
->Specific
.Dcb
.ParentDcbQueue
;
4447 Links
= Links
->Flink
) {
4449 Fcb
= CONTAINING_RECORD( Links
, FCB
, ParentDcbLinks
);
4451 if (FlagOn(Fcb
->FcbState
, FCB_STATE_NAMES_IN_SPLAY_TREE
) &&
4452 (Fcb
->DirentOffsetWithinDirectory
== DirentOffset
)) {
4454 ASSERT( NodeType(Fcb
) == FAT_NTC_FCB
);
4455 ASSERT( Fcb
->LfnOffsetWithinDirectory
== LfnOffset
);
4457 if ( Fcb
->UncleanCount
!= 0 ) {
4463 PERESOURCE Resource
;
4466 // Make this fcb "appear" deleted, synchronizing with
4470 FatRemoveNames( IrpContext
, Fcb
);
4472 Resource
= Fcb
->Header
.PagingIoResource
;
4474 (VOID
)ExAcquireResourceExclusiveLite( Resource
, TRUE
);
4476 SetFlag(Fcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
);
4478 Fcb
->ValidDataToDisk
= 0;
4479 Fcb
->Header
.FileSize
.QuadPart
=
4480 Fcb
->Header
.ValidDataLength
.QuadPart
= 0;
4482 Fcb
->FirstClusterOfFile
= 0;
4484 ExReleaseResourceLite( Resource
);
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
4496 Fcb
= FatCreateFcb( IrpContext
,
4506 Fcb
->Header
.FileSize
.LowPart
= 0;
4510 FatTruncateFileAllocation( IrpContext
, Fcb
, 0 );
4512 FatDeleteDirent( IrpContext
, Fcb
, NULL
, TRUE
);
4516 FatDeleteFcb( IrpContext
, Fcb
);
4521 // Internal Support Routine
4526 IN PIRP_CONTEXT IrpContext
,
4528 IN USHORT ExtendedAttributes
,
4529 IN POEM_STRING OldOemName
4532 BOOLEAN LockedEaFcb
= FALSE
;
4536 EA_RANGE EaSetRange
;
4537 PEA_SET_HEADER EaSetHeader
;
4541 RtlZeroMemory( &EaSetRange
, sizeof( EA_RANGE
));
4548 // Use a try-except to catch any errors.
4555 // Try to get the Ea file object. Return FALSE on failure.
4558 FatGetEaFile( IrpContext
,
4568 // If we didn't get the file because it doesn't exist, then the
4569 // disk is corrupted. We do nothing here.
4572 if (Vcb
->VirtualEaFile
!= NULL
) {
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.
4580 FatReadEaSet( IrpContext
,
4587 EaSetHeader
= (PEA_SET_HEADER
) EaSetRange
.Data
;
4590 // We now have the Ea set header for this file. We simply
4591 // overwrite the owning file name.
4594 RtlZeroMemory( EaSetHeader
->OwnerFileName
, 14 );
4596 RtlCopyMemory( EaSetHeader
->OwnerFileName
,
4597 Fcb
->ShortName
.Name
.Oem
.Buffer
,
4598 Fcb
->ShortName
.Name
.Oem
.Length
);
4600 FatMarkEaRangeDirty( IrpContext
, Vcb
->VirtualEaFile
, &EaSetRange
);
4601 FatUnpinEaRange( IrpContext
, &EaSetRange
);
4603 CcFlushCache( Vcb
->VirtualEaFile
->SectionObjectPointer
, NULL
, 0, NULL
);
4606 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
4609 // We catch all exceptions that Fat catches, but don't do
4610 // anything with them.
4617 // Unpin the EaDirent and the EaSetHeader if pinned.
4620 FatUnpinBcb( IrpContext
, EaBcb
);
4621 FatUnpinEaRange( IrpContext
, &EaSetRange
);
4624 // Release the Fcb for the Ea file if locked.
4629 FatReleaseFcb( IrpContext
, Vcb
->EaFcb
);