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