Sync with trunk.
[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
344 fileObject = IoCreateStreamFileObject (NULL, vcb->StorageDevice);
345
346 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
347 if (newCCB == NULL)
348 {
349 return STATUS_INSUFFICIENT_RESOURCES;
350 }
351 RtlZeroMemory(newCCB, sizeof (VFATCCB));
352
353 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
354 fileObject->FsContext = fcb;
355 fileObject->FsContext2 = newCCB;
356 fcb->FileObject = fileObject;
357 fcb->RefCount++;
358
359 CcInitializeCacheMap(fileObject,
360 (PCC_FILE_SIZES)(&fcb->RFCB.AllocationSize),
361 TRUE,
362 &VfatGlobalData->CacheMgrCallbacks,
363 fcb);
364
365 fcb->Flags |= FCB_CACHE_INITIALIZED;
366 return STATUS_SUCCESS;
367 }
368
369 PVFATFCB
370 vfatMakeRootFCB(
371 PDEVICE_EXTENSION pVCB)
372 {
373 PVFATFCB FCB;
374 ULONG FirstCluster, CurrentCluster, Size = 0;
375 NTSTATUS Status = STATUS_SUCCESS;
376 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
377
378 FCB = vfatNewFCB(pVCB, &NameU);
379 if (FCB->Flags & FCB_IS_FATX_ENTRY)
380 {
381 memset(FCB->entry.FatX.Filename, ' ', 42);
382 FCB->entry.FatX.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
383 FCB->entry.FatX.Attrib = FILE_ATTRIBUTE_DIRECTORY;
384 FCB->entry.FatX.FirstCluster = 1;
385 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
386 }
387 else
388 {
389 memset(FCB->entry.Fat.ShortName, ' ', 11);
390 FCB->entry.Fat.FileSize = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
391 FCB->entry.Fat.Attrib = FILE_ATTRIBUTE_DIRECTORY;
392 if (pVCB->FatInfo.FatType == FAT32)
393 {
394 CurrentCluster = FirstCluster = pVCB->FatInfo.RootCluster;
395 FCB->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0xffff);
396 FCB->entry.Fat.FirstClusterHigh = (unsigned short)(FirstCluster >> 16);
397
398 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
399 {
400 Size += pVCB->FatInfo.BytesPerCluster;
401 Status = NextCluster (pVCB, FirstCluster, &CurrentCluster, FALSE);
402 }
403 }
404 else
405 {
406 FCB->entry.Fat.FirstCluster = 1;
407 Size = pVCB->FatInfo.rootDirectorySectors * pVCB->FatInfo.BytesPerSector;
408 }
409 }
410 FCB->ShortHash.Hash = FCB->Hash.Hash;
411 FCB->RefCount = 2;
412 FCB->dirIndex = 0;
413 FCB->RFCB.FileSize.QuadPart = Size;
414 FCB->RFCB.ValidDataLength.QuadPart = Size;
415 FCB->RFCB.AllocationSize.QuadPart = Size;
416 FCB->RFCB.IsFastIoPossible = FastIoIsNotPossible;
417
418 vfatFCBInitializeCacheFromVolume(pVCB, FCB);
419 vfatAddFCBToTable(pVCB, FCB);
420
421 return FCB;
422 }
423
424 PVFATFCB
425 vfatOpenRootFCB(
426 PDEVICE_EXTENSION pVCB)
427 {
428 PVFATFCB FCB;
429 UNICODE_STRING NameU = RTL_CONSTANT_STRING(L"\\");
430
431 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
432 if (FCB == NULL)
433 {
434 FCB = vfatMakeRootFCB(pVCB);
435 }
436
437 return FCB;
438 }
439
440 NTSTATUS
441 vfatMakeFCBFromDirEntry(
442 PVCB vcb,
443 PVFATFCB directoryFCB,
444 PVFAT_DIRENTRY_CONTEXT DirContext,
445 PVFATFCB *fileFCB)
446 {
447 PVFATFCB rcFCB;
448 PWCHAR PathNameBuffer;
449 USHORT PathNameLength;
450 ULONG Size;
451 ULONG hash;
452
453 UNICODE_STRING NameU;
454
455 PathNameLength = directoryFCB->PathNameU.Length + max(DirContext->LongNameU.Length, DirContext->ShortNameU.Length);
456 if (!vfatFCBIsRoot (directoryFCB))
457 {
458 PathNameLength += sizeof(WCHAR);
459 }
460
461 if (PathNameLength > LONGNAME_MAX_LENGTH * sizeof(WCHAR))
462 {
463 return STATUS_OBJECT_NAME_INVALID;
464 }
465 PathNameBuffer = ExAllocatePoolWithTag(NonPagedPool, PathNameLength + sizeof(WCHAR), TAG_FCB);
466 if (!PathNameBuffer)
467 {
468 return STATUS_INSUFFICIENT_RESOURCES;
469 }
470 NameU.Buffer = PathNameBuffer;
471 NameU.Length = 0;
472 NameU.MaximumLength = PathNameLength;
473
474 RtlCopyUnicodeString(&NameU, &directoryFCB->PathNameU);
475 if (!vfatFCBIsRoot(directoryFCB))
476 {
477 RtlAppendUnicodeToString(&NameU, L"\\");
478 }
479 hash = vfatNameHash(0, &NameU);
480 if (DirContext->LongNameU.Length > 0)
481 {
482 RtlAppendUnicodeStringToString(&NameU, &DirContext->LongNameU);
483 }
484 else
485 {
486 RtlAppendUnicodeStringToString(&NameU, &DirContext->ShortNameU);
487 }
488 NameU.Buffer[NameU.Length / sizeof(WCHAR)] = 0;
489
490 rcFCB = vfatNewFCB(vcb, &NameU);
491 RtlCopyMemory(&rcFCB->entry, &DirContext->DirEntry, sizeof (DIR_ENTRY));
492 RtlCopyUnicodeString(&rcFCB->ShortNameU, &DirContext->ShortNameU);
493 if (vcb->Flags & VCB_IS_FATX)
494 {
495 rcFCB->ShortHash.Hash = rcFCB->Hash.Hash;
496 }
497 else
498 {
499 rcFCB->ShortHash.Hash = vfatNameHash(hash, &rcFCB->ShortNameU);
500 }
501
502 if (vfatFCBIsDirectory(rcFCB))
503 {
504 ULONG FirstCluster, CurrentCluster;
505 NTSTATUS Status = STATUS_SUCCESS;
506 Size = 0;
507 FirstCluster = vfatDirEntryGetFirstCluster(vcb, &rcFCB->entry);
508 if (FirstCluster == 1)
509 {
510 Size = vcb->FatInfo.rootDirectorySectors * vcb->FatInfo.BytesPerSector;
511 }
512 else if (FirstCluster != 0)
513 {
514 CurrentCluster = FirstCluster;
515 while (CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
516 {
517 Size += vcb->FatInfo.BytesPerCluster;
518 Status = NextCluster(vcb, FirstCluster, &CurrentCluster, FALSE);
519 }
520 }
521 }
522 else if (rcFCB->Flags & FCB_IS_FATX_ENTRY)
523 {
524 Size = rcFCB->entry.FatX.FileSize;
525 }
526 else
527 {
528 Size = rcFCB->entry.Fat.FileSize;
529 }
530 rcFCB->dirIndex = DirContext->DirIndex;
531 rcFCB->startIndex = DirContext->StartIndex;
532 if ((rcFCB->Flags & FCB_IS_FATX_ENTRY) && !vfatFCBIsRoot(directoryFCB))
533 {
534 ASSERT(DirContext->DirIndex >= 2 && DirContext->StartIndex >= 2);
535 rcFCB->dirIndex = DirContext->DirIndex-2;
536 rcFCB->startIndex = DirContext->StartIndex-2;
537 }
538 rcFCB->RFCB.FileSize.QuadPart = Size;
539 rcFCB->RFCB.ValidDataLength.QuadPart = Size;
540 rcFCB->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, vcb->FatInfo.BytesPerCluster);
541 rcFCB->RefCount++;
542 if (vfatFCBIsDirectory(rcFCB))
543 {
544 vfatFCBInitializeCacheFromVolume(vcb, rcFCB);
545 }
546 rcFCB->parentFcb = directoryFCB;
547 vfatAddFCBToTable(vcb, rcFCB);
548 *fileFCB = rcFCB;
549
550 ExFreePool(PathNameBuffer);
551 return STATUS_SUCCESS;
552 }
553
554 NTSTATUS
555 vfatAttachFCBToFileObject(
556 PDEVICE_EXTENSION vcb,
557 PVFATFCB fcb,
558 PFILE_OBJECT fileObject)
559 {
560 PVFATCCB newCCB;
561
562 UNREFERENCED_PARAMETER(vcb);
563
564 newCCB = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
565 if (newCCB == NULL)
566 {
567 return STATUS_INSUFFICIENT_RESOURCES;
568 }
569 RtlZeroMemory(newCCB, sizeof (VFATCCB));
570
571 fileObject->SectionObjectPointer = &fcb->SectionObjectPointers;
572 fileObject->FsContext = fcb;
573 fileObject->FsContext2 = newCCB;
574 DPRINT("file open: fcb:%p PathName:%wZ\n", fcb, &fcb->PathNameU);
575
576 return STATUS_SUCCESS;
577 }
578
579 NTSTATUS
580 vfatDirFindFile(
581 PDEVICE_EXTENSION pDeviceExt,
582 PVFATFCB pDirectoryFCB,
583 PUNICODE_STRING FileToFindU,
584 PVFATFCB *pFoundFCB)
585 {
586 NTSTATUS status;
587 PVOID Context = NULL;
588 PVOID Page = NULL;
589 BOOLEAN First = TRUE;
590 VFAT_DIRENTRY_CONTEXT DirContext;
591 /* This buffer must have a size of 260 characters, because
592 vfatMakeFCBFromDirEntry can copy 20 name entries with 13 characters. */
593 WCHAR LongNameBuffer[260];
594 WCHAR ShortNameBuffer[13];
595 BOOLEAN FoundLong = FALSE;
596 BOOLEAN FoundShort = FALSE;
597
598 ASSERT(pDeviceExt);
599 ASSERT(pDirectoryFCB);
600 ASSERT(FileToFindU);
601
602 DPRINT("vfatDirFindFile(VCB:%p, dirFCB:%p, File:%wZ)\n",
603 pDeviceExt, pDirectoryFCB, FileToFindU);
604 DPRINT("Dir Path:%wZ\n", &pDirectoryFCB->PathNameU);
605
606 DirContext.DirIndex = 0;
607 DirContext.LongNameU.Buffer = LongNameBuffer;
608 DirContext.LongNameU.Length = 0;
609 DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
610 DirContext.ShortNameU.Buffer = ShortNameBuffer;
611 DirContext.ShortNameU.Length = 0;
612 DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
613
614 while (TRUE)
615 {
616 status = pDeviceExt->GetNextDirEntry(&Context,
617 &Page,
618 pDirectoryFCB,
619 &DirContext,
620 First);
621 First = FALSE;
622 if (status == STATUS_NO_MORE_ENTRIES)
623 {
624 return STATUS_OBJECT_NAME_NOT_FOUND;
625 }
626 if (!NT_SUCCESS(status))
627 {
628 return status;
629 }
630
631 DPRINT(" Index:%u longName:%wZ\n",
632 DirContext.DirIndex, &DirContext.LongNameU);
633
634 if (!ENTRY_VOLUME(pDeviceExt, &DirContext.DirEntry))
635 {
636 FoundLong = RtlEqualUnicodeString(FileToFindU, &DirContext.LongNameU, TRUE);
637 if (FoundLong == FALSE)
638 {
639 FoundShort = RtlEqualUnicodeString(FileToFindU, &DirContext.ShortNameU, TRUE);
640 }
641 if (FoundLong || FoundShort)
642 {
643 status = vfatMakeFCBFromDirEntry(pDeviceExt,
644 pDirectoryFCB,
645 &DirContext,
646 pFoundFCB);
647 CcUnpinData(Context);
648 return status;
649 }
650 }
651 DirContext.DirIndex++;
652 }
653
654 return STATUS_OBJECT_NAME_NOT_FOUND;
655 }
656
657 NTSTATUS
658 vfatGetFCBForFile(
659 PDEVICE_EXTENSION pVCB,
660 PVFATFCB *pParentFCB,
661 PVFATFCB *pFCB,
662 PUNICODE_STRING pFileNameU)
663 {
664 NTSTATUS status;
665 PVFATFCB FCB = NULL;
666 PVFATFCB parentFCB;
667 UNICODE_STRING NameU;
668 UNICODE_STRING RootNameU = RTL_CONSTANT_STRING(L"\\");
669 UNICODE_STRING FileNameU;
670 WCHAR NameBuffer[260];
671 PWCHAR curr, prev, last;
672 ULONG Length;
673
674 DPRINT("vfatGetFCBForFile (%p,%p,%p,%wZ)\n",
675 pVCB, pParentFCB, pFCB, pFileNameU);
676
677 FileNameU.Buffer = NameBuffer;
678 FileNameU.MaximumLength = sizeof(NameBuffer);
679 RtlCopyUnicodeString(&FileNameU, pFileNameU);
680
681 parentFCB = *pParentFCB;
682
683 if (parentFCB == NULL)
684 {
685 // Trivial case, open of the root directory on volume
686 if (RtlEqualUnicodeString(&FileNameU, &RootNameU, FALSE))
687 {
688 DPRINT("returning root FCB\n");
689
690 FCB = vfatOpenRootFCB(pVCB);
691 *pFCB = FCB;
692 *pParentFCB = NULL;
693
694 return (FCB != NULL) ? STATUS_SUCCESS : STATUS_OBJECT_PATH_NOT_FOUND;
695 }
696
697 /* Check for an existing FCB */
698 FCB = vfatGrabFCBFromTable(pVCB, &FileNameU);
699 if (FCB)
700 {
701 *pFCB = FCB;
702 *pParentFCB = FCB->parentFcb;
703 (*pParentFCB)->RefCount++;
704 return STATUS_SUCCESS;
705 }
706
707 last = curr = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
708 while (*curr != L'\\' && curr > FileNameU.Buffer)
709 {
710 curr--;
711 }
712
713 if (curr > FileNameU.Buffer)
714 {
715 NameU.Buffer = FileNameU.Buffer;
716 NameU.MaximumLength = NameU.Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
717 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
718 if (FCB)
719 {
720 Length = (curr - FileNameU.Buffer) * sizeof(WCHAR);
721 if (Length != FCB->PathNameU.Length)
722 {
723 if (FileNameU.Length + FCB->PathNameU.Length - Length > FileNameU.MaximumLength)
724 {
725 vfatReleaseFCB(pVCB, FCB);
726 return STATUS_OBJECT_NAME_INVALID;
727 }
728 RtlMoveMemory(FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR),
729 curr, FileNameU.Length - Length);
730 FileNameU.Length += (USHORT)(FCB->PathNameU.Length - Length);
731 curr = FileNameU.Buffer + FCB->PathNameU.Length / sizeof(WCHAR);
732 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
733 }
734 RtlCopyMemory(FileNameU.Buffer, FCB->PathNameU.Buffer, FCB->PathNameU.Length);
735 }
736 }
737 else
738 {
739 FCB = NULL;
740 }
741
742 if (FCB == NULL)
743 {
744 FCB = vfatOpenRootFCB(pVCB);
745 curr = FileNameU.Buffer;
746 }
747
748 parentFCB = NULL;
749 prev = curr;
750 }
751 else
752 {
753 FCB = parentFCB;
754 parentFCB = NULL;
755 prev = curr = FileNameU.Buffer - 1;
756 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
757 }
758
759 while (curr <= last)
760 {
761 if (parentFCB)
762 {
763 vfatReleaseFCB(pVCB, parentFCB);
764 parentFCB = 0;
765 }
766 // fail if element in FCB is not a directory
767 if (!vfatFCBIsDirectory(FCB))
768 {
769 DPRINT ("Element in requested path is not a directory\n");
770
771 vfatReleaseFCB(pVCB, FCB);
772 FCB = NULL;
773 *pParentFCB = NULL;
774 *pFCB = NULL;
775
776 return STATUS_OBJECT_PATH_NOT_FOUND;
777 }
778 parentFCB = FCB;
779 if (prev < curr)
780 {
781 Length = (curr - prev) * sizeof(WCHAR);
782 if (Length != parentFCB->LongNameU.Length)
783 {
784 if (FileNameU.Length + parentFCB->LongNameU.Length - Length > FileNameU.MaximumLength)
785 {
786 vfatReleaseFCB(pVCB, parentFCB);
787 return STATUS_OBJECT_NAME_INVALID;
788 }
789 RtlMoveMemory(prev + parentFCB->LongNameU.Length / sizeof(WCHAR), curr,
790 FileNameU.Length - (curr - FileNameU.Buffer) * sizeof(WCHAR));
791 FileNameU.Length += (USHORT)(parentFCB->LongNameU.Length - Length);
792 curr = prev + parentFCB->LongNameU.Length / sizeof(WCHAR);
793 last = FileNameU.Buffer + FileNameU.Length / sizeof(WCHAR) - 1;
794 }
795 RtlCopyMemory(prev, parentFCB->LongNameU.Buffer, parentFCB->LongNameU.Length);
796 }
797 curr++;
798 prev = curr;
799 while (*curr != L'\\' && curr <= last)
800 {
801 curr++;
802 }
803 NameU.Buffer = FileNameU.Buffer;
804 NameU.Length = (curr - NameU.Buffer) * sizeof(WCHAR);
805 NameU.MaximumLength = FileNameU.MaximumLength;
806 DPRINT("%wZ\n", &NameU);
807 FCB = vfatGrabFCBFromTable(pVCB, &NameU);
808 if (FCB == NULL)
809 {
810 NameU.Buffer = prev;
811 NameU.MaximumLength = NameU.Length = (curr - prev) * sizeof(WCHAR);
812 status = vfatDirFindFile(pVCB, parentFCB, &NameU, &FCB);
813 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
814 {
815 *pFCB = NULL;
816 if (curr > last)
817 {
818 *pParentFCB = parentFCB;
819 return STATUS_OBJECT_NAME_NOT_FOUND;
820 }
821 else
822 {
823 vfatReleaseFCB(pVCB, parentFCB);
824 *pParentFCB = NULL;
825 return STATUS_OBJECT_PATH_NOT_FOUND;
826 }
827 }
828 else if (!NT_SUCCESS(status))
829 {
830 vfatReleaseFCB(pVCB, parentFCB);
831 *pParentFCB = NULL;
832 *pFCB = NULL;
833
834 return status;
835 }
836 }
837 }
838
839 *pParentFCB = parentFCB;
840 *pFCB = FCB;
841
842 return STATUS_SUCCESS;
843 }