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