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