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