* Sync up to trunk head (r65298).
[reactos.git] / drivers / filesystems / fastfat / dirwr.c
index 0e948ad..21b0e69 100644 (file)
@@ -1,16 +1,21 @@
 /*
  * COPYRIGHT:        See COPYING in the top level directory
  * PROJECT:          ReactOS kernel
- * FILE:             drivers/fs/vfat/dirwr.c
+ * FILE:             drivers/filesystems/fastfat/dirwr.c
  * PURPOSE:          VFAT Filesystem : write in directory
+ * PROGRAMMER:       Rex Jolliff (rex@lvcablemodem.com)
+ *                   Herve Poussineau (reactos@poussine.freesurf.fr)
+ *                   Pierre Schweitzer (pierre@reactos.org)
  *
  */
 
 /* INCLUDES *****************************************************************/
 
-#define NDEBUG
 #include "vfat.h"
 
+#define NDEBUG
+#include <debug.h>
+
 /*
  * update an existing FAT entry
  */
@@ -37,7 +42,7 @@ VfatUpdateEntry(
         dirIndex = pFcb->dirIndex;
     }
 
-    DPRINT("updEntry dirIndex %d, PathName \'%wZ\'\n", dirIndex, &pFcb->PathNameU);
+    DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex, &pFcb->PathNameU);
 
     if (vfatFCBIsRoot(pFcb) || (pFcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
     {
@@ -64,6 +69,70 @@ VfatUpdateEntry(
     }
 }
 
+/*
+ * rename an existing FAT entry
+ */
+NTSTATUS
+vfatRenameEntry(
+    IN PDEVICE_EXTENSION DeviceExt,
+    IN PVFATFCB pFcb,
+    IN PUNICODE_STRING FileName,
+    IN BOOLEAN CaseChangeOnly)
+{
+    OEM_STRING NameA;
+    ULONG StartIndex;
+    PVOID Context = NULL;
+    LARGE_INTEGER Offset;
+    PFATX_DIR_ENTRY pDirEntry;
+    UNICODE_STRING ShortName;
+    NTSTATUS Status;
+
+    DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt, pFcb, FileName, CaseChangeOnly);
+
+    if (pFcb->Flags & FCB_IS_FATX_ENTRY)
+    {
+        /* Open associated dir entry */
+        StartIndex = pFcb->startIndex;
+        Offset.u.HighPart = 0;
+        Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
+        if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FATX_DIR_ENTRY), TRUE,
+                       &Context, (PVOID*)&pDirEntry))
+        {
+            DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE);
+            return STATUS_UNSUCCESSFUL;
+        }
+
+        pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))];
+
+        /* Set file name */
+        NameA.Buffer = (PCHAR)pDirEntry->Filename;
+        NameA.Length = 0;
+        NameA.MaximumLength = 42;
+        RtlUnicodeStringToOemString(&NameA, FileName, FALSE);
+        pDirEntry->FilenameLength = (unsigned char)NameA.Length;
+
+        CcSetDirtyPinnedData(Context, NULL);
+        CcUnpinData(Context);
+
+        /* Update FCB */
+        ShortName.Length = 0;
+        ShortName.MaximumLength = 0;
+        ShortName.Buffer = NULL;
+        Status = vfatUpdateFCB(DeviceExt, pFcb, FileName, &ShortName, pFcb->parentFcb);
+        if (NT_SUCCESS(Status))
+        {
+            CcPurgeCacheSection(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, FALSE);
+        }
+
+        return Status;
+    }
+    else
+    {
+        /* This we cannot handle properly, move file - would likely need love */
+        return VfatMoveEntry(DeviceExt, pFcb, FileName, pFcb->parentFcb);
+    }
+}
+
 /*
  * try to find contiguous entries frees in directory,
  * extend a directory if is neccesary
@@ -98,9 +167,11 @@ vfatFindDirSpace(
             {
                 CcUnpinData(Context);
             }
-            /* FIXME: check return value */
-            CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
-                      TRUE, &Context, (PVOID*)&pFatEntry);
+            if (!CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
+                      TRUE, &Context, (PVOID*)&pFatEntry))
+            {
+                return FALSE;
+            }
             FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
         }
         if (ENTRY_END(DeviceExt, pFatEntry))
@@ -152,8 +223,11 @@ vfatFindDirSpace(
             /* clear the new dir cluster */
             FileOffset.u.LowPart = (ULONG)(pDirFcb->RFCB.FileSize.QuadPart -
                                            DeviceExt->FatInfo.BytesPerCluster);
-            CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
-                      TRUE, &Context, (PVOID*)&pFatEntry);
+            if (!CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster,
+                      TRUE, &Context, (PVOID*)&pFatEntry))
+            {
+                return FALSE;
+            }
             if (DeviceExt->Flags & VCB_IS_FATX)
                 memset(pFatEntry, 0xff, DeviceExt->FatInfo.BytesPerCluster);
             else
@@ -163,8 +237,11 @@ vfatFindDirSpace(
         {
             /* clear the entry after the last new entry */
             FileOffset.u.LowPart = (*start + nbSlots) * SizeDirEntry;
-            CcPinRead(pDirFcb->FileObject, &FileOffset, SizeDirEntry,
-                TRUE, &Context, (PVOID*)&pFatEntry);
+            if (!CcPinRead(pDirFcb->FileObject, &FileOffset, SizeDirEntry,
+                TRUE, &Context, (PVOID*)&pFatEntry))
+            {
+                return FALSE;
+            }
             if (DeviceExt->Flags & VCB_IS_FATX)
                 memset(pFatEntry, 0xff, SizeDirEntry);
             else
@@ -176,7 +253,7 @@ vfatFindDirSpace(
             CcUnpinData(Context);
         }
     }
-    DPRINT("nbSlots %d nbFree %d, entry number %d\n", nbSlots, nbFree, *start);
+    DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots, nbFree, *start);
     return TRUE;
 }
 
@@ -190,7 +267,8 @@ FATAddEntry(
     IN PVFATFCB* Fcb,
     IN PVFATFCB ParentFcb,
     IN ULONG RequestedOptions,
-    IN UCHAR ReqAttr)
+    IN UCHAR ReqAttr,
+    IN PVFAT_MOVE_CONTEXT MoveContext)
 {
     PVOID Context = NULL;
     PFAT_DIR_ENTRY pFatEntry;
@@ -220,7 +298,7 @@ FATAddEntry(
 
     /* nb of entry needed for long name+normal entry */
     nbSlots = (DirContext.LongNameU.Length / sizeof(WCHAR) + 12) / 13 + 1;
-    DPRINT("NameLen= %d, nbSlots =%d\n", DirContext.LongNameU.Length / sizeof(WCHAR), nbSlots);
+    DPRINT("NameLen= %u, nbSlots =%u\n", DirContext.LongNameU.Length / sizeof(WCHAR), nbSlots);
     Buffer = ExAllocatePoolWithTag(NonPagedPool, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY), TAG_VFAT);
     if (Buffer == NULL)
     {
@@ -318,7 +396,7 @@ FATAddEntry(
             needLong = TRUE;
         }
     }
-    DPRINT("'%s', '%wZ', needTilde=%d, needLong=%d\n",
+    DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
            aName, &DirContext.LongNameU, needTilde, needLong);
     memset(DirContext.DirEntry.Fat.ShortName, ' ', 11);
     for (i = 0; i < 8 && aName[i] && aName[i] != '.'; i++)
@@ -375,6 +453,13 @@ FATAddEntry(
     DirContext.DirEntry.Fat.UpdateDate = DirContext.DirEntry.Fat.CreationDate;
     DirContext.DirEntry.Fat.UpdateTime = DirContext.DirEntry.Fat.CreationTime;
     DirContext.DirEntry.Fat.AccessDate = DirContext.DirEntry.Fat.CreationDate;
+    /* If it's moving, preserve creation time and file size */
+    if (MoveContext != NULL)
+    {
+        DirContext.DirEntry.Fat.CreationDate = MoveContext->CreationDate;
+        DirContext.DirEntry.Fat.CreationTime = MoveContext->CreationTime;
+        DirContext.DirEntry.Fat.FileSize = MoveContext->FileSize;
+    }
 
     if (needLong)
     {
@@ -413,17 +498,36 @@ FATAddEntry(
     DirContext.DirIndex = DirContext.StartIndex + nbSlots - 1;
     if (RequestedOptions & FILE_DIRECTORY_FILE)
     {
-        CurrentCluster = 0;
-        Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE);
-        if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
+        /* If we aren't moving, use next */
+        if (MoveContext == NULL)
         {
-            ExFreePoolWithTag(Buffer, TAG_VFAT);
-            if (!NT_SUCCESS(Status))
+            CurrentCluster = 0;
+            Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE);
+            if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
             {
-                return Status;
+                ExFreePoolWithTag(Buffer, TAG_VFAT);
+                if (!NT_SUCCESS(Status))
+                {
+                    return Status;
+                }
+                return STATUS_DISK_FULL;
             }
-            return STATUS_DISK_FULL;
         }
+        else
+        {
+            CurrentCluster = MoveContext->FirstCluster;
+        }
+
+        if (DeviceExt->FatInfo.FatType == FAT32)
+        {
+            DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
+        }
+        DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
+    }
+    else if (MoveContext != NULL)
+    {
+        CurrentCluster = MoveContext->FirstCluster;
+
         if (DeviceExt->FatInfo.FatType == FAT32)
         {
             DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
@@ -437,8 +541,12 @@ FATAddEntry(
     if (DirContext.StartIndex / i == DirContext.DirIndex / i)
     {
         /* one cluster */
-        CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY),
-                  TRUE, &Context, (PVOID*)&pFatEntry);
+        if (!CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY),
+                  TRUE, &Context, (PVOID*)&pFatEntry))
+        {
+            ExFreePoolWithTag(Buffer, TAG_VFAT);
+            return STATUS_UNSUCCESSFUL;
+        }
         if (nbSlots > 1)
         {
             RtlCopyMemory(pFatEntry, Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
@@ -451,15 +559,23 @@ FATAddEntry(
         size = DeviceExt->FatInfo.BytesPerCluster -
                (DirContext.StartIndex * sizeof(FAT_DIR_ENTRY)) % DeviceExt->FatInfo.BytesPerCluster;
         i = size / sizeof(FAT_DIR_ENTRY);
-        CcPinRead(ParentFcb->FileObject, &FileOffset, size, TRUE,
-                  &Context, (PVOID*)&pFatEntry);
+        if (!CcPinRead(ParentFcb->FileObject, &FileOffset, size, TRUE,
+                  &Context, (PVOID*)&pFatEntry))
+        {
+            ExFreePoolWithTag(Buffer, TAG_VFAT);
+            return STATUS_UNSUCCESSFUL;
+        }
         RtlCopyMemory(pFatEntry, Buffer, size);
         CcSetDirtyPinnedData(Context, NULL);
         CcUnpinData(Context);
         FileOffset.u.LowPart += size;
-        CcPinRead(ParentFcb->FileObject, &FileOffset,
+        if (!CcPinRead(ParentFcb->FileObject, &FileOffset,
                   nbSlots * sizeof(FAT_DIR_ENTRY) - size,
-                  TRUE, &Context, (PVOID*)&pFatEntry);
+                  TRUE, &Context, (PVOID*)&pFatEntry))
+        {
+            ExFreePoolWithTag(Buffer, TAG_VFAT);
+            return STATUS_UNSUCCESSFUL;
+        }
         if (nbSlots - 1 > i)
         {
             RtlCopyMemory(pFatEntry, (PVOID)(Buffer + size), (nbSlots - 1 - i) * sizeof(FAT_DIR_ENTRY));
@@ -469,7 +585,17 @@ FATAddEntry(
     CcSetDirtyPinnedData(Context, NULL);
     CcUnpinData(Context);
 
-    Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
+    if (MoveContext != NULL)
+    {
+        /* We're modifying an existing FCB - likely rename/move */
+        Status = vfatUpdateFCB(DeviceExt, *Fcb, &DirContext.LongNameU, &DirContext.ShortNameU, ParentFcb);
+        (*Fcb)->dirIndex = DirContext.DirIndex;
+        (*Fcb)->startIndex = DirContext.StartIndex;
+    }
+    else
+    {
+        Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
+    }
     if (!NT_SUCCESS(Status))
     {
         ExFreePoolWithTag(Buffer, TAG_VFAT);
@@ -482,15 +608,23 @@ FATAddEntry(
     if (RequestedOptions & FILE_DIRECTORY_FILE)
     {
         FileOffset.QuadPart = 0;
-        CcPinRead((*Fcb)->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
-                  &Context, (PVOID*)&pFatEntry);
-        /* clear the new directory cluster */
-        RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
-        /* create '.' and '..' */
-        RtlCopyMemory(&pFatEntry[0].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
-        RtlCopyMemory(pFatEntry[0].ShortName, ".          ", 11);
-        RtlCopyMemory(&pFatEntry[1].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
-        RtlCopyMemory(pFatEntry[1].ShortName, "..         ", 11);
+        if (!CcPinRead((*Fcb)->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, TRUE,
+                  &Context, (PVOID*)&pFatEntry))
+        {
+            ExFreePoolWithTag(Buffer, TAG_VFAT);
+            return STATUS_UNSUCCESSFUL;
+        }
+        /* clear the new directory cluster if not moving */
+        if (MoveContext == NULL)
+        {
+            RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
+            /* create '.' and '..' */
+            RtlCopyMemory(&pFatEntry[0].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
+            RtlCopyMemory(pFatEntry[0].ShortName, ".          ", 11);
+            RtlCopyMemory(&pFatEntry[1].Attrib, &DirContext.DirEntry.Fat.Attrib, sizeof(FAT_DIR_ENTRY) - 11);
+            RtlCopyMemory(pFatEntry[1].ShortName, "..         ", 11);
+        }
+
         pFatEntry[1].FirstCluster = ParentFcb->entry.Fat.FirstCluster;
         pFatEntry[1].FirstClusterHigh = ParentFcb->entry.Fat.FirstClusterHigh;
         if (vfatFCBIsRoot(ParentFcb))
@@ -516,7 +650,8 @@ FATXAddEntry(
     IN PVFATFCB* Fcb,
     IN PVFATFCB ParentFcb,
     IN ULONG RequestedOptions,
-    IN UCHAR ReqAttr)
+    IN UCHAR ReqAttr,
+    IN PVFAT_MOVE_CONTEXT MoveContext)
 {
     PVOID Context = NULL;
     LARGE_INTEGER SystemTime, FileOffset;
@@ -552,7 +687,15 @@ FATXAddEntry(
     DirContext.ShortNameU.MaximumLength = 0;
     RtlZeroMemory(&DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
     memset(DirContext.DirEntry.FatX.Filename, 0xff, 42);
-    DirContext.DirEntry.FatX.FirstCluster = 0;
+    /* Use cluster, if moving */
+    if (MoveContext != NULL)
+    {
+        DirContext.DirEntry.FatX.FirstCluster = MoveContext->FirstCluster;
+    }
+    else
+    {
+        DirContext.DirEntry.FatX.FirstCluster = 0;
+    }
     DirContext.DirEntry.FatX.FileSize = 0;
 
     /* set file name */
@@ -577,18 +720,39 @@ FATXAddEntry(
     DirContext.DirEntry.FatX.UpdateTime = DirContext.DirEntry.FatX.CreationTime;
     DirContext.DirEntry.FatX.AccessDate = DirContext.DirEntry.FatX.CreationDate;
     DirContext.DirEntry.FatX.AccessTime = DirContext.DirEntry.FatX.CreationTime;
+    /* If it's moving, preserve creation time and file size */
+    if (MoveContext != NULL)
+    {
+        DirContext.DirEntry.FatX.CreationDate = MoveContext->CreationDate;
+        DirContext.DirEntry.FatX.CreationTime = MoveContext->CreationTime;
+        DirContext.DirEntry.FatX.FileSize = MoveContext->FileSize;
+    }
 
     /* add entry into parent directory */
     FileOffset.u.HighPart = 0;
     FileOffset.u.LowPart = Index * sizeof(FATX_DIR_ENTRY);
-    CcPinRead(ParentFcb->FileObject, &FileOffset, sizeof(FATX_DIR_ENTRY),
-              TRUE, &Context, (PVOID*)&pFatXDirEntry);
+    if (!CcPinRead(ParentFcb->FileObject, &FileOffset, sizeof(FATX_DIR_ENTRY),
+              TRUE, &Context, (PVOID*)&pFatXDirEntry))
+    {
+        return STATUS_UNSUCCESSFUL;
+    }
     RtlCopyMemory(pFatXDirEntry, &DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
     CcSetDirtyPinnedData(Context, NULL);
     CcUnpinData(Context);
 
-    /* FIXME: check status */
-    vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
+    if (MoveContext != NULL)
+    {
+        /* We're modifying an existing FCB - likely rename/move */
+        /* FIXME: check status */
+        vfatUpdateFCB(DeviceExt, *Fcb, &DirContext.LongNameU, &DirContext.ShortNameU, ParentFcb);
+        (*Fcb)->dirIndex = DirContext.DirIndex;
+        (*Fcb)->startIndex = DirContext.StartIndex;
+    }
+    else
+    {
+        /* FIXME: check status */
+        vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
+    }
 
     DPRINT("addentry ok\n");
     return STATUS_SUCCESS;
@@ -601,12 +765,13 @@ VfatAddEntry(
     IN PVFATFCB *Fcb,
     IN PVFATFCB ParentFcb,
     IN ULONG RequestedOptions,
-    IN UCHAR ReqAttr)
+    IN UCHAR ReqAttr,
+    IN PVFAT_MOVE_CONTEXT MoveContext)
 {
     if (DeviceExt->Flags & VCB_IS_FATX)
-        return FATXAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr);
+        return FATXAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr, MoveContext);
     else
-        return FATAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr);
+        return FATAddEntry(DeviceExt, NameU, Fcb, ParentFcb, RequestedOptions, ReqAttr, MoveContext);
 }
 
 /*
@@ -615,7 +780,8 @@ VfatAddEntry(
 static NTSTATUS
 FATDelEntry(
     IN PDEVICE_EXTENSION DeviceExt,
-    IN PVFATFCB pFcb)
+    IN PVFATFCB pFcb,
+    OUT PVFAT_MOVE_CONTEXT MoveContext)
 {
     ULONG CurrentCluster = 0, NextCluster, i;
     PVOID Context = NULL;
@@ -626,7 +792,7 @@ FATDelEntry(
     ASSERT(pFcb->parentFcb);
 
     DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
-    DPRINT("delete entry: %d to %d\n", pFcb->startIndex, pFcb->dirIndex);
+    DPRINT("delete entry: %u to %u\n", pFcb->startIndex, pFcb->dirIndex);
     Offset.u.HighPart = 0;
     for (i = pFcb->startIndex; i <= pFcb->dirIndex; i++)
     {
@@ -638,8 +804,11 @@ FATDelEntry(
                 CcUnpinData(Context);
             }
             Offset.u.LowPart = (i * sizeof(FAT_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
-            CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FAT_DIR_ENTRY), TRUE,
-                      &Context, (PVOID*)&pDirEntry);
+            if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FAT_DIR_ENTRY), TRUE,
+                      &Context, (PVOID*)&pDirEntry))
+            {
+                return STATUS_UNSUCCESSFUL;
+            }
         }
         pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))].Filename[0] = 0xe5;
         if (i == pFcb->dirIndex)
@@ -655,13 +824,26 @@ FATDelEntry(
         CcUnpinData(Context);
     }
 
-    while (CurrentCluster && CurrentCluster != 0xffffffff)
+    /* In case of moving, don't delete data */
+    if (MoveContext != NULL)
     {
-        GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
-        /* FIXME: check status */
-        WriteCluster(DeviceExt, CurrentCluster, 0);
-        CurrentCluster = NextCluster;
+        pDirEntry = &pDirEntry[pFcb->dirIndex % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))];
+        MoveContext->FirstCluster = CurrentCluster;
+        MoveContext->FileSize = pDirEntry->FileSize;
+        MoveContext->CreationTime = pDirEntry->CreationTime;
+        MoveContext->CreationDate = pDirEntry->CreationDate;
     }
+    else
+    {
+        while (CurrentCluster && CurrentCluster != 0xffffffff)
+        {
+            GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
+            /* FIXME: check status */
+            WriteCluster(DeviceExt, CurrentCluster, 0);
+            CurrentCluster = NextCluster;
+        }
+    }
+
     return STATUS_SUCCESS;
 }
 
@@ -671,7 +853,8 @@ FATDelEntry(
 static NTSTATUS
 FATXDelEntry(
     IN PDEVICE_EXTENSION DeviceExt,
-    IN PVFATFCB pFcb)
+    IN PVFATFCB pFcb,
+    OUT PVFAT_MOVE_CONTEXT MoveContext)
 {
     ULONG CurrentCluster = 0, NextCluster;
     PVOID Context = NULL;
@@ -686,7 +869,7 @@ FATXDelEntry(
     StartIndex = pFcb->startIndex;
 
     DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
-    DPRINT("delete entry: %d\n", StartIndex);
+    DPRINT("delete entry: %u\n", StartIndex);
     Offset.u.HighPart = 0;
     Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
     if (!CcPinRead(pFcb->parentFcb->FileObject, &Offset, sizeof(FATX_DIR_ENTRY), TRUE,
@@ -702,25 +885,78 @@ FATXDelEntry(
     CcSetDirtyPinnedData(Context, NULL);
     CcUnpinData(Context);
 
-    while (CurrentCluster && CurrentCluster != 0xffffffff)
+    /* In case of moving, don't delete data */
+    if (MoveContext != NULL)
     {
-        GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
-        /* FIXME: check status */
-        WriteCluster(DeviceExt, CurrentCluster, 0);
-        CurrentCluster = NextCluster;
+        MoveContext->FirstCluster = CurrentCluster;
+        MoveContext->FileSize = pDirEntry->FileSize;
+        MoveContext->CreationTime = pDirEntry->CreationTime;
+        MoveContext->CreationDate = pDirEntry->CreationDate;
     }
+    else
+    {
+        while (CurrentCluster && CurrentCluster != 0xffffffff)
+        {
+            GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
+            /* FIXME: check status */
+            WriteCluster(DeviceExt, CurrentCluster, 0);
+            CurrentCluster = NextCluster;
+        }
+    }
+
     return STATUS_SUCCESS;
 }
 
 NTSTATUS
 VfatDelEntry(
     IN PDEVICE_EXTENSION DeviceExt,
-    IN PVFATFCB pFcb)
+    IN PVFATFCB pFcb,
+    OUT PVFAT_MOVE_CONTEXT MoveContext)
 {
     if (DeviceExt->Flags & VCB_IS_FATX)
-        return FATXDelEntry(DeviceExt, pFcb);
+        return FATXDelEntry(DeviceExt, pFcb, MoveContext);
     else
-        return FATDelEntry(DeviceExt, pFcb);
+        return FATDelEntry(DeviceExt, pFcb, MoveContext);
+}
+
+/*
+ * move an existing FAT entry
+ */
+NTSTATUS
+VfatMoveEntry(
+    IN PDEVICE_EXTENSION DeviceExt,
+    IN PVFATFCB pFcb,
+    IN PUNICODE_STRING FileName,
+    IN PVFATFCB ParentFcb)
+{
+    NTSTATUS Status;
+    PVFATFCB OldParent;
+    VFAT_MOVE_CONTEXT MoveContext;
+
+    DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt, pFcb, FileName, ParentFcb);
+
+    /* Delete old entry while keeping data */
+    Status = VfatDelEntry(DeviceExt, pFcb, &MoveContext);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    OldParent = pFcb->parentFcb;
+    CcPurgeCacheSection(&OldParent->SectionObjectPointers, NULL, 0, FALSE);
+
+    /* Add our new entry with our cluster */
+    Status = VfatAddEntry(DeviceExt,
+                          FileName,
+                          &pFcb,
+                          ParentFcb,
+                          (vfatFCBIsDirectory(pFcb) ? FILE_DIRECTORY_FILE : 0),
+                          *pFcb->Attributes,
+                          &MoveContext);
+
+    CcPurgeCacheSection(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, FALSE);
+
+    return Status;
 }
 
 /* EOF */