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