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