xref: /reactos/drivers/filesystems/vfatfs/fcb.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:     Routines to manipulate FCBs
5  * COPYRIGHT:   Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
6  *              Copyright 2001 Rex Jolliff <rex@lvcablemodem.com>
7  *              Copyright 2005-2022 Hervé Poussineau <hpoussin@reactos.org>
8  *              Copyright 2008-2018 Pierre Schweitzer <pierre@reactos.org>
9  */
10 
11 /*  -------------------------------------------------------  INCLUDES  */
12 
13 #include "vfat.h"
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 #ifdef __GNUC__
19 #include <wctype.h> /* towlower prototype */
20 #endif
21 
22 /*  --------------------------------------------------------  DEFINES  */
23 
24 #ifdef KDBG
25 extern UNICODE_STRING DebugFile;
26 #endif
27 
28 /*  --------------------------------------------------------  PUBLICS  */
29 
30 static
31 ULONG
vfatNameHash(ULONG hash,PUNICODE_STRING NameU)32 vfatNameHash(
33     ULONG hash,
34     PUNICODE_STRING NameU)
35 {
36     PWCHAR last;
37     PWCHAR curr;
38     register WCHAR c;
39 
40     // LFN could start from "."
41     //ASSERT(NameU->Buffer[0] != L'.');
42     curr = NameU->Buffer;
43     last = NameU->Buffer + NameU->Length / sizeof(WCHAR);
44 
45     while(curr < last)
46     {
47         c = towlower(*curr++);
48         hash = (hash + (c << 4) + (c >> 4)) * 11;
49     }
50     return hash;
51 }
52 
53 VOID
vfatSplitPathName(PUNICODE_STRING PathNameU,PUNICODE_STRING DirNameU,PUNICODE_STRING FileNameU)54 vfatSplitPathName(
55     PUNICODE_STRING PathNameU,
56     PUNICODE_STRING DirNameU,
57     PUNICODE_STRING FileNameU)
58 {
59     PWCHAR pName;
60     USHORT Length = 0;
61     pName = PathNameU->Buffer + PathNameU->Length / sizeof(WCHAR) - 1;
62     while (*pName != L'\\' && pName >= PathNameU->Buffer)
63     {
64         pName--;
65         Length++;
66     }
67     ASSERT(*pName == L'\\' || pName < PathNameU->Buffer);
68     if (FileNameU)
69     {
70         FileNameU->Buffer = pName + 1;
71         FileNameU->Length = FileNameU->MaximumLength = Length * sizeof(WCHAR);
72     }
73     if (DirNameU)
74     {
75         DirNameU->Buffer = PathNameU->Buffer;
76         DirNameU->Length = (pName + 1 - PathNameU->Buffer) * sizeof(WCHAR);
77         DirNameU->MaximumLength = DirNameU->Length;
78     }
79 }
80 
81 static
82 VOID
vfatInitFcb(PVFATFCB Fcb,PUNICODE_STRING NameU)83 vfatInitFcb(
84     PVFATFCB Fcb,
85     PUNICODE_STRING NameU)
86 {
87     USHORT PathNameBufferLength;
88 
89     if (NameU)
90         PathNameBufferLength = NameU->Length + sizeof(WCHAR);
91     else
92         PathNameBufferLength = 0;
93 
94     Fcb->PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength, TAG_FCB);
95     if (!Fcb->PathNameBuffer)
96     {
97         /* FIXME: what to do if no more memory? */
98         DPRINT1("Unable to initialize FCB for filename '%wZ'\n", NameU);
99         KeBugCheckEx(FAT_FILE_SYSTEM, (ULONG_PTR)Fcb, (ULONG_PTR)NameU, 0, 0);
100     }
101 
102     Fcb->RFCB.NodeTypeCode = NODE_TYPE_FCB;
103     Fcb->RFCB.NodeByteSize = sizeof(VFATFCB);
104 
105     Fcb->PathNameU.Length = 0;
106     Fcb->PathNameU.Buffer = Fcb->PathNameBuffer;
107     Fcb->PathNameU.MaximumLength = PathNameBufferLength;
108     Fcb->ShortNameU.Length = 0;
109     Fcb->ShortNameU.Buffer = Fcb->ShortNameBuffer;
110     Fcb->ShortNameU.MaximumLength = sizeof(Fcb->ShortNameBuffer);
111     Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
112     if (NameU && NameU->Length)
113     {
114         RtlCopyUnicodeString(&Fcb->PathNameU, NameU);
115         vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
116     }
117     else
118     {
119         Fcb->DirNameU.Buffer = Fcb->LongNameU.Buffer = NULL;
120         Fcb->DirNameU.MaximumLength = Fcb->DirNameU.Length = 0;
121         Fcb->LongNameU.MaximumLength = Fcb->LongNameU.Length = 0;
122     }
123     RtlZeroMemory(&Fcb->FCBShareAccess, sizeof(SHARE_ACCESS));
124     Fcb->OpenHandleCount = 0;
125 }
126 
127 PVFATFCB
vfatNewFCB(PDEVICE_EXTENSION pVCB,PUNICODE_STRING pFileNameU)128 vfatNewFCB(
129     PDEVICE_EXTENSION pVCB,
130     PUNICODE_STRING pFileNameU)
131 {
132     PVFATFCB  rcFCB;
133 
134     DPRINT("'%wZ'\n", pFileNameU);
135 
136     rcFCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->FcbLookasideList);
137     if (rcFCB == NULL)
138     {
139         return NULL;
140     }
141     RtlZeroMemory(rcFCB, sizeof(VFATFCB));
142     vfatInitFcb(rcFCB, pFileNameU);
143     if (vfatVolumeIsFatX(pVCB))
144         rcFCB->Attributes = &rcFCB->entry.FatX.Attrib;
145     else
146         rcFCB->Attributes = &rcFCB->entry.Fat.Attrib;
147     rcFCB->Hash.Hash = vfatNameHash(0, &rcFCB->PathNameU);
148     rcFCB->Hash.self = rcFCB;
149     rcFCB->ShortHash.self = rcFCB;
150     ExInitializeResourceLite(&rcFCB->PagingIoResource);
151     ExInitializeResourceLite(&rcFCB->MainResource);
152     FsRtlInitializeFileLock(&rcFCB->FileLock, NULL, NULL);
153     ExInitializeFastMutex(&rcFCB->LastMutex);
154     rcFCB->RFCB.PagingIoResource = &rcFCB->PagingIoResource;
155     rcFCB->RFCB.Resource = &rcFCB->MainResource;
156     rcFCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
157     InitializeListHead(&rcFCB->ParentListHead);
158 
159     return  rcFCB;
160 }
161 
162 static
163 VOID
vfatDelFCBFromTable(PDEVICE_EXTENSION pVCB,PVFATFCB pFCB)164 vfatDelFCBFromTable(
165     PDEVICE_EXTENSION pVCB,
166     PVFATFCB pFCB)
167 {
168     ULONG Index;
169     ULONG ShortIndex;
170     HASHENTRY* entry;
171 
172     Index = pFCB->Hash.Hash % pVCB->HashTableSize;
173     ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
174 
175     if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
176     {
177         entry = pVCB->FcbHashTable[ShortIndex];
178         if (entry->self == pFCB)
179         {
180             pVCB->FcbHashTable[ShortIndex] = entry->next;
181         }
182         else
183         {
184             while (entry->next->self != pFCB)
185             {
186                 entry = entry->next;
187             }
188             entry->next = pFCB->ShortHash.next;
189         }
190     }
191     entry = pVCB->FcbHashTable[Index];
192     if (entry->self == pFCB)
193     {
194         pVCB->FcbHashTable[Index] = entry->next;
195     }
196     else
197     {
198         while (entry->next->self != pFCB)
199         {
200             entry = entry->next;
201         }
202         entry->next = pFCB->Hash.next;
203     }
204 
205     RemoveEntryList(&pFCB->FcbListEntry);
206 }
207 
208 static
209 NTSTATUS
vfatMakeFullName(PVFATFCB directoryFCB,PUNICODE_STRING LongNameU,PUNICODE_STRING ShortNameU,PUNICODE_STRING NameU)210 vfatMakeFullName(
211     PVFATFCB directoryFCB,
212     PUNICODE_STRING LongNameU,
213     PUNICODE_STRING ShortNameU,
214     PUNICODE_STRING NameU)
215 {
216     PWCHAR PathNameBuffer;
217     USHORT PathNameLength;
218 
219     PathNameLength = directoryFCB->PathNameU.Length + max(LongNameU->Length, ShortNameU->Length);
220     if (!vfatFCBIsRoot(directoryFCB))
221     {
222         PathNameLength += sizeof(WCHAR);
223     }
224 
225     if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR))
226     {
227         return  STATUS_OBJECT_NAME_INVALID;
228     }
229     PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB);
230     if (!PathNameBuffer)
231     {
232         return STATUS_INSUFFICIENT_RESOURCES;
233     }
234     NameU->Buffer = PathNameBuffer;
235     NameU->Length = 0;
236     NameU->MaximumLength = PathNameLength;
237 
238     RtlCopyUnicodeString(NameU, &directoryFCB->PathNameU);
239     if (!vfatFCBIsRoot(directoryFCB))
240     {
241         RtlAppendUnicodeToString(NameU, L"\\");
242     }
243     if (LongNameU->Length > 0)
244     {
245         RtlAppendUnicodeStringToString(NameU, LongNameU);
246     }
247     else
248     {
249         RtlAppendUnicodeStringToString(NameU, ShortNameU);
250     }
251     NameU->Buffer[NameU->Length / sizeof(WCHAR)] = 0;
252 
253     return STATUS_SUCCESS;
254 }
255 
256 VOID
vfatDestroyCCB(PVFATCCB pCcb)257 vfatDestroyCCB(
258     PVFATCCB pCcb)
259 {
260     if (pCcb->SearchPattern.Buffer)
261     {
262         ExFreePoolWithTag(pCcb->SearchPattern.Buffer, TAG_SEARCH);
263     }
264     ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, pCcb);
265 }
266 
267 VOID
vfatDestroyFCB(PVFATFCB pFCB)268 vfatDestroyFCB(
269     PVFATFCB pFCB)
270 {
271 #ifdef KDBG
272     if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL))
273     {
274         DPRINT1("Destroying: %p (%wZ) %d\n", pFCB, &pFCB->PathNameU, pFCB->RefCount);
275     }
276 #endif
277 
278     FsRtlUninitializeFileLock(&pFCB->FileLock);
279 
280     if (!vfatFCBIsRoot(pFCB) &&
281         !BooleanFlagOn(pFCB->Flags, FCB_IS_FAT) && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME))
282     {
283         RemoveEntryList(&pFCB->ParentListEntry);
284     }
285     ExFreePool(pFCB->PathNameBuffer);
286     ExDeleteResourceLite(&pFCB->PagingIoResource);
287     ExDeleteResourceLite(&pFCB->MainResource);
288     ASSERT(IsListEmpty(&pFCB->ParentListHead));
289     ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB);
290 }
291 
292 BOOLEAN
vfatFCBIsRoot(PVFATFCB FCB)293 vfatFCBIsRoot(
294     PVFATFCB FCB)
295 {
296     return FCB->PathNameU.Length == sizeof(WCHAR) && FCB->PathNameU.Buffer[0] == L'\\' ? TRUE : FALSE;
297 }
298 
299 VOID
300 #ifndef KDBG
vfatGrabFCB(PDEVICE_EXTENSION pVCB,PVFATFCB pFCB,PCSTR File,ULONG Line,PCSTR Func)301 vfatGrabFCB(
302 #else
303 _vfatGrabFCB(
304 #endif
305     PDEVICE_EXTENSION pVCB,
306     PVFATFCB pFCB
307 #ifdef KDBG
308     ,
309     PCSTR File,
310     ULONG Line,
311     PCSTR Func
312 #endif
313 )
314 {
315 #ifdef KDBG
316     if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL))
317     {
318         DPRINT1("Inc ref count (%d, oc: %d) for: %p (%wZ) at: %s(%d) %s\n", pFCB->RefCount, pFCB->OpenHandleCount, pFCB, &pFCB->PathNameU, File, Line, Func);
319     }
320 #else
321     DPRINT("Grabbing FCB at %p: %wZ, refCount:%d\n",
322            pFCB, &pFCB->PathNameU, pFCB->RefCount);
323 #endif
324 
325     ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource));
326 
327     ASSERT(!BooleanFlagOn(pFCB->Flags, FCB_IS_FAT));
328     ASSERT(pFCB != pVCB->VolumeFcb && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME));
329     ASSERT(pFCB->RefCount > 0);
330     ++pFCB->RefCount;
331 }
332 
333 VOID
334 #ifndef KDBG
vfatReleaseFCB(PDEVICE_EXTENSION pVCB,PVFATFCB pFCB,PCSTR File,ULONG Line,PCSTR Func)335 vfatReleaseFCB(
336 #else
337 _vfatReleaseFCB(
338 #endif
339     PDEVICE_EXTENSION pVCB,
340     PVFATFCB pFCB
341 #ifdef KDBG
342     ,
343     PCSTR File,
344     ULONG Line,
345     PCSTR Func
346 #endif
347 )
348 {
349     PVFATFCB tmpFcb;
350 
351 #ifdef KDBG
352     if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &pFCB->LongNameU, FALSE, NULL))
353     {
354         DPRINT1("Dec ref count (%d, oc: %d) for: %p (%wZ) at: %s(%d) %s\n", pFCB->RefCount, pFCB->OpenHandleCount, pFCB, &pFCB->PathNameU, File, Line, Func);
355     }
356 #else
357     DPRINT("Releasing FCB at %p: %wZ, refCount:%d\n",
358            pFCB, &pFCB->PathNameU, pFCB->RefCount);
359 #endif
360 
361     ASSERT(ExIsResourceAcquiredExclusive(&pVCB->DirResource));
362 
363     while (pFCB)
364     {
365         ULONG RefCount;
366 
367         ASSERT(!BooleanFlagOn(pFCB->Flags, FCB_IS_FAT));
368         ASSERT(pFCB != pVCB->VolumeFcb && !BooleanFlagOn(pFCB->Flags, FCB_IS_VOLUME));
369         ASSERT(pFCB->RefCount > 0);
370         RefCount = --pFCB->RefCount;
371 
372         if (RefCount == 1 && BooleanFlagOn(pFCB->Flags, FCB_CACHE_INITIALIZED))
373         {
374             PFILE_OBJECT tmpFileObject;
375             tmpFileObject = pFCB->FileObject;
376 
377             pFCB->FileObject = NULL;
378             CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
379             ClearFlag(pFCB->Flags, FCB_CACHE_INITIALIZED);
380             ObDereferenceObject(tmpFileObject);
381         }
382 
383         if (RefCount == 0)
384         {
385             ASSERT(pFCB->OpenHandleCount == 0);
386             tmpFcb = pFCB->parentFcb;
387             vfatDelFCBFromTable(pVCB, pFCB);
388             vfatDestroyFCB(pFCB);
389         }
390         else
391         {
392             tmpFcb = NULL;
393         }
394         pFCB = tmpFcb;
395     }
396 }
397 
398 static
399 VOID
vfatAddFCBToTable(PDEVICE_EXTENSION pVCB,PVFATFCB pFCB)400 vfatAddFCBToTable(
401     PDEVICE_EXTENSION pVCB,
402     PVFATFCB pFCB)
403 {
404     ULONG Index;
405     ULONG ShortIndex;
406 
407     ASSERT(pFCB->Hash.Hash == vfatNameHash(0, &pFCB->PathNameU));
408     Index = pFCB->Hash.Hash % pVCB->HashTableSize;
409     ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
410 
411     InsertTailList(&pVCB->FcbListHead, &pFCB->FcbListEntry);
412 
413     pFCB->Hash.next = pVCB->FcbHashTable[Index];
414     pVCB->FcbHashTable[Index] = &pFCB->Hash;
415     if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
416     {
417         pFCB->ShortHash.next = pVCB->FcbHashTable[ShortIndex];
418         pVCB->FcbHashTable[ShortIndex] = &pFCB->ShortHash;
419     }
420     if (pFCB->parentFcb)
421     {
422         vfatGrabFCB(pVCB, pFCB->parentFcb);
423     }
424 }
425 
426 static
427 VOID
vfatInitFCBFromDirEntry(PDEVICE_EXTENSION Vcb,PVFATFCB ParentFcb,PVFATFCB Fcb,PVFAT_DIRENTRY_CONTEXT DirContext)428 vfatInitFCBFromDirEntry(
429     PDEVICE_EXTENSION Vcb,
430     PVFATFCB ParentFcb,
431     PVFATFCB Fcb,
432     PVFAT_DIRENTRY_CONTEXT DirContext)
433 {
434     ULONG Size;
435 
436     RtlCopyMemory(&Fcb->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY));
437     RtlCopyUnicodeString(&Fcb->ShortNameU, &DirContext->ShortNameU);
438     Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU);
439     if (vfatVolumeIsFatX(Vcb))
440     {
441         Fcb->ShortHash.Hash = Fcb->Hash.Hash;
442     }
443     else
444     {
445         Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU);
446         Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU);
447     }
448 
449     if (vfatFCBIsDirectory(Fcb))
450     {
451         ULONG FirstCluster, CurrentCluster;
452         NTSTATUS Status = STATUS_SUCCESS;
453         Size = 0;
454         FirstCluster = vfatDirEntryGetFirstCluster(Vcb, &Fcb->entry);
455         if (FirstCluster == 1)
456         {
457             Size = Vcb->FatInfo.rootDirectorySectors * Vcb->FatInfo.BytesPerSector;
458         }
459         else if (FirstCluster != 0)
460         {
461             CurrentCluster = FirstCluster;
462             while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
463             {
464                 Size += Vcb->FatInfo.BytesPerCluster;
465                 Status = NextCluster(Vcb, FirstCluster, &CurrentCluster, FALSE);
466             }
467         }
468     }
469     else if (vfatVolumeIsFatX(Vcb))
470     {
471         Size = Fcb->entry.FatX.FileSize;
472     }
473     else
474     {
475         Size = Fcb->entry.Fat.FileSize;
476     }
477     Fcb->dirIndex = DirContext->DirIndex;
478     Fcb->startIndex = DirContext->StartIndex;
479     Fcb->parentFcb = ParentFcb;
480     if (vfatVolumeIsFatX(Vcb) && !vfatFCBIsRoot(ParentFcb))
481     {
482         ASSERT(DirContext->DirIndex >= 2 && DirContext->StartIndex >= 2);
483         Fcb->dirIndex = DirContext->DirIndex-2;
484         Fcb->startIndex = DirContext->StartIndex-2;
485     }
486     Fcb->RFCB.FileSize.QuadPart = Size;
487     Fcb->RFCB.ValidDataLength.QuadPart = Size;
488     Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, Vcb->FatInfo.BytesPerCluster);
489 }
490 
491 NTSTATUS
vfatSetFCBNewDirName(PDEVICE_EXTENSION pVCB,PVFATFCB Fcb,PVFATFCB ParentFcb)492 vfatSetFCBNewDirName(
493     PDEVICE_EXTENSION pVCB,
494     PVFATFCB Fcb,
495     PVFATFCB ParentFcb)
496 {
497     NTSTATUS Status;
498     UNICODE_STRING NewNameU;
499 
500     /* Get full path name */
501     Status = vfatMakeFullName(ParentFcb, &Fcb->LongNameU, &Fcb->ShortNameU, &NewNameU);
502     if (!NT_SUCCESS(Status))
503     {
504         return Status;
505     }
506 
507     /* Delete old name */
508     if (Fcb->PathNameBuffer)
509     {
510         ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB);
511     }
512     Fcb->PathNameU = NewNameU;
513 
514     /* Delete from table */
515     vfatDelFCBFromTable(pVCB, Fcb);
516 
517     /* Split it properly */
518     Fcb->PathNameBuffer = Fcb->PathNameU.Buffer;
519     Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
520     vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
521     Fcb->Hash.Hash = vfatNameHash(0, &Fcb->PathNameU);
522     if (vfatVolumeIsFatX(pVCB))
523     {
524         Fcb->ShortHash.Hash = Fcb->Hash.Hash;
525     }
526     else
527     {
528         Fcb->ShortHash.Hash = vfatNameHash(0, &Fcb->DirNameU);
529         Fcb->ShortHash.Hash = vfatNameHash(Fcb->ShortHash.Hash, &Fcb->ShortNameU);
530     }
531 
532     vfatAddFCBToTable(pVCB, Fcb);
533     vfatReleaseFCB(pVCB, ParentFcb);
534 
535     return STATUS_SUCCESS;
536 }
537 
538 NTSTATUS
vfatUpdateFCB(PDEVICE_EXTENSION pVCB,PVFATFCB Fcb,PVFAT_DIRENTRY_CONTEXT DirContext,PVFATFCB ParentFcb)539 vfatUpdateFCB(
540     PDEVICE_EXTENSION pVCB,
541     PVFATFCB Fcb,
542     PVFAT_DIRENTRY_CONTEXT DirContext,
543     PVFATFCB ParentFcb)
544 {
545     NTSTATUS Status;
546     PVFATFCB OldParent;
547 
548     DPRINT("vfatUpdateFCB(%p, %p, %p, %p)\n", pVCB, Fcb, DirContext, ParentFcb);
549 
550     /* Get full path name */
551     Status = vfatMakeFullName(ParentFcb, &DirContext->LongNameU, &DirContext->ShortNameU, &Fcb->PathNameU);
552     if (!NT_SUCCESS(Status))
553     {
554         return Status;
555     }
556 
557     /* Delete old name */
558     if (Fcb->PathNameBuffer)
559     {
560         ExFreePoolWithTag(Fcb->PathNameBuffer, TAG_FCB);
561     }
562 
563     /* Delete from table */
564     vfatDelFCBFromTable(pVCB, Fcb);
565 
566     /* Split it properly */
567     Fcb->PathNameBuffer = Fcb->PathNameU.Buffer;
568     Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
569     vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
570 
571     /* Save old parent */
572     OldParent = Fcb->parentFcb;
573     RemoveEntryList(&Fcb->ParentListEntry);
574 
575     /* Reinit FCB */
576     vfatInitFCBFromDirEntry(pVCB, ParentFcb, Fcb, DirContext);
577 
578     if (vfatFCBIsDirectory(Fcb))
579     {
580         CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);
581     }
582     InsertTailList(&ParentFcb->ParentListHead, &Fcb->ParentListEntry);
583     vfatAddFCBToTable(pVCB, Fcb);
584 
585     /* If we moved across directories, dereference our old parent
586      * We also dereference in case we're just renaming since AddFCBToTable references it
587      */
588     vfatReleaseFCB(pVCB, OldParent);
589 
590     return STATUS_SUCCESS;
591 }
592 
593 PVFATFCB
vfatGrabFCBFromTable(PDEVICE_EXTENSION pVCB,PUNICODE_STRING PathNameU)594 vfatGrabFCBFromTable(
595     PDEVICE_EXTENSION pVCB,
596     PUNICODE_STRING PathNameU)
597 {
598     PVFATFCB  rcFCB;
599     ULONG Hash;
600     UNICODE_STRING DirNameU;
601     UNICODE_STRING FileNameU;
602     PUNICODE_STRING FcbNameU;
603 
604     HASHENTRY* entry;
605 
606     DPRINT("'%wZ'\n", PathNameU);
607 
608     ASSERT(PathNameU->Length >= sizeof(WCHAR) && PathNameU->Buffer[0] == L'\\');
609     Hash = vfatNameHash(0, PathNameU);
610 
611     entry = pVCB->FcbHashTable[Hash % pVCB->HashTableSize];
612     if (entry)
613     {
614         vfatSplitPathName(PathNameU, &DirNameU, &FileNameU);
615     }
616 
617     while (entry)
618     {
619         if (entry->Hash == Hash)
620         {
621             rcFCB = entry->self;
622             DPRINT("'%wZ' '%wZ'\n", &DirNameU, &rcFCB->DirNameU);
623             if (RtlEqualUnicodeString(&DirNameU, &rcFCB->DirNameU, TRUE))
624             {
625                 if (rcFCB->Hash.Hash == Hash)
626                 {
627                     FcbNameU = &rcFCB->LongNameU;
628                 }
629                 else
630                 {
631                     FcbNameU = &rcFCB->ShortNameU;
632                 }
633                 /* compare the file name */
634                 DPRINT("'%wZ' '%wZ'\n", &FileNameU, FcbNameU);
635                 if (RtlEqualUnicodeString(&FileNameU, FcbNameU, TRUE))
636                 {
637                     vfatGrabFCB(pVCB, rcFCB);
638                     return rcFCB;
639                 }
640             }
641         }
642         entry = entry->next;
643     }
644     return NULL;
645 }
646 
647 PVFATFCB
vfatMakeRootFCB(PDEVICE_EXTENSION pVCB)648 vfatMakeRootFCB(
649     PDEVICE_EXTENSION pVCB)
650 {
651     PVFATFCB FCB;
652     ULONG FirstCluster, CurrentCluster, Size = 0;
653     NTSTATUS Status = STATUS_SUCCESS;
654     UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
655 
656     ASSERT(pVCB->RootFcb == NULL);
657 
658     FCB = vfatNewFCB(pVCB, &NameU);
659     if (vfatVolumeIsFatX(pVCB))
660     {
661         memset(FCB->entry.FatX.Filename, ' ', 42);
662         FCB->entry.FatX.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
663         FCB->entry.FatX.Attrib = FILE_ATTRIBUTE_DIRECTORY;
664         FCB->entry.FatX.FirstCluster = 1;
665         Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
666     }
667     else
668     {
669         memset(FCB->entry.Fat.ShortName, ' ', 11);
670         FCB->entry.Fat.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
671         FCB->entry.Fat.Attrib = FILE_ATTRIBUTE_DIRECTORY;
672         if (pVCB->FatInfo.FatType == FAT32)
673         {
674             CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
675             FCB->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0xffff);
676             FCB->entry.Fat.FirstClusterHigh = (unsigned short)(FirstCluster >> 16);
677 
678             while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
679             {
680                 Size += pVCB->FatInfo.BytesPerCluster;
681                 Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE);
682             }
683         }
684         else
685         {
686             FCB->entry.Fat.FirstCluster = 1;
687             Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
688         }
689     }
690     FCB->ShortHash.Hash = FCB->Hash.Hash;
691     FCB->RefCount = 2;
692     FCB->dirIndex = 0;
693     FCB->RFCB.FileSize.QuadPart = Size;
694     FCB->RFCB.ValidDataLength.QuadPart = Size;
695     FCB->RFCB.AllocationSize.QuadPart = Size;
696     FCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
697 
698     vfatFCBInitializeCacheFromVolume(pVCB, FCB);
699     vfatAddFCBToTable(pVCB, FCB);
700 
701     /* Cache it */
702     pVCB->RootFcb = FCB;
703 
704     return FCB;
705 }
706 
707 PVFATFCB
vfatOpenRootFCB(PDEVICE_EXTENSION pVCB)708 vfatOpenRootFCB(
709     PDEVICE_EXTENSION pVCB)
710 {
711     PVFATFCB FCB;
712     UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
713 
714     FCB = vfatGrabFCBFromTable(pVCB, &NameU);
715     if (FCB == NULL)
716     {
717         FCB = vfatMakeRootFCB(pVCB);
718     }
719     ASSERT(FCB == pVCB->RootFcb);
720 
721     return FCB;
722 }
723 
724 NTSTATUS
vfatMakeFCBFromDirEntry(PVCB vcb,PVFATFCB directoryFCB,PVFAT_DIRENTRY_CONTEXT DirContext,PVFATFCB * fileFCB)725 vfatMakeFCBFromDirEntry(
726     PVCB vcb,
727     PVFATFCB directoryFCB,
728     PVFAT_DIRENTRY_CONTEXT DirContext,
729     PVFATFCB *fileFCB)
730 {
731     PVFATFCB rcFCB;
732     UNICODE_STRING NameU;
733     NTSTATUS Status;
734 
735     Status = vfatMakeFullName(directoryFCB, &DirContext->LongNameU, &DirContext->ShortNameU, &NameU);
736     if (!NT_SUCCESS(Status))
737     {
738         return Status;
739     }
740 
741     rcFCB = vfatNewFCB(vcb, &NameU);
742     vfatInitFCBFromDirEntry(vcb, directoryFCB, rcFCB, DirContext);
743 
744     rcFCB->RefCount = 1;
745     InsertTailList(&directoryFCB->ParentListHead, &rcFCB->ParentListEntry);
746     vfatAddFCBToTable(vcb, rcFCB);
747     *fileFCB = rcFCB;
748 
749     ExFreePoolWithTag(NameU.Buffer, TAG_FCB);
750     return STATUS_SUCCESS;
751 }
752 
753 NTSTATUS
vfatAttachFCBToFileObject(PDEVICE_EXTENSION vcb,PVFATFCB fcb,PFILE_OBJECT fileObject)754 vfatAttachFCBToFileObject(
755     PDEVICE_EXTENSION vcb,
756     PVFATFCB fcb,
757     PFILE_OBJECT fileObject)
758 {
759     PVFATCCB newCCB;
760 
761 #ifdef KDBG
762     if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &fcb->LongNameU, FALSE, NULL))
763     {
764         DPRINT1("Attaching %p to %p (%d)\n", fcb, fileObject, fcb->RefCount);
765     }
766 #endif
767 
768     newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
769     if (newCCB == NULL)
770     {
771         return  STATUS_INSUFFICIENT_RESOURCES;
772     }
773     RtlZeroMemory(newCCB, sizeof (VFATCCB));
774 
775     fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
776     fileObject->FsContext = fcb;
777     fileObject->FsContext2 = newCCB;
778     fileObject->Vpb = vcb->IoVPB;
779     DPRINT("file open: fcb:%p PathName:%wZ\n", fcb, &fcb->PathNameU);
780 
781 #ifdef KDBG
782     fcb->Flags &= ~FCB_CLEANED_UP;
783     fcb->Flags &= ~FCB_CLOSED;
784 #endif
785 
786     return STATUS_SUCCESS;
787 }
788 
789 NTSTATUS
vfatDirFindFile(PDEVICE_EXTENSION pDeviceExt,PVFATFCB pDirectoryFCB,PUNICODE_STRING FileToFindU,PVFATFCB * pFoundFCB)790 vfatDirFindFile(
791     PDEVICE_EXTENSION pDeviceExt,
792     PVFATFCB pDirectoryFCB,
793     PUNICODE_STRING FileToFindU,
794     PVFATFCB *pFoundFCB)
795 {
796     NTSTATUS status;
797     PVOID Context = NULL;
798     PVOID Page = NULL;
799     BOOLEAN First = TRUE;
800     VFAT_DIRENTRY_CONTEXT DirContext;
801     /* This buffer must have a size of 260 characters, because
802     vfatMakeFCBFromDirEntry can copy 20 name entries with 13 characters. */
803     WCHAR LongNameBuffer[260];
804     WCHAR ShortNameBuffer[13];
805     BOOLEAN FoundLong = FALSE;
806     BOOLEAN FoundShort = FALSE;
807     BOOLEAN IsFatX = vfatVolumeIsFatX(pDeviceExt);
808 
809     ASSERT(pDeviceExt);
810     ASSERT(pDirectoryFCB);
811     ASSERT(FileToFindU);
812 
813     DPRINT("vfatDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
814            pDeviceExt, pDirectoryFCB, FileToFindU);
815     DPRINT("Dir Path:%wZ\n", &pDirectoryFCB->PathNameU);
816 
817     DirContext.DirIndex = 0;
818     DirContext.LongNameU.Buffer = LongNameBuffer;
819     DirContext.LongNameU.Length = 0;
820     DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
821     DirContext.ShortNameU.Buffer = ShortNameBuffer;
822     DirContext.ShortNameU.Length = 0;
823     DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
824     DirContext.DeviceExt = pDeviceExt;
825 
826     while (TRUE)
827     {
828         status = VfatGetNextDirEntry(pDeviceExt,
829             &Context,
830             &Page,
831             pDirectoryFCB,
832             &DirContext,
833             First);
834         First = FALSE;
835         if (status == STATUS_NO_MORE_ENTRIES)
836         {
837             return STATUS_OBJECT_NAME_NOT_FOUND;
838         }
839         if (!NT_SUCCESS(status))
840         {
841             return status;
842         }
843 
844         DPRINT("  Index:%u  longName:%wZ\n",
845                DirContext.DirIndex, &DirContext.LongNameU);
846 
847         if (!ENTRY_VOLUME(IsFatX, &DirContext.DirEntry))
848         {
849             if (DirContext.LongNameU.Length == 0 ||
850                 DirContext.ShortNameU.Length == 0)
851             {
852                 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
853                 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
854                 {
855                     ASSERT(DirContext.LongNameU.Length != 0 &&
856                            DirContext.ShortNameU.Length != 0);
857                 }
858                 DirContext.DirIndex++;
859                 continue;
860             }
861             FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE);
862             if (FoundLong == FALSE)
863             {
864                 FoundShort = RtlEqualUnicodeString(FileToFindU, &DirContext.ShortNameU, TRUE);
865             }
866             if (FoundLong || FoundShort)
867             {
868                 status = vfatMakeFCBFromDirEntry(pDeviceExt,
869                     pDirectoryFCB,
870                     &DirContext,
871                     pFoundFCB);
872                 CcUnpinData(Context);
873                 return status;
874             }
875         }
876         DirContext.DirIndex++;
877     }
878 
879     return STATUS_OBJECT_NAME_NOT_FOUND;
880 }
881 
882 NTSTATUS
vfatGetFCBForFile(PDEVICE_EXTENSION pVCB,PVFATFCB * pParentFCB,PVFATFCB * pFCB,PUNICODE_STRING pFileNameU)883 vfatGetFCBForFile(
884     PDEVICE_EXTENSION pVCB,
885     PVFATFCB *pParentFCB,
886     PVFATFCB *pFCB,
887     PUNICODE_STRING pFileNameU)
888 {
889     NTSTATUS status;
890     PVFATFCB FCB = NULL;
891     PVFATFCB parentFCB;
892     UNICODE_STRING NameU;
893     UNICODE_STRING RootNameU = RTL_CONSTANT_STRING(L"\\");
894     UNICODE_STRING FileNameU;
895     WCHAR NameBuffer[260];
896     PWCHAR curr, prev, last;
897     ULONG Length;
898 
899     DPRINT("vfatGetFCBForFile (%p,%p,%p,%wZ)\n",
900            pVCB, pParentFCB, pFCB, pFileNameU);
901 
902     RtlInitEmptyUnicodeString(&FileNameU, NameBuffer, sizeof(NameBuffer));
903 
904     parentFCB = *pParentFCB;
905 
906     if (parentFCB == NULL)
907     {
908         /* Passed-in name is the full name */
909         RtlCopyUnicodeString(&FileNameU, pFileNameU);
910 
911         //  Trivial case, open of the root directory on volume
912         if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE))
913         {
914             DPRINT("returning root FCB\n");
915 
916             FCB = vfatOpenRootFCB(pVCB);
917             *pFCB = FCB;
918             *pParentFCB = NULL;
919 
920             return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
921         }
922 
923         /* Check for an existing FCB */
924         FCB = vfatGrabFCBFromTable(pVCB, &FileNameU);
925         if (FCB)
926         {
927             *pFCB = FCB;
928             *pParentFCB = FCB->parentFcb;
929             vfatGrabFCB(pVCB, *pParentFCB);
930             return STATUS_SUCCESS;
931         }
932 
933         last = curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
934         while (*curr != L'\\' && curr > FileNameU.Buffer)
935         {
936             curr--;
937         }
938 
939         if (curr > FileNameU.Buffer)
940         {
941             NameU.Buffer = FileNameU.Buffer;
942             NameU.MaximumLength = NameU.Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
943             FCB = vfatGrabFCBFromTable(pVCB, &NameU);
944             if (FCB)
945             {
946                 Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
947                 if (Length != FCB->PathNameU.Length)
948                 {
949                     if (FileNameU.Length + FCB->PathNameU.Length - Length > FileNameU.MaximumLength)
950                     {
951                         vfatReleaseFCB(pVCB, FCB);
952                         return STATUS_OBJECT_NAME_INVALID;
953                     }
954                     RtlMoveMemory(FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR),
955                         curr, FileNameU.Length - Length);
956                     FileNameU.Length += (USHORT)(FCB->PathNameU.Length - Length);
957                     curr = FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR);
958                     last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
959                 }
960                 RtlCopyMemory(FileNameU.Buffer, FCB->PathNameU.Buffer, FCB->PathNameU.Length);
961             }
962         }
963         else
964         {
965             FCB = NULL;
966         }
967 
968         if (FCB == NULL)
969         {
970             FCB = vfatOpenRootFCB(pVCB);
971             curr = FileNameU.Buffer;
972         }
973 
974         parentFCB = NULL;
975         prev = curr;
976     }
977     else
978     {
979         /* Make absolute path */
980         RtlCopyUnicodeString(&FileNameU, &parentFCB->PathNameU);
981         curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
982         if (*curr != L'\\')
983         {
984             RtlAppendUnicodeToString(&FileNameU, L"\\");
985             curr++;
986         }
987         ASSERT(*curr == L'\\');
988         RtlAppendUnicodeStringToString(&FileNameU, pFileNameU);
989 
990         FCB = parentFCB;
991         parentFCB = NULL;
992         prev = curr;
993         last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
994     }
995 
996     while (curr <= last)
997     {
998         if (parentFCB)
999         {
1000             vfatReleaseFCB(pVCB, parentFCB);
1001             parentFCB = NULL;
1002         }
1003         //  fail if element in FCB is not a directory
1004         if (!vfatFCBIsDirectory(FCB))
1005         {
1006             DPRINT ("Element in requested path is not a directory\n");
1007 
1008             vfatReleaseFCB(pVCB, FCB);
1009             FCB = NULL;
1010             *pParentFCB = NULL;
1011             *pFCB = NULL;
1012 
1013             return  STATUS_OBJECT_PATH_NOT_FOUND;
1014         }
1015         parentFCB = FCB;
1016         if (prev < curr)
1017         {
1018             Length = (curr - prev) * sizeof(WCHAR);
1019             if (Length != parentFCB->LongNameU.Length)
1020             {
1021                 if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength)
1022                 {
1023                     vfatReleaseFCB(pVCB, parentFCB);
1024                     *pParentFCB = NULL;
1025                     *pFCB = NULL;
1026                     return STATUS_OBJECT_NAME_INVALID;
1027                 }
1028                 RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr,
1029                     FileNameU.Length - (curr - FileNameU.Buffer) * sizeof(WCHAR));
1030                 FileNameU.Length += (USHORT)(parentFCB->LongNameU.Length - Length);
1031                 curr = prev + parentFCB->LongNameU.Length / sizeof(WCHAR);
1032                 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
1033             }
1034             RtlCopyMemory(prev, parentFCB->LongNameU.Buffer, parentFCB->LongNameU.Length);
1035         }
1036         curr++;
1037         prev = curr;
1038         while (*curr != L'\\' && curr <= last)
1039         {
1040             curr++;
1041         }
1042         NameU.Buffer = FileNameU.Buffer;
1043         NameU.Length = (curr - NameU.Buffer) * sizeof(WCHAR);
1044         NameU.MaximumLength = FileNameU.MaximumLength;
1045         DPRINT("%wZ\n", &NameU);
1046         FCB = vfatGrabFCBFromTable(pVCB, &NameU);
1047         if (FCB == NULL)
1048         {
1049             NameU.Buffer = prev;
1050             NameU.MaximumLength = NameU.Length = (curr - prev) * sizeof(WCHAR);
1051             status = vfatDirFindFile(pVCB, parentFCB, &NameU, &FCB);
1052             if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1053             {
1054                 *pFCB = NULL;
1055                 if (curr > last)
1056                 {
1057                     *pParentFCB = parentFCB;
1058                     return STATUS_OBJECT_NAME_NOT_FOUND;
1059                 }
1060                 else
1061                 {
1062                     vfatReleaseFCB(pVCB, parentFCB);
1063                     *pParentFCB = NULL;
1064                     return STATUS_OBJECT_PATH_NOT_FOUND;
1065                 }
1066             }
1067             else if (!NT_SUCCESS(status))
1068             {
1069                 vfatReleaseFCB(pVCB, parentFCB);
1070                 *pParentFCB = NULL;
1071                 *pFCB = NULL;
1072 
1073                 return status;
1074             }
1075         }
1076     }
1077 
1078     *pParentFCB = parentFCB;
1079     *pFCB = FCB;
1080 
1081     return STATUS_SUCCESS;
1082 }
1083