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