[FASTFAT] Only initialize directory cache on use.
[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 PVFATFCB
644 vfatMakeRootFCB(
645 PDEVICE_EXTENSION pVCB)
646 {
647 PVFATFCB FCB;
648 ULONG FirstCluster, CurrentCluster, Size = 0;
649 NTSTATUS Status = STATUS_SUCCESS;
650 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
651
652 FCB = vfatNewFCB(pVCB, &NameU);
653 if (vfatVolumeIsFatX(pVCB))
654 {
655 memset(FCB->entry.FatX.Filename, ' ', 42);
656 FCB->entry.FatX.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
657 FCB->entry.FatX.Attrib = FILE_ATTRIBUTE_DIRECTORY;
658 FCB->entry.FatX.FirstCluster = 1;
659 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
660 }
661 else
662 {
663 memset(FCB->entry.Fat.ShortName, ' ', 11);
664 FCB->entry.Fat.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
665 FCB->entry.Fat.Attrib = FILE_ATTRIBUTE_DIRECTORY;
666 if (pVCB->FatInfo.FatType == FAT32)
667 {
668 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
669 FCB->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0xffff);
670 FCB->entry.Fat.FirstClusterHigh = (unsigned short)(FirstCluster >> 16);
671
672 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
673 {
674 Size += pVCB->FatInfo.BytesPerCluster;
675 Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE);
676 }
677 }
678 else
679 {
680 FCB->entry.Fat.FirstCluster = 1;
681 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
682 }
683 }
684 FCB->ShortHash.Hash = FCB->Hash.Hash;
685 FCB->RefCount = 2;
686 FCB->dirIndex = 0;
687 FCB->RFCB.FileSize.QuadPart = Size;
688 FCB->RFCB.ValidDataLength.QuadPart = Size;
689 FCB->RFCB.AllocationSize.QuadPart = Size;
690 FCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
691
692 vfatFCBInitializeCacheFromVolume(pVCB, FCB);
693 vfatAddFCBToTable(pVCB, FCB);
694
695 return FCB;
696 }
697
698 PVFATFCB
699 vfatOpenRootFCB(
700 PDEVICE_EXTENSION pVCB)
701 {
702 PVFATFCB FCB;
703 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
704
705 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
706 if (FCB == NULL)
707 {
708 FCB = vfatMakeRootFCB(pVCB);
709 }
710
711 return FCB;
712 }
713
714 NTSTATUS
715 vfatMakeFCBFromDirEntry(
716 PVCB vcb,
717 PVFATFCB directoryFCB,
718 PVFAT_DIRENTRY_CONTEXT DirContext,
719 PVFATFCB *fileFCB)
720 {
721 PVFATFCB rcFCB;
722 UNICODE_STRING NameU;
723 NTSTATUS Status;
724
725 Status = vfatMakeFullName(directoryFCB, &DirContext->LongNameU, &DirContext->ShortNameU, &NameU);
726 if (!NT_SUCCESS(Status))
727 {
728 return Status;
729 }
730
731 rcFCB = vfatNewFCB(vcb, &NameU);
732 vfatInitFCBFromDirEntry(vcb, rcFCB, DirContext);
733
734 rcFCB->RefCount = 1;
735 rcFCB->parentFcb = directoryFCB;
736 InsertTailList(&directoryFCB->ParentListHead, &rcFCB->ParentListEntry);
737 vfatAddFCBToTable(vcb, rcFCB);
738 *fileFCB = rcFCB;
739
740 ExFreePoolWithTag(NameU.Buffer, TAG_FCB);
741 return STATUS_SUCCESS;
742 }
743
744 NTSTATUS
745 vfatAttachFCBToFileObject(
746 PDEVICE_EXTENSION vcb,
747 PVFATFCB fcb,
748 PFILE_OBJECT fileObject)
749 {
750 PVFATCCB newCCB;
751
752 UNREFERENCED_PARAMETER(vcb);
753
754 #ifdef KDBG
755 if (DebugFile.Buffer != NULL && FsRtlIsNameInExpression(&DebugFile, &fcb->LongNameU, FALSE, NULL))
756 {
757 DPRINT1("Attaching %p to %p (%d)\n", fcb, fileObject, fcb->RefCount);
758 }
759 #endif
760
761 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
762 if (newCCB == NULL)
763 {
764 return STATUS_INSUFFICIENT_RESOURCES;
765 }
766 RtlZeroMemory(newCCB, sizeof (VFATCCB));
767
768 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
769 fileObject->FsContext = fcb;
770 fileObject->FsContext2 = newCCB;
771 fileObject->Vpb = vcb->IoVPB;
772 DPRINT("file open: fcb:%p PathName:%wZ\n", fcb, &fcb->PathNameU);
773
774 #ifdef KDBG
775 fcb->Flags &= ~FCB_CLEANED_UP;
776 fcb->Flags &= ~FCB_CLOSED;
777 #endif
778
779 return STATUS_SUCCESS;
780 }
781
782 NTSTATUS
783 vfatDirFindFile(
784 PDEVICE_EXTENSION pDeviceExt,
785 PVFATFCB pDirectoryFCB,
786 PUNICODE_STRING FileToFindU,
787 PVFATFCB *pFoundFCB)
788 {
789 NTSTATUS status;
790 PVOID Context = NULL;
791 PVOID Page = NULL;
792 BOOLEAN First = TRUE;
793 VFAT_DIRENTRY_CONTEXT DirContext;
794 /* This buffer must have a size of 260 characters, because
795 vfatMakeFCBFromDirEntry can copy 20 name entries with 13 characters. */
796 WCHAR LongNameBuffer[260];
797 WCHAR ShortNameBuffer[13];
798 BOOLEAN FoundLong = FALSE;
799 BOOLEAN FoundShort = FALSE;
800 BOOLEAN IsFatX = vfatVolumeIsFatX(pDeviceExt);
801
802 ASSERT(pDeviceExt);
803 ASSERT(pDirectoryFCB);
804 ASSERT(FileToFindU);
805
806 DPRINT("vfatDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
807 pDeviceExt, pDirectoryFCB, FileToFindU);
808 DPRINT("Dir Path:%wZ\n", &pDirectoryFCB->PathNameU);
809
810 DirContext.DirIndex = 0;
811 DirContext.LongNameU.Buffer = LongNameBuffer;
812 DirContext.LongNameU.Length = 0;
813 DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
814 DirContext.ShortNameU.Buffer = ShortNameBuffer;
815 DirContext.ShortNameU.Length = 0;
816 DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
817 DirContext.DeviceExt = pDeviceExt;
818
819 while (TRUE)
820 {
821 status = VfatGetNextDirEntry(pDeviceExt,
822 &Context,
823 &Page,
824 pDirectoryFCB,
825 &DirContext,
826 First);
827 First = FALSE;
828 if (status == STATUS_NO_MORE_ENTRIES)
829 {
830 return STATUS_OBJECT_NAME_NOT_FOUND;
831 }
832 if (!NT_SUCCESS(status))
833 {
834 return status;
835 }
836
837 DPRINT(" Index:%u longName:%wZ\n",
838 DirContext.DirIndex, &DirContext.LongNameU);
839
840 if (!ENTRY_VOLUME(IsFatX, &DirContext.DirEntry))
841 {
842 if (DirContext.LongNameU.Length == 0 ||
843 DirContext.ShortNameU.Length == 0)
844 {
845 DPRINT1("WARNING: File system corruption detected. You may need to run a disk repair utility.\n");
846 if (VfatGlobalData->Flags & VFAT_BREAK_ON_CORRUPTION)
847 {
848 ASSERT(DirContext.LongNameU.Length != 0 &&
849 DirContext.ShortNameU.Length != 0);
850 }
851 DirContext.DirIndex++;
852 continue;
853 }
854 FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE);
855 if (FoundLong == FALSE)
856 {
857 FoundShort = RtlEqualUnicodeString(FileToFindU, &DirContext.ShortNameU, TRUE);
858 }
859 if (FoundLong || FoundShort)
860 {
861 status = vfatMakeFCBFromDirEntry(pDeviceExt,
862 pDirectoryFCB,
863 &DirContext,
864 pFoundFCB);
865 CcUnpinData(Context);
866 return status;
867 }
868 }
869 DirContext.DirIndex++;
870 }
871
872 return STATUS_OBJECT_NAME_NOT_FOUND;
873 }
874
875 NTSTATUS
876 vfatGetFCBForFile(
877 PDEVICE_EXTENSION pVCB,
878 PVFATFCB *pParentFCB,
879 PVFATFCB *pFCB,
880 PUNICODE_STRING pFileNameU)
881 {
882 NTSTATUS status;
883 PVFATFCB FCB = NULL;
884 PVFATFCB parentFCB;
885 UNICODE_STRING NameU;
886 UNICODE_STRING RootNameU = RTL_CONSTANT_STRING(L"\\");
887 UNICODE_STRING FileNameU;
888 WCHAR NameBuffer[260];
889 PWCHAR curr, prev, last;
890 ULONG Length;
891
892 DPRINT("vfatGetFCBForFile (%p,%p,%p,%wZ)\n",
893 pVCB, pParentFCB, pFCB, pFileNameU);
894
895 RtlInitEmptyUnicodeString(&FileNameU, NameBuffer, sizeof(NameBuffer));
896
897 parentFCB = *pParentFCB;
898
899 if (parentFCB == NULL)
900 {
901 /* Passed-in name is the full name */
902 RtlCopyUnicodeString(&FileNameU, pFileNameU);
903
904 // Trivial case, open of the root directory on volume
905 if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE))
906 {
907 DPRINT("returning root FCB\n");
908
909 FCB = vfatOpenRootFCB(pVCB);
910 *pFCB = FCB;
911 *pParentFCB = NULL;
912
913 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
914 }
915
916 /* Check for an existing FCB */
917 FCB = vfatGrabFCBFromTable(pVCB, &FileNameU);
918 if (FCB)
919 {
920 *pFCB = FCB;
921 *pParentFCB = FCB->parentFcb;
922 vfatGrabFCB(pVCB, *pParentFCB);
923 return STATUS_SUCCESS;
924 }
925
926 last = curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
927 while (*curr != L'\\' && curr > FileNameU.Buffer)
928 {
929 curr--;
930 }
931
932 if (curr > FileNameU.Buffer)
933 {
934 NameU.Buffer = FileNameU.Buffer;
935 NameU.MaximumLength = NameU.Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
936 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
937 if (FCB)
938 {
939 Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
940 if (Length != FCB->PathNameU.Length)
941 {
942 if (FileNameU.Length + FCB->PathNameU.Length - Length > FileNameU.MaximumLength)
943 {
944 vfatReleaseFCB(pVCB, FCB);
945 return STATUS_OBJECT_NAME_INVALID;
946 }
947 RtlMoveMemory(FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR),
948 curr, FileNameU.Length - Length);
949 FileNameU.Length += (USHORT)(FCB->PathNameU.Length - Length);
950 curr = FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR);
951 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
952 }
953 RtlCopyMemory(FileNameU.Buffer, FCB->PathNameU.Buffer, FCB->PathNameU.Length);
954 }
955 }
956 else
957 {
958 FCB = NULL;
959 }
960
961 if (FCB == NULL)
962 {
963 FCB = vfatOpenRootFCB(pVCB);
964 curr = FileNameU.Buffer;
965 }
966
967 parentFCB = NULL;
968 prev = curr;
969 }
970 else
971 {
972 /* Make absolute path */
973 RtlCopyUnicodeString(&FileNameU, &parentFCB->PathNameU);
974 curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
975 if (*curr != L'\\')
976 {
977 RtlAppendUnicodeToString(&FileNameU, L"\\");
978 curr++;
979 }
980 ASSERT(*curr == L'\\');
981 RtlAppendUnicodeStringToString(&FileNameU, pFileNameU);
982
983 FCB = parentFCB;
984 parentFCB = NULL;
985 prev = curr;
986 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
987 }
988
989 while (curr <= last)
990 {
991 if (parentFCB)
992 {
993 vfatReleaseFCB(pVCB, parentFCB);
994 parentFCB = NULL;
995 }
996 // fail if element in FCB is not a directory
997 if (!vfatFCBIsDirectory(FCB))
998 {
999 DPRINT ("Element in requested path is not a directory\n");
1000
1001 vfatReleaseFCB(pVCB, FCB);
1002 FCB = NULL;
1003 *pParentFCB = NULL;
1004 *pFCB = NULL;
1005
1006 return STATUS_OBJECT_PATH_NOT_FOUND;
1007 }
1008 parentFCB = FCB;
1009 if (prev < curr)
1010 {
1011 Length = (curr - prev) * sizeof(WCHAR);
1012 if (Length != parentFCB->LongNameU.Length)
1013 {
1014 if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength)
1015 {
1016 vfatReleaseFCB(pVCB, parentFCB);
1017 *pParentFCB = NULL;
1018 *pFCB = NULL;
1019 return STATUS_OBJECT_NAME_INVALID;
1020 }
1021 RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr,
1022 FileNameU.Length - (curr - FileNameU.Buffer) * sizeof(WCHAR));
1023 FileNameU.Length += (USHORT)(parentFCB->LongNameU.Length - Length);
1024 curr = prev + parentFCB->LongNameU.Length / sizeof(WCHAR);
1025 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
1026 }
1027 RtlCopyMemory(prev, parentFCB->LongNameU.Buffer, parentFCB->LongNameU.Length);
1028 }
1029 curr++;
1030 prev = curr;
1031 while (*curr != L'\\' && curr <= last)
1032 {
1033 curr++;
1034 }
1035 NameU.Buffer = FileNameU.Buffer;
1036 NameU.Length = (curr - NameU.Buffer) * sizeof(WCHAR);
1037 NameU.MaximumLength = FileNameU.MaximumLength;
1038 DPRINT("%wZ\n", &NameU);
1039 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
1040 if (FCB == NULL)
1041 {
1042 NameU.Buffer = prev;
1043 NameU.MaximumLength = NameU.Length = (curr - prev) * sizeof(WCHAR);
1044 status = vfatDirFindFile(pVCB, parentFCB, &NameU, &FCB);
1045 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1046 {
1047 *pFCB = NULL;
1048 if (curr > last)
1049 {
1050 *pParentFCB = parentFCB;
1051 return STATUS_OBJECT_NAME_NOT_FOUND;
1052 }
1053 else
1054 {
1055 vfatReleaseFCB(pVCB, parentFCB);
1056 *pParentFCB = NULL;
1057 return STATUS_OBJECT_PATH_NOT_FOUND;
1058 }
1059 }
1060 else if (!NT_SUCCESS(status))
1061 {
1062 vfatReleaseFCB(pVCB, parentFCB);
1063 *pParentFCB = NULL;
1064 *pFCB = NULL;
1065
1066 return status;
1067 }
1068 }
1069 }
1070
1071 *pParentFCB = parentFCB;
1072 *pFCB = FCB;
1073
1074 return STATUS_SUCCESS;
1075 }