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