[FASTFAT]
[reactos.git] / reactos / drivers / filesystems / fastfat / fcb.c
1 /*
2 * FILE: drivers/fs/vfat/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 */
10
11 /* ------------------------------------------------------- INCLUDES */
12
13 #ifdef __GNUC__
14 #include <wctype.h> /* towlower prototype */
15 #endif
16
17 #define NDEBUG
18 #include "vfat.h"
19
20 /* -------------------------------------------------------- DEFINES */
21
22 #define TAG_FCB 'BCFV'
23
24 /* -------------------------------------------------------- PUBLICS */
25
26 static
27 ULONG
28 vfatNameHash(
29 ULONG hash,
30 PUNICODE_STRING NameU)
31 {
32 PWCHAR last;
33 PWCHAR curr;
34 register WCHAR c;
35
36 // LFN could start from "."
37 //ASSERT(NameU->Buffer[0] != L'.');
38 curr = NameU->Buffer;
39 last = NameU->Buffer + NameU->Length / sizeof(WCHAR);
40
41 while(curr < last)
42 {
43 c = towlower(*curr++);
44 hash = (hash + (c << 4) + (c >> 4)) * 11;
45 }
46 return hash;
47 }
48
49 VOID
50 vfatSplitPathName(
51 PUNICODE_STRING PathNameU,
52 PUNICODE_STRING DirNameU,
53 PUNICODE_STRING FileNameU)
54 {
55 PWCHAR pName;
56 USHORT Length = 0;
57 pName = PathNameU->Buffer + PathNameU->Length / sizeof(WCHAR) - 1;
58 while (*pName != L'\\' && pName >= PathNameU->Buffer)
59 {
60 pName--;
61 Length++;
62 }
63 ASSERT(*pName == L'\\' || pName < PathNameU->Buffer);
64 if (FileNameU)
65 {
66 FileNameU->Buffer = pName + 1;
67 FileNameU->Length = FileNameU->MaximumLength = Length * sizeof(WCHAR);
68 }
69 if (DirNameU)
70 {
71 DirNameU->Buffer = PathNameU->Buffer;
72 DirNameU->Length = (pName + 1 - PathNameU->Buffer) * sizeof(WCHAR);
73 DirNameU->MaximumLength = DirNameU->Length;
74 }
75 }
76
77 static
78 VOID
79 vfatInitFcb(
80 PVFATFCB Fcb,
81 PUNICODE_STRING NameU)
82 {
83 USHORT PathNameBufferLength;
84
85 if (NameU)
86 PathNameBufferLength = NameU->Length + sizeof(WCHAR);
87 else
88 PathNameBufferLength = 0;
89
90 Fcb->PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameBufferLength, TAG_FCB);
91 if (!Fcb->PathNameBuffer)
92 {
93 /* FIXME: what to do if no more memory? */
94 DPRINT1("Unable to initialize FCB for filename '%wZ'\n", NameU);
95 KeBugCheckEx(FAT_FILE_SYSTEM, (ULONG_PTR)Fcb, (ULONG_PTR)NameU, 0, 0);
96 }
97
98 Fcb->PathNameU.Length = 0;
99 Fcb->PathNameU.Buffer = Fcb->PathNameBuffer;
100 Fcb->PathNameU.MaximumLength = PathNameBufferLength;
101 Fcb->ShortNameU.Length = 0;
102 Fcb->ShortNameU.Buffer = Fcb->ShortNameBuffer;
103 Fcb->ShortNameU.MaximumLength = sizeof(Fcb->ShortNameBuffer);
104 Fcb->DirNameU.Buffer = Fcb->PathNameU.Buffer;
105 if (NameU && NameU->Length)
106 {
107 RtlCopyUnicodeString(&Fcb->PathNameU, NameU);
108 vfatSplitPathName(&Fcb->PathNameU, &Fcb->DirNameU, &Fcb->LongNameU);
109 }
110 else
111 {
112 Fcb->DirNameU.Buffer = Fcb->LongNameU.Buffer = NULL;
113 Fcb->DirNameU.MaximumLength = Fcb->DirNameU.Length = 0;
114 Fcb->LongNameU.MaximumLength = Fcb->LongNameU.Length = 0;
115 }
116 RtlZeroMemory(&Fcb->FCBShareAccess, sizeof(SHARE_ACCESS));
117 Fcb->OpenHandleCount = 0;
118 }
119
120 PVFATFCB
121 vfatNewFCB(
122 PDEVICE_EXTENSION pVCB,
123 PUNICODE_STRING pFileNameU)
124 {
125 PVFATFCB rcFCB;
126
127 DPRINT("'%wZ'\n", pFileNameU);
128
129 rcFCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->FcbLookasideList);
130 if (rcFCB == NULL)
131 {
132 return NULL;
133 }
134 RtlZeroMemory(rcFCB, sizeof(VFATFCB));
135 vfatInitFcb(rcFCB, pFileNameU);
136 if (pVCB->Flags & VCB_IS_FATX)
137 {
138 rcFCB->Flags |= FCB_IS_FATX_ENTRY;
139 rcFCB->Attributes = &rcFCB->entry.FatX.Attrib;
140 }
141 else
142 rcFCB->Attributes = &rcFCB->entry.Fat.Attrib;
143 rcFCB->Hash.Hash = vfatNameHash(0, &rcFCB->PathNameU);
144 rcFCB->Hash.self = rcFCB;
145 rcFCB->ShortHash.self = rcFCB;
146 ExInitializeResourceLite(&rcFCB->PagingIoResource);
147 ExInitializeResourceLite(&rcFCB->MainResource);
148 FsRtlInitializeFileLock(&rcFCB->FileLock, NULL, NULL);
149 ExInitializeFastMutex(&rcFCB->LastMutex);
150 rcFCB->RFCB.PagingIoResource = &rcFCB->PagingIoResource;
151 rcFCB->RFCB.Resource = &rcFCB->MainResource;
152 rcFCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
153
154 return rcFCB;
155 }
156
157 VOID
158 vfatDestroyCCB(
159 PVFATCCB pCcb)
160 {
161 if (pCcb->SearchPattern.Buffer)
162 {
163 ExFreePoolWithTag(pCcb->SearchPattern.Buffer, TAG_VFAT);
164 }
165 ExFreeToNPagedLookasideList(&VfatGlobalData->CcbLookasideList, pCcb);
166 }
167
168 VOID
169 vfatDestroyFCB(
170 PVFATFCB pFCB)
171 {
172 FsRtlUninitializeFileLock(&pFCB->FileLock);
173 ExFreePool(pFCB->PathNameBuffer);
174 ExDeleteResourceLite(&pFCB->PagingIoResource);
175 ExDeleteResourceLite(&pFCB->MainResource);
176 ExFreeToNPagedLookasideList(&VfatGlobalData->FcbLookasideList, pFCB);
177 }
178
179 BOOLEAN
180 vfatFCBIsDirectory(
181 PVFATFCB FCB)
182 {
183 return *FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY;
184 }
185
186 BOOLEAN
187 vfatFCBIsRoot(
188 PVFATFCB FCB)
189 {
190 return FCB->PathNameU.Length == sizeof(WCHAR) && FCB->PathNameU.Buffer[0] == L'\\' ? TRUE : FALSE;
191 }
192
193 VOID
194 vfatReleaseFCB(
195 PDEVICE_EXTENSION pVCB,
196 PVFATFCB pFCB)
197 {
198 HASHENTRY* entry;
199 ULONG Index;
200 ULONG ShortIndex;
201 PVFATFCB tmpFcb;
202
203 DPRINT("releasing FCB at %p: %wZ, refCount:%d\n",
204 pFCB, &pFCB->PathNameU, pFCB->RefCount);
205
206 while (pFCB)
207 {
208 Index = pFCB->Hash.Hash % pVCB->HashTableSize;
209 ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
210 pFCB->RefCount--;
211 if (pFCB->RefCount == 0)
212 {
213 tmpFcb = pFCB->parentFcb;
214 RemoveEntryList (&pFCB->FcbListEntry);
215 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
216 {
217 entry = pVCB->FcbHashTable[ShortIndex];
218 if (entry->self == pFCB)
219 {
220 pVCB->FcbHashTable[ShortIndex] = entry->next;
221 }
222 else
223 {
224 while (entry->next->self != pFCB)
225 {
226 entry = entry->next;
227 }
228 entry->next = pFCB->ShortHash.next;
229 }
230 }
231 entry = pVCB->FcbHashTable[Index];
232 if (entry->self == pFCB)
233 {
234 pVCB->FcbHashTable[Index] = entry->next;
235 }
236 else
237 {
238 while (entry->next->self != pFCB)
239 {
240 entry = entry->next;
241 }
242 entry->next = pFCB->Hash.next;
243 }
244 vfatDestroyFCB(pFCB);
245 }
246 else
247 {
248 tmpFcb = NULL;
249 }
250 pFCB = tmpFcb;
251 }
252 }
253
254 VOID
255 vfatAddFCBToTable(
256 PDEVICE_EXTENSION pVCB,
257 PVFATFCB pFCB)
258 {
259 ULONG Index;
260 ULONG ShortIndex;
261
262 Index = pFCB->Hash.Hash % pVCB->HashTableSize;
263 ShortIndex = pFCB->ShortHash.Hash % pVCB->HashTableSize;
264
265 InsertTailList(&pVCB->FcbListHead, &pFCB->FcbListEntry);
266
267 pFCB->Hash.next = pVCB->FcbHashTable[Index];
268 pVCB->FcbHashTable[Index] = &pFCB->Hash;
269 if (pFCB->Hash.Hash != pFCB->ShortHash.Hash)
270 {
271 pFCB->ShortHash.next = pVCB->FcbHashTable[ShortIndex];
272 pVCB->FcbHashTable[ShortIndex] = &pFCB->ShortHash;
273 }
274 if (pFCB->parentFcb)
275 {
276 pFCB->parentFcb->RefCount++;
277 }
278 }
279
280 PVFATFCB
281 vfatGrabFCBFromTable(
282 PDEVICE_EXTENSION pVCB,
283 PUNICODE_STRING PathNameU)
284 {
285 PVFATFCB rcFCB;
286 ULONG Hash;
287 UNICODE_STRING DirNameU;
288 UNICODE_STRING FileNameU;
289 PUNICODE_STRING FcbNameU;
290
291 HASHENTRY* entry;
292
293 DPRINT("'%wZ'\n", PathNameU);
294
295 Hash = vfatNameHash(0, PathNameU);
296
297 entry = pVCB->FcbHashTable[Hash % pVCB->HashTableSize];
298 if (entry)
299 {
300 vfatSplitPathName(PathNameU, &DirNameU, &FileNameU);
301 }
302
303 while (entry)
304 {
305 if (entry->Hash == Hash)
306 {
307 rcFCB = entry->self;
308 DPRINT("'%wZ' '%wZ'\n", &DirNameU, &rcFCB->DirNameU);
309 if (RtlEqualUnicodeString(&DirNameU, &rcFCB->DirNameU, TRUE))
310 {
311 if (rcFCB->Hash.Hash == Hash)
312 {
313 FcbNameU = &rcFCB->LongNameU;
314 }
315 else
316 {
317 FcbNameU = &rcFCB->ShortNameU;
318 }
319 /* compare the file name */
320 DPRINT("'%wZ' '%wZ'\n", &FileNameU, FcbNameU);
321 if (RtlEqualUnicodeString(&FileNameU, FcbNameU, TRUE))
322 {
323 rcFCB->RefCount++;
324 return rcFCB;
325 }
326 }
327 }
328 entry = entry->next;
329 }
330 return NULL;
331 }
332
333 static
334 NTSTATUS
335 vfatFCBInitializeCacheFromVolume(
336 PVCB vcb,
337 PVFATFCB fcb)
338 {
339 PFILE_OBJECT fileObject;
340 PVFATCCB newCCB;
341
342 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
343
344 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
345 if (newCCB == NULL)
346 {
347 return STATUS_INSUFFICIENT_RESOURCES;
348 }
349 RtlZeroMemory(newCCB, sizeof (VFATCCB));
350
351 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
352 fileObject->FsContext = fcb;
353 fileObject->FsContext2 = newCCB;
354 fcb->FileObject = fileObject;
355 fcb->RefCount++;
356
357 CcInitializeCacheMap(fileObject,
358 (PCC_FILE_SIZES)(&fcb->RFCB.AllocationSize),
359 TRUE,
360 &VfatGlobalData->CacheMgrCallbacks,
361 fcb);
362
363 fcb->Flags |= FCB_CACHE_INITIALIZED;
364 return STATUS_SUCCESS;
365 }
366
367 PVFATFCB
368 vfatMakeRootFCB(
369 PDEVICE_EXTENSION pVCB)
370 {
371 PVFATFCB FCB;
372 ULONG FirstCluster, CurrentCluster, Size = 0;
373 NTSTATUS Status = STATUS_SUCCESS;
374 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
375
376 FCB = vfatNewFCB(pVCB, &NameU);
377 if (FCB->Flags & FCB_IS_FATX_ENTRY)
378 {
379 memset(FCB->entry.FatX.Filename, ' ', 42);
380 FCB->entry.FatX.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
381 FCB->entry.FatX.Attrib = FILE_ATTRIBUTE_DIRECTORY;
382 FCB->entry.FatX.FirstCluster = 1;
383 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
384 }
385 else
386 {
387 memset(FCB->entry.Fat.ShortName, ' ', 11);
388 FCB->entry.Fat.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
389 FCB->entry.Fat.Attrib = FILE_ATTRIBUTE_DIRECTORY;
390 if (pVCB->FatInfo.FatType == FAT32)
391 {
392 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
393 FCB->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0xffff);
394 FCB->entry.Fat.FirstClusterHigh = (unsigned short)(FirstCluster >> 16);
395
396 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
397 {
398 Size += pVCB->FatInfo.BytesPerCluster;
399 Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE);
400 }
401 }
402 else
403 {
404 FCB->entry.Fat.FirstCluster = 1;
405 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
406 }
407 }
408 FCB->ShortHash.Hash = FCB->Hash.Hash;
409 FCB->RefCount = 2;
410 FCB->dirIndex = 0;
411 FCB->RFCB.FileSize.QuadPart = Size;
412 FCB->RFCB.ValidDataLength.QuadPart = Size;
413 FCB->RFCB.AllocationSize.QuadPart = Size;
414 FCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
415
416 vfatFCBInitializeCacheFromVolume(pVCB, FCB);
417 vfatAddFCBToTable(pVCB, FCB);
418
419 return FCB;
420 }
421
422 PVFATFCB
423 vfatOpenRootFCB(
424 PDEVICE_EXTENSION pVCB)
425 {
426 PVFATFCB FCB;
427 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
428
429 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
430 if (FCB == NULL)
431 {
432 FCB = vfatMakeRootFCB(pVCB);
433 }
434
435 return FCB;
436 }
437
438 NTSTATUS
439 vfatMakeFCBFromDirEntry(
440 PVCB vcb,
441 PVFATFCB directoryFCB,
442 PVFAT_DIRENTRY_CONTEXT DirContext,
443 PVFATFCB *fileFCB)
444 {
445 PVFATFCB rcFCB;
446 PWCHAR PathNameBuffer;
447 USHORT PathNameLength;
448 ULONG Size;
449 ULONG hash;
450
451 UNICODE_STRING NameU;
452
453 PathNameLength = directoryFCB->PathNameU.Length + max(DirContext->LongNameU.Length, DirContext->ShortNameU.Length);
454 if (!vfatFCBIsRoot (directoryFCB))
455 {
456 PathNameLength += sizeof(WCHAR);
457 }
458
459 if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR))
460 {
461 return STATUS_OBJECT_NAME_INVALID;
462 }
463 PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB);
464 if (!PathNameBuffer)
465 {
466 return STATUS_INSUFFICIENT_RESOURCES;
467 }
468 NameU.Buffer = PathNameBuffer;
469 NameU.Length = 0;
470 NameU.MaximumLength = PathNameLength;
471
472 RtlCopyUnicodeString(&NameU, &directoryFCB->PathNameU);
473 if (!vfatFCBIsRoot(directoryFCB))
474 {
475 RtlAppendUnicodeToString(&NameU, L"\\");
476 }
477 hash = vfatNameHash(0, &NameU);
478 if (DirContext->LongNameU.Length > 0)
479 {
480 RtlAppendUnicodeStringToString(&NameU, &DirContext->LongNameU);
481 }
482 else
483 {
484 RtlAppendUnicodeStringToString(&NameU, &DirContext->ShortNameU);
485 }
486 NameU.Buffer[NameU.Length / sizeof(WCHAR)] = 0;
487
488 rcFCB = vfatNewFCB(vcb, &NameU);
489 RtlCopyMemory(&rcFCB->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY));
490 RtlCopyUnicodeString(&rcFCB->ShortNameU, &DirContext->ShortNameU);
491 if (vcb->Flags & VCB_IS_FATX)
492 {
493 rcFCB->ShortHash.Hash = rcFCB->Hash.Hash;
494 }
495 else
496 {
497 rcFCB->ShortHash.Hash = vfatNameHash(hash, &rcFCB->ShortNameU);
498 }
499
500 if (vfatFCBIsDirectory(rcFCB))
501 {
502 ULONG FirstCluster, CurrentCluster;
503 NTSTATUS Status = STATUS_SUCCESS;
504 Size = 0;
505 FirstCluster = vfatDirEntryGetFirstCluster(vcb, &rcFCB->entry);
506 if (FirstCluster == 1)
507 {
508 Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
509 }
510 else if (FirstCluster != 0)
511 {
512 CurrentCluster = FirstCluster;
513 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
514 {
515 Size += vcb->FatInfo.BytesPerCluster;
516 Status = NextCluster(vcb, FirstCluster, &CurrentCluster, FALSE);
517 }
518 }
519 }
520 else if (rcFCB->Flags & FCB_IS_FATX_ENTRY)
521 {
522 Size = rcFCB->entry.FatX.FileSize;
523 }
524 else
525 {
526 Size = rcFCB->entry.Fat.FileSize;
527 }
528 rcFCB->dirIndex = DirContext->DirIndex;
529 rcFCB->startIndex = DirContext->StartIndex;
530 if ((rcFCB->Flags & FCB_IS_FATX_ENTRY) && !vfatFCBIsRoot(directoryFCB))
531 {
532 ASSERT(DirContext->DirIndex >= 2 && DirContext->StartIndex >= 2);
533 rcFCB->dirIndex = DirContext->DirIndex-2;
534 rcFCB->startIndex = DirContext->StartIndex-2;
535 }
536 rcFCB->RFCB.FileSize.QuadPart = Size;
537 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
538 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
539 rcFCB->RefCount++;
540 if (vfatFCBIsDirectory(rcFCB))
541 {
542 vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
543 }
544 rcFCB->parentFcb = directoryFCB;
545 vfatAddFCBToTable(vcb, rcFCB);
546 *fileFCB = rcFCB;
547
548 ExFreePool(PathNameBuffer);
549 return STATUS_SUCCESS;
550 }
551
552 NTSTATUS
553 vfatAttachFCBToFileObject(
554 PDEVICE_EXTENSION vcb,
555 PVFATFCB fcb,
556 PFILE_OBJECT fileObject)
557 {
558 PVFATCCB newCCB;
559
560 UNREFERENCED_PARAMETER(vcb);
561
562 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
563 if (newCCB == NULL)
564 {
565 return STATUS_INSUFFICIENT_RESOURCES;
566 }
567 RtlZeroMemory(newCCB, sizeof (VFATCCB));
568
569 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
570 fileObject->FsContext = fcb;
571 fileObject->FsContext2 = newCCB;
572 DPRINT("file open: fcb:%p PathName:%wZ\n", fcb, &fcb->PathNameU);
573
574 return STATUS_SUCCESS;
575 }
576
577 NTSTATUS
578 vfatDirFindFile(
579 PDEVICE_EXTENSION pDeviceExt,
580 PVFATFCB pDirectoryFCB,
581 PUNICODE_STRING FileToFindU,
582 PVFATFCB *pFoundFCB)
583 {
584 NTSTATUS status;
585 PVOID Context = NULL;
586 PVOID Page = NULL;
587 BOOLEAN First = TRUE;
588 VFAT_DIRENTRY_CONTEXT DirContext;
589 /* This buffer must have a size of 260 characters, because
590 vfatMakeFCBFromDirEntry can copy 20 name entries with 13 characters. */
591 WCHAR LongNameBuffer[260];
592 WCHAR ShortNameBuffer[13];
593 BOOLEAN FoundLong = FALSE;
594 BOOLEAN FoundShort = FALSE;
595
596 ASSERT(pDeviceExt);
597 ASSERT(pDirectoryFCB);
598 ASSERT(FileToFindU);
599
600 DPRINT("vfatDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
601 pDeviceExt, pDirectoryFCB, FileToFindU);
602 DPRINT("Dir Path:%wZ\n", &pDirectoryFCB->PathNameU);
603
604 DirContext.DirIndex = 0;
605 DirContext.LongNameU.Buffer = LongNameBuffer;
606 DirContext.LongNameU.Length = 0;
607 DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
608 DirContext.ShortNameU.Buffer = ShortNameBuffer;
609 DirContext.ShortNameU.Length = 0;
610 DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
611
612 while (TRUE)
613 {
614 status = pDeviceExt->GetNextDirEntry(&Context,
615 &Page,
616 pDirectoryFCB,
617 &DirContext,
618 First);
619 First = FALSE;
620 if (status == STATUS_NO_MORE_ENTRIES)
621 {
622 return STATUS_OBJECT_NAME_NOT_FOUND;
623 }
624 if (!NT_SUCCESS(status))
625 {
626 return status;
627 }
628
629 DPRINT(" Index:%u longName:%wZ\n",
630 DirContext.DirIndex, &DirContext.LongNameU);
631
632 if (!ENTRY_VOLUME(pDeviceExt, &DirContext.DirEntry))
633 {
634 FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE);
635 if (FoundLong == FALSE)
636 {
637 FoundShort = RtlEqualUnicodeString(FileToFindU, &DirContext.ShortNameU, TRUE);
638 }
639 if (FoundLong || FoundShort)
640 {
641 status = vfatMakeFCBFromDirEntry(pDeviceExt,
642 pDirectoryFCB,
643 &DirContext,
644 pFoundFCB);
645 CcUnpinData(Context);
646 return status;
647 }
648 }
649 DirContext.DirIndex++;
650 }
651
652 return STATUS_OBJECT_NAME_NOT_FOUND;
653 }
654
655 NTSTATUS
656 vfatGetFCBForFile(
657 PDEVICE_EXTENSION pVCB,
658 PVFATFCB *pParentFCB,
659 PVFATFCB *pFCB,
660 PUNICODE_STRING pFileNameU)
661 {
662 NTSTATUS status;
663 PVFATFCB FCB = NULL;
664 PVFATFCB parentFCB;
665 UNICODE_STRING NameU;
666 UNICODE_STRING RootNameU = RTL_CONSTANT_STRING(L"\\");
667 UNICODE_STRING FileNameU;
668 WCHAR NameBuffer[260];
669 PWCHAR curr, prev, last;
670 ULONG Length;
671
672 DPRINT("vfatGetFCBForFile (%p,%p,%p,%wZ)\n",
673 pVCB, pParentFCB, pFCB, pFileNameU);
674
675 FileNameU.Buffer = NameBuffer;
676 FileNameU.MaximumLength = sizeof(NameBuffer);
677 RtlCopyUnicodeString(&FileNameU, pFileNameU);
678
679 parentFCB = *pParentFCB;
680
681 if (parentFCB == NULL)
682 {
683 // Trivial case, open of the root directory on volume
684 if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE))
685 {
686 DPRINT("returning root FCB\n");
687
688 FCB = vfatOpenRootFCB(pVCB);
689 *pFCB = FCB;
690 *pParentFCB = NULL;
691
692 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
693 }
694
695 /* Check for an existing FCB */
696 FCB = vfatGrabFCBFromTable(pVCB, &FileNameU);
697 if (FCB)
698 {
699 *pFCB = FCB;
700 *pParentFCB = FCB->parentFcb;
701 (*pParentFCB)->RefCount++;
702 return STATUS_SUCCESS;
703 }
704
705 last = curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
706 while (*curr != L'\\' && curr > FileNameU.Buffer)
707 {
708 curr--;
709 }
710
711 if (curr > FileNameU.Buffer)
712 {
713 NameU.Buffer = FileNameU.Buffer;
714 NameU.MaximumLength = NameU.Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
715 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
716 if (FCB)
717 {
718 Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
719 if (Length != FCB->PathNameU.Length)
720 {
721 if (FileNameU.Length + FCB->PathNameU.Length - Length > FileNameU.MaximumLength)
722 {
723 vfatReleaseFCB(pVCB, FCB);
724 return STATUS_OBJECT_NAME_INVALID;
725 }
726 RtlMoveMemory(FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR),
727 curr, FileNameU.Length - Length);
728 FileNameU.Length += (USHORT)(FCB->PathNameU.Length - Length);
729 curr = FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR);
730 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
731 }
732 RtlCopyMemory(FileNameU.Buffer, FCB->PathNameU.Buffer, FCB->PathNameU.Length);
733 }
734 }
735 else
736 {
737 FCB = NULL;
738 }
739
740 if (FCB == NULL)
741 {
742 FCB = vfatOpenRootFCB(pVCB);
743 curr = FileNameU.Buffer;
744 }
745
746 parentFCB = NULL;
747 prev = curr;
748 }
749 else
750 {
751 FCB = parentFCB;
752 parentFCB = NULL;
753 prev = curr = FileNameU.Buffer - 1;
754 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
755 }
756
757 while (curr <= last)
758 {
759 if (parentFCB)
760 {
761 vfatReleaseFCB(pVCB, parentFCB);
762 parentFCB = 0;
763 }
764 // fail if element in FCB is not a directory
765 if (!vfatFCBIsDirectory(FCB))
766 {
767 DPRINT ("Element in requested path is not a directory\n");
768
769 vfatReleaseFCB(pVCB, FCB);
770 FCB = NULL;
771 *pParentFCB = NULL;
772 *pFCB = NULL;
773
774 return STATUS_OBJECT_PATH_NOT_FOUND;
775 }
776 parentFCB = FCB;
777 if (prev < curr)
778 {
779 Length = (curr - prev) * sizeof(WCHAR);
780 if (Length != parentFCB->LongNameU.Length)
781 {
782 if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength)
783 {
784 vfatReleaseFCB(pVCB, parentFCB);
785 return STATUS_OBJECT_NAME_INVALID;
786 }
787 RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr,
788 FileNameU.Length - (curr - FileNameU.Buffer) * sizeof(WCHAR));
789 FileNameU.Length += (USHORT)(parentFCB->LongNameU.Length - Length);
790 curr = prev + parentFCB->LongNameU.Length / sizeof(WCHAR);
791 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
792 }
793 RtlCopyMemory(prev, parentFCB->LongNameU.Buffer, parentFCB->LongNameU.Length);
794 }
795 curr++;
796 prev = curr;
797 while (*curr != L'\\' && curr <= last)
798 {
799 curr++;
800 }
801 NameU.Buffer = FileNameU.Buffer;
802 NameU.Length = (curr - NameU.Buffer) * sizeof(WCHAR);
803 NameU.MaximumLength = FileNameU.MaximumLength;
804 DPRINT("%wZ\n", &NameU);
805 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
806 if (FCB == NULL)
807 {
808 NameU.Buffer = prev;
809 NameU.MaximumLength = NameU.Length = (curr - prev) * sizeof(WCHAR);
810 status = vfatDirFindFile(pVCB, parentFCB, &NameU, &FCB);
811 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
812 {
813 *pFCB = NULL;
814 if (curr > last)
815 {
816 *pParentFCB = parentFCB;
817 return STATUS_OBJECT_NAME_NOT_FOUND;
818 }
819 else
820 {
821 vfatReleaseFCB(pVCB, parentFCB);
822 *pParentFCB = NULL;
823 return STATUS_OBJECT_PATH_NOT_FOUND;
824 }
825 }
826 else if (!NT_SUCCESS(status))
827 {
828 vfatReleaseFCB(pVCB, parentFCB);
829 *pParentFCB = NULL;
830 *pFCB = NULL;
831
832 return status;
833 }
834 }
835 }
836
837 *pParentFCB = parentFCB;
838 *pFCB = FCB;
839
840 return STATUS_SUCCESS;
841 }
842