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