reactos/drivers/filesystems/fastfat/close.c

240 lines
6.5 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
[FASTFAT] - Implement vfatPrepareTargetForRename() that prepares for renaming. It tries to open target and deletes it if it exists and if allowed. And then, it opens the parent directory. - Implement VfatSetRenameInformation() that actually does the renaming (call as SetInformationFile). It handles cases we we have (or we don't have) TargetDirectory provided. It sends notifications as appropriated on demands. - Implement vfatRenameEntry() that renames an entry in place. So far, it only supports FATX entries renaming. FAT entries are a bit more complex. It falls back to vfatMoveEntry() in later cases. - Implement VfatMoveEntry() that will move an entry accross directories (or in place for FAT). Its principles are simple: it deletes the entry in old parent, and recreate it in new parent, keeping file metadata & data. - Modify VfatDelEntry() and VfatAddEntry() so that they can handle deleting an entry without touching its data and adding an entry with an already provided FCB and thus use the given metadata. - Implement vfatDelFCBFromTable() which is just old code moved to new routine to allow reuse. It deletes a FCB entry from hash table. Doesn't deal with references! - Implement vfatMakeFullName() which is mostly old code moved to new routine to allow reuse. It allocates buffer and copy data for FCB full name. - Implement vfatUpdateFCB() that will update a FCB with new names and parent. It will remove anything related to old name and will recreate using new data. It will adjust references count. - Modify vfatMakeFCBFromDirEntry() so that it calls vfatMakeFullName(). - Modify vfatReleaseFCB() so that it calls vfatDelFCBFromTable(). - Revert VfatOpenFile() to its previous features. - Modify VfatCreateFile() to reimplement support for SL_OPEN_TARGET_DIRECTORY. It is way less hackish than previously. It also properly opens parent now, by incrementing its handle count and by setting appropriate access rights. [KERNEL32] - Rewritten MoveFileWithProgressW() to implement all the missing features that are used in Windows 2k3 including links and reparse points. - Implemented BasepMoveFileDelayed() to replace deprecated add_boot_rename_entry(). This functions is matching the features implemented in SMSS. - Implemented BasepMoveFileCopyProgress() which is used in MoveFileWithProgressW(). - Stubbed BasepNotifyTrackingService() which is not use at the moment (FastFAT, even in Windows doesn't provide such feature). - Reimplemented ReplaceFileA(), MoveFileWithProgressA() to quit Winisms and use our internal helpers. - Make MoveFileX() use MoveFileWithProgressX() directly. - Fixed a few prototypes. TL;DR: This (huge) commit implements file and directory renaming in FastFAT driver. This allows getting rid of old implementation in kernel32 where files were force copied. A feature is still missing, but Jérôme should implement it anytime soon (he prototyped it already): moving directories across volumes. This requires some work in BasepCopyFileExW(). Kudos to all the devs who helped me on this: Christoph, Hervé, Jérôme, Thomas. This finally allows killing CR-52... It was about time! svn path=/trunk/; revision=64836
2014-10-19 21:38:32 +00:00
* FILE: drivers/filesystems/fastfat/close.c
* PURPOSE: VFAT Filesystem
* PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
* Pierre Schweitzer (pierre@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include "vfat.h"
#define NDEBUG
#include <debug.h>
/* FUNCTIONS ****************************************************************/
VOID
VfatCommonCloseFile(
PDEVICE_EXTENSION DeviceExt,
PVFATFCB pFcb)
{
[FASTFAT] Improvements for volume dismount + minor bugfixing. - Cache the RootFcb so that its cleanup can be handled separately during dismounting. - Force volume dismount at cleanup if the VCB_DISMOUNT_PENDING flag is set. - Actually dismount a volume if its VCB has been flagged as not good, or if we force dismounting. NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers as well as MS' fastfat, perform a comparison check of the current VCB's VPB ReferenceCount with some sort of "dangling"/"residual" open count. It seems to be related to the fact that the volume root directory as well as auxiliary data stream(s) are still opened, and only these are allowed to be opened at that moment. After analysis it appears that for the ReactOS' fastfat, this number is equal to "3". - On dismounting, cleanup and destroy the RootFcb, VolumeFcb and the FATFileObject. Then cleanup the SpareVPB or the IoVPB members, and finish by removing the dismounted volume from the VolumeListEntry and cleaning up the notify synchronization object and the resources. - During dismounting, and on shutdown, flush the volume before resetting its dirty bit. - On shutdown, after volume flushing, try to unmount it without forcing. - Release the VCB resources only when we actually dismount the volume in VfatCheckForDismount(). - Initialize first the notify list and the synchronization object, before sending the FSRTL_VOLUME_MOUNT notification. - If we failed at mounting a volume but its VCB's FATFileObject was already initialized, first call CcUninitializeCacheMap() on it before dereferencing it. - Send FSRTL_VOLUME_LOCK, FSRTL_VOLUME_LOCK_FAILED and FSRTL_VOLUME_UNLOCK notifications during volume locking (and failure) and volume unlocking. - Flush the volume before locking it, and clean its dirty bit if needed. NOTE: In addition to checking for VCB_CLEAR_DIRTY, we also check for the presence of the VCB_IS_DIRTY flag before cleaning up the dirty bit: this allows us to not re-clean the bit if it has been previously cleaned. This is needed for instance in this scenario: - The volume is locked (it gets flushed and the dirty bit is possibly cleared); - The volume then gets formatted with a completely different FS, that possibly clears up the first sector (e.g. BTRFS ignores 1st sector); - The volume is then dismounted: if we didn't check whether VCB_IS_DIRTY was set prior to resetting it, we could attempt clearing it again! But now that the volume's filesystem has been completely changed, we would then try to modify the dirty bit on an erroneous position on disk! That's why it should not be touched in this case during dismounting. - The volume is unlocked (same comment as above), and later can be detected as being BTRFS.
2018-11-11 16:17:48 +00:00
/* Nothing to do for volumes or for the FAT file object */
if (BooleanFlagOn(pFcb->Flags, FCB_IS_FAT | FCB_IS_VOLUME))
{
return;
}
/* If cache is still initialized, release it
* This only affects directories
*/
if (pFcb->OpenHandleCount == 0 && BooleanFlagOn(pFcb->Flags, FCB_CACHE_INITIALIZED))
{
PFILE_OBJECT tmpFileObject;
tmpFileObject = pFcb->FileObject;
if (tmpFileObject != NULL)
{
pFcb->FileObject = NULL;
CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
ClearFlag(pFcb->Flags, FCB_CACHE_INITIALIZED);
ObDereferenceObject(tmpFileObject);
}
}
#ifdef KDBG
pFcb->Flags |= FCB_CLOSED;
#endif
/* Release the FCB, we likely cause its deletion */
vfatReleaseFCB(DeviceExt, pFcb);
}
VOID
NTAPI
VfatCloseWorker(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context)
{
PLIST_ENTRY Entry;
PVFATFCB pFcb;
PDEVICE_EXTENSION Vcb;
PVFAT_CLOSE_CONTEXT CloseContext;
BOOLEAN ConcurrentDeletion;
/* Start removing work items */
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
while (!IsListEmpty(&VfatGlobalData->CloseListHead))
{
Entry = RemoveHeadList(&VfatGlobalData->CloseListHead);
CloseContext = CONTAINING_RECORD(Entry, VFAT_CLOSE_CONTEXT, CloseListEntry);
/* One less */
--VfatGlobalData->CloseCount;
/* Reset its entry to detect concurrent deletions */
InitializeListHead(&CloseContext->CloseListEntry);
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
/* Get the elements */
Vcb = CloseContext->Vcb;
pFcb = CloseContext->Fcb;
ExAcquireResourceExclusiveLite(&Vcb->DirResource, TRUE);
/* If it didn't got deleted in between */
if (BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE))
{
/* Close it! */
DPRINT("Late closing: %wZ\n", &pFcb->PathNameU);
ClearFlag(pFcb->Flags, FCB_DELAYED_CLOSE);
pFcb->CloseContext = NULL;
VfatCommonCloseFile(Vcb, pFcb);
ConcurrentDeletion = FALSE;
}
else
{
/* Otherwise, mark not to delete it */
ConcurrentDeletion = TRUE;
}
ExReleaseResourceLite(&Vcb->DirResource);
/* If we were the fastest, delete the context */
if (!ConcurrentDeletion)
{
ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext);
}
/* Lock again the list */
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
}
/* We're done, bye! */
VfatGlobalData->CloseWorkerRunning = FALSE;
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
}
NTSTATUS
VfatPostCloseFile(
PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject)
{
PVFAT_CLOSE_CONTEXT CloseContext;
/* Allocate a work item */
CloseContext = ExAllocateFromPagedLookasideList(&VfatGlobalData->CloseContextLookasideList);
if (CloseContext == NULL)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Set relevant fields */
CloseContext->Vcb = DeviceExt;
CloseContext->Fcb = FileObject->FsContext;
CloseContext->Fcb->CloseContext = CloseContext;
/* Acquire the lock to insert in list */
ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
/* One more element */
InsertTailList(&VfatGlobalData->CloseListHead, &CloseContext->CloseListEntry);
++VfatGlobalData->CloseCount;
/* If we have more than 16 items in list, and no worker thread
* start a new one
*/
if (VfatGlobalData->CloseCount > 16 && !VfatGlobalData->CloseWorkerRunning)
{
VfatGlobalData->CloseWorkerRunning = TRUE;
IoQueueWorkItem(VfatGlobalData->CloseWorkItem, VfatCloseWorker, CriticalWorkQueue, NULL);
}
/* We're done */
ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
return STATUS_SUCCESS;
}
/*
* FUNCTION: Closes a file
*/
NTSTATUS
VfatCloseFile(
PDEVICE_EXTENSION DeviceExt,
PFILE_OBJECT FileObject)
{
PVFATFCB pFcb;
PVFATCCB pCcb;
BOOLEAN IsVolume;
NTSTATUS Status = STATUS_SUCCESS;
DPRINT("VfatCloseFile(DeviceExt %p, FileObject %p)\n",
DeviceExt, FileObject);
/* FIXME : update entry in directory? */
pCcb = (PVFATCCB) (FileObject->FsContext2);
pFcb = (PVFATFCB) (FileObject->FsContext);
if (pFcb == NULL)
{
return STATUS_SUCCESS;
}
IsVolume = BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME);
if (pCcb)
{
vfatDestroyCCB(pCcb);
}
/* If we have to close immediately, or if delaying failed, close */
if (VfatGlobalData->ShutdownStarted || !BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE) ||
!NT_SUCCESS(VfatPostCloseFile(DeviceExt, FileObject)))
{
VfatCommonCloseFile(DeviceExt, pFcb);
}
FileObject->FsContext2 = NULL;
FileObject->FsContext = NULL;
FileObject->SectionObjectPointer = NULL;
#ifdef ENABLE_SWAPOUT
if (IsVolume && DeviceExt->OpenHandleCount == 0)
{
VfatCheckForDismount(DeviceExt, FALSE);
}
#endif
return Status;
}
/*
* FUNCTION: Closes a file
*/
NTSTATUS
VfatClose(
PVFAT_IRP_CONTEXT IrpContext)
{
NTSTATUS Status;
DPRINT("VfatClose(DeviceObject %p, Irp %p)\n", IrpContext->DeviceObject, IrpContext->Irp);
if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
{
DPRINT("Closing file system\n");
IrpContext->Irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}
if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
{
return VfatMarkIrpContextForQueue(IrpContext);
}
Status = VfatCloseFile(IrpContext->DeviceExt, IrpContext->FileObject);
ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
IrpContext->Irp->IoStatus.Information = 0;
return Status;
}
/* EOF */