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