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