text: /*++
  
  Copyright (c) 1989-2000 Microsoft Corporation
  
  Module Name:
  
      FileInfo.c
  
  Abstract:
  
      This module implements the File Information routines for Fat called by
      the dispatch driver.
  
  
  --*/
  
  #include "FatProcs.h"
  
  //
  //  The Bug check file id for this module
  //
  
  #define BugCheckFileId                   (FAT_BUG_CHECK_FILEINFO)
  
  //
  //  The local debug trace level
  //
  
  #define Dbg                              (DEBUG_TRACE_FILEINFO)
  
  VOID
  FatQueryBasicInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN PFILE_OBJECT FileObject,
      IN OUT PFILE_BASIC_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatQueryStandardInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_STANDARD_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  VOID
  FatQueryInternalInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_INTERNAL_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  VOID
  FatQueryEaInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_EA_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  VOID
  FatQueryPositionInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFILE_OBJECT FileObject,
      IN OUT PFILE_POSITION_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatQueryNameInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN PCCB Ccb,
      IN BOOLEAN Normalized,
      IN OUT PFILE_NAME_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  VOID
  FatQueryShortNameInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_NAME_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatQueryNetworkInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN PFILE_OBJECT FileObject,
      IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
      IN OUT PLONG Length
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetBasicInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFCB Fcb,
      IN PCCB Ccb
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetDispositionInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject,
      IN PFCB Fcb
      );
  
  NTSTATUS
  FatSetRenameInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PVCB Vcb,
      IN PFCB Fcb,
      IN PCCB Ccb
      );
  
  NTSTATUS
  FatSetPositionInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetAllocationInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFCB Fcb,
      IN PFILE_OBJECT FileObject
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetEndOfFileInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject,
      IN PVCB Vcb,
      IN PFCB Fcb
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetValidDataLengthInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject,
      IN PFCB Fcb,
      IN PCCB Ccb
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatDeleteFile (
      IN PIRP_CONTEXT IrpContext,
      IN PDCB TargetDcb,
      IN ULONG LfnOffset,
      IN ULONG DirentOffset,
      IN PDIRENT Dirent,
      IN PUNICODE_STRING Lfn
      );
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatRenameEAs (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN USHORT ExtendedAttributes,
      IN POEM_STRING OldOemName
      );
  
  #ifdef ALLOC_PRAGMA
  #pragma alloc_text(PAGE, FatCommonQueryInformation)
  #pragma alloc_text(PAGE, FatCommonSetInformation)
  #pragma alloc_text(PAGE, FatFsdQueryInformation)
  #pragma alloc_text(PAGE, FatFsdSetInformation)
  #pragma alloc_text(PAGE, FatQueryBasicInfo)
  #pragma alloc_text(PAGE, FatQueryEaInfo)
  #pragma alloc_text(PAGE, FatQueryInternalInfo)
  #pragma alloc_text(PAGE, FatQueryNameInfo)
  #pragma alloc_text(PAGE, FatQueryNetworkInfo)
  #pragma alloc_text(PAGE, FatQueryShortNameInfo)
  #pragma alloc_text(PAGE, FatQueryPositionInfo)
  #pragma alloc_text(PAGE, FatQueryStandardInfo)
  #pragma alloc_text(PAGE, FatSetAllocationInfo)
  #pragma alloc_text(PAGE, FatSetBasicInfo)
  #pragma alloc_text(PAGE, FatSetDispositionInfo)
  #pragma alloc_text(PAGE, FatSetEndOfFileInfo)
  #pragma alloc_text(PAGE, FatSetValidDataLengthInfo)
  #pragma alloc_text(PAGE, FatSetPositionInfo)
  #pragma alloc_text(PAGE, FatSetRenameInfo)
  #pragma alloc_text(PAGE, FatDeleteFile)
  #pragma alloc_text(PAGE, FatRenameEAs)
  #endif
  
  
  _Function_class_(IRP_MJ_QUERY_INFORMATION)
  _Function_class_(DRIVER_DISPATCH)
  NTSTATUS
  FatFsdQueryInformation (
      _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
      _Inout_ PIRP Irp
      )
  
  /*++
  
  Routine Description:
  
      This routine implements the Fsd part of the NtQueryInformationFile API
      call.
  
  Arguments:
  
      VolumeDeviceObject - Supplies the volume device object where the file
          being queried exists.
  
      Irp - Supplies the Irp being processed.
  
  Return Value:
  
      NTSTATUS - The FSD status for the Irp.
  
  --*/
  
  {
      NTSTATUS Status;
      PIRP_CONTEXT IrpContext = NULL;
  
      BOOLEAN TopLevel;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatFsdQueryInformation\n", 0);
  
      //
      //  Call the common query routine, with blocking allowed if synchronous
      //
  
      FsRtlEnterFileSystem();
  
      TopLevel = FatIsIrpTopLevel( Irp );
  
      try {
  
          IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  
          Status = FatCommonQueryInformation( IrpContext, Irp );
  
      } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  
          //
          //  We had some trouble trying to perform the requested
          //  operation, so we'll abort the I/O request with
          //  the error status that we get back from the
          //  execption code
          //
  
          Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
      }
  
      if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  
      FsRtlExitFileSystem();
  
      //
      //  And return to our caller
      //
  
      DebugTrace(-1, Dbg, "FatFsdQueryInformation -> %08lx\n", Status);
  
      UNREFERENCED_PARAMETER( VolumeDeviceObject );
  
      return Status;
  }
  
  
  _Function_class_(IRP_MJ_SET_INFORMATION)
  _Function_class_(DRIVER_DISPATCH)
  NTSTATUS
  FatFsdSetInformation (
      _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
      _Inout_ PIRP Irp
      )
  
  /*++
  
  Routine Description:
  
      This routine implements the FSD part of the NtSetInformationFile API
      call.
  
  Arguments:
  
      VolumeDeviceObject - Supplies the volume device object where the file
          being set exists.
  
      Irp - Supplies the Irp being processed.
  
  Return Value:
  
      NTSTATUS - The FSD status for the Irp.
  
  --*/
  
  {
      NTSTATUS Status;
      PIRP_CONTEXT IrpContext = NULL;
  
      BOOLEAN TopLevel;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatFsdSetInformation\n", 0);
  
      //
      //  Call the common set routine, with blocking allowed if synchronous
      //
  
      FsRtlEnterFileSystem();
  
      TopLevel = FatIsIrpTopLevel( Irp );
  
      try {
  
          IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
  
          Status = FatCommonSetInformation( IrpContext, Irp );
  
      } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  
          //
          //  We had some trouble trying to perform the requested
          //  operation, so we'll abort the I/O request with
          //  the error status that we get back from the
          //  execption code
          //
  
          Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
      }
  
      if (TopLevel) { IoSetTopLevelIrp( NULL ); }
  
      FsRtlExitFileSystem();
  
      //
      //  And return to our caller
      //
  
      DebugTrace(-1, Dbg, "FatFsdSetInformation -> %08lx\n", Status);
  
      UNREFERENCED_PARAMETER( VolumeDeviceObject );
  
      return Status;
  }
  
  
  _Requires_lock_held_(_Global_critical_region_)    
  NTSTATUS
  FatCommonQueryInformation (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp
      )
  
  /*++
  
  Routine Description:
  
      This is the common routine for querying file information called by both
      the fsd and fsp threads.
  
  Arguments:
  
      Irp - Supplies the Irp being processed
  
  Return Value:
  
      NTSTATUS - The return status for the operation
  
  --*/
  
  {
      NTSTATUS Status;
  
      PIO_STACK_LOCATION IrpSp;
  
      PFILE_OBJECT FileObject;
  
      LONG Length;
      FILE_INFORMATION_CLASS FileInformationClass;
      PVOID Buffer;
  
      TYPE_OF_OPEN TypeOfOpen;
      PVCB Vcb;
      PFCB Fcb;
      PCCB Ccb;
  
      BOOLEAN FcbAcquired = FALSE;
      BOOLEAN VcbAcquired = FALSE;
  
      PFILE_ALL_INFORMATION AllInfo;
  
      PAGED_CODE();
  
      //
      //  Get the current stack location
      //
  
      IrpSp = IoGetCurrentIrpStackLocation( Irp );
  
      FileObject = IrpSp->FileObject;
  
      DebugTrace(+1, Dbg, "FatCommonQueryInformation...\n", 0);
      DebugTrace( 0, Dbg, "Irp                    = %08lx\n", Irp);
      DebugTrace( 0, Dbg, "->Length               = %08lx\n", IrpSp->Parameters.QueryFile.Length);
      DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryFile.FileInformationClass);
      DebugTrace( 0, Dbg, "->Buffer               = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
  
      //
      //  Reference our input parameters to make things easier
      //
  
      Length = (LONG)IrpSp->Parameters.QueryFile.Length;
      FileInformationClass = IrpSp->Parameters.QueryFile.FileInformationClass;
      Buffer = Irp->AssociatedIrp.SystemBuffer;
  
      //
      //  Decode the file object
      //
  
      TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
  
      Status = STATUS_SUCCESS;
  
      try {
  
          //
          //  Case on the type of open we're dealing with
          //
  
          switch (TypeOfOpen) {
  
          case UserVolumeOpen:
  
              //
              //  We cannot query the user volume open.
              //
  
              Status = STATUS_INVALID_PARAMETER;
              break;
  
          case UserFileOpen:
          case UserDirectoryOpen:
          case DirectoryFile:
  
              //
              //  NameInfo requires synchronization with deletion in order to perform
              //  the full filename query.  A lighter-weight way to do this would be per
              //  directory as the full name is built up and since the multiple Fcb
              //  lockorder is bottom up, this is conceivable.  At this time, though,
              //  this change is safer.
              //
  
              if (FileInformationClass == FileNameInformation ||
                  FileInformationClass == FileAllInformation) {
  
                  if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
  
                      DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
  
                      Status = FatFsdPostRequest( IrpContext, Irp );
                      IrpContext = NULL;
                      Irp = NULL;
  
                      try_return( Status );
                  }
  
                  VcbAcquired = TRUE;
              }
  
              //
              //  Acquire shared access to the fcb, except for a paging file
              //  in order to avoid deadlocks with Mm.
              //
              //  The "removable" check was added specifically for ReadyBoost,
              //  which opens its cache file on a removable device as a paging file and
              //  relies on the file system to validate its mapping information after a
              //  power transition.
              //
              
              if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) || 
                  FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
  
                  if (!FatAcquireSharedFcb( IrpContext, Fcb )) {
  
                      DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
  
                      Status = FatFsdPostRequest( IrpContext, Irp );
                      IrpContext = NULL;
                      Irp = NULL;
  
                      try_return( Status );
                  }
  
                  FcbAcquired = TRUE;
              }
  
              //
              //  Make sure the Fcb is in a usable condition.  This
              //  will raise an error condition if the fcb is unusable
              //
  
              FatVerifyFcb( IrpContext, Fcb );
  
              //
              //  Based on the information class we'll do different
              //  actions.  Each of hte procedures that we're calling fills
              //  up the output buffer, if possible.  They will raise the
              //  status STATUS_BUFFER_OVERFLOW for an insufficient buffer.
              //  This is considered a somewhat unusual case and is handled
              //  more cleanly with the exception mechanism rather than
              //  testing a return status value for each call.
              //
  
              switch (FileInformationClass) {
  
              case FileAllInformation:
  
                  //
                  //  For the all information class we'll typecast a local
                  //  pointer to the output buffer and then call the
                  //  individual routines to fill in the buffer.
                  //
  
                  AllInfo = Buffer;
                  Length -= (sizeof(FILE_ACCESS_INFORMATION)
                             + sizeof(FILE_MODE_INFORMATION)
                             + sizeof(FILE_ALIGNMENT_INFORMATION));
  
                  FatQueryBasicInfo( IrpContext, Fcb, FileObject, &AllInfo->BasicInformation, &Length );
                  FatQueryStandardInfo( IrpContext, Fcb, &AllInfo->StandardInformation, &Length );
                  FatQueryInternalInfo( IrpContext, Fcb, &AllInfo->InternalInformation, &Length );
                  FatQueryEaInfo( IrpContext, Fcb, &AllInfo->EaInformation, &Length );
                  FatQueryPositionInfo( IrpContext, FileObject, &AllInfo->PositionInformation, &Length );
                  FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, &AllInfo->NameInformation, &Length );
  
                  break;
  
              case FileBasicInformation:
  
                  FatQueryBasicInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
                  break;
  
              case FileStandardInformation:
  
                  FatQueryStandardInfo( IrpContext, Fcb, Buffer, &Length );
                  break;
  
              case FileInternalInformation:
  
                  FatQueryInternalInfo( IrpContext, Fcb, Buffer, &Length );
                  break;
  
              case FileEaInformation:
  
                  FatQueryEaInfo( IrpContext, Fcb, Buffer, &Length );
                  break;
  
              case FilePositionInformation:
  
                  FatQueryPositionInfo( IrpContext, FileObject, Buffer, &Length );
                  break;
  
              case FileNameInformation:
  
                  FatQueryNameInfo( IrpContext, Fcb, Ccb, FALSE, Buffer, &Length );
                  break;
  
              case FileNormalizedNameInformation:
  
                  FatQueryNameInfo( IrpContext, Fcb, Ccb, TRUE, Buffer, &Length );
                  break;
  
              case FileAlternateNameInformation:
  
                  FatQueryShortNameInfo( IrpContext, Fcb, Buffer, &Length );
                  break;
  
              case FileNetworkOpenInformation:
  
                  FatQueryNetworkInfo( IrpContext, Fcb, FileObject, Buffer, &Length );
                  break;
  
              default:
  
                  Status = STATUS_INVALID_PARAMETER;
                  break;
              }
  
              break;
  
          default:
  
              KdPrintEx((DPFLTR_FASTFAT_ID,
                         DPFLTR_INFO_LEVEL,
                         "FATQueryFile, Illegal TypeOfOpen = %08lx\n",
                         TypeOfOpen));
  
              Status = STATUS_INVALID_PARAMETER;
              break;
          }
  
          //
          //  If we overflowed the buffer, set the length to 0 and change the
          //  status to STATUS_BUFFER_OVERFLOW.
          //
  
          if ( Length < 0 ) {
  
              Status = STATUS_BUFFER_OVERFLOW;
  
              Length = 0;
          }
  
          //
          //  Set the information field to the number of bytes actually filled in
          //  and then complete the request
          //
  
          Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - Length;
  
      try_exit: NOTHING;
      } finally {
  
          DebugUnwind( FatCommonQueryInformation );
  
          if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
          if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
  
          if (!AbnormalTermination()) {
  
              FatCompleteRequest( IrpContext, Irp, Status );
          }
  
          DebugTrace(-1, Dbg, "FatCommonQueryInformation -> %08lx\n", Status);
      }
  
      return Status;
  }
  
  
  _Requires_lock_held_(_Global_critical_region_)    
  NTSTATUS
  FatCommonSetInformation (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp
      )
  
  /*++
  
  Routine Description:
  
      This is the common routine for setting file information called by both
      the fsd and fsp threads.
  
  Arguments:
  
      Irp - Supplies the Irp being processed
  
  Return Value:
  
      NTSTATUS - The return status for the operation
  
  --*/
  
  {
      NTSTATUS Status = STATUS_SUCCESS;
  
      PIO_STACK_LOCATION IrpSp;
  
      PFILE_OBJECT FileObject;
      FILE_INFORMATION_CLASS FileInformationClass;
  
      TYPE_OF_OPEN TypeOfOpen;
      PVCB Vcb;
      PFCB Fcb;
      PCCB Ccb;
  
      BOOLEAN VcbAcquired = FALSE;
      BOOLEAN FcbAcquired = FALSE;
  
      PAGED_CODE();
  
      //
      //  Get the current stack location
      //
  
      IrpSp = IoGetCurrentIrpStackLocation( Irp );
  
      DebugTrace(+1, Dbg, "FatCommonSetInformation...\n", 0);
      DebugTrace( 0, Dbg, "Irp                    = %08lx\n", Irp);
      DebugTrace( 0, Dbg, "->Length               = %08lx\n", IrpSp->Parameters.SetFile.Length);
      DebugTrace( 0, Dbg, "->FileInformationClass = %08lx\n", IrpSp->Parameters.SetFile.FileInformationClass);
      DebugTrace( 0, Dbg, "->FileObject           = %08lx\n", IrpSp->Parameters.SetFile.FileObject);
      DebugTrace( 0, Dbg, "->ReplaceIfExists      = %08lx\n", IrpSp->Parameters.SetFile.ReplaceIfExists);
      DebugTrace( 0, Dbg, "->Buffer               = %08lx\n", Irp->AssociatedIrp.SystemBuffer);
  
      //
      //  Reference our input parameters to make things easier
      //
  
      FileInformationClass = IrpSp->Parameters.SetFile.FileInformationClass;
      FileObject = IrpSp->FileObject;
  
      //
      //  Decode the file object
      //
  
      TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
  
      try {
  
          //
          //  Case on the type of open we're dealing with
          //
  
          switch (TypeOfOpen) {
  
          case UserVolumeOpen:
  
              //
              //  We cannot query the user volume open.
              //
  
              try_return( Status = STATUS_INVALID_PARAMETER );
              break;
  
          case UserFileOpen:
  
              if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) &&
                  ((FileInformationClass == FileEndOfFileInformation) ||
                   (FileInformationClass == FileAllocationInformation) ||
                   (FileInformationClass == FileValidDataLengthInformation))) {
  
                  //
                  //  We check whether we can proceed
                  //  based on the state of the file oplocks.
                  //
  
                  Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
                                             Irp,
                                             IrpContext,
                                             NULL,
                                             NULL );
  
                  if (Status != STATUS_SUCCESS) {
  
                      try_return( Status );
                  }
  
                  //
                  //  Set the flag indicating if Fast I/O is possible
                  //
  
                  Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
              }
              break;
  
          case UserDirectoryOpen:
  
              break;
  
          default:
  
              try_return( Status = STATUS_INVALID_PARAMETER );
          }
  
          //
          //  We can only do a set on a nonroot dcb, so we do the test
          //  and then fall through to the user file open code.
          //
  
          if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
  
              if (FileInformationClass == FileDispositionInformation) {
  
                  try_return( Status = STATUS_CANNOT_DELETE );
              }
  
              try_return( Status = STATUS_INVALID_PARAMETER );
          }
  
          //
          //  In the following two cases, we cannot have creates occuring
          //  while we are here, so acquire the volume exclusive.
          //
  
          if ((FileInformationClass == FileDispositionInformation) ||
              (FileInformationClass == FileRenameInformation)) {
  
              if (!FatAcquireExclusiveVcb( IrpContext, Vcb )) {
  
                  DebugTrace(0, Dbg, "Cannot acquire Vcb\n", 0);
  
                  Status = FatFsdPostRequest( IrpContext, Irp );
                  Irp = NULL;
                  IrpContext = NULL;
  
                  try_return( Status );
              }
  
              VcbAcquired = TRUE;
  
              //
              //  Make sure we haven't been called recursively by a filter inside an existing
              //  create request.
              //
  
              if (FlagOn( Vcb->VcbState, VCB_STATE_FLAG_CREATE_IN_PROGRESS)) {
                  
  #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
                  FatBugCheck( 0, 0, 0);
              }
          }
  
          //
          //  Acquire exclusive access to the Fcb,  We use exclusive
          //  because it is probable that one of the subroutines
          //  that we call will need to monkey with file allocation,
          //  create/delete extra fcbs.  So we're willing to pay the
          //  cost of exclusive Fcb access.
          //
          //  Note that we do not acquire the resource for paging file
          //  operations in order to avoid deadlock with Mm.
          //
          //  The "removable" check was added specifically for ReadyBoost,
          //  which opens its cache file on a removable device as a paging file and
          //  relies on the file system to validate its mapping information after a
          //  power transition.
          //
  
          if (!FlagOn( Fcb->FcbState, FCB_STATE_PAGING_FILE ) || 
              FlagOn(Fcb->Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA)) {
  
              if (!FatAcquireExclusiveFcb( IrpContext, Fcb )) {
  
                  DebugTrace(0, Dbg, "Cannot acquire Fcb\n", 0);
  
                  Status = FatFsdPostRequest( IrpContext, Irp );
                  Irp = NULL;
                  IrpContext = NULL;
  
                  try_return( Status );
              }
  
              FcbAcquired = TRUE;
          }
  
          Status = STATUS_SUCCESS;
  
          //
          //  Make sure the Fcb is in a usable condition.  This
          //  will raise an error condition if the fcb is unusable
          //
  
          FatVerifyFcb( IrpContext, Fcb );
  
          //
          //  Now that we've acquired the file, do an oplock check if the operation
          //  so warrants.
          //
  
          if (FatIsFileOplockable( Fcb )&&
              ((FileInformationClass == FileRenameInformation) ||
               ((FileInformationClass == FileDispositionInformation) &&
                ((PFILE_DISPOSITION_INFORMATION) Irp->AssociatedIrp.SystemBuffer)->DeleteFile))) {
  
              Status = FsRtlCheckOplock( FatGetFcbOplock(Fcb),
                                         Irp,
                                         IrpContext,
                                         FatOplockComplete,
                                         NULL );
  
              //
              //  Set the flag indicating if Fast I/O is possible
              //
  
              Fcb->Header.IsFastIoPossible = FatIsFastIoPossible( Fcb );
  
              //
              //  If STATUS_PENDING is returned it means the oplock
              //  package has the Irp.  Don't complete the request here.
              //
  
              if (Status == STATUS_PENDING) {
  
                  Irp = NULL;
                  IrpContext = NULL;
              }
                  
              if (!NT_SUCCESS( Status ) ||
                  (Status == STATUS_PENDING)) {
  
                  try_return( Status );
              }
          }
          
          //
          //  Based on the information class we'll do different
          //  actions.  Each of the procedures that we're calling will either
          //  complete the request of send the request off to the fsp
          //  to do the work.
          //
  
          switch (FileInformationClass) {
  
          case FileBasicInformation:
  
              Status = FatSetBasicInfo( IrpContext, Irp, Fcb, Ccb );
              break;
  
          case FileDispositionInformation:
  
              //
              //  If this is on deferred flush media, we have to be able to wait.
              //
  
              if ( FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DEFERRED_FLUSH) &&
                   !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {
  
                  Status = FatFsdPostRequest( IrpContext, Irp );
                  Irp = NULL;
                  IrpContext = NULL;
  
              } else {
  
                  Status = FatSetDispositionInfo( IrpContext, Irp, FileObject, Fcb );
              }
  
              break;
  
          case FileRenameInformation:
  
              //
              //  We proceed with this operation only if we can wait
              //
  
              if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
  
                  Status = FatFsdPostRequest( IrpContext, Irp );
                  Irp = NULL;
                  IrpContext = NULL;
  
              } else {
  
                  Status = FatSetRenameInfo( IrpContext, Irp, Vcb, Fcb, Ccb );
  
                  //
                  //  If STATUS_PENDING is returned it means the oplock
                  //  package has the Irp.  Don't complete the request here.
                  //
  
                  if (Status == STATUS_PENDING) {
                      Irp = NULL;
                      IrpContext = NULL;
                  }
              }
  
              break;
  
          case FilePositionInformation:
  
              Status = FatSetPositionInfo( IrpContext, Irp, FileObject );
              break;
  
          case FileLinkInformation:
  
              Status = STATUS_INVALID_DEVICE_REQUEST;
              break;
  
          case FileAllocationInformation:
  
              Status = FatSetAllocationInfo( IrpContext, Irp, Fcb, FileObject );
              break;
  
          case FileEndOfFileInformation:
  
              Status = FatSetEndOfFileInfo( IrpContext, Irp, FileObject, Vcb, Fcb );
              break;
  
          case FileValidDataLengthInformation:
  
              Status = FatSetValidDataLengthInfo( IrpContext, Irp, FileObject, Fcb, Ccb );
              break;
  
          default:
  
              Status = STATUS_INVALID_PARAMETER;
              break;
          }
  
          if ( IrpContext != NULL ) {
  
              FatUnpinRepinnedBcbs( IrpContext );
          }
  
      try_exit: NOTHING;
      } finally {
  
          DebugUnwind( FatCommonSetInformation );
  
          if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
          
          if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
  
          if (!AbnormalTermination()) {
  
              FatCompleteRequest( IrpContext, Irp, Status );
          }
  
          DebugTrace(-1, Dbg, "FatCommonSetInformation -> %08lx\n", Status);
      }
  
      return Status;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  VOID
  FatQueryBasicInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN PFILE_OBJECT FileObject,
      IN OUT PFILE_BASIC_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
   Description:
  
      This routine performs the query basic information function for fat.
  
  Arguments:
  
      Fcb - Supplies the Fcb being queried, it has been verified
  
      FileObject - Supplies the flag bit that indicates the file was modified.
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      PAGED_CODE();
  
      UNREFERENCED_PARAMETER( FileObject );
      UNREFERENCED_PARAMETER( IrpContext );
      
      DebugTrace(+1, Dbg, "FatQueryBasicInfo...\n", 0);
  
      //
      //  Zero out the output buffer, and set it to indicate that
      //  the query is a normal file.  Later we might overwrite the
      //  attribute.
      //
  
      RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );
  
      //
      //  Extract the data and fill in the non zero fields of the output
      //  buffer
      //
  
      if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
  
          //
          //  We have to munge a lie on the fly.  Every time we have to
          //  use 1/1/80 we need to convert to GMT since the TZ may have
          //  changed on us.
          //
  
          ExLocalTimeToSystemTime( &FatJanOne1980,
                                   &Buffer->LastWriteTime );
          Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
  
      } else {
  
          Buffer->LastWriteTime = Fcb->LastWriteTime;
          Buffer->CreationTime = Fcb->CreationTime;
          Buffer->LastAccessTime = Fcb->LastAccessTime;
      }
  
      Buffer->FileAttributes = Fcb->DirentFatFlags;
  
      //
      //  If the temporary flag is set, then set it in the buffer.
      //
  
      if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
  
          SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
      }
  
      //
      //  If no attributes were set, set the normal bit.
      //
  
      if (Buffer->FileAttributes == 0) {
  
          Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
      }
  
      //
      //  Update the length and status output variables
      //
  
      *Length -= sizeof( FILE_BASIC_INFORMATION );
  
      DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
      DebugTrace(-1, Dbg, "FatQueryBasicInfo -> VOID\n", 0);
  
      return;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatQueryStandardInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_STANDARD_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the query standard information function for fat.
  
  Arguments:
  
      Fcb - Supplies the Fcb being queried, it has been verified
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatQueryStandardInfo...\n", 0);
  
      //
      //  Zero out the output buffer, and fill in the number of links
      //  and the delete pending flag.
      //
  
      RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );
  
      Buffer->NumberOfLinks = 1;
      Buffer->DeletePending = BooleanFlagOn( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
  
      //
      //  Case on whether this is a file or a directory, and extract
      //  the information and fill in the fcb/dcb specific parts
      //  of the output buffer
      //
  
      if (NodeType(Fcb) == FAT_NTC_FCB) {
  
          if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  
              FatLookupFileAllocationSize( IrpContext, Fcb );
          }
  
          Buffer->AllocationSize = Fcb->Header.AllocationSize;
          Buffer->EndOfFile = Fcb->Header.FileSize;
  
          Buffer->Directory = FALSE;
  
      } else {
  
          Buffer->Directory = TRUE;
      }
  
      //
      //  Update the length and status output variables
      //
  
      *Length -= sizeof( FILE_STANDARD_INFORMATION );
  
      DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
      DebugTrace(-1, Dbg, "FatQueryStandardInfo -> VOID\n", 0);
  
      return;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  VOID
  FatQueryInternalInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_INTERNAL_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the query internal information function for fat.
  
  Arguments:
  
      Fcb - Supplies the Fcb being queried, it has been verified
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      PAGED_CODE();
  
      UNREFERENCED_PARAMETER( IrpContext );
      
      DebugTrace(+1, Dbg, "FatQueryInternalInfo...\n", 0);
  
      try {
  
          Buffer->IndexNumber.QuadPart = FatGenerateFileIdFromFcb( Fcb );
  
          //
          //  Update the length and status output variables
          //
  
          *Length -= sizeof( FILE_INTERNAL_INFORMATION );
  
      } finally {
  
          DebugUnwind( FatQueryInternalInfo );
  
          DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
          DebugTrace(-1, Dbg, "FatQueryInternalInfo -> VOID\n", 0);
      }
  
      return;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  VOID
  FatQueryEaInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_EA_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the query Ea information function for fat.
  
  Arguments:
  
      Fcb - Supplies the Fcb being queried, it has been verified
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      PBCB Bcb;
  
      PAGED_CODE();
  
      UNREFERENCED_PARAMETER( Fcb );
      UNREFERENCED_PARAMETER( IrpContext );
      
      DebugTrace(+1, Dbg, "FatQueryEaInfo...\n", 0);
  
      Bcb = NULL;
  
      try {
  
          //
          //  Zero out the output buffer
          //
  
          RtlZeroMemory( Buffer, sizeof(FILE_EA_INFORMATION) );
  
  #if 0
          //
          //  The Root dcb does not have any EAs so don't look for any.  Fat32
          //  doesn't have any, either.
          //
  
          if ( NodeType( Fcb ) != FAT_NTC_ROOT_DCB &&
               !FatIsFat32( Fcb->Vcb )) {
  
              PDIRENT Dirent;
  
              //
              //  Try to get the dirent for this file.
              //
  
              FatGetDirentFromFcbOrDcb( IrpContext,
                                        Fcb,
                                        &Dirent,
                                        &Bcb );
  
              if (Dirent != NULL) {
  
                  //
                  //  Get a the size needed to store the full eas for the file.
                  //
  
                  FatGetEaLength( IrpContext,
                                  Fcb->Vcb,
                                  Dirent,
                                  &Buffer->EaSize );
              }
          }
  #endif
  
          //
          //  Update the length and status output variables
          //
  
          *Length -= sizeof( FILE_EA_INFORMATION );
  
      } finally {
  
          DebugUnwind( FatQueryEaInfo );
  
          //
          //  Unpin the dirent if pinned.
          //
  
          FatUnpinBcb( IrpContext, Bcb );
  
          DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
          DebugTrace(-1, Dbg, "FatQueryEaInfo -> VOID\n", 0);
      }
  }
  
  
  //
  //  Internal Support Routine
  //
  
  VOID
  FatQueryPositionInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFILE_OBJECT FileObject,
      IN OUT PFILE_POSITION_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the query position information function for fat.
  
  Arguments:
  
      FileObject - Supplies the File object being queried
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatQueryPositionInfo...\n", 0);
  
      //
      //  Get the current position found in the file object.
      //
  
      Buffer->CurrentByteOffset = FileObject->CurrentByteOffset;
  
      //
      //  Update the length and status output variables
      //
  
      *Length -= sizeof( FILE_POSITION_INFORMATION );
  
      DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
      DebugTrace(-1, Dbg, "FatQueryPositionInfo -> VOID\n", 0);
  
      UNREFERENCED_PARAMETER( IrpContext );
  
      return;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatQueryNameInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN PCCB Ccb,
      IN BOOLEAN Normalized,
      IN OUT PFILE_NAME_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the query name information function for fat.
  
  Arguments:
  
      Fcb - Supplies the Fcb being queried, it has been verified
  
      Ccb - Supplies the Ccb for the context of the user open
  
      Normalized - if true the caller wants a normalized name (w/out short names).
          This means we're servicing a FileNormalizedNameInformation query.
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      ULONG BytesToCopy;
      LONG TrimLength;
      BOOLEAN Overflow = FALSE;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
  
      //
      //  Convert the name to UNICODE
      //
  
      *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
  
      //
      //  Use the full filename to build the path up.  If we wanted to be
      //  slick in the future, we'd just build the path directly into the
      //  return buffer and avoid constructing the full filename, but since
      //  the full filename winds up being required so often lets not
      //  over optimize this case yet.
      //
  
      if (Fcb->FullFileName.Buffer == NULL) {
  
          FatSetFullFileNameInFcb( IrpContext, Fcb );
      }
  
      //
      //  Here is where it gets a smidge tricky.  FinalNameLength is the length
      //  of the LFN element if it exists, and since the long name is always used
      //  to build FullFileName, we have two cases:
      //
      //  1) short name: use FinalNameLength to tear off the path from FullFileName
      //      and append the UNICODE converted short name.
      //  2) long name: just use FullFileName
      //
      //  We bias to the name the user thinks they opened by.  This winds
      //  up fixing some oddball tunneling cases where intermediate filters
      //  translate operations like delete into renames - this lets them
      //  do the operation in the context of the name the user was using.
      //
      //  It also matches what NTFS does, and so we have the definition of
      //  correct behavior.
      //
  
      //
      //
      //  Assume there is no long name and we are just going to use
      //  FullFileName.
      //
  
      TrimLength = 0;
  
      //
      //  If a LongName exists, the caller isn't asking for the normalized name,
      //  and the original open was by the short name then set TrimLength to point
      //  to the place where the short name goes.
      //
      //  Note: The Ccb can be NULL.  The lazy writer calls to get the name of
      //  a DirectoryOpen FILE_OBJECT that it wants to display in the lost
      //  delayed write popup.  Handle this case by just using the FileFullName.
      //
  
      if (!Normalized &&
          (Fcb->LongName.Unicode.Name.Unicode.Buffer != NULL)) {
  
          if ((Ccb != NULL) &&
              FlagOn(Ccb->Flags, CCB_FLAG_OPENED_BY_SHORTNAME)) {
  
              TrimLength = Fcb->FinalNameLength;
          }
      }
  
      if (*Length < Fcb->FullFileName.Length - TrimLength) {
  
          BytesToCopy = *Length;
          Overflow = TRUE;
  
      } else {
  
          BytesToCopy = Fcb->FullFileName.Length - TrimLength;
          *Length -= BytesToCopy;
      }
  
      RtlCopyMemory( &Buffer->FileName[0],
                     Fcb->FullFileName.Buffer,
                     BytesToCopy );
  
      //
      //  Note that this is just the amount of name we've copied so far.  It'll
      //  either be all of it (long) or the path element including the \ (short).
      //
  
      Buffer->FileNameLength = Fcb->FullFileName.Length - TrimLength;
  
      //
      //  If we trimmed off the name element, this is the short name case.  Pick
      //  up the UNICODE conversion and append it.
      //
  
      if (TrimLength != 0) {
  
          UNICODE_STRING ShortName;
          WCHAR ShortNameBuffer[12];
          NTSTATUS Status;
  
          //
          //  Convert the short name to UNICODE and figure out how much
          //  of it can fit.  Again, we always bump the returned length
          //  to indicate how much is available even if we can't return it.
          //
  
          ShortName.Length = 0;
          ShortName.MaximumLength = sizeof(ShortNameBuffer);
          ShortName.Buffer = ShortNameBuffer;
  
  #pragma prefast( suppress:28931, "needed for debug build" )
          Status = RtlOemStringToCountedUnicodeString( &ShortName,
                                                       &Fcb->ShortName.Name.Oem,
                                                       FALSE );
  
          NT_ASSERT( Status == STATUS_SUCCESS );
  
          if (!Overflow) {
  
              if (*Length < ShortName.Length) {
  
                  BytesToCopy = *Length;
                  Overflow = TRUE;
  
              } else {
  
                  BytesToCopy = ShortName.Length;
                  *Length -= BytesToCopy;
              }
  
              RtlCopyMemory( (PUCHAR)&Buffer->FileName[0] + Buffer->FileNameLength,
                             ShortName.Buffer,
                             BytesToCopy );
          }
  
          Buffer->FileNameLength += ShortName.Length;
      }
  
      if (Overflow) {
  
          *Length = -1;
      }
  
      //
      //  Return to caller
      //
  
      DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
      DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
  
      UNREFERENCED_PARAMETER( IrpContext );
  
      return;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  VOID
  FatQueryShortNameInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN OUT PFILE_NAME_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
  
  Routine Description:
  
      This routine queries the short name of the file.
  
  Arguments:
  
      Fcb - Supplies the Fcb being queried, it has been verified
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      NTSTATUS Status;
  
      ULONG BytesToCopy;
      WCHAR ShortNameBuffer[12];
      UNICODE_STRING ShortName;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatQueryNameInfo...\n", 0);
  
      //
      //  Convert the name to UNICODE
      //
  
      ShortName.Length = 0;
      ShortName.MaximumLength = sizeof(ShortNameBuffer);
      ShortName.Buffer = ShortNameBuffer;
  
      *Length -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
  
  #pragma prefast( suppress:28931, "needed for debug build" )
      Status = RtlOemStringToCountedUnicodeString( &ShortName,
                                                   &Fcb->ShortName.Name.Oem,
                                                   FALSE );
  
      NT_ASSERT( Status == STATUS_SUCCESS );
  
      //
      //  If we overflow, set *Length to -1 as a flag.
      //
  
      if (*Length < ShortName.Length) {
  
          BytesToCopy = *Length;
          *Length = -1;
  
      } else {
  
          BytesToCopy = ShortName.Length;
          *Length -= ShortName.Length;
      }
  
      RtlCopyMemory( &Buffer->FileName[0],
                     &ShortName.Buffer[0],
                     BytesToCopy );
  
      Buffer->FileNameLength = ShortName.Length;
  
      //
      //  Return to caller
      //
  
      DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
      DebugTrace(-1, Dbg, "FatQueryNameInfo -> VOID\n", 0);
  
      UNREFERENCED_PARAMETER( IrpContext );
  
      return;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatQueryNetworkInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN PFILE_OBJECT FileObject,
      IN OUT PFILE_NETWORK_OPEN_INFORMATION Buffer,
      IN OUT PLONG Length
      )
  
  /*++
   Description:
  
      This routine performs the query network open information function for fat.
  
  Arguments:
  
      Fcb - Supplies the Fcb being queried, it has been verified
  
      FileObject - Supplies the flag bit that indicates the file was modified.
  
      Buffer - Supplies a pointer to the buffer where the information is to
          be returned
  
      Length - Supplies the length of the buffer in bytes, and receives the
          remaining bytes free in the buffer upon return.
  
  Return Value:
  
      None
  
  --*/
  
  {
      PAGED_CODE();
      
      UNREFERENCED_PARAMETER( FileObject );   
  
      DebugTrace(+1, Dbg, "FatQueryNetworkInfo...\n", 0);
  
      //
      //  Zero out the output buffer, and set it to indicate that
      //  the query is a normal file.  Later we might overwrite the
      //  attribute.
      //
  
      RtlZeroMemory( Buffer, sizeof(FILE_NETWORK_OPEN_INFORMATION) );
  
      //
      //  Extract the data and fill in the non zero fields of the output
      //  buffer
      //
  
      if (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB) {
  
          //
          //  We have to munge a lie on the fly.  Every time we have to
          //  use 1/1/80 we need to convert to GMT since the TZ may have
          //  changed on us.
          //
  
          ExLocalTimeToSystemTime( &FatJanOne1980,
                                   &Buffer->LastWriteTime );
          Buffer->CreationTime = Buffer->LastAccessTime = Buffer->LastWriteTime;
  
      } else {
  
          Buffer->LastWriteTime.QuadPart = Fcb->LastWriteTime.QuadPart;
          Buffer->CreationTime.QuadPart = Fcb->CreationTime.QuadPart;
          Buffer->LastAccessTime.QuadPart = Fcb->LastAccessTime.QuadPart;
      }
  
      Buffer->FileAttributes = Fcb->DirentFatFlags;
  
      //
      //  If the temporary flag is set, then set it in the buffer.
      //
  
      if (FlagOn( Fcb->FcbState, FCB_STATE_TEMPORARY )) {
  
          SetFlag( Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY );
      }
  
      //
      //  If no attributes were set, set the normal bit.
      //
  
      if (Buffer->FileAttributes == 0) {
  
          Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
      }
      //
      //  Case on whether this is a file or a directory, and extract
      //  the information and fill in the fcb/dcb specific parts
      //  of the output buffer
      //
  
      if (NodeType(Fcb) == FAT_NTC_FCB) {
  
          if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  
              FatLookupFileAllocationSize( IrpContext, Fcb );
          }
  
          Buffer->AllocationSize.QuadPart = Fcb->Header.AllocationSize.QuadPart;
          Buffer->EndOfFile.QuadPart = Fcb->Header.FileSize.QuadPart;
      }
  
      //
      //  Update the length and status output variables
      //
  
      *Length -= sizeof( FILE_NETWORK_OPEN_INFORMATION );
  
      DebugTrace( 0, Dbg, "*Length = %08lx\n", *Length);
  
      DebugTrace(-1, Dbg, "FatQueryNetworkInfo -> VOID\n", 0);
  
      return;
  }
  
  
  //
  //  Internal Support routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetBasicInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFCB Fcb,
      IN PCCB Ccb
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the set basic information for fat.  It either
      completes the request or enqueues it off to the fsp.
  
  Arguments:
  
      Irp - Supplies the irp being processed
  
      Fcb - Supplies the Fcb or Dcb being processed, already known not to
          be the root dcb
  
      Ccb - Supplies the flag bit that control updating the last modify
          time on cleanup.
  
  Return Value:
  
      NTSTATUS - The result of this operation if it completes without
                 an exception.
  
  --*/
  
  {
      NTSTATUS Status;
  
      PFILE_BASIC_INFORMATION Buffer;
  
      PDIRENT Dirent;
      PBCB DirentBcb;
  
      FAT_TIME_STAMP CreationTime = {0};
      UCHAR CreationMSec = 0;
      FAT_TIME_STAMP LastWriteTime = {0};
      FAT_TIME_STAMP LastAccessTime = {0};
      FAT_DATE LastAccessDate = {0};
      UCHAR Attributes;
  
      BOOLEAN ModifyCreation = FALSE;
      BOOLEAN ModifyLastWrite = FALSE;
      BOOLEAN ModifyLastAccess = FALSE;
  
      BOOLEAN ModifiedAttributes = FALSE;
  
      LARGE_INTEGER LargeCreationTime = {0};
      LARGE_INTEGER LargeLastWriteTime = {0};
      LARGE_INTEGER LargeLastAccessTime = {0};
  
      ULONG NotifyFilter = 0;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatSetBasicInfo...\n", 0);
  
      Buffer = Irp->AssociatedIrp.SystemBuffer;
  
      //
      //  If the user is specifying -1 for a field, that means
      //  we should leave that field unchanged, even if we might
      //  have otherwise set it ourselves.  We'll set the Ccb flag
      //  saying that the user set the field so that we
      //  don't do our default updating.
      //
      //  We set the field to 0 then so we know not to actually
      //  set the field to the user-specified (and in this case,
      //  illegal) value.
      //
  
      if (Buffer->LastWriteTime.QuadPart == -1) {
  
          SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
          Buffer->LastWriteTime.QuadPart = 0;
      }
  
      if (Buffer->LastAccessTime.QuadPart == -1) {
  
          SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
          Buffer->LastAccessTime.QuadPart = 0;
      }
  
      if (Buffer->CreationTime.QuadPart == -1) {
  
          SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
          Buffer->CreationTime.QuadPart = 0;
      }
  
      DirentBcb = NULL;
  
      Status = STATUS_SUCCESS;
  
      try {
  
          LARGE_INTEGER FatLocalDecThirtyOne1979;
          LARGE_INTEGER FatLocalJanOne1980;
  
          ExLocalTimeToSystemTime( &FatDecThirtyOne1979,
                                   &FatLocalDecThirtyOne1979 );
  
          ExLocalTimeToSystemTime( &FatJanOne1980,
                                   &FatLocalJanOne1980 );
  
          //
          //  Get a pointer to the dirent
          //
  
          NT_ASSERT( Fcb->FcbCondition == FcbGood );
  
          FatGetDirentFromFcbOrDcb( IrpContext,
                                    Fcb,
                                    FALSE,
                                    &Dirent,
                                    &DirentBcb );
          //
          //  Check if the user specified a non-zero creation time
          //
  
          if (FatData.ChicagoMode && (Buffer->CreationTime.QuadPart != 0)) {
  
              LargeCreationTime = Buffer->CreationTime;
  
              //
              //  Convert the Nt time to a Fat time
              //
  
              if ( !FatNtTimeToFatTime( IrpContext,
                                        &LargeCreationTime,
                                        FALSE,
                                        &CreationTime,
                                        &CreationMSec )) {
  
                  //
                  //  Special case the value 12/31/79 and treat this as 1/1/80.
                  //  This '79 value can happen because of time zone issues.
                  //
  
                  if ((LargeCreationTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
                      (LargeCreationTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
  
                      CreationTime = FatTimeJanOne1980;
                      LargeCreationTime = FatLocalJanOne1980;
  
                  } else {
  
                      DebugTrace(0, Dbg, "Invalid CreationTime\n", 0);
                      try_return( Status = STATUS_INVALID_PARAMETER );
                  }
  
                  //
                  //  Don't worry about CreationMSec
                  //
  
                  CreationMSec = 0;
              }
  
              ModifyCreation = TRUE;
          }
  
          //
          //  Check if the user specified a non-zero last access time
          //
  
          if (FatData.ChicagoMode && (Buffer->LastAccessTime.QuadPart != 0)) {
  
              LargeLastAccessTime = Buffer->LastAccessTime;
  
              //
              //  Convert the Nt time to a Fat time
              //
  
              if ( !FatNtTimeToFatTime( IrpContext,
                                        &LargeLastAccessTime,
                                        TRUE,
                                        &LastAccessTime,
                                        NULL )) {
  
                  //
                  //  Special case the value 12/31/79 and treat this as 1/1/80.
                  //  This '79 value can happen because of time zone issues.
                  //
  
                  if ((LargeLastAccessTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
                      (LargeLastAccessTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
  
                      LastAccessTime = FatTimeJanOne1980;
                      LargeLastAccessTime = FatLocalJanOne1980;
  
                  } else {
  
                      DebugTrace(0, Dbg, "Invalid LastAccessTime\n", 0);
                      try_return( Status = STATUS_INVALID_PARAMETER );
                  }
              }
  
              LastAccessDate = LastAccessTime.Date;
              ModifyLastAccess = TRUE;
          }
  
          //
          //  Check if the user specified a non-zero last write time
          //
  
          if (Buffer->LastWriteTime.QuadPart != 0) {
  
              //
              //  First do a quick check here if the this time is the same
              //  time as LastAccessTime.
              //
  
              if (ModifyLastAccess &&
                  (Buffer->LastWriteTime.QuadPart == Buffer->LastAccessTime.QuadPart)) {
  
                  ModifyLastWrite = TRUE;
                  LastWriteTime = LastAccessTime;
                  LargeLastWriteTime = LargeLastAccessTime;
  
              } else {
  
                  LargeLastWriteTime = Buffer->LastWriteTime;
  
                  //
                  //  Convert the Nt time to a Fat time
                  //
  
                  if ( !FatNtTimeToFatTime( IrpContext,
                                            &LargeLastWriteTime,
                                            TRUE,
                                            &LastWriteTime,
                                            NULL )) {
  
  
                      //
                      //  Special case the value 12/31/79 and treat this as 1/1/80.
                      //  This '79 value can happen because of time zone issues.
                      //
  
                      if ((LargeLastWriteTime.QuadPart >= FatLocalDecThirtyOne1979.QuadPart) &&
                          (LargeLastWriteTime.QuadPart < FatLocalJanOne1980.QuadPart)) {
  
                          LastWriteTime = FatTimeJanOne1980;
                          LargeLastWriteTime = FatLocalJanOne1980;
  
                      } else {
  
                          DebugTrace(0, Dbg, "Invalid LastWriteTime\n", 0);
                          try_return( Status = STATUS_INVALID_PARAMETER );
                      }
                  }
  
                  ModifyLastWrite = TRUE;
              }
          }
  
  
          //
          //  Check if the user specified a non zero file attributes byte
          //
  
          if (Buffer->FileAttributes != 0) {
  
              //
              //  Only permit the attributes that FAT understands.  The rest are silently
              //  dropped on the floor.
              //
  
              Attributes = (UCHAR)(Buffer->FileAttributes & (FILE_ATTRIBUTE_READONLY |
                                                             FILE_ATTRIBUTE_HIDDEN |
                                                             FILE_ATTRIBUTE_SYSTEM |
                                                             FILE_ATTRIBUTE_DIRECTORY |
                                                             FILE_ATTRIBUTE_ARCHIVE));
  
              //
              //  Make sure that for a file the directory bit is not set
              //  and that for a directory the bit is set.
              //
  
              if (NodeType(Fcb) == FAT_NTC_FCB) {
  
                  if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_DIRECTORY)) {
  
                      DebugTrace(0, Dbg, "Attempt to set dir attribute on file\n", 0);
                      try_return( Status = STATUS_INVALID_PARAMETER );
                  }
  
              } else {
  
                  Attributes |= FAT_DIRENT_ATTR_DIRECTORY;
              }
  
              //
              //  Mark the FcbState temporary flag correctly.
              //
  
              if (FlagOn(Buffer->FileAttributes, FILE_ATTRIBUTE_TEMPORARY)) {
  
                  //
                  //  Don't allow the temporary bit to be set on directories.
                  //
  
                  if (NodeType(Fcb) == FAT_NTC_DCB) {
  
                      DebugTrace(0, Dbg, "No temporary directories\n", 0);
                      try_return( Status = STATUS_INVALID_PARAMETER );
                  }
  
                  SetFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
  
                  SetFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
                           FO_TEMPORARY_FILE );
  
              } else {
  
                  ClearFlag( Fcb->FcbState, FCB_STATE_TEMPORARY );
  
                  ClearFlag( IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags,
                             FO_TEMPORARY_FILE );
              }
  
              //
              //  Now, only proceed if the requested file attributes are different
              //  than the existing attributes.
              //
  
              if (Dirent->Attributes != Attributes) {
  
                  //
                  //  Set the new attributes byte, and mark the bcb dirty
                  //
  
                  Fcb->DirentFatFlags = Attributes;
  
                  Dirent->Attributes = Attributes;
  
                  NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
  
                  ModifiedAttributes = TRUE;
              }
          }
  
          if ( ModifyCreation ) {
  
              //
              //  Set the new last write time in the dirent, and mark
              //  the bcb dirty
              //
  
              Fcb->CreationTime = LargeCreationTime;
              Dirent->CreationTime = CreationTime;
              Dirent->CreationMSec = CreationMSec;
  
  
              NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
              //
              //  Now we have to round the time in the Fcb up to the
              //  nearest tem msec.
              //
  
              Fcb->CreationTime.QuadPart =
  
                  ((Fcb->CreationTime.QuadPart + AlmostTenMSec) /
                   TenMSec) * TenMSec;
  
              //
              //  Now because the user just set the creation time we
              //  better not set the creation time on close
              //
  
              SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_CREATION );
          }
  
          if ( ModifyLastAccess ) {
  
              //
              //  Set the new last write time in the dirent, and mark
              //  the bcb dirty
              //
  
              Fcb->LastAccessTime = LargeLastAccessTime;
              Dirent->LastAccessDate = LastAccessDate;
  
              NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
  
              //
              //  Now we have to truncate the time in the Fcb down to the
              //  current day.  This has to be in LocalTime though, so first
              //  convert to local, trunacate, then set back to GMT.
              //
  
              ExSystemTimeToLocalTime( &Fcb->LastAccessTime,
                                       &Fcb->LastAccessTime );
  
              Fcb->LastAccessTime.QuadPart =
  
                  (Fcb->LastAccessTime.QuadPart /
                   FatOneDay.QuadPart) * FatOneDay.QuadPart;
  
              ExLocalTimeToSystemTime( &Fcb->LastAccessTime,
                                       &Fcb->LastAccessTime );
  
              //
              //  Now because the user just set the last access time we
              //  better not set the last access time on close
              //
  
              SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_ACCESS );
          }
  
          if ( ModifyLastWrite ) {
  
              //
              //  Set the new last write time in the dirent, and mark
              //  the bcb dirty
              //
  
              Fcb->LastWriteTime = LargeLastWriteTime;
              Dirent->LastWriteTime = LastWriteTime;
  
              NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  
              //
              //  Now we have to round the time in the Fcb up to the
              //  nearest two seconds.
              //
  
              Fcb->LastWriteTime.QuadPart =
  
                  ((Fcb->LastWriteTime.QuadPart + AlmostTwoSeconds) /
                   TwoSeconds) * TwoSeconds;
  
              //
              //  Now because the user just set the last write time we
              //  better not set the last write time on close
              //
  
              SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
          }
  
          //
          //  If we modified any of the values, we report this to the notify
          //  package.
          //
          //  We also take this opportunity to set the current file size and
          //  first cluster in the Dirent in order to support a server hack.
          //
  
          if (NotifyFilter != 0) {
  
              if (NodeType(Fcb) == FAT_NTC_FCB) {
  
                  Dirent->FileSize = Fcb->Header.FileSize.LowPart;
  
                  Dirent->FirstClusterOfFile = (USHORT)Fcb->FirstClusterOfFile;
  
                  if (FatIsFat32(Fcb->Vcb)) {
  
                      Dirent->FirstClusterOfFileHi =
                              (USHORT)(Fcb->FirstClusterOfFile >> 16);
                  }
              }
  
              FatNotifyReportChange( IrpContext,
                                     Fcb->Vcb,
                                     Fcb,
                                     NotifyFilter,
                                     FILE_ACTION_MODIFIED );
  
              FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
          }
  
  #if (NTDDI_VERSION >= NTDDI_WIN8)
          //
          //  If last-access, last-write, or any attribute bits changed, break
          //  parent directory oplock.
          //
  
          if ((Fcb->ParentDcb != NULL) &&
              (ModifyLastAccess ||
               ModifyLastWrite ||
               ModifiedAttributes)) {
  
              FsRtlCheckOplockEx( FatGetFcbOplock(Fcb->ParentDcb),
                                  IrpContext->OriginatingIrp,
                                  OPLOCK_FLAG_PARENT_OBJECT,
                                  NULL,
                                  NULL,
                                  NULL );
          }
  #endif
  
      try_exit: NOTHING;
      } finally {
  
          DebugUnwind( FatSetBasicInfo );
  
          FatUnpinBcb( IrpContext, DirentBcb );
  
          DebugTrace(-1, Dbg, "FatSetBasicInfo -> %08lx\n", Status);
      }
  
      return Status;
  }
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetDispositionInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject,
      IN PFCB Fcb
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the set disposition information for fat.  It either
      completes the request or enqueues it off to the fsp.
  
  Arguments:
  
      Irp - Supplies the irp being processed
  
      FileObject - Supplies the file object being processed
  
      Fcb - Supplies the Fcb or Dcb being processed, already known not to
          be the root dcb
  
  Return Value:
  
      NTSTATUS - The result of this operation if it completes without
                 an exception.
  
  --*/
  
  {
      PFILE_DISPOSITION_INFORMATION Buffer;
      PBCB Bcb;
      PDIRENT Dirent;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatSetDispositionInfo...\n", 0);
  
      Buffer = Irp->AssociatedIrp.SystemBuffer;
  
      //
      //  Check if the user wants to delete the file or not delete
      //  the file
      //
  
      if (Buffer->DeleteFile) {
  
          //
          //  Check if the file is marked read only
          //
  
          if (FlagOn(Fcb->DirentFatFlags, FAT_DIRENT_ATTR_READ_ONLY)) {
  
              DebugTrace(-1, Dbg, "Cannot delete readonly file\n", 0);
  
              return STATUS_CANNOT_DELETE;
          }
  
          //
          //  Make sure there is no process mapping this file as an image.
          //
  
          if (!MmFlushImageSection( &Fcb->NonPaged->SectionObjectPointers,
                                    MmFlushForDelete )) {
  
              DebugTrace(-1, Dbg, "Cannot delete user mapped image\n", 0);
  
              return STATUS_CANNOT_DELETE;
          }
  
          //
          //  Check if this is a dcb and if so then only allow
          //  the request if the directory is empty.
          //
  
          if (NodeType(Fcb) == FAT_NTC_ROOT_DCB) {
  
              DebugTrace(-1, Dbg, "Cannot delete root Directory\n", 0);
  
              return STATUS_CANNOT_DELETE;
          }
  
          if (NodeType(Fcb) == FAT_NTC_DCB) {
  
              DebugTrace(-1, Dbg, "User wants to delete a directory\n", 0);
  
              //
              //  Check if the directory is empty
              //
  
              if ( !FatIsDirectoryEmpty(IrpContext, Fcb) ) {
  
                  DebugTrace(-1, Dbg, "Directory is not empty\n", 0);
  
                  return STATUS_DIRECTORY_NOT_EMPTY;
              }
          }
  
          //
          //  If this is a floppy, touch the volume so to verify that it
          //  is not write protected.
          //
  
          if ( FlagOn(Fcb->Vcb->Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) {
  
              PVCB Vcb;
              PBCB LocalBcb = NULL;
              UCHAR *LocalBuffer;
              UCHAR TmpChar;
              ULONG BytesToMap;
  
              IO_STATUS_BLOCK Iosb;
  
              Vcb = Fcb->Vcb;
  
              BytesToMap = Vcb->AllocationSupport.FatIndexBitSize == 12 ?
                           FatReservedBytes(&Vcb->Bpb) +
                           FatBytesPerFat(&Vcb->Bpb):PAGE_SIZE;
  
              FatReadVolumeFile( IrpContext,
                                 Vcb,
                                 0,
                                 BytesToMap,
                                 &LocalBcb,
                                 (PVOID *)&LocalBuffer );
  
              try {
  
                  if (!CcPinMappedData( Vcb->VirtualVolumeFile,
                                        &FatLargeZero,
                                        BytesToMap,
                                        BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT),
                                        &LocalBcb )) {
  
                      //
                      // Could not pin the data without waiting (cache miss).
                      //
  
                      FatRaiseStatus( IrpContext, STATUS_CANT_WAIT );
                  }
  
                  //
                  //  Make Mm, myself, and Cc think the byte is dirty, and then
                  //  force a writethrough.
                  //
  
                  LocalBuffer += FatReservedBytes(&Vcb->Bpb);
  
                  TmpChar = LocalBuffer[0];
                  LocalBuffer[0] = TmpChar;
  
                  FatAddMcbEntry( Vcb, &Vcb->DirtyFatMcb,
                                  FatReservedBytes( &Vcb->Bpb ),
                                  FatReservedBytes( &Vcb->Bpb ),
                                  Vcb->Bpb.BytesPerSector );
  
              } finally {
  
                  if (AbnormalTermination() && (LocalBcb != NULL)) {
  
                      FatUnpinBcb( IrpContext, LocalBcb );
                  }
              }
  
              CcRepinBcb( LocalBcb );
              CcSetDirtyPinnedData( LocalBcb, NULL );
              CcUnpinData( LocalBcb );
              DbgDoit( NT_ASSERT( IrpContext->PinCount ));
              DbgDoit( IrpContext->PinCount -= 1 );
              CcUnpinRepinnedBcb( LocalBcb, TRUE, &Iosb );
  
              //
              //  If this was not successful, raise the status.
              //
  
              if ( !NT_SUCCESS(Iosb.Status) ) {
  
                  FatNormalizeAndRaiseStatus( IrpContext, Iosb.Status );
              }
  
          } else {
  
              //
              //  Just set a Bcb dirty here.  The above code was only there to
              //  detect a write protected floppy, while the below code works
              //  for any write protected media and only takes a hit when the
              //  volume in clean.
              //
  
              FatGetDirentFromFcbOrDcb( IrpContext,
                                        Fcb,
                                        FALSE,
                                        &Dirent,
                                        &Bcb );
  
              //
              //  This has to work for the usual reasons (we verified the Fcb within
              //  volume synch).
              //
  
              try {
  
                  FatSetDirtyBcb( IrpContext, Bcb, Fcb->Vcb, TRUE );
  
              } finally {
  
                  FatUnpinBcb( IrpContext, Bcb );
              }
          }
  
          //
          //  At this point either we have a file or an empty directory
          //  so we know the delete can proceed.
          //
  
          SetFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
          FileObject->DeletePending = TRUE;
  
          //
          //  If this is a directory then report this delete pending to
          //  the dir notify package.
          //
  
          if (NodeType(Fcb) == FAT_NTC_DCB) {
  
              FsRtlNotifyFullChangeDirectory( Fcb->Vcb->NotifySync,
                                              &Fcb->Vcb->DirNotifyList,
                                              FileObject->FsContext,
                                              NULL,
                                              FALSE,
                                              FALSE,
                                              0,
                                              NULL,
                                              NULL,
                                              NULL );
          }
      } else {
  
          //
          //  The user doesn't want to delete the file so clear
          //  the delete on close bit
          //
  
          DebugTrace(0, Dbg, "User want to not delete file\n", 0);
  
          ClearFlag( Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE );
          FileObject->DeletePending = FALSE;
      }
  
      DebugTrace(-1, Dbg, "FatSetDispositionInfo -> STATUS_SUCCESS\n", 0);
  
      return STATUS_SUCCESS;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  NTSTATUS
  FatSetRenameInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PVCB Vcb,
      IN PFCB Fcb,
      IN PCCB Ccb
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the set name information for fat.  It either
      completes the request or enqueues it off to the fsp.
  
  Arguments:
  
      Irp - Supplies the irp being processed
  
      Vcb - Supplies the Vcb being processed
  
      Fcb - Supplies the Fcb or Dcb being processed, already known not to
          be the root dcb
  
      Ccb - Supplies the Ccb corresponding to the handle opening the source
          file
  
  Return Value:
  
      NTSTATUS - The result of this operation if it completes without
                 an exception.
  
  --*/
  
  {
      BOOLEAN AllLowerComponent;
      BOOLEAN AllLowerExtension;
      BOOLEAN CaseOnlyRename;
      BOOLEAN ContinueWithRename;
      BOOLEAN CreateLfn = FALSE;
      BOOLEAN DeleteSourceDirent;
      BOOLEAN DeleteTarget;
      BOOLEAN NewDirentFromPool;
      BOOLEAN RenamedAcrossDirectories;
      BOOLEAN ReplaceIfExists;
  
      CCB LocalCcb;
      PCCB SourceCcb;
  
      DIRENT Dirent;
  
      NTSTATUS Status = STATUS_SUCCESS;
  
      OEM_STRING OldOemName;
      OEM_STRING NewOemName;
      UCHAR OemNameBuffer[24*2];
  
      PBCB DotDotBcb;
      PBCB NewDirentBcb;
      PBCB OldDirentBcb;
      PBCB SecondPageBcb;
      PBCB TargetDirentBcb;
  
      PDCB TargetDcb = NULL;
      PDCB OldParentDcb;
  
      PDIRENT DotDotDirent = NULL;
      PDIRENT FirstPageDirent = NULL;
      PDIRENT NewDirent = NULL;
      PDIRENT OldDirent = NULL;
      PDIRENT SecondPageDirent = NULL;
      PDIRENT ShortDirent = NULL;
      PDIRENT TargetDirent = NULL;
  
      PFCB TempFcb;
  
      PFILE_OBJECT TargetFileObject;
      PFILE_OBJECT FileObject;
  
      PIO_STACK_LOCATION IrpSp;
  
      PLIST_ENTRY Links;
  
      ULONG BytesInFirstPage = 0;
      ULONG DirentsInFirstPage = 0;
      ULONG DirentsRequired = 0;
      ULONG NewOffset = 0;
      ULONG NotifyAction = 0;
      ULONG SecondPageOffset = 0;
      ULONG ShortDirentOffset = 0;
      ULONG TargetDirentOffset = 0;
      ULONG TargetLfnOffset = 0;
  
      UNICODE_STRING NewName;
      UNICODE_STRING NewUpcasedName;
      UNICODE_STRING OldName;
      UNICODE_STRING OldUpcasedName;
      UNICODE_STRING TargetLfn;
  
      PWCHAR UnicodeBuffer;
  
      UNICODE_STRING UniTunneledShortName;
      WCHAR UniTunneledShortNameBuffer[12];
      UNICODE_STRING UniTunneledLongName;
      WCHAR UniTunneledLongNameBuffer[26];
      LARGE_INTEGER TunneledCreationTime;
      ULONG TunneledDataSize;
      BOOLEAN HaveTunneledInformation = FALSE;
      BOOLEAN UsingTunneledLfn = FALSE;
  
      BOOLEAN InvalidateFcbOnRaise = FALSE;
  
      PFILE_OBJECT DirectoryFileObject = NULL;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatSetRenameInfo...\n", 0);
  
      //
      //  P H A S E  0: Initialize some variables.
      //
  
      CaseOnlyRename = FALSE;
      ContinueWithRename = FALSE;
      DeleteSourceDirent = FALSE;
      DeleteTarget = FALSE;
      NewDirentFromPool = FALSE;
      RenamedAcrossDirectories = FALSE;
  
      DotDotBcb = NULL;
      NewDirentBcb = NULL;
      OldDirentBcb = NULL;
      SecondPageBcb = NULL;
      TargetDirentBcb = NULL;
  
      NewOemName.Length = 0;
      NewOemName.MaximumLength = 24;
      NewOemName.Buffer = (PCHAR)&OemNameBuffer[0];
  
      OldOemName.Length = 0;
      OldOemName.MaximumLength = 24;
      OldOemName.Buffer = (PCHAR)&OemNameBuffer[24];
  
      UnicodeBuffer = FsRtlAllocatePoolWithTag( PagedPool,
                                                4 * MAX_LFN_CHARACTERS * sizeof(WCHAR),
                                                TAG_FILENAME_BUFFER );
  
      NewUpcasedName.Length = 0;
      NewUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
      NewUpcasedName.Buffer = &UnicodeBuffer[0];
  
      OldName.Length = 0;
      OldName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
      OldName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS];
  
      OldUpcasedName.Length = 0;
      OldUpcasedName.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
      OldUpcasedName.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 2];
  
      TargetLfn.Length = 0;
      TargetLfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
      TargetLfn.Buffer = &UnicodeBuffer[MAX_LFN_CHARACTERS * 3];
  
      UniTunneledShortName.Length = 0;
      UniTunneledShortName.MaximumLength = sizeof(UniTunneledShortNameBuffer);
      UniTunneledShortName.Buffer = &UniTunneledShortNameBuffer[0];
  
      UniTunneledLongName.Length = 0;
      UniTunneledLongName.MaximumLength = sizeof(UniTunneledLongNameBuffer);
      UniTunneledLongName.Buffer = &UniTunneledLongNameBuffer[0];
  
      //
      //  Remember the name in case we have to modify the name
      //  value in the ea.
      //
  
      RtlCopyMemory( OldOemName.Buffer,
                     Fcb->ShortName.Name.Oem.Buffer,
                     OldOemName.Length );
  
      //
      //  Get the current stack location
      //
  
      IrpSp = IoGetCurrentIrpStackLocation( Irp );
  
      //
      //  Extract information from the Irp to make our life easier
      //
  
      FileObject = IrpSp->FileObject;
      SourceCcb = FileObject->FsContext2;
      TargetFileObject = IrpSp->Parameters.SetFile.FileObject;
      ReplaceIfExists = IrpSp->Parameters.SetFile.ReplaceIfExists;
  
      RtlZeroMemory( &LocalCcb, sizeof(CCB) );
  
      //
      //  P H A S E  1:
      //
      //  Test if rename is legal.  Only small side-effects are not undone.
      //
  
      try {
  
          //
          //  Can't rename the root directory
          //
  
          if ( NodeType(Fcb) == FAT_NTC_ROOT_DCB ) {
  
              try_return( Status = STATUS_INVALID_PARAMETER );
          }
  
          //
          //  Check that we were not given a dcb with open handles beneath
          //  it.  If there are only UncleanCount == 0 Fcbs beneath us, then
          //  remove them from the prefix table, and they will just close
          //  and go away naturally.
          //
  
          if (NodeType(Fcb) == FAT_NTC_DCB) {
  
              PFCB BatchOplockFcb;
              ULONG BatchOplockCount;
  
              //
              //  Loop until there are no batch oplocks in the subtree below
              //  this directory.
              //
  
              while (TRUE) {
  
                  BatchOplockFcb = NULL;
                  BatchOplockCount = 0;
  
                  //
                  //  First look for any UncleanCount != 0 Fcbs, and fail if we
                  //  find any.
                  //
  
                  for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
                        TempFcb != Fcb;
                        TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
  
                       if ( TempFcb->UncleanCount != 0 ) {
  
                           //
                           // If there is a batch oplock on this file then
                           // increment our count and remember the Fcb if
                           // this is the first.
                           //
  
                           if (FatIsFileOplockable( TempFcb ) &&
                               (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) )
  #if (NTDDI_VERSION >= NTDDI_WIN7)
                                || 
                                FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) )
  #endif
                                )) {
  
                               BatchOplockCount += 1;
                               if ( BatchOplockFcb == NULL ) {
  
                                   BatchOplockFcb = TempFcb;
                               }
  
                           } else {
  
                              try_return( Status = STATUS_ACCESS_DENIED );
                           }
                       }
                  }
  
                  //
                  //  If this is not the first pass for rename and the number
                  //  of batch oplocks has not decreased then give up.
                  //
  
                  if ( BatchOplockFcb != NULL ) {
  
                      if ( (Irp->IoStatus.Information != 0) &&
                           (BatchOplockCount >= Irp->IoStatus.Information) ) {
  
                          try_return( Status = STATUS_ACCESS_DENIED );
                      }
  
                      //
                      //  Try to break this batch oplock.
                      //
  
                      Irp->IoStatus.Information = BatchOplockCount;
                      Status = FsRtlCheckOplock( FatGetFcbOplock(BatchOplockFcb),
                                                 Irp,
                                                 IrpContext,
                                                 FatOplockComplete,
                                                 NULL );
  
                      //
                      //  If the oplock was already broken then look for more
                      //  batch oplocks.
                      //
  
                      if (Status == STATUS_SUCCESS) {
  
                          continue;
                      }
  
                      //
                      //  Otherwise the oplock package will post or complete the
                      //  request.
                      //
  
                      try_return( Status = STATUS_PENDING );
                  }
  
                  break;
              }
  
              //
              //  Now try to get as many of these file object, and thus Fcbs
              //  to go away as possible, flushing first, of course.
              //
  
              FatPurgeReferencedFileObjects( IrpContext, Fcb, Flush );
  
              //
              //  OK, so there are no UncleanCount != 0, Fcbs.  Infact, there
              //  shouldn't really be any Fcbs left at all, except obstinate
              //  ones from user mapped sections .... Remove the full file name
              //  and exact case lfn.
              //
  
              for ( TempFcb = FatGetNextFcbBottomUp(IrpContext, NULL, Fcb);
                    TempFcb != Fcb;
                    TempFcb = FatGetNextFcbBottomUp(IrpContext, TempFcb, Fcb) ) {
  
                  FatAcquireExclusiveFcb( IrpContext, TempFcb );
  
                  if (TempFcb->FullFileName.Buffer != NULL) {
  
                      ExFreePool( TempFcb->FullFileName.Buffer );
                      TempFcb->FullFileName.Buffer = NULL;
                  }
  
                  FatReleaseFcb( IrpContext, TempFcb );
              }
          }
  
          //
          //  Check if this is a simple rename or a fully-qualified rename
          //  In both cases we need to figure out what the TargetDcb, and
          //  NewName are.
          //
  
          if (TargetFileObject == NULL) {
  
              //
              //  In the case of a simple rename the target dcb is the
              //  same as the source file's parent dcb, and the new file name
              //  is taken from the system buffer
              //
  
              PFILE_RENAME_INFORMATION Buffer;
  
              Buffer = Irp->AssociatedIrp.SystemBuffer;
  
              TargetDcb = Fcb->ParentDcb;
  
              NewName.Length = (USHORT) Buffer->FileNameLength;
              NewName.Buffer = (PWSTR) &Buffer->FileName;
  
              //
              //  Make sure the name is of legal length.
              //
  
              if (NewName.Length > 255*sizeof(WCHAR)) {
  
                  try_return( Status = STATUS_OBJECT_NAME_INVALID );
              }
  
          } else {
  
              //
              //  For a fully-qualified rename the target dcb is taken from
              //  the target file object, which must be on the same vcb as
              //  the source.
              //
  
              PVCB TargetVcb;
              PCCB TargetCcb;
  
              if ((FatDecodeFileObject( TargetFileObject,
                                        &TargetVcb,
                                        &TargetDcb,
                                        &TargetCcb ) != UserDirectoryOpen) ||
                  (TargetVcb != Vcb)) {
  
                  try_return( Status = STATUS_INVALID_PARAMETER );
              }
  
              //
              //  This name is by definition legal.
              //
  
              NewName = *((PUNICODE_STRING)&TargetFileObject->FileName);
          }
  
          //
          //  We will need an upcased version of the unicode name and the
          //  old name as well.
          //
  
          Status = RtlUpcaseUnicodeString( &NewUpcasedName, &NewName, FALSE );
  
          if (!NT_SUCCESS(Status)) {
  
              try_return( Status );
          }
  
          FatGetUnicodeNameFromFcb( IrpContext, Fcb, &OldName );
  
          Status = RtlUpcaseUnicodeString( &OldUpcasedName, &OldName, FALSE );
  
          if (!NT_SUCCESS(Status)) {
              try_return(Status);
          }
  
          //
          //  Check if the current name and new name are equal, and the
          //  DCBs are equal.  If they are then our work is already done.
          //
  
          if (TargetDcb == Fcb->ParentDcb) {
  
              //
              //  OK, now if we found something then check if it was an exact
              //  match or just a case match.  If it was an exact match, then
              //  we can bail here.
              //
  
              if (FsRtlAreNamesEqual( &NewName,
                                      &OldName,
                                      FALSE,
                                      NULL )) {
  
                   try_return( Status = STATUS_SUCCESS );
              }
  
              //
              //  Check now for a case only rename.
              //
  
  
              if (FsRtlAreNamesEqual( &NewUpcasedName,
                                      &OldUpcasedName,
                                      FALSE,
                                      NULL )) {
  
                   CaseOnlyRename = TRUE;
              }
  
          } else {
  
              RenamedAcrossDirectories = TRUE;
          }
  
          //
          //  Upcase the name and convert it to the Oem code page.
          //
          //  If the new UNICODE name is already more than 12 characters,
          //  then we know the Oem name will not be valid
          //
  
          if (NewName.Length <= 12*sizeof(WCHAR)) {
  
              FatUnicodeToUpcaseOem( IrpContext, &NewOemName, &NewName );
  
              //
              //  If the name is not valid 8.3, zero the length.
              //
  
              if (FatSpaceInName( IrpContext, &NewName ) ||
                  !FatIsNameShortOemValid( IrpContext, NewOemName, FALSE, FALSE, FALSE)) {
  
                  NewOemName.Length = 0;
              }
  
          } else {
  
              NewOemName.Length = 0;
          }
  
          //
          //  Look in the tunnel cache for names and timestamps to restore
          //
  
          TunneledDataSize = sizeof(LARGE_INTEGER);
          HaveTunneledInformation = FsRtlFindInTunnelCache( &Vcb->Tunnel,
                                                            FatDirectoryKey(TargetDcb),
                                                            &NewName,
                                                            &UniTunneledShortName,
                                                            &UniTunneledLongName,
                                                            &TunneledDataSize,
                                                            &TunneledCreationTime );
          NT_ASSERT(TunneledDataSize == sizeof(LARGE_INTEGER));
  
          //
          //  Now we need to determine how many dirents this new name will
          //  require.
          //
  
          if ((NewOemName.Length == 0) ||
              (FatEvaluateNameCase( IrpContext,
                                    &NewName,
                                    &AllLowerComponent,
                                    &AllLowerExtension,
                                    &CreateLfn ),
               CreateLfn)) {
  
              DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&NewName) + 1;
  
          } else {
  
              //
              //  The user-given name is a short name, but we might still have
              //  a tunneled long name we want to use. See if we can.
              //
  
              if (UniTunneledLongName.Length &&
                  !FatLfnDirentExists(IrpContext, TargetDcb, &UniTunneledLongName, &TargetLfn)) {
  
                  UsingTunneledLfn = CreateLfn = TRUE;
                  DirentsRequired = FAT_LFN_DIRENTS_NEEDED(&UniTunneledLongName) + 1;
  
              } else {
  
                  //
                  //  This really is a simple dirent.  Note that the two AllLower BOOLEANs
                  //  are correctly set now.
                  //
  
                  DirentsRequired = 1;
              }
          }
  
          //
          //  Do some extra checks here if we are not in Chicago mode.
          //
  
          if (!FatData.ChicagoMode) {
  
              //
              //  If the name was not 8.3 valid, fail the rename.
              //
  
              if (NewOemName.Length == 0) {
  
                  try_return( Status = STATUS_OBJECT_NAME_INVALID );
              }
  
              //
              //  Don't use the magic bits.
              //
  
              AllLowerComponent = FALSE;
              AllLowerExtension = FALSE;
              CreateLfn = FALSE;
              UsingTunneledLfn = FALSE;
          }
  
          if (!CaseOnlyRename) {
  
              //
              //  Check if the new name already exists, wait is known to be
              //  true.
              //
  
              if (NewOemName.Length != 0) {
  
                  FatStringTo8dot3( IrpContext,
                                    NewOemName,
                                    &LocalCcb.OemQueryTemplate.Constant );
  
              } else {
  
                  SetFlag( LocalCcb.Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
              }
  
              LocalCcb.UnicodeQueryTemplate = NewUpcasedName;
              LocalCcb.ContainsWildCards = FALSE;
  
              FatLocateDirent( IrpContext,
                               TargetDcb,
                               &LocalCcb,
                               0,
                               &TargetDirent,
                               &TargetDirentBcb,
                               (PVBO)&TargetDirentOffset,
                               NULL,
                               &TargetLfn);
  
              if (TargetDirent != NULL) {
  
                  //
                  //  The name already exists, check if the user wants
                  //  to overwrite the name, and has access to do the overwrite
                  //  We cannot overwrite a directory.
                  //
  
                  if ((!ReplaceIfExists) ||
                      (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY)) ||
                      (FlagOn(TargetDirent->Attributes, FAT_DIRENT_ATTR_READ_ONLY))) {
  
                      try_return( Status = STATUS_OBJECT_NAME_COLLISION );
                  }
  
                  //
                  //  Check that the file has no open user handles, if it does
                  //  then we will deny access.  We do the check by searching
                  //  down the list of fcbs opened under our parent Dcb, and making
                  //  sure none of the maching Fcbs have a non-zero unclean count or
                  //  outstanding image sections.
                  //
  
                  for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
                       Links != &TargetDcb->Specific.Dcb.ParentDcbQueue; ) {
  
                      TempFcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  
                      //
                      //  Advance now.  The image section flush may cause the final
                      //  close, which will recursively happen underneath of us here.
                      //  It would be unfortunate if we looked through free memory.
                      //
  
                      Links = Links->Flink;
  
                      if ((TempFcb->DirentOffsetWithinDirectory == TargetDirentOffset) &&
                          ((TempFcb->UncleanCount != 0) ||
                           !MmFlushImageSection( &TempFcb->NonPaged->SectionObjectPointers,
                                                 MmFlushForDelete))) {
  
                          //
                          //  If there are batch oplocks on this file then break the
                          //  oplocks before failing the rename.
                          //
  
                          Status = STATUS_ACCESS_DENIED;
  
                          if (FatIsFileOplockable( TempFcb ) &&
                              (FsRtlCurrentBatchOplock( FatGetFcbOplock(TempFcb) )
  #if (NTDDI_VERSION >= NTDDI_WIN7)
                               ||
                               FsRtlCurrentOplockH( FatGetFcbOplock(TempFcb) )
  #endif
                               )) {
  
                              //
                              //  Do all of our cleanup now since the IrpContext
                              //  could go away when this request is posted.
                              //
  
                              FatUnpinBcb( IrpContext, TargetDirentBcb );
  
                              Status = FsRtlCheckOplock( FatGetFcbOplock(TempFcb),
                                                         Irp,
                                                         IrpContext,
                                                         FatOplockComplete,
                                                         NULL );
  
                              if (Status != STATUS_PENDING) {
  
                                  Status = STATUS_ACCESS_DENIED;
                              }
                          }
  
                          try_return( NOTHING );
                      }
                  }
  
                  //
                  //  OK, this target is toast.  Remember the Lfn offset.
                  //
  
                  TargetLfnOffset = TargetDirentOffset -
                                    FAT_LFN_DIRENTS_NEEDED(&TargetLfn) *
                                    sizeof(DIRENT);
  
                  DeleteTarget = TRUE;
              }
          }
  
          //
          //  If we will need more dirents than we have, allocate them now.
          //
  
          if ((TargetDcb != Fcb->ParentDcb) ||
              (DirentsRequired !=
               (Fcb->DirentOffsetWithinDirectory -
                Fcb->LfnOffsetWithinDirectory) / sizeof(DIRENT) + 1)) {
  
              //
              //  Get some new allocation
              //
  
              NewOffset = FatCreateNewDirent( IrpContext,
                                              TargetDcb,
                                              DirentsRequired );
  
              DeleteSourceDirent = TRUE;
  
          } else {
  
              NewOffset = Fcb->LfnOffsetWithinDirectory;
          }
  
          ContinueWithRename = TRUE;
  
      try_exit: NOTHING;
  
      } finally {
  
          if (!ContinueWithRename) {
  
              //
              //  Undo everything from above.
              //
  
              ExFreePool( UnicodeBuffer );
              FatUnpinBcb( IrpContext, TargetDirentBcb );
          }
      }
  
      //
      //  Now, if we are already done, return here.
      //
  
      if (!ContinueWithRename) {
  
          return Status;
      }
  
      //
      //  P H A S E  2: Actually perform the rename.
      //
  
      try {
  
          //
          //  Report the fact that we are going to remove this entry.
          //  If we renamed within the same directory and the new name for the
          //  file did not previously exist, we report this as a rename old
          //  name.  Otherwise this is a removed file.
          //
  
          if (!RenamedAcrossDirectories && !DeleteTarget) {
  
              NotifyAction = FILE_ACTION_RENAMED_OLD_NAME;
  
          } else {
  
              NotifyAction = FILE_ACTION_REMOVED;
          }
  
          FatNotifyReportChange( IrpContext,
                                 Vcb,
                                 Fcb,
                                 ((NodeType( Fcb ) == FAT_NTC_FCB)
                                  ? FILE_NOTIFY_CHANGE_FILE_NAME
                                  : FILE_NOTIFY_CHANGE_DIR_NAME ),
                                 NotifyAction );
  
          try {
  
              //
              //  Capture a copy of the source dirent.
              //
  
              FatGetDirentFromFcbOrDcb( IrpContext, Fcb, FALSE, &OldDirent, &OldDirentBcb );
  
              Dirent = *OldDirent;
  
              //
              //  Tunnel the source Fcb - the names are disappearing regardless of
              //  whether the dirent allocation physically changed
              //
  
              FatTunnelFcbOrDcb( Fcb, SourceCcb );
  
              //
              //  From here until very nearly the end of the operation, if we raise there
              //  is no reasonable way to suppose we'd be able to undo the damage.  Not
              //  being a transactional filesystem, FAT is at the mercy of a lot of things
              //  (as the astute reader has no doubt realized by now).
              //
  
              InvalidateFcbOnRaise = TRUE;
  
              //
              //  Delete our current dirent(s) if we got a new one.
              //
  
              if (DeleteSourceDirent) {
  
                  FatDeleteDirent( IrpContext, Fcb, NULL, FALSE );
              }
  
              //
              //  Delete a target conflict if we were meant to.
              //
  
              if (DeleteTarget) {
  
                  FatDeleteFile( IrpContext,
                                 TargetDcb,
                                 TargetLfnOffset,
                                 TargetDirentOffset,
                                 TargetDirent,
                                 &TargetLfn );
              }
  
              //
              //  We need to evaluate any short names required.  If there were any
              //  conflicts in existing short names, they would have been deleted above.
              //
              //  It isn't neccesary to worry about the UsingTunneledLfn case. Since we
              //  actually already know whether CreateLfn will be set either NewName is
              //  an Lfn and !UsingTunneledLfn is implied or NewName is a short name and
              //  we can handle that externally.
              //
  
              FatSelectNames( IrpContext,
                              TargetDcb,
                              &NewOemName,
                              &NewName,
                              &NewOemName,
                              (HaveTunneledInformation ? &UniTunneledShortName : NULL),
                              &AllLowerComponent,
                              &AllLowerExtension,
                              &CreateLfn );
  
              if (!CreateLfn && UsingTunneledLfn) {
  
                  CreateLfn = TRUE;
                  NewName = UniTunneledLongName;
  
                  //
                  //  Short names are always upcase if an LFN exists
                  //
  
                  AllLowerComponent = FALSE;
                  AllLowerExtension = FALSE;
              }
  
              //
              //  OK, now setup the new dirent(s) for the new name.
              //
  
              FatPrepareWriteDirectoryFile( IrpContext,
                                            TargetDcb,
                                            NewOffset,
                                            sizeof(DIRENT),
                                            &NewDirentBcb,
                                            &NewDirent,
                                            FALSE,
                                            TRUE,
                                            &Status );
  
              NT_ASSERT( NT_SUCCESS( Status ) );
  
              //
              //  Deal with the special case of an LFN + Dirent structure crossing
              //  a page boundry.
              //
  
              if ((NewOffset / PAGE_SIZE) !=
                  ((NewOffset + (DirentsRequired - 1) * sizeof(DIRENT)) / PAGE_SIZE)) {
  
                  SecondPageOffset = (NewOffset & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
  
                  BytesInFirstPage = SecondPageOffset - NewOffset;
  
                  DirentsInFirstPage = BytesInFirstPage / sizeof(DIRENT);
  
                  FatPrepareWriteDirectoryFile( IrpContext,
                                                TargetDcb,
                                                SecondPageOffset,
                                                sizeof(DIRENT),
                                                &SecondPageBcb,
                                                &SecondPageDirent,
                                                FALSE,
                                                TRUE,
                                                &Status );
  
                  NT_ASSERT( NT_SUCCESS( Status ) );
  
                  FirstPageDirent = NewDirent;
  
                  NewDirent = FsRtlAllocatePoolWithTag( PagedPool,
                                                        DirentsRequired * sizeof(DIRENT),
                                                        TAG_DIRENT );
  
                  NewDirentFromPool = TRUE;
              }
  
              //
              //  Bump up Dirent and DirentOffset
              //
  
              ShortDirent = NewDirent + DirentsRequired - 1;
              ShortDirentOffset = NewOffset + (DirentsRequired - 1) * sizeof(DIRENT);
  
              //
              //  Fill in the fields of the dirent.
              //
  
              *ShortDirent = Dirent;
  
              FatConstructDirent( IrpContext,
                                  ShortDirent,
                                  &NewOemName,
                                  AllLowerComponent,
                                  AllLowerExtension,
                                  CreateLfn ? &NewName : NULL,
                                  Dirent.Attributes,
                                  FALSE,
                                  (HaveTunneledInformation ? &TunneledCreationTime : NULL) );
  
              if (HaveTunneledInformation) {
  
                  //
                  //  Need to go in and fix the timestamps in the FCB. Note that we can't use
                  //  the TunneledCreationTime since the conversions may have failed.
                  //
  
                  Fcb->CreationTime = FatFatTimeToNtTime(IrpContext, ShortDirent->CreationTime, ShortDirent->CreationMSec);
                  Fcb->LastWriteTime = FatFatTimeToNtTime(IrpContext, ShortDirent->LastWriteTime, 0);
                  Fcb->LastAccessTime = FatFatDateToNtTime(IrpContext, ShortDirent->LastAccessDate);
              }
  
              //
              //  If the dirent crossed pages, split the contents of the
              //  temporary pool between the two pages.
              //
  
              if (NewDirentFromPool) {
  
                  RtlCopyMemory( FirstPageDirent, NewDirent, BytesInFirstPage );
  
                  RtlCopyMemory( SecondPageDirent,
                                 NewDirent + DirentsInFirstPage,
                                 DirentsRequired*sizeof(DIRENT) - BytesInFirstPage );
  
                  ShortDirent = SecondPageDirent +
                                (DirentsRequired - DirentsInFirstPage) - 1;
              }
  
              Dirent = *ShortDirent;
  
          } finally {
  
              //
              //  Remove the entry from the splay table, and then remove the
              //  full file name and exact case lfn. It is important that we
              //  always remove the name from the prefix table regardless of
              //  other errors.
              //
  
              FatRemoveNames( IrpContext, Fcb );
  
              if (Fcb->FullFileName.Buffer != NULL) {
  
                  ExFreePool( Fcb->FullFileName.Buffer );
                  Fcb->FullFileName.Buffer = NULL;
              }
  
              if (Fcb->ExactCaseLongName.Buffer) {
  
                  ExFreePool( Fcb->ExactCaseLongName.Buffer );
                  Fcb->ExactCaseLongName.Buffer = NULL;
              }
  
              FatUnpinBcb( IrpContext, OldDirentBcb );
              FatUnpinBcb( IrpContext, TargetDirentBcb );
              FatUnpinBcb( IrpContext, NewDirentBcb );
              FatUnpinBcb( IrpContext, SecondPageBcb );
          }
  
          //
          //  Now we need to update the location of the file's directory
          //  offset and move the fcb from its current parent dcb to
          //  the target dcb.
          //
  
          Fcb->LfnOffsetWithinDirectory = NewOffset;
          Fcb->DirentOffsetWithinDirectory = ShortDirentOffset;
  
          RemoveEntryList( &Fcb->ParentDcbLinks );
  
          //
          //  There is a deep reason we put files on the tail, others on the head,
          //  which is to allow us to easily enumerate all child directories before
          //  child files. This is important to let us maintain whole-volume lockorder
          //  via BottomUp enumeration.
          //
  
          if (NodeType(Fcb) == FAT_NTC_FCB) {
  
              InsertTailList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
                              &Fcb->ParentDcbLinks );
  
          } else {
  
              InsertHeadList( &TargetDcb->Specific.Dcb.ParentDcbQueue,
                              &Fcb->ParentDcbLinks );
          }
  
          OldParentDcb = Fcb->ParentDcb;
          Fcb->ParentDcb = TargetDcb;
  
  #if (NTDDI_VERSION >= NTDDI_WIN8)
          //
          //  Break parent directory oplock on the old parent.  Directory oplock
          //  breaks are always advisory, so we will never block/get STATUS_PENDING
          //  here.
          //
          
          FsRtlCheckOplockEx( FatGetFcbOplock(OldParentDcb),
                              IrpContext->OriginatingIrp,
                              OPLOCK_FLAG_PARENT_OBJECT,
                              NULL,
                              NULL,
                              NULL );
  #endif
  
          //
          //  If we renamed across directories, some cleanup is now in order.
          //
  
          if (RenamedAcrossDirectories) {
  
  #if (NTDDI_VERSION >= NTDDI_WIN8)
              //
              //  Break parent directory oplock on the new parent.  Directory oplock
              //  breaks are always advisory, so we will never block/get STATUS_PENDING
              //  here.
              //
              
              FsRtlCheckOplockEx( FatGetFcbOplock(TargetDcb),
                                  IrpContext->OriginatingIrp,
                                  OPLOCK_FLAG_PARENT_OBJECT,
                                  NULL,
                                  NULL,
                                  NULL );
  #endif
  
              //
              //  See if we need to uninitialize the cachemap for the source directory.
              //  Do this now in case we get unlucky and raise trying to finalize the
              //  operation.
              //
  
              if (IsListEmpty(&OldParentDcb->Specific.Dcb.ParentDcbQueue) &&
                  (OldParentDcb->OpenCount == 0) &&
                  (OldParentDcb->Specific.Dcb.DirectoryFile != NULL)) {
  
                  NT_ASSERT( NodeType(OldParentDcb) == FAT_NTC_DCB );
  
                  DirectoryFileObject = OldParentDcb->Specific.Dcb.DirectoryFile;
  
                  OldParentDcb->Specific.Dcb.DirectoryFile = NULL;
              }
  
              //
              //  If we move a directory across directories, we have to change
              //  the cluster number in its .. entry
              //
  
              if (NodeType(Fcb) == FAT_NTC_DCB) {
  
                  FatPrepareWriteDirectoryFile( IrpContext,
                                                Fcb,
                                                sizeof(DIRENT),
                                                sizeof(DIRENT),
                                                &DotDotBcb,
                                                &DotDotDirent,
                                                FALSE,
                                                TRUE,
                                                &Status );
  
                  NT_ASSERT( NT_SUCCESS( Status ) );
  
                  DotDotDirent->FirstClusterOfFile = (USHORT)
                      ( NodeType(TargetDcb) == FAT_NTC_ROOT_DCB ?
                        0 : TargetDcb->FirstClusterOfFile);
  
                  if (FatIsFat32( Vcb )) {
  
                      DotDotDirent->FirstClusterOfFileHi = (USHORT)
                      ( NodeType( TargetDcb ) == FAT_NTC_ROOT_DCB ?
                        0 : (TargetDcb->FirstClusterOfFile >> 16));
                  }
              }
          }
  
          //
          //  Now we need to setup the splay table and the name within
          //  the fcb.  Free the old short name at this point.
          //
  
          ExFreePool( Fcb->ShortName.Name.Oem.Buffer );
          Fcb->ShortName.Name.Oem.Buffer = NULL;
  
          FatConstructNamesInFcb( IrpContext,
                                  Fcb,
                                  &Dirent,
                                  CreateLfn ? &NewName : NULL );
  
          FatSetFullNameInFcb( IrpContext, Fcb, &NewName );
  
          //
          //  The rest of the actions taken are not related to correctness of
          //  the in-memory structures, so we shouldn't toast the Fcb if we
          //  raise from here to the end.
          //
  
          InvalidateFcbOnRaise = FALSE;
  
          //
          //  If a file, set the file as modified so that the archive bit
          //  is set.  We prevent this from adjusting the write time by
          //  indicating the user flag in the ccb.
          //
  
          if (Fcb->Header.NodeTypeCode == FAT_NTC_FCB) {
  
              SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
              SetFlag( Ccb->Flags, CCB_FLAG_USER_SET_LAST_WRITE );
          }
  
          //
          //  We have three cases to report.
          //
          //      1.  If we overwrote an existing file, we report this as
          //          a modified file.
          //
          //      2.  If we renamed to a new directory, then we added a file.
          //
          //      3.  If we renamed in the same directory, then we report the
          //          the renamednewname.
          //
  
          if (DeleteTarget) {
  
              FatNotifyReportChange( IrpContext,
                                     Vcb,
                                     Fcb,
                                     FILE_NOTIFY_CHANGE_ATTRIBUTES
                                     | FILE_NOTIFY_CHANGE_SIZE
                                     | FILE_NOTIFY_CHANGE_LAST_WRITE
                                     | FILE_NOTIFY_CHANGE_LAST_ACCESS
                                     | FILE_NOTIFY_CHANGE_CREATION
                                     | FILE_NOTIFY_CHANGE_EA,
                                     FILE_ACTION_MODIFIED );
  
          } else if (RenamedAcrossDirectories) {
  
              FatNotifyReportChange( IrpContext,
                                     Vcb,
                                     Fcb,
                                     ((NodeType( Fcb ) == FAT_NTC_FCB)
                                      ? FILE_NOTIFY_CHANGE_FILE_NAME
                                      : FILE_NOTIFY_CHANGE_DIR_NAME ),
                                     FILE_ACTION_ADDED );
  
          } else {
  
              FatNotifyReportChange( IrpContext,
                                     Vcb,
                                     Fcb,
                                     ((NodeType( Fcb ) == FAT_NTC_FCB)
                                      ? FILE_NOTIFY_CHANGE_FILE_NAME
                                      : FILE_NOTIFY_CHANGE_DIR_NAME ),
                                     FILE_ACTION_RENAMED_NEW_NAME );
          }
  
          //
          //  We need to update the file name in the dirent.  This value
          //  is never used elsewhere, so we don't concern ourselves
          //  with any error we may encounter.  We let chkdsk fix the
          //  disk at some later time.
          //
  
          if (!FatIsFat32(Vcb) &&
              Dirent.ExtendedAttributes != 0) {
  
              FatRenameEAs( IrpContext,
                            Fcb,
                            Dirent.ExtendedAttributes,
                            &OldOemName );
          }
  
          FatUnpinBcb( IrpContext, DotDotBcb );
  
          FatUnpinRepinnedBcbs( IrpContext );
  
          //
          //  Set our final status
          //
  
          Status = STATUS_SUCCESS;
  
      } finally {
  
          DebugUnwind( FatSetRenameInfo );
  
          ExFreePool( UnicodeBuffer );
  
          if (UniTunneledLongName.Buffer != UniTunneledLongNameBuffer) {
  
              //
              //  Free pool if the buffer was grown on tunneling lookup
              //
  
              ExFreePool(UniTunneledLongName.Buffer);
          }
  
          if (NewDirentFromPool) {
  
              ExFreePool( NewDirent );
          }
  
          FatUnpinBcb( IrpContext, TargetDirentBcb );
          FatUnpinBcb( IrpContext, DotDotBcb );
  
          //
          //  Uninitialize the cachemap for the source directory if we need to.
          //
  
          if (DirectoryFileObject) {
  
              DebugTrace(0, Dbg, "Uninitialize our parent Stream Cache Map\n", 0);
  
              CcUninitializeCacheMap( DirectoryFileObject, NULL, NULL );
              
              ObDereferenceObject( DirectoryFileObject );
          }
  
          //
          //  If this was an abnormal termination, then we are in trouble.
          //  Should the operation have been in a sensitive state there is
          //  nothing we can do but invalidate the Fcb.
          //
  
          if (AbnormalTermination() && InvalidateFcbOnRaise) {
  
              Fcb->FcbCondition = FcbBad;
          }
  
          DebugTrace(-1, Dbg, "FatSetRenameInfo -> %08lx\n", Status);
      }
  
      return Status;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  NTSTATUS
  FatSetPositionInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the set position information for fat.  It either
      completes the request or enqueues it off to the fsp.
  
  Arguments:
  
      Irp - Supplies the irp being processed
  
      FileObject - Supplies the file object being processed
  
  Return Value:
  
      NTSTATUS - The result of this operation if it completes without
                 an exception.
  
  --*/
  
  {
      PFILE_POSITION_INFORMATION Buffer;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatSetPositionInfo...\n", 0);
  
      Buffer = Irp->AssociatedIrp.SystemBuffer;
  
      //
      //  Check if the file does not use intermediate buffering.  If it
      //  does not use intermediate buffering then the new position we're
      //  supplied must be aligned properly for the device
      //
  
      if (FlagOn( FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING )) {
  
          PDEVICE_OBJECT DeviceObject;
  
          DeviceObject = IoGetCurrentIrpStackLocation( Irp )->DeviceObject;
  
          if ((Buffer->CurrentByteOffset.LowPart & DeviceObject->AlignmentRequirement) != 0) {
  
              DebugTrace(0, Dbg, "Cannot set position due to aligment conflict\n", 0);
              DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_INVALID_PARAMETER);
  
              return STATUS_INVALID_PARAMETER;
          }
      }
  
      //
      //  The input parameter is fine so set the current byte offset and
      //  complete the request
      //
  
      DebugTrace(0, Dbg, "Set the new position to %08lx\n", Buffer->CurrentByteOffset);
  
      FileObject->CurrentByteOffset = Buffer->CurrentByteOffset;
  
      DebugTrace(-1, Dbg, "FatSetPositionInfo -> %08lx\n", STATUS_SUCCESS);
  
      UNREFERENCED_PARAMETER( IrpContext );
  
      return STATUS_SUCCESS;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetAllocationInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFCB Fcb,
      IN PFILE_OBJECT FileObject
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the set Allocation information for fat.  It either
      completes the request or enqueues it off to the fsp.
  
  Arguments:
  
      Irp - Supplies the irp being processed
  
      Fcb - Supplies the Fcb or Dcb being processed, already known not to
          be the root dcb
  
      FileObject - Supplies the FileObject being processed, already known not to
          be the root dcb
  
  Return Value:
  
      NTSTATUS - The result of this operation if it completes without
                 an exception.
  
  --*/
  
  {
      NTSTATUS Status = STATUS_SUCCESS;
      PFILE_ALLOCATION_INFORMATION Buffer;
      ULONG NewAllocationSize = 0;
  
      BOOLEAN FileSizeTruncated = FALSE;
      BOOLEAN CacheMapInitialized = FALSE;
      BOOLEAN ResourceAcquired = FALSE;
      ULONG OriginalFileSize = 0;
      ULONG OriginalValidDataLength = 0;
      ULONG OriginalValidDataToDisk = 0;
  
      PAGED_CODE();
  
      Buffer = Irp->AssociatedIrp.SystemBuffer;
  
      NewAllocationSize = Buffer->AllocationSize.LowPart;
  
      DebugTrace(+1, Dbg, "FatSetAllocationInfo.. to %08lx\n", NewAllocationSize);
  
      //
      //  Allocation is only allowed on a file and not a directory
      //
  
      if (NodeType(Fcb) == FAT_NTC_DCB) {
  
          DebugTrace(-1, Dbg, "Cannot change allocation of a directory\n", 0);
  
          return STATUS_INVALID_DEVICE_REQUEST;
      }
  
      //
      //  Check that the new file allocation is legal
      //
  
      if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->AllocationSize, 0 )) {
  
          DebugTrace(-1, Dbg, "Illegal allocation size\n", 0);
  
          return STATUS_DISK_FULL;
      }
  
      //
      //  If we haven't yet looked up the correct AllocationSize, do so.
      //
  
      if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  
          FatLookupFileAllocationSize( IrpContext, Fcb );
      }
  
      //
      //  This is kinda gross, but if the file is not cached, but there is
      //  a data section, we have to cache the file to avoid a bunch of
      //  extra work.
      //
  
      if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
          (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
          !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
  
          NT_ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
  
          //
          //  Now initialize the cache map.
          //
  
          FatInitializeCacheMap( FileObject,
                                 (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
                                 FALSE,
                                 &FatData.CacheManagerCallbacks,
                                 Fcb );
  
          CacheMapInitialized = TRUE;
      }
  
      //
      //  Now mark the fact that the file needs to be truncated on close
      //
  
      Fcb->FcbState |= FCB_STATE_TRUNCATE_ON_CLOSE;
  
      //
      //  Now mark that the time on the dirent needs to be updated on close.
      //
  
      SetFlag( FileObject->Flags, FO_FILE_MODIFIED );
  
      try {
  
          //
          //  Increase or decrease the allocation size.
          //
  
          if (NewAllocationSize > Fcb->Header.AllocationSize.LowPart) {
  
              FatAddFileAllocation( IrpContext, Fcb, FileObject, NewAllocationSize);
  
          } else {
  
              //
              //  Check here if we will be decreasing file size and synchonize with
              //  paging IO.
              //
  
              if ( Fcb->Header.FileSize.LowPart > NewAllocationSize ) {
  
                  //
                  //  Before we actually truncate, check to see if the purge
                  //  is going to fail.
                  //
  
                  if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
                                             &Buffer->AllocationSize )) {
  
                      try_return( Status = STATUS_USER_MAPPED_FILE );
                  }
  
                  FileSizeTruncated = TRUE;
  
                  OriginalFileSize = Fcb->Header.FileSize.LowPart;
                  OriginalValidDataLength = Fcb->Header.ValidDataLength.LowPart;
                  OriginalValidDataToDisk = Fcb->ValidDataToDisk;
  
                  (VOID)ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
                  ResourceAcquired = TRUE;
  
                  Fcb->Header.FileSize.LowPart = NewAllocationSize;
  
                  //
                  //  If we reduced the file size to less than the ValidDataLength,
                  //  adjust the VDL.  Likewise ValidDataToDisk.
                  //
  
                  if (Fcb->Header.ValidDataLength.LowPart > Fcb->Header.FileSize.LowPart) {
  
                      Fcb->Header.ValidDataLength.LowPart = Fcb->Header.FileSize.LowPart;
                  }
                  if (Fcb->ValidDataToDisk > Fcb->Header.FileSize.LowPart) {
  
                      Fcb->ValidDataToDisk = Fcb->Header.FileSize.LowPart;
                  }
  
              }
  
              //
              //  Now that File Size is down, actually do the truncate.
              //
  
              FatTruncateFileAllocation( IrpContext, Fcb, NewAllocationSize);
  
              //
              //  Now check if we needed to decrease the file size accordingly.
              //
  
              if ( FileSizeTruncated ) {
  
                  //
                  //  Tell the cache manager we reduced the file size.
                  //  The call is unconditional, because MM always wants to know.
                  //
  
  #if DBG
                  try {
  #endif
  
                      CcSetFileSizes( FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
  
  #if DBG
                  } except(FatBugCheckExceptionFilter( GetExceptionInformation() )) {
  
                        NOTHING;
                  }
  #endif
  
                  NT_ASSERT( FileObject->DeleteAccess || FileObject->WriteAccess );
  
                  //
                  //  There is no going back from this. If we run into problems updating
                  //  the dirent we will have to live with the consequences. Not sending
                  //  the notifies is likewise pretty benign compared to failing the entire
                  //  operation and trying to back out everything, which could fail for the
                  //  same reasons.
                  //
                  //  If you want a transacted filesystem, use NTFS ...
                  //
  
                  FileSizeTruncated = FALSE;
  
                  FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
  
                  //
                  //  Report that we just reduced the file size.
                  //
  
                  FatNotifyReportChange( IrpContext,
                                         Fcb->Vcb,
                                         Fcb,
                                         FILE_NOTIFY_CHANGE_SIZE,
                                         FILE_ACTION_MODIFIED );
              }
          }
  
      try_exit: NOTHING;
  
      } finally {
  
          if ( AbnormalTermination() && FileSizeTruncated ) {
  
              Fcb->Header.FileSize.LowPart = OriginalFileSize;
              Fcb->Header.ValidDataLength.LowPart = OriginalValidDataLength;
              Fcb->ValidDataToDisk = OriginalValidDataToDisk;
  
              //
              //  Make sure Cc knows the right filesize.
              //
  
              if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  
                  *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
              }
  
              NT_ASSERT( Fcb->Header.FileSize.LowPart <= Fcb->Header.AllocationSize.LowPart );
          }
  
          if (CacheMapInitialized) {
  
              CcUninitializeCacheMap( FileObject, NULL, NULL );
          }
  
          if (ResourceAcquired) {
  
              ExReleaseResourceLite( Fcb->Header.PagingIoResource );
  
          }
  
      }
  
      DebugTrace(-1, Dbg, "FatSetAllocationInfo -> %08lx\n", STATUS_SUCCESS);
  
      return Status;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetEndOfFileInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject,
      IN PVCB Vcb,
      IN PFCB Fcb
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the set End of File information for fat.  It either
      completes the request or enqueues it off to the fsp.
  
  Arguments:
  
      Irp - Supplies the irp being processed
  
      FileObject - Supplies the file object being processed
  
      Vcb - Supplies the Vcb being processed
  
      Fcb - Supplies the Fcb or Dcb being processed, already known not to
          be the root dcb
  
  Return Value:
  
      NTSTATUS - The result of this operation if it completes without
                 an exception.
  
  --*/
  
  {
      NTSTATUS Status = STATUS_SUCCESS;
  
      PFILE_END_OF_FILE_INFORMATION Buffer;
  
      ULONG NewFileSize = 0;
      ULONG InitialFileSize = 0;
      ULONG InitialValidDataLength = 0;
      ULONG InitialValidDataToDisk = 0;
  
      BOOLEAN CacheMapInitialized = FALSE;
      BOOLEAN UnwindFileSizes = FALSE;
      BOOLEAN ResourceAcquired = FALSE;
  
      PAGED_CODE();
  
      DebugTrace(+1, Dbg, "FatSetEndOfFileInfo...\n", 0);
  
      Buffer = Irp->AssociatedIrp.SystemBuffer;
  
      try {
  
          //
          //  File Size changes are only allowed on a file and not a directory
          //
  
          if (NodeType(Fcb) != FAT_NTC_FCB) {
  
              DebugTrace(0, Dbg, "Cannot change size of a directory\n", 0);
  
              try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
          }
  
          //
          //  Check that the new file size is legal
          //
  
          if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->EndOfFile, 0 )) {
  
              DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
  
              try_return( Status = STATUS_DISK_FULL );
          }
  
          NewFileSize = Buffer->EndOfFile.LowPart;
  
          //
          //  If we haven't yet looked up the correct AllocationSize, do so.
          //
  
          if (Fcb->Header.AllocationSize.QuadPart == FCB_LOOKUP_ALLOCATIONSIZE_HINT) {
  
              FatLookupFileAllocationSize( IrpContext, Fcb );
          }
  
          //
          //  This is kinda gross, but if the file is not cached, but there is
          //  a data section, we have to cache the file to avoid a bunch of
          //  extra work.
          //
  
          if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
              (FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
              !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
  
              if (FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ))  {
  
                  //
                  //  This IRP has raced (and lost) with a close (=>cleanup)
                  //  on the same fileobject.  We don't want to reinitialise the
                  //  cachemap here now because we'll leak it (unless we do so &
                  //  then tear it down again here,  which is too much of a change at
                  //  this stage).   So we'll just say the file is closed - which
                  //  is arguably the right thing to do anyway,  since a caller
                  //  racing operations in this way is broken.  The only stumbling
                  //  block is possibly filters - do they operate on cleaned
                  //  up fileobjects?
                  //
  
                  FatRaiseStatus( IrpContext, STATUS_FILE_CLOSED);
              }
  
              //
              //  Now initialize the cache map.
              //
  
              FatInitializeCacheMap( FileObject,
                                     (PCC_FILE_SIZES)&Fcb->Header.AllocationSize,
                                     FALSE,
                                     &FatData.CacheManagerCallbacks,
                                     Fcb );
  
              CacheMapInitialized = TRUE;
          }
  
          //
          //  Do a special case here for the lazy write of file sizes.
          //
  
          if (IoGetCurrentIrpStackLocation(Irp)->Parameters.SetFile.AdvanceOnly) {
  
              //
              //  Only attempt this if the file hasn't been "deleted on close" and
              //  this is a good FCB.
              //
  
              if (!IsFileDeleted( IrpContext, Fcb ) && (Fcb->FcbCondition == FcbGood)) {
  
                  PDIRENT Dirent = NULL;
                  PBCB DirentBcb;
  
                  //
                  //  Never have the dirent filesize larger than the fcb filesize
                  //
  
                  if (NewFileSize >= Fcb->Header.FileSize.LowPart) {
  
                      NewFileSize = Fcb->Header.FileSize.LowPart;
                  }
  
                  //
                  //  Make sure we don't set anything higher than the alloc size.
                  //
  
                  NT_ASSERT( NewFileSize <= Fcb->Header.AllocationSize.LowPart );
  
                  //
                  //  Only advance the file size, never reduce it with this call
                  //
  
                  FatGetDirentFromFcbOrDcb( IrpContext,
                                            Fcb,
                                            FALSE,
                                            &Dirent,
                                            &DirentBcb );
                  try {
  
                      if ( NewFileSize > Dirent->FileSize ) {
  
                          Dirent->FileSize = NewFileSize;
  
                          FatSetDirtyBcb( IrpContext, DirentBcb, Fcb->Vcb, TRUE );
  
                          //
                          //  Report that we just changed the file size.
                          //
  
                          FatNotifyReportChange( IrpContext,
                                                 Vcb,
                                                 Fcb,
                                                 FILE_NOTIFY_CHANGE_SIZE,
                                                 FILE_ACTION_MODIFIED );
                      }
  
                  } finally {
  
                      FatUnpinBcb( IrpContext, DirentBcb );
                  }
  
              } else {
  
                  DebugTrace(0, Dbg, "Cannot set size on deleted file.\n", 0);
              }
  
              try_return( Status = STATUS_SUCCESS );
          }
  
          //
          //  Check if the new file size is greater than the current
          //  allocation size.  If it is then we need to increase the
          //  allocation size.
          //
  
          if ( NewFileSize > Fcb->Header.AllocationSize.LowPart ) {
  
              //
              //  Change the file allocation
              //
  
              FatAddFileAllocation( IrpContext, Fcb, FileObject, NewFileSize );
          }
  
          //
          //  At this point we have enough allocation for the file.
          //  So check if we are really changing the file size
          //
  
          if (Fcb->Header.FileSize.LowPart != NewFileSize) {
  
              if ( NewFileSize < Fcb->Header.FileSize.LowPart ) {
  
                  //
                  //  Before we actually truncate, check to see if the purge
                  //  is going to fail.
                  //
  
                  if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
                                             &Buffer->EndOfFile )) {
  
                      try_return( Status = STATUS_USER_MAPPED_FILE );
                  }
  
                  //
                  //  This call is unconditional, because MM always wants to know.
                  //  Also serialize here with paging io since we are truncating
                  //  the file size.
                  //
  
                  ResourceAcquired =
                      ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
              }
  
              //
              //  Set the new file size
              //
  
              InitialFileSize = Fcb->Header.FileSize.LowPart;
              InitialValidDataLength = Fcb->Header.ValidDataLength.LowPart;
              InitialValidDataToDisk = Fcb->ValidDataToDisk;
              UnwindFileSizes = TRUE;
  
              Fcb->Header.FileSize.LowPart = NewFileSize;
  
              //
              //  If we reduced the file size to less than the ValidDataLength,
              //  adjust the VDL.  Likewise ValidDataToDisk.
              //
  
              if (Fcb->Header.ValidDataLength.LowPart > NewFileSize) {
  
                  Fcb->Header.ValidDataLength.LowPart = NewFileSize;
              }
  
              if (Fcb->ValidDataToDisk > NewFileSize) {
  
                  Fcb->ValidDataToDisk = NewFileSize;
              }
  
              DebugTrace(0, Dbg, "New file size is 0x%08lx.\n", NewFileSize);
  
              //
              //  We must now update the cache mapping (benign if not cached).
              //
  
              CcSetFileSizes( FileObject,
                              (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
  
              FatSetFileSizeInDirent( IrpContext, Fcb, NULL );
  
              //
              //  Report that we just changed the file size.
              //
  
              FatNotifyReportChange( IrpContext,
                                     Vcb,
                                     Fcb,
                                     FILE_NOTIFY_CHANGE_SIZE,
                                     FILE_ACTION_MODIFIED );
  
              //
              //  Mark the fact that the file will need to checked for
              //  truncation on cleanup.
              //
  
              SetFlag( Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE );
          }
  
          //
          //  Set this handle as having modified the file
          //
  
          FileObject->Flags |= FO_FILE_MODIFIED;
  
          //
          //  Set our return status to success
          //
  
          Status = STATUS_SUCCESS;
  
      try_exit: NOTHING;
  
          FatUnpinRepinnedBcbs( IrpContext );
  
      } finally {
  
          DebugUnwind( FatSetEndOfFileInfo );
  
          if (AbnormalTermination() && UnwindFileSizes) {
  
              Fcb->Header.FileSize.LowPart = InitialFileSize;
              Fcb->Header.ValidDataLength.LowPart = InitialValidDataLength;
              Fcb->ValidDataToDisk = InitialValidDataToDisk;
  
              if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  
                  *CcGetFileSizePointer(FileObject) = Fcb->Header.FileSize;
              }
              
              //
              //  WinSE bug #307418 "Occasional data corruption when
              //  standby/resume while copying files to removable FAT
              //  formatted media".
              //  On system suspend FatUnpinRepinnedBcbs() can fail
              //  because the underlying drive is already marked with DO_VERIFY
              //  flag. FatUnpinRepinnedBcbs() will raise in this case and
              //  the file size changes will be un-rolled in FCB but the change
              //  to Dirent file still can make it to the disk since its BCB
              //  will not be purged by FatUnpinRepinnedBcbs(). In this case
              //  we'll also try to un-roll the change to Dirent to keep
              //  in-memory and on-disk metadata in sync.
              //
              
              FatSetFileSizeInDirentNoRaise( IrpContext, Fcb, NULL );
  
          }
  
          if (CacheMapInitialized) {
  
              CcUninitializeCacheMap( FileObject, NULL, NULL );
          }
  
          if ( ResourceAcquired ) {
  
              ExReleaseResourceLite( Fcb->Header.PagingIoResource );
          }
  
          DebugTrace(-1, Dbg, "FatSetEndOfFileInfo -> %08lx\n", Status);
      }
  
      return Status;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  NTSTATUS
  FatSetValidDataLengthInfo (
      IN PIRP_CONTEXT IrpContext,
      IN PIRP Irp,
      IN PFILE_OBJECT FileObject,
      IN PFCB Fcb,
      IN PCCB Ccb
      )
  
  /*++
  
  Routine Description:
  
      This routine performs the set valid data length information for fat.  It either
      completes the request or enqueues it off to the fsp.
  
  Arguments:
  
      Irp - Supplies the irp being processed
  
      FileObject - Supplies the file object being processed
  
      Fcb - Supplies the Fcb or Dcb being processed, already known not to
          be the root dcb
  
      Ccb - Supplies the Ccb corresponding to the handle opening the source
          file
  
  Return Value:
  
      NTSTATUS - The result of this operation if it completes without
                 an exception.
  
  --*/
  
  {
      NTSTATUS Status = STATUS_SUCCESS;
  
      PFILE_VALID_DATA_LENGTH_INFORMATION Buffer;
  
      ULONG NewValidDataLength;
      BOOLEAN ResourceAcquired = FALSE;
  
      PAGED_CODE();
  
      UNREFERENCED_PARAMETER( IrpContext );
  
      DebugTrace(+1, Dbg, "FatSetValidDataLengthInfo...\n", 0);
  
      Buffer = Irp->AssociatedIrp.SystemBuffer;
  
      try {
  
          //
          //  User must have manage volume privilege to explicitly tweak the VDL
          //
          
          if ((Ccb == NULL) || !FlagOn( Ccb->Flags, CCB_FLAG_MANAGE_VOLUME_ACCESS )) {
  
              try_return( Status = STATUS_INVALID_PARAMETER );
          }
  
          //
          //  Valid data length changes are only allowed on a file and not a directory
          //
  
          if (NodeType(Fcb) != FAT_NTC_FCB) {
  
              DebugTrace(0, Dbg, "Cannot change VDL of a directory\n", 0);
  
              try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
          }
  
          //
          //  Check that the new file size is legal
          //
  
          if (!FatIsIoRangeValid( Fcb->Vcb, Buffer->ValidDataLength, 0 )) {
  
              DebugTrace(0, Dbg, "Illegal allocation size\n", 0);
  
              try_return( Status = STATUS_DISK_FULL );
          }
  
          NewValidDataLength = Buffer->ValidDataLength.LowPart;
  
          //
          //  VDL can only move forward
          //
  
          if ((NewValidDataLength < Fcb->Header.ValidDataLength.LowPart) ||
              (NewValidDataLength > Fcb->Header.FileSize.LowPart)) {
          
              try_return( Status = STATUS_INVALID_PARAMETER );
          }
  
          //
          //  We can't change the VDL without being able to purge. This should stay
          //  constant since we own everything exclusive
          //
  
          if (!MmCanFileBeTruncated( FileObject->SectionObjectPointer,
                                     &Buffer->ValidDataLength )) {
  
              try_return( Status = STATUS_USER_MAPPED_FILE );
          }
  
          //
          //  Flush old data out and purge the cache so we can see new data.
          //
  
          if (FileObject->SectionObjectPointer->DataSectionObject != NULL) {
  
              ResourceAcquired =
                  ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE );
  
              CcFlushCache( FileObject->SectionObjectPointer,
                            NULL,
                            0,
                            &Irp->IoStatus );
  
              if (!NT_SUCCESS( Irp->IoStatus.Status )) {
  
                  try_return( Irp->IoStatus.Status );
              }
  
              CcPurgeCacheSection( FileObject->SectionObjectPointer,
                                   NULL,
                                   0,
                                   FALSE );
          }
  
          //
          //  Set the new ValidDataLength, Likewise ValidDataToDisk.
          //
  
          Fcb->Header.ValidDataLength.LowPart = NewValidDataLength;
          Fcb->ValidDataToDisk = NewValidDataLength;
  
          DebugTrace(0, Dbg, "New VDL is 0x%08lx.\n", NewValidDataLength);
  
          //
          //  We must now update the cache mapping.
          //
  
          if (FileObject->SectionObjectPointer->SharedCacheMap != NULL) {
  
              CcSetFileSizes( FileObject,
                              (PCC_FILE_SIZES)&Fcb->Header.AllocationSize );
          }
  
          //
          //  Set this handle as having modified the file
          //
  
          FileObject->Flags |= FO_FILE_MODIFIED;
  
          //
          //  Set our return status to success
          //
  
          Status = STATUS_SUCCESS;
  
      try_exit: NOTHING;
  
      } finally {
  
          DebugUnwind( FatSetValidDataLengthInfo );
  
          if (ResourceAcquired) {
  
              ExReleaseResourceLite( Fcb->Header.PagingIoResource );
          }
  
          DebugTrace(-1, Dbg, "FatSetValidDataLengthInfo -> %08lx\n", Status);
      }
  
      return Status;
  }
  
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatDeleteFile (
      IN PIRP_CONTEXT IrpContext,
      IN PDCB TargetDcb,
      IN ULONG LfnOffset,
      IN ULONG DirentOffset,
      IN PDIRENT Dirent,
      IN PUNICODE_STRING Lfn
      )
  {
      PFCB Fcb;
      PLIST_ENTRY Links;
  
      PAGED_CODE();
  
      //
      //  We can do the replace by removing the other Fcb(s) from
      //  the prefix table.
      //
  
      for (Links = TargetDcb->Specific.Dcb.ParentDcbQueue.Flink;
           Links != &TargetDcb->Specific.Dcb.ParentDcbQueue;
           Links = Links->Flink) {
  
          Fcb = CONTAINING_RECORD( Links, FCB, ParentDcbLinks );
  
          if (FlagOn(Fcb->FcbState, FCB_STATE_NAMES_IN_SPLAY_TREE) &&
              (Fcb->DirentOffsetWithinDirectory == DirentOffset)) {
  
              NT_ASSERT( NodeType(Fcb) == FAT_NTC_FCB );
              NT_ASSERT( Fcb->LfnOffsetWithinDirectory == LfnOffset );
  
              if ( Fcb->UncleanCount != 0 ) {
                  
  #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
                  FatBugCheck(0,0,0);
  
              } else {
  
                  PERESOURCE Resource;
  
                  //
                  //  Make this fcb "appear" deleted, synchronizing with
                  //  paging IO.
                  //
  
                  FatRemoveNames( IrpContext, Fcb );
  
                  Resource = Fcb->Header.PagingIoResource;
  
                  (VOID)ExAcquireResourceExclusiveLite( Resource, TRUE );
  
                  SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);
  
                  Fcb->ValidDataToDisk = 0;
                  Fcb->Header.FileSize.QuadPart =
                  Fcb->Header.ValidDataLength.QuadPart = 0;
  
                  Fcb->FirstClusterOfFile = 0;
  
                  ExReleaseResourceLite( Resource );
              }
          }
      }
  
      //
      //  The file is not currently opened so we can delete the file
      //  that is being overwritten.  To do the operation we dummy
      //  up an fcb, truncate allocation, delete the fcb, and delete
      //  the dirent.
      //
  
      Fcb = FatCreateFcb( IrpContext,
                          TargetDcb->Vcb,
                          TargetDcb,
                          LfnOffset,
                          DirentOffset,
                          Dirent,
                          Lfn,
                          FALSE,
                          FALSE );
  
      Fcb->Header.FileSize.LowPart = 0;
  
      try {
  
          FatTruncateFileAllocation( IrpContext, Fcb, 0 );
  
          FatDeleteDirent( IrpContext, Fcb, NULL, TRUE );
  
      } finally {
  
          FatDeleteFcb( IrpContext, &Fcb );
      }
  }
  
  //
  //  Internal Support Routine
  //
  
  _Requires_lock_held_(_Global_critical_region_)
  VOID
  FatRenameEAs (
      IN PIRP_CONTEXT IrpContext,
      IN PFCB Fcb,
      IN USHORT ExtendedAttributes,
      IN POEM_STRING OldOemName
      )
  {
      BOOLEAN LockedEaFcb = FALSE;
  
      PBCB EaBcb = NULL;
      PDIRENT EaDirent;
      EA_RANGE EaSetRange;
      PEA_SET_HEADER EaSetHeader;
  
      PVCB Vcb;
  
      PAGED_CODE();
  
      RtlZeroMemory( &EaSetRange, sizeof( EA_RANGE ));
  
      Vcb = Fcb->Vcb;
  
      try {
  
          //
          //  Use a try-except to catch any errors.
          //
  
          try {
  
  
              //
              //  Try to get the Ea file object.  Return FALSE on failure.
              //
  
              FatGetEaFile( IrpContext,
                            Vcb,
                            &EaDirent,
                            &EaBcb,
                            FALSE,
                            FALSE );
  
              LockedEaFcb = TRUE;
  
              //
              //  If we didn't get the file because it doesn't exist, then the
              //  disk is corrupted.  We do nothing here.
              //
  
              if (Vcb->VirtualEaFile != NULL) {
  
                  //
                  //  Try to pin down the Ea set header for the index in the
                  //  dirent.  If the operation doesn't complete, return FALSE
                  //  from this routine.
                  //
  
                  FatReadEaSet( IrpContext,
                                Vcb,
                                ExtendedAttributes,
                                OldOemName,
                                FALSE,
                                &EaSetRange );
  
                  EaSetHeader = (PEA_SET_HEADER) EaSetRange.Data;
  
                  //
                  //  We now have the Ea set header for this file.  We simply
                  //  overwrite the owning file name.
                  //
  
                  RtlZeroMemory( EaSetHeader->OwnerFileName, 14 );
  
                  RtlCopyMemory( EaSetHeader->OwnerFileName,
                                 Fcb->ShortName.Name.Oem.Buffer,
                                 Fcb->ShortName.Name.Oem.Length );
  
                  FatMarkEaRangeDirty( IrpContext, Vcb->VirtualEaFile, &EaSetRange );
                  FatUnpinEaRange( IrpContext, &EaSetRange );
  
                  CcFlushCache( Vcb->VirtualEaFile->SectionObjectPointer, NULL, 0, NULL );
              }
  
          } except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
  
              //
              //  We catch all exceptions that Fat catches, but don't do
              //  anything with them.
              //
          }
  
      } finally {
  
          //
          //  Unpin the EaDirent and the EaSetHeader if pinned.
          //
  
          FatUnpinBcb( IrpContext, EaBcb );
          FatUnpinEaRange( IrpContext, &EaSetRange );
  
          //
          //  Release the Fcb for the Ea file if locked.
          //
  
          if (LockedEaFcb) {
  
              FatReleaseFcb( IrpContext, Vcb->EaFcb );
          }
      }
  
      return;
  }
  
  