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