xref: /reactos/drivers/filesystems/vfatfs/dirwr.c (revision 542e9f2b)
1 /*
2  * PROJECT:     VFAT Filesystem
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Write in directory
5  * COPYRIGHT:   Copyright 1999-2001 Rex Jolliff <rex@lvcablemodem.com>
6  *              Copyright 2004-2008 Hervé Poussineau <hpoussin@reactos.org>
7  *              Copyright 2010-2018 Pierre Schweitzer <pierre@reactos.org>
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include "vfat.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 #ifdef KDBG
18 extern UNICODE_STRING DebugFile;
19 #endif
20 
21 NTSTATUS
vfatFCBInitializeCacheFromVolume(PVCB vcb,PVFATFCB fcb)22 vfatFCBInitializeCacheFromVolume(
23     PVCB vcb,
24     PVFATFCB fcb)
25 {
26     PFILE_OBJECT fileObject;
27     PVFATCCB newCCB;
28     NTSTATUS status;
29     BOOLEAN Acquired;
30 
31     /* Don't re-initialize if already done */
32     if (BooleanFlagOn(fcb->Flags, FCB_CACHE_INITIALIZED))
33     {
34         return STATUS_SUCCESS;
35     }
36 
37     ASSERT(vfatFCBIsDirectory(fcb));
38     ASSERT(fcb->FileObject == NULL);
39 
40     Acquired = FALSE;
41     if (!ExIsResourceAcquiredExclusive(&vcb->DirResource))
42     {
43         ExAcquireResourceExclusiveLite(&vcb->DirResource, TRUE);
44         Acquired = TRUE;
45     }
46 
47     fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
48     if (fileObject == NULL)
49     {
50         status = STATUS_INSUFFICIENT_RESOURCES;
51         goto Quit;
52     }
53 
54 #ifdef KDBG
55     if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &fcb->LongNameU, FALSE, NULL))
56     {
57         DPRINT1("Attaching %p to %p (%d)\n", fcb, fileObject, fcb->RefCount);
58     }
59 #endif
60 
61     newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
62     if (newCCB == NULL)
63     {
64         status = STATUS_INSUFFICIENT_RESOURCES;
65         ObDereferenceObject(fileObject);
66         goto Quit;
67     }
68     RtlZeroMemory(newCCB, sizeof (VFATCCB));
69 
70     fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
71     fileObject->FsContext = fcb;
72     fileObject->FsContext2 = newCCB;
73     fileObject->Vpb = vcb->IoVPB;
74     fcb->FileObject = fileObject;
75 
76     _SEH2_TRY
77     {
78         CcInitializeCacheMap(fileObject,
79                              (PCC_FILE_SIZES)(&fcb->RFCB.AllocationSize),
80                              TRUE,
81                              &VfatGlobalData->CacheMgrCallbacks,
82                              fcb);
83     }
84     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
85     {
86         status = _SEH2_GetExceptionCode();
87         fcb->FileObject = NULL;
88         ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, newCCB);
89         ObDereferenceObject(fileObject);
90         if (Acquired)
91         {
92             ExReleaseResourceLite(&vcb->DirResource);
93         }
94         return status;
95     }
96     _SEH2_END;
97 
98     vfatGrabFCB(vcb, fcb);
99     SetFlag(fcb->Flags, FCB_CACHE_INITIALIZED);
100     status = STATUS_SUCCESS;
101 
102 Quit:
103     if (Acquired)
104     {
105         ExReleaseResourceLite(&vcb->DirResource);
106     }
107 
108     return status;
109 }
110 
111 /*
112  * update an existing FAT entry
113  */
114 NTSTATUS
VfatUpdateEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb)115 VfatUpdateEntry(
116     IN PDEVICE_EXTENSION DeviceExt,
117     IN PVFATFCB pFcb)
118 {
119     PVOID Context;
120     PDIR_ENTRY PinEntry;
121     LARGE_INTEGER Offset;
122     ULONG SizeDirEntry;
123     ULONG dirIndex;
124     NTSTATUS Status;
125 
126     ASSERT(pFcb);
127 
128     if (vfatVolumeIsFatX(DeviceExt))
129     {
130         SizeDirEntry = sizeof(FATX_DIR_ENTRY);
131         dirIndex = pFcb->startIndex;
132     }
133     else
134     {
135         SizeDirEntry = sizeof(FAT_DIR_ENTRY);
136         dirIndex = pFcb->dirIndex;
137     }
138 
139     DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex, &pFcb->PathNameU);
140 
141     if (vfatFCBIsRoot(pFcb) || BooleanFlagOn(pFcb->Flags, FCB_IS_FAT | FCB_IS_VOLUME))
142     {
143         return STATUS_SUCCESS;
144     }
145 
146     ASSERT(pFcb->parentFcb);
147 
148     Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
149     if (!NT_SUCCESS(Status))
150     {
151         return Status;
152     }
153 
154     Offset.u.HighPart = 0;
155     Offset.u.LowPart = dirIndex * SizeDirEntry;
156     _SEH2_TRY
157     {
158         CcPinRead(pFcb->parentFcb->FileObject, &Offset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&PinEntry);
159     }
160     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
161     {
162         DPRINT1("Failed write to \'%wZ\'.\n", &pFcb->parentFcb->PathNameU);
163         _SEH2_YIELD(return _SEH2_GetExceptionCode());
164     }
165     _SEH2_END;
166 
167     pFcb->Flags &= ~FCB_IS_DIRTY;
168     RtlCopyMemory(PinEntry, &pFcb->entry, SizeDirEntry);
169     CcSetDirtyPinnedData(Context, NULL);
170     CcUnpinData(Context);
171     return STATUS_SUCCESS;
172 }
173 
174 /*
175  * rename an existing FAT entry
176  */
177 NTSTATUS
vfatRenameEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,IN PUNICODE_STRING FileName,IN BOOLEAN CaseChangeOnly)178 vfatRenameEntry(
179     IN PDEVICE_EXTENSION DeviceExt,
180     IN PVFATFCB pFcb,
181     IN PUNICODE_STRING FileName,
182     IN BOOLEAN CaseChangeOnly)
183 {
184     OEM_STRING NameA;
185     ULONG StartIndex;
186     PVOID Context = NULL;
187     LARGE_INTEGER Offset;
188     PFATX_DIR_ENTRY pDirEntry;
189     NTSTATUS Status;
190 
191     DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt, pFcb, FileName, CaseChangeOnly);
192 
193     Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
194     if (!NT_SUCCESS(Status))
195     {
196         return Status;
197     }
198 
199     if (vfatVolumeIsFatX(DeviceExt))
200     {
201         VFAT_DIRENTRY_CONTEXT DirContext;
202 
203         /* Open associated dir entry */
204         StartIndex = pFcb->startIndex;
205         Offset.u.HighPart = 0;
206         Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
207         _SEH2_TRY
208         {
209             CcPinRead(pFcb->parentFcb->FileObject, &Offset, PAGE_SIZE, PIN_WAIT, &Context, (PVOID*)&pDirEntry);
210         }
211         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
212         {
213             DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE);
214             _SEH2_YIELD(return _SEH2_GetExceptionCode());
215         }
216         _SEH2_END;
217 
218         pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))];
219 
220         /* Set file name */
221         NameA.Buffer = (PCHAR)pDirEntry->Filename;
222         NameA.Length = 0;
223         NameA.MaximumLength = 42;
224         RtlUnicodeStringToOemString(&NameA, FileName, FALSE);
225         pDirEntry->FilenameLength = (unsigned char)NameA.Length;
226 
227         /* Update FCB */
228         DirContext.DeviceExt = DeviceExt;
229         DirContext.ShortNameU.Length = 0;
230         DirContext.ShortNameU.MaximumLength = 0;
231         DirContext.ShortNameU.Buffer = NULL;
232         DirContext.LongNameU = *FileName;
233         DirContext.DirEntry.FatX = *pDirEntry;
234 
235         CcSetDirtyPinnedData(Context, NULL);
236         CcUnpinData(Context);
237 
238         Status = vfatUpdateFCB(DeviceExt, pFcb, &DirContext, pFcb->parentFcb);
239         if (NT_SUCCESS(Status))
240         {
241             CcFlushCache(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, NULL);
242         }
243 
244         return Status;
245     }
246     else
247     {
248         /* This we cannot handle properly, move file - would likely need love */
249         return VfatMoveEntry(DeviceExt, pFcb, FileName, pFcb->parentFcb);
250     }
251 }
252 
253 /*
254  * try to find contiguous entries frees in directory,
255  * extend a directory if is necessary
256  */
257 BOOLEAN
vfatFindDirSpace(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pDirFcb,IN ULONG nbSlots,OUT PULONG start)258 vfatFindDirSpace(
259     IN PDEVICE_EXTENSION DeviceExt,
260     IN PVFATFCB pDirFcb,
261     IN ULONG nbSlots,
262     OUT PULONG start)
263 {
264     LARGE_INTEGER FileOffset;
265     ULONG i, count, size, nbFree = 0;
266     PDIR_ENTRY pFatEntry = NULL;
267     PVOID Context = NULL;
268     NTSTATUS Status;
269     ULONG SizeDirEntry;
270     BOOLEAN IsFatX = vfatVolumeIsFatX(DeviceExt);
271     FileOffset.QuadPart = 0;
272 
273     if (IsFatX)
274         SizeDirEntry = sizeof(FATX_DIR_ENTRY);
275     else
276         SizeDirEntry = sizeof(FAT_DIR_ENTRY);
277 
278     Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pDirFcb);
279     if (!NT_SUCCESS(Status))
280     {
281         return FALSE;
282     }
283 
284     count = pDirFcb->RFCB.FileSize.u.LowPart / SizeDirEntry;
285     size = DeviceExt->FatInfo.BytesPerCluster / SizeDirEntry;
286     for (i = 0; i < count; i++, pFatEntry = (PDIR_ENTRY)((ULONG_PTR)pFatEntry + SizeDirEntry))
287     {
288         if (Context == NULL || (i % size) == 0)
289         {
290             if (Context)
291             {
292                 CcUnpinData(Context);
293             }
294             _SEH2_TRY
295             {
296                 CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
297             }
298             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
299             {
300                 _SEH2_YIELD(return FALSE);
301             }
302             _SEH2_END;
303 
304             FileOffset.u.LowPart += DeviceExt->FatInfo.BytesPerCluster;
305         }
306         if (ENTRY_END(IsFatX, pFatEntry))
307         {
308             break;
309         }
310         if (ENTRY_DELETED(IsFatX, pFatEntry))
311         {
312             nbFree++;
313         }
314         else
315         {
316             nbFree = 0;
317         }
318         if (nbFree == nbSlots)
319         {
320             break;
321         }
322     }
323     if (Context)
324     {
325         CcUnpinData(Context);
326         Context = NULL;
327     }
328     if (nbFree == nbSlots)
329     {
330         /* found enough contiguous free slots */
331         *start = i - nbSlots + 1;
332     }
333     else
334     {
335         *start = i - nbFree;
336         if (*start + nbSlots > count)
337         {
338             LARGE_INTEGER AllocationSize;
339             /* extend the directory */
340             if (vfatFCBIsRoot(pDirFcb) && DeviceExt->FatInfo.FatType != FAT32)
341             {
342                 /* We can't extend a root directory on a FAT12/FAT16/FATX partition */
343                 return FALSE;
344             }
345             AllocationSize.QuadPart = pDirFcb->RFCB.FileSize.u.LowPart + DeviceExt->FatInfo.BytesPerCluster;
346             Status = VfatSetAllocationSizeInformation(pDirFcb->FileObject, pDirFcb,
347                                                       DeviceExt, &AllocationSize);
348             if (!NT_SUCCESS(Status))
349             {
350                 return FALSE;
351             }
352             /* clear the new dir cluster */
353             FileOffset.u.LowPart = (ULONG)(pDirFcb->RFCB.FileSize.QuadPart -
354                                            DeviceExt->FatInfo.BytesPerCluster);
355             _SEH2_TRY
356             {
357                 CcPinRead(pDirFcb->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
358             }
359             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
360             {
361                 _SEH2_YIELD(return FALSE);
362             }
363             _SEH2_END;
364 
365             if (IsFatX)
366                 memset(pFatEntry, 0xff, DeviceExt->FatInfo.BytesPerCluster);
367             else
368                 RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
369         }
370         else if (*start + nbSlots < count)
371         {
372             /* clear the entry after the last new entry */
373             FileOffset.u.LowPart = (*start + nbSlots) * SizeDirEntry;
374             _SEH2_TRY
375             {
376                 CcPinRead(pDirFcb->FileObject, &FileOffset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
377             }
378             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
379             {
380                 _SEH2_YIELD(return FALSE);
381             }
382             _SEH2_END;
383 
384             if (IsFatX)
385                 memset(pFatEntry, 0xff, SizeDirEntry);
386             else
387                 RtlZeroMemory(pFatEntry, SizeDirEntry);
388         }
389         if (Context)
390         {
391             CcSetDirtyPinnedData(Context, NULL);
392             CcUnpinData(Context);
393         }
394     }
395     DPRINT("nbSlots %u nbFree %u, entry number %u\n", nbSlots, nbFree, *start);
396     return TRUE;
397 }
398 
399 /*
400   create a new FAT entry
401 */
402 static NTSTATUS
FATAddEntry(IN PDEVICE_EXTENSION DeviceExt,IN PUNICODE_STRING NameU,IN PVFATFCB * Fcb,IN PVFATFCB ParentFcb,IN ULONG RequestedOptions,IN UCHAR ReqAttr,IN PVFAT_MOVE_CONTEXT MoveContext)403 FATAddEntry(
404     IN PDEVICE_EXTENSION DeviceExt,
405     IN PUNICODE_STRING NameU,
406     IN PVFATFCB* Fcb,
407     IN PVFATFCB ParentFcb,
408     IN ULONG RequestedOptions,
409     IN UCHAR ReqAttr,
410     IN PVFAT_MOVE_CONTEXT MoveContext)
411 {
412     PVOID Context = NULL;
413     PFAT_DIR_ENTRY pFatEntry;
414     slot *pSlots;
415     USHORT nbSlots = 0, j;
416     PUCHAR Buffer;
417     BOOLEAN needTilde = FALSE, needLong = FALSE;
418     BOOLEAN BaseAllLower, BaseAllUpper;
419     BOOLEAN ExtensionAllLower, ExtensionAllUpper;
420     BOOLEAN InExtension;
421     BOOLEAN IsDirectory;
422     WCHAR c;
423     ULONG CurrentCluster;
424     LARGE_INTEGER SystemTime, FileOffset;
425     NTSTATUS Status = STATUS_SUCCESS;
426     ULONG size;
427     long i;
428 
429     OEM_STRING NameA;
430     CHAR aName[13];
431     BOOLEAN IsNameLegal;
432     BOOLEAN SpacesFound;
433 
434     VFAT_DIRENTRY_CONTEXT DirContext;
435     WCHAR LongNameBuffer[LONGNAME_MAX_LENGTH + 1];
436     WCHAR ShortNameBuffer[13];
437 
438     DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU, &ParentFcb->PathNameU);
439 
440     DirContext.LongNameU = *NameU;
441     IsDirectory = BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE);
442 
443     /* nb of entry needed for long name+normal entry */
444     nbSlots = (DirContext.LongNameU.Length / sizeof(WCHAR) + 12) / 13 + 1;
445     DPRINT("NameLen= %u, nbSlots =%u\n", DirContext.LongNameU.Length / sizeof(WCHAR), nbSlots);
446     Buffer = ExAllocatePoolWithTag(NonPagedPool, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY), TAG_DIRENT);
447     if (Buffer == NULL)
448     {
449         return STATUS_INSUFFICIENT_RESOURCES;
450     }
451     RtlZeroMemory(Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
452     pSlots = (slot *) Buffer;
453 
454     NameA.Buffer = aName;
455     NameA.Length = 0;
456     NameA.MaximumLength = sizeof(aName);
457 
458     DirContext.DeviceExt = DeviceExt;
459     DirContext.ShortNameU.Buffer = ShortNameBuffer;
460     DirContext.ShortNameU.Length = 0;
461     DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
462 
463     RtlZeroMemory(&DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
464 
465     IsNameLegal = RtlIsNameLegalDOS8Dot3(&DirContext.LongNameU, &NameA, &SpacesFound);
466 
467     if (!IsNameLegal || SpacesFound)
468     {
469         GENERATE_NAME_CONTEXT NameContext;
470         VFAT_DIRENTRY_CONTEXT SearchContext;
471         WCHAR ShortSearchName[13];
472         needTilde = TRUE;
473         needLong = TRUE;
474         RtlZeroMemory(&NameContext, sizeof(GENERATE_NAME_CONTEXT));
475         SearchContext.DeviceExt = DeviceExt;
476         SearchContext.LongNameU.Buffer = LongNameBuffer;
477         SearchContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
478         SearchContext.ShortNameU.Buffer = ShortSearchName;
479         SearchContext.ShortNameU.MaximumLength = sizeof(ShortSearchName);
480 
481         for (i = 0; i < 100; i++)
482         {
483             RtlGenerate8dot3Name(&DirContext.LongNameU, FALSE, &NameContext, &DirContext.ShortNameU);
484             DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
485                                          SearchContext.DirIndex = 0;
486             Status = FindFile(DeviceExt, ParentFcb, &DirContext.ShortNameU, &SearchContext, TRUE);
487             if (!NT_SUCCESS(Status))
488             {
489                 break;
490             }
491             else if (MoveContext)
492             {
493                 ASSERT(*Fcb);
494                 if (strncmp((char *)SearchContext.DirEntry.Fat.ShortName, (char *)(*Fcb)->entry.Fat.ShortName, 11) == 0)
495                 {
496                     if (MoveContext->InPlace)
497                     {
498                         ASSERT(SearchContext.DirEntry.Fat.FileSize == MoveContext->FileSize);
499                         break;
500                     }
501                 }
502             }
503         }
504         if (i == 100) /* FIXME : what to do after this ? */
505         {
506             ExFreePoolWithTag(Buffer, TAG_DIRENT);
507             return STATUS_UNSUCCESSFUL;
508         }
509         IsNameLegal = RtlIsNameLegalDOS8Dot3(&DirContext.ShortNameU, &NameA, &SpacesFound);
510     }
511     else
512     {
513         BaseAllLower = BaseAllUpper = TRUE;
514         ExtensionAllLower = ExtensionAllUpper = TRUE;
515         InExtension = FALSE;
516         for (i = 0; i < DirContext.LongNameU.Length / sizeof(WCHAR); i++)
517         {
518             c = DirContext.LongNameU.Buffer[i];
519             if (c >= L'A' && c <= L'Z')
520             {
521                 if (InExtension)
522                     ExtensionAllLower = FALSE;
523                 else
524                     BaseAllLower = FALSE;
525             }
526             else if (c >= L'a' && c <= L'z')
527             {
528                 if (InExtension)
529                     ExtensionAllUpper = FALSE;
530                 else
531                     BaseAllUpper = FALSE;
532             }
533             else if (c > 0x7f)
534             {
535                 needLong = TRUE;
536                 break;
537             }
538 
539             if (c == L'.')
540             {
541                 InExtension = TRUE;
542             }
543         }
544 
545         if ((!BaseAllLower && !BaseAllUpper) ||
546             (!ExtensionAllLower && !ExtensionAllUpper))
547         {
548             needLong = TRUE;
549         }
550 
551         RtlUpcaseUnicodeString(&DirContext.ShortNameU, &DirContext.LongNameU, FALSE);
552         DirContext.ShortNameU.Buffer[DirContext.ShortNameU.Length / sizeof(WCHAR)] = 0;
553     }
554     aName[NameA.Length] = 0;
555     DPRINT("'%s', '%wZ', needTilde=%u, needLong=%u\n",
556            aName, &DirContext.LongNameU, needTilde, needLong);
557     memset(DirContext.DirEntry.Fat.ShortName, ' ', 11);
558     for (i = 0; i < 8 && aName[i] && aName[i] != '.'; i++)
559     {
560         DirContext.DirEntry.Fat.Filename[i] = aName[i];
561     }
562     if (aName[i] == '.')
563     {
564         i++;
565         for (j = 0; j < 3 && aName[i]; j++, i++)
566         {
567             DirContext.DirEntry.Fat.Ext[j] = aName[i];
568         }
569     }
570     if (DirContext.DirEntry.Fat.Filename[0] == 0xe5)
571     {
572         DirContext.DirEntry.Fat.Filename[0] = 0x05;
573     }
574 
575     if (needLong)
576     {
577         RtlCopyMemory(LongNameBuffer, DirContext.LongNameU.Buffer, DirContext.LongNameU.Length);
578         DirContext.LongNameU.Buffer = LongNameBuffer;
579         DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
580         DirContext.LongNameU.Buffer[DirContext.LongNameU.Length / sizeof(WCHAR)] = 0;
581         memset(DirContext.LongNameU.Buffer + DirContext.LongNameU.Length / sizeof(WCHAR) + 1, 0xff,
582                DirContext.LongNameU.MaximumLength - DirContext.LongNameU.Length - sizeof(WCHAR));
583     }
584     else
585     {
586         nbSlots = 1;
587         if (BaseAllLower && !BaseAllUpper)
588         {
589             DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_BASE;
590         }
591         if (ExtensionAllLower && !ExtensionAllUpper)
592         {
593             DirContext.DirEntry.Fat.lCase |= VFAT_CASE_LOWER_EXT;
594         }
595     }
596 
597     DPRINT ("dos name=%11.11s\n", DirContext.DirEntry.Fat.Filename);
598 
599     /* set attributes */
600     DirContext.DirEntry.Fat.Attrib = ReqAttr;
601     if (IsDirectory)
602     {
603         DirContext.DirEntry.Fat.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
604     }
605     /* set dates and times */
606     KeQuerySystemTime(&SystemTime);
607     FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.Fat.CreationDate,
608                                &DirContext.DirEntry.Fat.CreationTime);
609     DirContext.DirEntry.Fat.UpdateDate = DirContext.DirEntry.Fat.CreationDate;
610     DirContext.DirEntry.Fat.UpdateTime = DirContext.DirEntry.Fat.CreationTime;
611     DirContext.DirEntry.Fat.AccessDate = DirContext.DirEntry.Fat.CreationDate;
612     /* If it's moving, preserve creation time and file size */
613     if (MoveContext != NULL)
614     {
615         DirContext.DirEntry.Fat.CreationDate = MoveContext->CreationDate;
616         DirContext.DirEntry.Fat.CreationTime = MoveContext->CreationTime;
617         DirContext.DirEntry.Fat.FileSize = MoveContext->FileSize;
618     }
619 
620     if (needLong)
621     {
622         /* calculate checksum for 8.3 name */
623         for (pSlots[0].alias_checksum = 0, i = 0; i < 11; i++)
624         {
625             pSlots[0].alias_checksum = (((pSlots[0].alias_checksum & 1) << 7
626                                      | ((pSlots[0].alias_checksum & 0xfe) >> 1))
627                                      + DirContext.DirEntry.Fat.ShortName[i]);
628         }
629         /* construct slots and entry */
630         for (i = nbSlots - 2; i >= 0; i--)
631         {
632             DPRINT("construct slot %d\n", i);
633             pSlots[i].attr = 0xf;
634             if (i)
635             {
636                 pSlots[i].id = (unsigned char)(nbSlots - i - 1);
637             }
638             else
639             {
640                 pSlots[i].id = (unsigned char)(nbSlots - i - 1 + 0x40);
641             }
642             pSlots[i].alias_checksum = pSlots[0].alias_checksum;
643             RtlCopyMemory(pSlots[i].name0_4, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13, 10);
644             RtlCopyMemory(pSlots[i].name5_10, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 5, 12);
645             RtlCopyMemory(pSlots[i].name11_12, DirContext.LongNameU.Buffer + (nbSlots - i - 2) * 13 + 11, 4);
646         }
647     }
648     /* try to find nbSlots contiguous entries frees in directory */
649     if (!vfatFindDirSpace(DeviceExt, ParentFcb, nbSlots, &DirContext.StartIndex))
650     {
651         ExFreePoolWithTag(Buffer, TAG_DIRENT);
652         return STATUS_DISK_FULL;
653     }
654     DirContext.DirIndex = DirContext.StartIndex + nbSlots - 1;
655     if (IsDirectory)
656     {
657         /* If we aren't moving, use next */
658         if (MoveContext == NULL)
659         {
660             CurrentCluster = 0;
661             Status = NextCluster(DeviceExt, 0, &CurrentCluster, TRUE);
662             if (CurrentCluster == 0xffffffff || !NT_SUCCESS(Status))
663             {
664                 ExFreePoolWithTag(Buffer, TAG_DIRENT);
665                 if (!NT_SUCCESS(Status))
666                 {
667                     return Status;
668                 }
669                 return STATUS_DISK_FULL;
670             }
671 
672             if (DeviceExt->FatInfo.FatType == FAT32)
673             {
674                 FAT32UpdateFreeClustersCount(DeviceExt);
675             }
676         }
677         else
678         {
679             CurrentCluster = MoveContext->FirstCluster;
680         }
681 
682         if (DeviceExt->FatInfo.FatType == FAT32)
683         {
684             DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
685         }
686         DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
687     }
688     else if (MoveContext != NULL)
689     {
690         CurrentCluster = MoveContext->FirstCluster;
691 
692         if (DeviceExt->FatInfo.FatType == FAT32)
693         {
694             DirContext.DirEntry.Fat.FirstClusterHigh = (unsigned short)(CurrentCluster >> 16);
695         }
696         DirContext.DirEntry.Fat.FirstCluster = (unsigned short)CurrentCluster;
697     }
698 
699     /* No need to init cache here, vfatFindDirSpace() will have done it for us */
700     ASSERT(BooleanFlagOn(ParentFcb->Flags, FCB_CACHE_INITIALIZED));
701 
702     i = DeviceExt->FatInfo.BytesPerCluster / sizeof(FAT_DIR_ENTRY);
703     FileOffset.u.HighPart = 0;
704     FileOffset.u.LowPart = DirContext.StartIndex * sizeof(FAT_DIR_ENTRY);
705     if (DirContext.StartIndex / i == DirContext.DirIndex / i)
706     {
707         /* one cluster */
708         _SEH2_TRY
709         {
710             CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY), PIN_WAIT, &Context, (PVOID*)&pFatEntry);
711         }
712         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
713         {
714             ExFreePoolWithTag(Buffer, TAG_DIRENT);
715             _SEH2_YIELD(return _SEH2_GetExceptionCode());
716         }
717         _SEH2_END;
718 
719         if (nbSlots > 1)
720         {
721             RtlCopyMemory(pFatEntry, Buffer, (nbSlots - 1) * sizeof(FAT_DIR_ENTRY));
722         }
723         RtlCopyMemory(pFatEntry + (nbSlots - 1), &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
724     }
725     else
726     {
727         /* two clusters */
728         size = DeviceExt->FatInfo.BytesPerCluster -
729                (DirContext.StartIndex * sizeof(FAT_DIR_ENTRY)) % DeviceExt->FatInfo.BytesPerCluster;
730         i = size / sizeof(FAT_DIR_ENTRY);
731         _SEH2_TRY
732         {
733             CcPinRead(ParentFcb->FileObject, &FileOffset, size, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
734         }
735         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
736         {
737             ExFreePoolWithTag(Buffer, TAG_DIRENT);
738             _SEH2_YIELD(return _SEH2_GetExceptionCode());
739         }
740         _SEH2_END;
741         RtlCopyMemory(pFatEntry, Buffer, size);
742         CcSetDirtyPinnedData(Context, NULL);
743         CcUnpinData(Context);
744         FileOffset.u.LowPart += size;
745         _SEH2_TRY
746         {
747             CcPinRead(ParentFcb->FileObject, &FileOffset, nbSlots * sizeof(FAT_DIR_ENTRY) - size, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
748         }
749         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
750         {
751             ExFreePoolWithTag(Buffer, TAG_DIRENT);
752             _SEH2_YIELD(return _SEH2_GetExceptionCode());
753         }
754         _SEH2_END;
755         if (nbSlots - 1 > i)
756         {
757             RtlCopyMemory(pFatEntry, (PVOID)(Buffer + size), (nbSlots - 1 - i) * sizeof(FAT_DIR_ENTRY));
758         }
759         RtlCopyMemory(pFatEntry + nbSlots - 1 - i, &DirContext.DirEntry.Fat, sizeof(FAT_DIR_ENTRY));
760     }
761     CcSetDirtyPinnedData(Context, NULL);
762     CcUnpinData(Context);
763 
764     if (MoveContext != NULL)
765     {
766         /* We're modifying an existing FCB - likely rename/move */
767         Status = vfatUpdateFCB(DeviceExt, *Fcb, &DirContext, ParentFcb);
768     }
769     else
770     {
771         Status = vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
772     }
773     if (!NT_SUCCESS(Status))
774     {
775         ExFreePoolWithTag(Buffer, TAG_DIRENT);
776         return Status;
777     }
778 
779     DPRINT("new : entry=%11.11s\n", (*Fcb)->entry.Fat.Filename);
780     DPRINT("new : entry=%11.11s\n", DirContext.DirEntry.Fat.Filename);
781 
782     if (IsDirectory)
783     {
784         Status = vfatFCBInitializeCacheFromVolume(DeviceExt, (*Fcb));
785         if (!NT_SUCCESS(Status))
786         {
787             ExFreePoolWithTag(Buffer, TAG_DIRENT);
788             return Status;
789         }
790 
791         FileOffset.QuadPart = 0;
792         _SEH2_TRY
793         {
794             CcPinRead((*Fcb)->FileObject, &FileOffset, DeviceExt->FatInfo.BytesPerCluster, PIN_WAIT, &Context, (PVOID*)&pFatEntry);
795         }
796         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
797         {
798             ExFreePoolWithTag(Buffer, TAG_DIRENT);
799             _SEH2_YIELD(return _SEH2_GetExceptionCode());
800         }
801         _SEH2_END;
802         /* clear the new directory cluster if not moving */
803         if (MoveContext == NULL)
804         {
805             RtlZeroMemory(pFatEntry, DeviceExt->FatInfo.BytesPerCluster);
806             /* create '.' and '..' */
807             pFatEntry[0] = DirContext.DirEntry.Fat;
808             RtlCopyMemory(pFatEntry[0].ShortName, ".          ", 11);
809             pFatEntry[1] = DirContext.DirEntry.Fat;
810             RtlCopyMemory(pFatEntry[1].ShortName, "..         ", 11);
811         }
812 
813         pFatEntry[1].FirstCluster = ParentFcb->entry.Fat.FirstCluster;
814         pFatEntry[1].FirstClusterHigh = ParentFcb->entry.Fat.FirstClusterHigh;
815         if (vfatFCBIsRoot(ParentFcb))
816         {
817             pFatEntry[1].FirstCluster = 0;
818             pFatEntry[1].FirstClusterHigh = 0;
819         }
820         CcSetDirtyPinnedData(Context, NULL);
821         CcUnpinData(Context);
822     }
823     ExFreePoolWithTag(Buffer, TAG_DIRENT);
824     DPRINT("addentry ok\n");
825     return STATUS_SUCCESS;
826 }
827 
828 /*
829   create a new FAT entry
830 */
831 static NTSTATUS
FATXAddEntry(IN PDEVICE_EXTENSION DeviceExt,IN PUNICODE_STRING NameU,IN PVFATFCB * Fcb,IN PVFATFCB ParentFcb,IN ULONG RequestedOptions,IN UCHAR ReqAttr,IN PVFAT_MOVE_CONTEXT MoveContext)832 FATXAddEntry(
833     IN PDEVICE_EXTENSION DeviceExt,
834     IN PUNICODE_STRING NameU,
835     IN PVFATFCB* Fcb,
836     IN PVFATFCB ParentFcb,
837     IN ULONG RequestedOptions,
838     IN UCHAR ReqAttr,
839     IN PVFAT_MOVE_CONTEXT MoveContext)
840 {
841     PVOID Context = NULL;
842     LARGE_INTEGER SystemTime, FileOffset;
843     OEM_STRING NameA;
844     VFAT_DIRENTRY_CONTEXT DirContext;
845     PFATX_DIR_ENTRY pFatXDirEntry;
846     ULONG Index;
847 
848     DPRINT("addEntry: Name='%wZ', Dir='%wZ'\n", NameU, &ParentFcb->PathNameU);
849 
850     DirContext.LongNameU = *NameU;
851 
852     if (DirContext.LongNameU.Length / sizeof(WCHAR) > 42)
853     {
854         /* name too long */
855         return STATUS_NAME_TOO_LONG;
856     }
857 
858     /* try to find 1 entry free in directory */
859     if (!vfatFindDirSpace(DeviceExt, ParentFcb, 1, &DirContext.StartIndex))
860     {
861         return STATUS_DISK_FULL;
862     }
863     Index = DirContext.DirIndex = DirContext.StartIndex;
864     if (!vfatFCBIsRoot(ParentFcb))
865     {
866         DirContext.DirIndex += 2;
867         DirContext.StartIndex += 2;
868     }
869 
870     DirContext.ShortNameU.Buffer = 0;
871     DirContext.ShortNameU.Length = 0;
872     DirContext.ShortNameU.MaximumLength = 0;
873     DirContext.DeviceExt = DeviceExt;
874     RtlZeroMemory(&DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
875     memset(DirContext.DirEntry.FatX.Filename, 0xff, 42);
876     /* Use cluster, if moving */
877     if (MoveContext != NULL)
878     {
879         DirContext.DirEntry.FatX.FirstCluster = MoveContext->FirstCluster;
880     }
881     else
882     {
883         DirContext.DirEntry.FatX.FirstCluster = 0;
884     }
885     DirContext.DirEntry.FatX.FileSize = 0;
886 
887     /* set file name */
888     NameA.Buffer = (PCHAR)DirContext.DirEntry.FatX.Filename;
889     NameA.Length = 0;
890     NameA.MaximumLength = 42;
891     RtlUnicodeStringToOemString(&NameA, &DirContext.LongNameU, FALSE);
892     DirContext.DirEntry.FatX.FilenameLength = (unsigned char)NameA.Length;
893 
894     /* set attributes */
895     DirContext.DirEntry.FatX.Attrib = ReqAttr;
896     if (BooleanFlagOn(RequestedOptions, FILE_DIRECTORY_FILE))
897     {
898         DirContext.DirEntry.FatX.Attrib |= FILE_ATTRIBUTE_DIRECTORY;
899     }
900 
901     /* set dates and times */
902     KeQuerySystemTime(&SystemTime);
903     FsdSystemTimeToDosDateTime(DeviceExt, &SystemTime, &DirContext.DirEntry.FatX.CreationDate,
904                                &DirContext.DirEntry.FatX.CreationTime);
905     DirContext.DirEntry.FatX.UpdateDate = DirContext.DirEntry.FatX.CreationDate;
906     DirContext.DirEntry.FatX.UpdateTime = DirContext.DirEntry.FatX.CreationTime;
907     DirContext.DirEntry.FatX.AccessDate = DirContext.DirEntry.FatX.CreationDate;
908     DirContext.DirEntry.FatX.AccessTime = DirContext.DirEntry.FatX.CreationTime;
909     /* If it's moving, preserve creation time and file size */
910     if (MoveContext != NULL)
911     {
912         DirContext.DirEntry.FatX.CreationDate = MoveContext->CreationDate;
913         DirContext.DirEntry.FatX.CreationTime = MoveContext->CreationTime;
914         DirContext.DirEntry.FatX.FileSize = MoveContext->FileSize;
915     }
916 
917     /* No need to init cache here, vfatFindDirSpace() will have done it for us */
918     ASSERT(BooleanFlagOn(ParentFcb->Flags, FCB_CACHE_INITIALIZED));
919 
920     /* add entry into parent directory */
921     FileOffset.u.HighPart = 0;
922     FileOffset.u.LowPart = Index * sizeof(FATX_DIR_ENTRY);
923     _SEH2_TRY
924     {
925         CcPinRead(ParentFcb->FileObject, &FileOffset, sizeof(FATX_DIR_ENTRY), PIN_WAIT, &Context, (PVOID*)&pFatXDirEntry);
926     }
927     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
928     {
929         _SEH2_YIELD(return _SEH2_GetExceptionCode());
930     }
931     _SEH2_END;
932     RtlCopyMemory(pFatXDirEntry, &DirContext.DirEntry.FatX, sizeof(FATX_DIR_ENTRY));
933     CcSetDirtyPinnedData(Context, NULL);
934     CcUnpinData(Context);
935 
936     if (MoveContext != NULL)
937     {
938         /* We're modifying an existing FCB - likely rename/move */
939         /* FIXME: check status */
940         vfatUpdateFCB(DeviceExt, *Fcb, &DirContext, ParentFcb);
941     }
942     else
943     {
944         /* FIXME: check status */
945         vfatMakeFCBFromDirEntry(DeviceExt, ParentFcb, &DirContext, Fcb);
946     }
947 
948     DPRINT("addentry ok\n");
949     return STATUS_SUCCESS;
950 }
951 
952 /*
953  * deleting an existing FAT entry
954  */
955 static NTSTATUS
FATDelEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,OUT PVFAT_MOVE_CONTEXT MoveContext)956 FATDelEntry(
957     IN PDEVICE_EXTENSION DeviceExt,
958     IN PVFATFCB pFcb,
959     OUT PVFAT_MOVE_CONTEXT MoveContext)
960 {
961     ULONG CurrentCluster = 0, NextCluster, i;
962     PVOID Context = NULL;
963     LARGE_INTEGER Offset;
964     PFAT_DIR_ENTRY pDirEntry = NULL;
965     NTSTATUS Status;
966 
967     ASSERT(pFcb);
968     ASSERT(pFcb->parentFcb);
969 
970     Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
971     if (!NT_SUCCESS(Status))
972     {
973         return Status;
974     }
975 
976     DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
977     DPRINT("delete entry: %u to %u\n", pFcb->startIndex, pFcb->dirIndex);
978     Offset.u.HighPart = 0;
979     for (i = pFcb->startIndex; i <= pFcb->dirIndex; i++)
980     {
981         if (Context == NULL || ((i * sizeof(FAT_DIR_ENTRY)) % PAGE_SIZE) == 0)
982         {
983             if (Context)
984             {
985                 CcSetDirtyPinnedData(Context, NULL);
986                 CcUnpinData(Context);
987             }
988             Offset.u.LowPart = (i * sizeof(FAT_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
989             _SEH2_TRY
990             {
991                 CcPinRead(pFcb->parentFcb->FileObject, &Offset, PAGE_SIZE, PIN_WAIT, &Context, (PVOID*)&pDirEntry);
992             }
993             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
994             {
995                 _SEH2_YIELD(return _SEH2_GetExceptionCode());
996             }
997             _SEH2_END;
998         }
999         pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))].Filename[0] = 0xe5;
1000         if (i == pFcb->dirIndex)
1001         {
1002             CurrentCluster =
1003             vfatDirEntryGetFirstCluster(DeviceExt,
1004                                         (PDIR_ENTRY)&pDirEntry[i % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))]);
1005         }
1006     }
1007 
1008     /* In case of moving, save properties */
1009     if (MoveContext != NULL)
1010     {
1011         pDirEntry = &pDirEntry[pFcb->dirIndex % (PAGE_SIZE / sizeof(FAT_DIR_ENTRY))];
1012         MoveContext->FirstCluster = CurrentCluster;
1013         MoveContext->FileSize = pDirEntry->FileSize;
1014         MoveContext->CreationTime = pDirEntry->CreationTime;
1015         MoveContext->CreationDate = pDirEntry->CreationDate;
1016     }
1017 
1018     if (Context)
1019     {
1020         CcSetDirtyPinnedData(Context, NULL);
1021         CcUnpinData(Context);
1022     }
1023 
1024     /* In case of moving, don't delete data */
1025     if (MoveContext == NULL)
1026     {
1027         while (CurrentCluster && CurrentCluster != 0xffffffff)
1028         {
1029             GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
1030             /* FIXME: check status */
1031             WriteCluster(DeviceExt, CurrentCluster, 0);
1032             CurrentCluster = NextCluster;
1033         }
1034 
1035         if (DeviceExt->FatInfo.FatType == FAT32)
1036         {
1037             FAT32UpdateFreeClustersCount(DeviceExt);
1038         }
1039     }
1040 
1041     return STATUS_SUCCESS;
1042 }
1043 
1044 /*
1045  * deleting an existing FAT entry
1046  */
1047 static NTSTATUS
FATXDelEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,OUT PVFAT_MOVE_CONTEXT MoveContext)1048 FATXDelEntry(
1049     IN PDEVICE_EXTENSION DeviceExt,
1050     IN PVFATFCB pFcb,
1051     OUT PVFAT_MOVE_CONTEXT MoveContext)
1052 {
1053     ULONG CurrentCluster = 0, NextCluster;
1054     PVOID Context = NULL;
1055     LARGE_INTEGER Offset;
1056     PFATX_DIR_ENTRY pDirEntry;
1057     ULONG StartIndex;
1058     NTSTATUS Status;
1059 
1060     ASSERT(pFcb);
1061     ASSERT(pFcb->parentFcb);
1062     ASSERT(vfatVolumeIsFatX(DeviceExt));
1063 
1064     StartIndex = pFcb->startIndex;
1065 
1066     Status = vfatFCBInitializeCacheFromVolume(DeviceExt, pFcb->parentFcb);
1067     if (!NT_SUCCESS(Status))
1068     {
1069         return Status;
1070     }
1071 
1072     DPRINT("delEntry PathName \'%wZ\'\n", &pFcb->PathNameU);
1073     DPRINT("delete entry: %u\n", StartIndex);
1074     Offset.u.HighPart = 0;
1075     Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE;
1076     _SEH2_TRY
1077     {
1078         CcPinRead(pFcb->parentFcb->FileObject, &Offset, PAGE_SIZE, PIN_WAIT, &Context, (PVOID*)&pDirEntry);
1079     }
1080     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1081     {
1082         DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE);
1083         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1084     }
1085     _SEH2_END;
1086     pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))];
1087     pDirEntry->FilenameLength = 0xe5;
1088     CurrentCluster = vfatDirEntryGetFirstCluster(DeviceExt,
1089                                                  (PDIR_ENTRY)pDirEntry);
1090 
1091     /* In case of moving, save properties */
1092     if (MoveContext != NULL)
1093     {
1094         MoveContext->FirstCluster = CurrentCluster;
1095         MoveContext->FileSize = pDirEntry->FileSize;
1096         MoveContext->CreationTime = pDirEntry->CreationTime;
1097         MoveContext->CreationDate = pDirEntry->CreationDate;
1098     }
1099 
1100     CcSetDirtyPinnedData(Context, NULL);
1101     CcUnpinData(Context);
1102 
1103     /* In case of moving, don't delete data */
1104     if (MoveContext == NULL)
1105     {
1106         while (CurrentCluster && CurrentCluster != 0xffffffff)
1107         {
1108             GetNextCluster(DeviceExt, CurrentCluster, &NextCluster);
1109             /* FIXME: check status */
1110             WriteCluster(DeviceExt, CurrentCluster, 0);
1111             CurrentCluster = NextCluster;
1112         }
1113     }
1114 
1115     return STATUS_SUCCESS;
1116 }
1117 
1118 /*
1119  * move an existing FAT entry
1120  */
1121 NTSTATUS
VfatMoveEntry(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB pFcb,IN PUNICODE_STRING FileName,IN PVFATFCB ParentFcb)1122 VfatMoveEntry(
1123     IN PDEVICE_EXTENSION DeviceExt,
1124     IN PVFATFCB pFcb,
1125     IN PUNICODE_STRING FileName,
1126     IN PVFATFCB ParentFcb)
1127 {
1128     NTSTATUS Status;
1129     PVFATFCB OldParent;
1130     VFAT_MOVE_CONTEXT MoveContext;
1131 
1132     DPRINT("VfatMoveEntry(%p, %p, %wZ, %p)\n", DeviceExt, pFcb, FileName, ParentFcb);
1133 
1134     /* Delete old entry while keeping data */
1135     Status = VfatDelEntry(DeviceExt, pFcb, &MoveContext);
1136     if (!NT_SUCCESS(Status))
1137     {
1138         return Status;
1139     }
1140 
1141     OldParent = pFcb->parentFcb;
1142     CcFlushCache(&OldParent->SectionObjectPointers, NULL, 0, NULL);
1143     MoveContext.InPlace = (OldParent == ParentFcb);
1144 
1145     /* Add our new entry with our cluster */
1146     Status = VfatAddEntry(DeviceExt,
1147                           FileName,
1148                           &pFcb,
1149                           ParentFcb,
1150                           (vfatFCBIsDirectory(pFcb) ? FILE_DIRECTORY_FILE : 0),
1151                           *pFcb->Attributes,
1152                           &MoveContext);
1153 
1154     CcFlushCache(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, NULL);
1155 
1156     return Status;
1157 }
1158 
1159 extern BOOLEAN FATXIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb);
1160 extern BOOLEAN FATIsDirectoryEmpty(PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb);
1161 extern NTSTATUS FATGetNextDirEntry(PVOID *pContext, PVOID *pPage, PVFATFCB pDirFcb, PVFAT_DIRENTRY_CONTEXT DirContext, BOOLEAN First);
1162 extern NTSTATUS FATXGetNextDirEntry(PVOID *pContext, PVOID *pPage, PVFATFCB pDirFcb, PVFAT_DIRENTRY_CONTEXT DirContext, BOOLEAN First);
1163 
1164 VFAT_DISPATCH FatXDispatch = {
1165     FATXIsDirectoryEmpty,   // .IsDirectoryEmpty
1166     FATXAddEntry,           // .AddEntry
1167     FATXDelEntry,           // .DelEntry
1168     FATXGetNextDirEntry,    // .GetNextDirEntry
1169 };
1170 
1171 VFAT_DISPATCH FatDispatch = {
1172     FATIsDirectoryEmpty,    // .IsDirectoryEmpty
1173     FATAddEntry,            // .AddEntry
1174     FATDelEntry,            // .DelEntry
1175     FATGetNextDirEntry,     // .GetNextDirEntry
1176 };
1177 
1178 /* EOF */
1179