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