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