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