[fastfat_new]
[reactos.git] / reactos / drivers / filesystems / fastfat_new / fcb.c
1 /*
2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/filesystems/fastfat/fcb.c
5 * PURPOSE: FCB manipulation routines.
6 * PROGRAMMERS: Aleksey Bragin <aleksey@reactos.org>
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #define NDEBUG
12 #include "fastfat.h"
13
14 #define TAG_FILENAME 'fBnF'
15
16 /* FUNCTIONS ****************************************************************/
17
18 FSRTL_COMPARISON_RESULT
19 NTAPI
20 FatiCompareNames(PSTRING NameA,
21 PSTRING NameB)
22 {
23 ULONG MinimumLen, i;
24
25 /* Calc the minimum length */
26 MinimumLen = NameA->Length < NameB->Length ? NameA->Length :
27 NameB->Length;
28
29 /* Actually compare them */
30 i = (ULONG)RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinimumLen );
31
32 if (i < MinimumLen)
33 {
34 /* Compare prefixes */
35 if (NameA->Buffer[i] < NameB->Buffer[i])
36 return LessThan;
37 else
38 return GreaterThan;
39 }
40
41 /* Final comparison */
42 if (NameA->Length < NameB->Length)
43 return LessThan;
44 else if (NameA->Length > NameB->Length)
45 return GreaterThan;
46 else
47 return EqualTo;
48 }
49
50 PFCB
51 NTAPI
52 FatFindFcb(PFAT_IRP_CONTEXT IrpContext,
53 PRTL_SPLAY_LINKS *RootNode,
54 PSTRING AnsiName,
55 PBOOLEAN IsDosName)
56 {
57 PFCB_NAME_LINK Node;
58 FSRTL_COMPARISON_RESULT Comparison;
59 PRTL_SPLAY_LINKS Links;
60
61 Links = *RootNode;
62
63 while (Links)
64 {
65 Node = CONTAINING_RECORD(Links, FCB_NAME_LINK, Links);
66
67 /* Compare the prefix */
68 if (*(PUCHAR)Node->Name.Ansi.Buffer != *(PUCHAR)AnsiName->Buffer)
69 {
70 if (*(PUCHAR)Node->Name.Ansi.Buffer < *(PUCHAR)AnsiName->Buffer)
71 Comparison = LessThan;
72 else
73 Comparison = GreaterThan;
74 }
75 else
76 {
77 /* Perform real comparison */
78 Comparison = FatiCompareNames(&Node->Name.Ansi, AnsiName);
79 }
80
81 /* Do they match? */
82 if (Comparison == GreaterThan)
83 {
84 /* No, it's greater, go to the left child */
85 Links = RtlLeftChild(Links);
86 }
87 else if (Comparison == LessThan)
88 {
89 /* No, it's lesser, go to the right child */
90 Links = RtlRightChild(Links);
91 }
92 else
93 {
94 /* Exact match, balance the tree */
95 *RootNode = RtlSplay(Links);
96
97 /* Save type of the name, if needed */
98 if (IsDosName)
99 *IsDosName = Node->IsDosName;
100
101 /* Return the found fcb */
102 return Node->Fcb;
103 }
104 }
105
106 /* Nothing found */
107 return NULL;
108 }
109
110 PFCB
111 NTAPI
112 FatCreateFcb(IN PFAT_IRP_CONTEXT IrpContext,
113 IN PVCB Vcb,
114 IN PFCB ParentDcb,
115 IN FF_FILE *FileHandle)
116 {
117 PFCB Fcb;
118
119 /* Allocate it and zero it */
120 Fcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(FCB), TAG_FCB);
121 RtlZeroMemory(Fcb, sizeof(FCB));
122
123 /* Set node types */
124 Fcb->Header.NodeTypeCode = FAT_NTC_FCB;
125 Fcb->Header.NodeByteSize = sizeof(FCB);
126 Fcb->Condition = FcbGood;
127
128 /* Initialize resources */
129 Fcb->Header.Resource = &Fcb->Resource;
130 ExInitializeResourceLite(Fcb->Header.Resource);
131
132 Fcb->Header.PagingIoResource = &Fcb->PagingIoResource;
133 ExInitializeResourceLite(Fcb->Header.PagingIoResource);
134
135 /* Initialize mutexes */
136 Fcb->Header.FastMutex = &Fcb->HeaderMutex;
137 ExInitializeFastMutex(&Fcb->HeaderMutex);
138 FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->HeaderMutex);
139
140 /* Insert into parent's DCB list */
141 InsertTailList(&ParentDcb->Dcb.ParentDcbList, &Fcb->ParentDcbLinks);
142
143 /* Set backlinks */
144 Fcb->ParentFcb = ParentDcb;
145 Fcb->Vcb = Vcb;
146
147 /* Set file handle and sizes */
148 Fcb->Header.FileSize.LowPart = FileHandle->Filesize;
149 Fcb->Header.ValidDataLength.LowPart = FileHandle->Filesize;
150 Fcb->FatHandle = FileHandle;
151
152 /* Set names */
153 FatSetFcbNames(IrpContext, Fcb);
154
155 return Fcb;
156 }
157
158 PCCB
159 NTAPI
160 FatCreateCcb()
161 {
162 PCCB Ccb;
163
164 /* Allocate the CCB and zero it */
165 Ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
166 RtlZeroMemory(Ccb, sizeof(CCB));
167
168 /* Set mandatory header */
169 Ccb->NodeTypeCode = FAT_NTC_FCB;
170 Ccb->NodeByteSize = sizeof(CCB);
171
172 return Ccb;
173 }
174
175 IO_STATUS_BLOCK
176 NTAPI
177 FatiOpenExistingFcb(IN PFAT_IRP_CONTEXT IrpContext,
178 IN PFILE_OBJECT FileObject,
179 IN PVCB Vcb,
180 IN PFCB Fcb,
181 IN PACCESS_MASK DesiredAccess,
182 IN USHORT ShareAccess,
183 IN ULONG AllocationSize,
184 IN PFILE_FULL_EA_INFORMATION EaBuffer,
185 IN ULONG EaLength,
186 IN UCHAR FileAttributes,
187 IN ULONG CreateDisposition,
188 IN BOOLEAN NoEaKnowledge,
189 IN BOOLEAN DeleteOnClose,
190 IN BOOLEAN OpenedAsDos,
191 OUT PBOOLEAN OplockPostIrp)
192 {
193 IO_STATUS_BLOCK Iosb = {{0}};
194 ACCESS_MASK AddedAccess = 0;
195 BOOLEAN Hidden;
196 BOOLEAN System;
197 PCCB Ccb = NULL;
198 NTSTATUS Status;
199
200 /* Acquire exclusive FCB lock */
201 (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb);
202
203 *OplockPostIrp = FALSE;
204
205 /* Check if there is a batch oplock */
206 if (FsRtlCurrentBatchOplock(&Fcb->Fcb.Oplock))
207 {
208 /* Return with a special information field */
209 Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY;
210
211 /* Check the oplock */
212 Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
213 IrpContext->Irp,
214 IrpContext,
215 FatOplockComplete,
216 FatPrePostIrp);
217
218 if (Iosb.Status != STATUS_SUCCESS &&
219 Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
220 {
221 /* The Irp needs to be queued */
222 *OplockPostIrp = TRUE;
223
224 /* Release the FCB and return */
225 FatReleaseFcb(IrpContext, Fcb);
226 return Iosb;
227 }
228 }
229
230 /* Validate parameters and modify access */
231 if (CreateDisposition == FILE_CREATE)
232 {
233 Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
234
235 /* Release the FCB and return */
236 FatReleaseFcb(IrpContext, Fcb);
237 return Iosb;
238 }
239 else if (CreateDisposition == FILE_SUPERSEDE)
240 {
241 SetFlag(AddedAccess, DELETE & ~(*DesiredAccess));
242 *DesiredAccess |= DELETE;
243 }
244 else if ((CreateDisposition == FILE_OVERWRITE) ||
245 (CreateDisposition == FILE_OVERWRITE_IF))
246 {
247 SetFlag(AddedAccess,
248 (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
249 & ~(*DesiredAccess) );
250
251 *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
252 }
253
254 // TODO: Check desired access
255
256 // TODO: Check if this file is readonly and DeleteOnClose is set
257
258 /* Validate disposition information */
259 if ((CreateDisposition == FILE_SUPERSEDE) ||
260 (CreateDisposition == FILE_OVERWRITE) ||
261 (CreateDisposition == FILE_OVERWRITE_IF))
262 {
263 // TODO: Get this attributes from the dirent
264 Hidden = FALSE;
265 System = FALSE;
266
267 if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
268 (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM)))
269 {
270 DPRINT1("Hidden/system attributes don't match\n");
271
272 Iosb.Status = STATUS_ACCESS_DENIED;
273
274 /* Release the FCB and return */
275 FatReleaseFcb(IrpContext, Fcb);
276 return Iosb;
277 }
278
279 // TODO: Check for write protected volume
280 }
281
282 /* Check share access */
283 Iosb.Status = IoCheckShareAccess(*DesiredAccess,
284 ShareAccess,
285 FileObject,
286 &Fcb->ShareAccess,
287 FALSE);
288 if (!NT_SUCCESS(Iosb.Status))
289 {
290 /* Release the FCB and return */
291 FatReleaseFcb(IrpContext, Fcb);
292 return Iosb;
293 }
294
295 /* Check the oplock status after checking for share access */
296 Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
297 IrpContext->Irp,
298 IrpContext,
299 FatOplockComplete,
300 FatPrePostIrp );
301
302 if (Iosb.Status != STATUS_SUCCESS &&
303 Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
304 {
305 /* The Irp needs to be queued */
306 *OplockPostIrp = TRUE;
307
308 /* Release the FCB and return */
309 FatReleaseFcb(IrpContext, Fcb);
310 return Iosb;
311 }
312
313 /* Set Fast I/O flag */
314 Fcb->Header.IsFastIoPossible = FALSE; //FatiIsFastIoPossible(Fcb);
315
316 /* Make sure image is not mapped */
317 if (DeleteOnClose || FlagOn(*DesiredAccess, FILE_WRITE_DATA))
318 {
319 /* Try to flush the image section */
320 if (!MmFlushImageSection(&Fcb->SectionObjectPointers, MmFlushForWrite))
321 {
322 /* Yes, image section exists, set correct status code */
323 if (DeleteOnClose)
324 Iosb.Status = STATUS_CANNOT_DELETE;
325 else
326 Iosb.Status = STATUS_SHARING_VIOLATION;
327
328 /* Release the FCB and return */
329 FatReleaseFcb(IrpContext, Fcb);
330 return Iosb;
331 }
332 }
333
334 /* Flush the cache if it's non-cached non-pagefile access */
335 if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING) &&
336 Fcb->SectionObjectPointers.DataSectionObject &&
337 !FlagOn(Fcb->State, FCB_STATE_PAGEFILE))
338 {
339 /* Set the flag that create is in progress */
340 SetFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
341
342 /* Flush the cache */
343 CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);
344
345 /* Acquire and release Paging I/O resource before purging the cache section
346 to let lazy writer finish */
347 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
348 ExReleaseResourceLite( Fcb->Header.PagingIoResource );
349
350 /* Delete the cache section */
351 CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE);
352
353 /* Clear the flag */
354 ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
355 }
356
357 /* Check create disposition flags and branch accordingly */
358 if (CreateDisposition == FILE_OPEN ||
359 CreateDisposition == FILE_OPEN_IF)
360 {
361 DPRINT("Opening a file\n");
362
363 /* Check if we need to bother with EA */
364 if (NoEaKnowledge && FALSE /* FatIsFat32(Vcb)*/)
365 {
366 UNIMPLEMENTED;
367 }
368
369 /* Set up file object */
370 Ccb = FatCreateCcb(IrpContext);
371 FatSetFileObject(FileObject,
372 UserFileOpen,
373 Fcb,
374 Ccb);
375
376 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
377
378 /* The file is opened */
379 Iosb.Information = FILE_OPENED;
380 goto SuccComplete;
381 }
382 else if ((CreateDisposition == FILE_SUPERSEDE) ||
383 (CreateDisposition == FILE_OVERWRITE) ||
384 (CreateDisposition == FILE_OVERWRITE_IF))
385 {
386 UNIMPLEMENTED;
387 ASSERT(FALSE);
388 }
389 else
390 {
391 /* We can't get here */
392 KeBugCheckEx(0x23, CreateDisposition, 0, 0, 0);
393 }
394
395
396 SuccComplete:
397 /* If all is fine */
398 if (Iosb.Status != STATUS_PENDING &&
399 NT_SUCCESS(Iosb.Status))
400 {
401 /* Update access if needed */
402 if (AddedAccess)
403 {
404 /* Remove added access flags from desired access */
405 ClearFlag(*DesiredAccess, AddedAccess);
406
407 /* Check share access */
408 Status = IoCheckShareAccess(*DesiredAccess,
409 ShareAccess,
410 FileObject,
411 &Fcb->ShareAccess,
412 TRUE);
413
414 /* Make sure it's success */
415 ASSERT(Status == STATUS_SUCCESS);
416 }
417 else
418 {
419 /* Update the share access */
420 IoUpdateShareAccess(FileObject, &Fcb->ShareAccess);
421 }
422
423 /* Clear the delay close */
424 ClearFlag(Fcb->State, FCB_STATE_DELAY_CLOSE);
425
426 /* Increase global volume counter */
427 Vcb->OpenFileCount++;
428
429 // TODO: Handle DeleteOnClose and OpenedAsDos by storing those flags in CCB
430 }
431
432 return Iosb;
433 }
434
435 VOID
436 NTAPI
437 FatGetFcbUnicodeName(IN PFAT_IRP_CONTEXT IrpContext,
438 IN PFCB Fcb,
439 OUT PUNICODE_STRING LongName)
440 {
441 FF_DIRENT DirEnt;
442 FF_ERROR Err;
443 OEM_STRING ShortName;
444 CHAR ShortNameBuf[13];
445 UCHAR EntryBuffer[32];
446 UCHAR NumLFNs;
447 OEM_STRING LongNameOem;
448 NTSTATUS Status;
449
450 /* Make sure this FCB has a FullFAT handle associated with it */
451 if (Fcb->FatHandle == NULL &&
452 FatNodeType(Fcb) == FAT_NTC_DCB)
453 {
454 /* Open the dir with FullFAT */
455 Fcb->FatHandle = FF_OpenW(Fcb->Vcb->Ioman, &Fcb->FullFileName, FF_MODE_DIR, NULL);
456 if (!Fcb->FatHandle)
457 {
458 ASSERT(FALSE);
459 }
460 }
461
462 /* Get the dir entry */
463 Err = FF_GetEntry(Fcb->Vcb->Ioman,
464 Fcb->FatHandle->DirEntry,
465 Fcb->FatHandle->DirCluster,
466 &DirEnt);
467
468 if (Err != FF_ERR_NONE)
469 {
470 DPRINT1("Error %d getting dirent of a file\n", Err);
471 return;
472 }
473
474 /* Read the dirent to fetch the raw short name */
475 FF_FetchEntry(Fcb->Vcb->Ioman,
476 Fcb->FatHandle->DirCluster,
477 Fcb->FatHandle->DirEntry,
478 EntryBuffer);
479 NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
480
481 /* Check if we only have a short name.
482 Convert it to unicode and return if that's the case */
483 if (NumLFNs == 0)
484 {
485 /* Initialize short name string */
486 ShortName.Buffer = ShortNameBuf;
487 ShortName.Length = 0;
488 ShortName.MaximumLength = 12;
489
490 /* Convert raw short name to a proper string */
491 Fati8dot3ToString((PCHAR)EntryBuffer, FALSE, &ShortName);
492
493 /* Convert it to unicode */
494 Status = RtlOemStringToCountedUnicodeString(LongName,
495 &ShortName,
496 FALSE);
497
498 /* Ensure conversion was successful */
499 ASSERT(Status == STATUS_SUCCESS);
500
501 /* Exit */
502 return;
503 }
504
505 /* Convert LFN from OEM to unicode and return */
506 LongNameOem.Buffer = DirEnt.FileName;
507 LongNameOem.MaximumLength = FF_MAX_FILENAME;
508 LongNameOem.Length = strlen(DirEnt.FileName);
509
510 /* Convert it to unicode */
511 Status = RtlOemStringToUnicodeString(LongName, &LongNameOem, FALSE);
512
513 /* Ensure conversion was successful */
514 ASSERT(Status == STATUS_SUCCESS);
515 }
516
517
518 VOID
519 NTAPI
520 FatSetFullNameInFcb(PFCB Fcb,
521 PUNICODE_STRING Name)
522 {
523 PUNICODE_STRING ParentName;
524
525 /* Make sure this FCB's name wasn't already set */
526 ASSERT(Fcb->FullFileName.Buffer == NULL);
527
528 /* First of all, check exact case name */
529 if (Fcb->ExactCaseLongName.Buffer)
530 {
531 ASSERT(Fcb->ExactCaseLongName.Length != 0);
532
533 /* Use exact case name */
534 Name = &Fcb->ExactCaseLongName;
535 }
536
537 /* Treat root dir different */
538 if (FatNodeType(Fcb->ParentFcb) == FAT_NTC_ROOT_DCB)
539 {
540 /* Set lengths */
541 Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + Name->Length;
542 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
543
544 /* Allocate a buffer */
545 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
546 Fcb->FullFileName.Length,
547 TAG_FILENAME);
548
549 /* Prefix with a backslash */
550 Fcb->FullFileName.Buffer[0] = L'\\';
551
552 /* Copy the name here */
553 RtlCopyMemory(&Fcb->FullFileName.Buffer[1],
554 &Name->Buffer[0],
555 Name->Length );
556 }
557 else
558 {
559 ParentName = &Fcb->ParentFcb->FullFileName;
560
561 /* Check if parent's name is set */
562 if (!ParentName->Buffer)
563 return;
564
565 /* Set lengths */
566 Fcb->FullFileName.MaximumLength =
567 ParentName->Length + sizeof(WCHAR) + Name->Length;
568 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
569
570 /* Allocate a buffer */
571 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
572 Fcb->FullFileName.Length,
573 TAG_FILENAME );
574
575 /* Copy parent's name here */
576 RtlCopyMemory(&Fcb->FullFileName.Buffer[0],
577 &ParentName->Buffer[0],
578 ParentName->Length );
579
580 /* Add a backslash */
581 Fcb->FullFileName.Buffer[ParentName->Length / sizeof(WCHAR)] = L'\\';
582
583 /* Copy given name here */
584 RtlCopyMemory(&Fcb->FullFileName.Buffer[(ParentName->Length / sizeof(WCHAR)) + 1],
585 &Name->Buffer[0],
586 Name->Length );
587 }
588 }
589
590 VOID
591 NTAPI
592 FatSetFullFileNameInFcb(IN PFAT_IRP_CONTEXT IrpContext,
593 IN PFCB Fcb)
594 {
595 UNICODE_STRING LongName;
596 PFCB CurFcb = Fcb;
597 PFCB StopFcb;
598 PWCHAR TmpBuffer;
599 ULONG PathLength = 0;
600
601 /* Do nothing if it's already set */
602 if (Fcb->FullFileName.Buffer) return;
603
604 /* Allocate a temporary buffer */
605 LongName.Length = 0;
606 LongName.MaximumLength = FF_MAX_FILENAME * sizeof(WCHAR);
607 LongName.Buffer =
608 FsRtlAllocatePoolWithTag(PagedPool,
609 FF_MAX_FILENAME * sizeof(WCHAR),
610 TAG_FILENAME);
611
612 /* Go through all parents to calculate needed length */
613 while (CurFcb != Fcb->Vcb->RootDcb)
614 {
615 /* Does current FCB have FullFileName set? */
616 if (CurFcb != Fcb &&
617 CurFcb->FullFileName.Buffer)
618 {
619 /* Yes, just use it! */
620 PathLength += CurFcb->FullFileName.Length;
621
622 Fcb->FullFileName.Buffer =
623 FsRtlAllocatePoolWithTag(PagedPool,
624 PathLength,
625 TAG_FILENAME);
626
627 RtlCopyMemory(Fcb->FullFileName.Buffer,
628 CurFcb->FullFileName.Buffer,
629 CurFcb->FullFileName.Length);
630
631 break;
632 }
633
634 /* Sum up length of a current item */
635 PathLength += CurFcb->FileNameLength + sizeof(WCHAR);
636
637 /* Go to the parent */
638 CurFcb = CurFcb->ParentFcb;
639 }
640
641 /* Allocate FullFileName if it wasn't already allocated above */
642 if (!Fcb->FullFileName.Buffer)
643 {
644 Fcb->FullFileName.Buffer =
645 FsRtlAllocatePoolWithTag(PagedPool,
646 PathLength,
647 TAG_FILENAME);
648 }
649
650 StopFcb = CurFcb;
651
652 CurFcb = Fcb;
653 TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR);
654
655 /* Set lengths */
656 Fcb->FullFileName.Length = PathLength;
657 Fcb->FullFileName.MaximumLength = PathLength;
658
659 while (CurFcb != StopFcb)
660 {
661 /* Get its unicode name */
662 FatGetFcbUnicodeName(IrpContext,
663 CurFcb,
664 &LongName);
665
666 /* Copy it */
667 TmpBuffer -= LongName.Length / sizeof(WCHAR);
668 RtlCopyMemory(TmpBuffer, LongName.Buffer, LongName.Length);
669
670 /* Append with a backslash */
671 TmpBuffer -= 1;
672 *TmpBuffer = L'\\';
673
674 /* Go to the parent */
675 CurFcb = CurFcb->ParentFcb;
676 }
677
678 /* Free the temp buffer */
679 ExFreePool(LongName.Buffer);
680 }
681
682
683 VOID
684 NTAPI
685 FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext,
686 IN PFCB Fcb)
687 {
688 FF_DIRENT DirEnt;
689 FF_ERROR Err;
690 POEM_STRING ShortName;
691 CHAR ShortNameRaw[13];
692 UCHAR EntryBuffer[32];
693 UCHAR NumLFNs;
694 PUNICODE_STRING UnicodeName;
695 OEM_STRING LongNameOem;
696 NTSTATUS Status;
697
698 /* Get the dir entry */
699 Err = FF_GetEntry(Fcb->Vcb->Ioman,
700 Fcb->FatHandle->DirEntry,
701 Fcb->FatHandle->DirCluster,
702 &DirEnt);
703
704 if (Err != FF_ERR_NONE)
705 {
706 DPRINT1("Error %d getting dirent of a file\n", Err);
707 return;
708 }
709
710 /* Read the dirent to fetch the raw short name */
711 FF_FetchEntry(Fcb->Vcb->Ioman,
712 Fcb->FatHandle->DirCluster,
713 Fcb->FatHandle->DirEntry,
714 EntryBuffer);
715 NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
716 RtlCopyMemory(ShortNameRaw, EntryBuffer, 11);
717
718 /* Initialize short name string */
719 ShortName = &Fcb->ShortName.Name.Ansi;
720 ShortName->Buffer = Fcb->ShortNameBuffer;
721 ShortName->Length = 0;
722 ShortName->MaximumLength = sizeof(Fcb->ShortNameBuffer);
723
724 /* Convert raw short name to a proper string */
725 Fati8dot3ToString(ShortNameRaw, FALSE, ShortName);
726
727 /* Add the short name link */
728 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksAnsi, &Fcb->ShortName);
729 Fcb->ShortName.Fcb = Fcb;
730
731 /* Get the long file name (if any) */
732 if (NumLFNs > 0)
733 {
734 /* Prepare the oem string */
735 LongNameOem.Buffer = DirEnt.FileName;
736 LongNameOem.MaximumLength = FF_MAX_FILENAME;
737 LongNameOem.Length = strlen(DirEnt.FileName);
738
739 /* Prepare the unicode string */
740 UnicodeName = &Fcb->LongName.Name.String;
741 UnicodeName->Length = (LongNameOem.Length + 1) * sizeof(WCHAR);
742 UnicodeName->MaximumLength = UnicodeName->Length;
743 UnicodeName->Buffer = FsRtlAllocatePool(PagedPool, UnicodeName->Length);
744
745 /* Convert it to unicode */
746 Status = RtlOemStringToUnicodeString(UnicodeName, &LongNameOem, FALSE);
747 if (!NT_SUCCESS(Status))
748 {
749 ASSERT(FALSE);
750 }
751
752 /* Set its length */
753 Fcb->FileNameLength = UnicodeName->Length;
754
755 /* Save case-preserved copy */
756 Fcb->ExactCaseLongName.Length = UnicodeName->Length;
757 Fcb->ExactCaseLongName.MaximumLength = UnicodeName->Length;
758 Fcb->ExactCaseLongName.Buffer =
759 FsRtlAllocatePoolWithTag(PagedPool, UnicodeName->Length, TAG_FILENAME);
760
761 RtlCopyMemory(Fcb->ExactCaseLongName.Buffer,
762 UnicodeName->Buffer,
763 UnicodeName->Length);
764
765 /* Perform a trick which is done by MS's FASTFAT driver to monocase
766 the filename */
767 RtlDowncaseUnicodeString(UnicodeName, UnicodeName, FALSE);
768 RtlUpcaseUnicodeString(UnicodeName, UnicodeName, FALSE);
769
770 DPRINT("Converted long name: %wZ\n", UnicodeName);
771
772 /* Add the long unicode name link */
773 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksUnicode, &Fcb->LongName);
774 Fcb->LongName.Fcb = Fcb;
775
776 /* Indicate that this FCB has a unicode long name */
777 SetFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
778 }
779 else
780 {
781 /* No LFN, set exact case name to 0 length */
782 Fcb->ExactCaseLongName.Length = 0;
783 Fcb->ExactCaseLongName.MaximumLength = 0;
784
785 /* Set the length based on the short name */
786 Fcb->FileNameLength = RtlOemStringToCountedUnicodeSize(ShortName);
787 }
788
789 /* Mark the fact that names were added to splay trees*/
790 SetFlag(Fcb->State, FCB_STATE_HAS_NAMES);
791 }
792
793 VOID
794 NTAPI
795 Fati8dot3ToString(IN PCHAR FileName,
796 IN BOOLEAN DownCase,
797 OUT POEM_STRING OutString)
798 {
799 ULONG BaseLen, ExtLen;
800 CHAR *cString = OutString->Buffer;
801 ULONG i;
802
803 /* Calc base and ext lens */
804 for (BaseLen = 8; BaseLen > 0; BaseLen--)
805 {
806 if (FileName[BaseLen - 1] != ' ') break;
807 }
808
809 for (ExtLen = 3; ExtLen > 0; ExtLen--)
810 {
811 if (FileName[8 + ExtLen - 1] != ' ') break;
812 }
813
814 /* Process base name */
815 if (BaseLen)
816 {
817 RtlCopyMemory(cString, FileName, BaseLen);
818
819 /* Substitute the e5 thing */
820 if (cString[0] == 0x05) cString[0] = 0xe5;
821
822 /* Downcase if asked to */
823 if (DownCase)
824 {
825 /* Do it manually */
826 for (i = 0; i < BaseLen; i++)
827 {
828 if (cString[i] >= 'A' &&
829 cString[i] <= 'Z')
830 {
831 /* Lowercase it */
832 cString[i] += 'a' - 'A';
833 }
834
835 }
836 }
837 }
838
839 /* Process extension */
840 if (ExtLen)
841 {
842 /* Add the dot */
843 cString[BaseLen] = '.';
844 BaseLen++;
845
846 /* Copy the extension */
847 for (i = 0; i < ExtLen; i++)
848 {
849 cString[BaseLen + i] = FileName[8 + i];
850 }
851
852 /* Lowercase the extension if asked to */
853 if (DownCase)
854 {
855 /* Do it manually */
856 for (i = BaseLen; i < BaseLen + ExtLen; i++)
857 {
858 if (cString[i] >= 'A' &&
859 cString[i] <= 'Z')
860 {
861 /* Lowercase it */
862 cString[i] += 'a' - 'A';
863 }
864 }
865 }
866 }
867
868 /* Set the length */
869 OutString->Length = BaseLen + ExtLen;
870
871 DPRINT("'%s', len %d\n", OutString->Buffer, OutString->Length);
872 }
873
874 VOID
875 NTAPI
876 FatInsertName(IN PFAT_IRP_CONTEXT IrpContext,
877 IN PRTL_SPLAY_LINKS *RootNode,
878 IN PFCB_NAME_LINK Name)
879 {
880 PFCB_NAME_LINK NameLink;
881 FSRTL_COMPARISON_RESULT Comparison;
882
883 /* Initialize the splay links */
884 RtlInitializeSplayLinks(&Name->Links);
885
886 /* Is this the first entry? */
887 if (*RootNode == NULL)
888 {
889 /* Yes, become root and return */
890 *RootNode = &Name->Links;
891 return;
892 }
893
894 /* Get the name link */
895 NameLink = CONTAINING_RECORD(*RootNode, FCB_NAME_LINK, Links);
896 while (TRUE)
897 {
898 /* Compare prefixes */
899 Comparison = FatiCompareNames(&NameLink->Name.Ansi, &Name->Name.Ansi);
900
901 /* Check the bad case first */
902 if (Comparison == EqualTo)
903 {
904 /* Must not happen */
905 ASSERT(FALSE);
906 }
907
908 /* Check comparison result */
909 if (Comparison == GreaterThan)
910 {
911 /* Go to the left child */
912 if (!RtlLeftChild(&NameLink->Links))
913 {
914 /* It's absent, insert here and break */
915 RtlInsertAsLeftChild(&NameLink->Links, &NameLink->Links);
916 break;
917 }
918 else
919 {
920 /* It's present, go inside it */
921 NameLink = CONTAINING_RECORD(RtlLeftChild(&NameLink->Links),
922 FCB_NAME_LINK,
923 Links);
924 }
925 }
926 else
927 {
928 /* Go to the right child */
929 if (!RtlRightChild(&NameLink->Links))
930 {
931 /* It's absent, insert here and break */
932 RtlInsertAsRightChild(&NameLink->Links, &Name->Links);
933 break;
934 }
935 else
936 {
937 /* It's present, go inside it */
938 NameLink = CONTAINING_RECORD(RtlRightChild(&NameLink->Links),
939 FCB_NAME_LINK,
940 Links);
941 }
942 }
943 }
944 }
945
946 VOID
947 NTAPI
948 FatRemoveNames(IN PFAT_IRP_CONTEXT IrpContext,
949 IN PFCB Fcb)
950 {
951 PRTL_SPLAY_LINKS RootNew;
952 PFCB Parent;
953
954 /* Reference the parent for simplicity */
955 Parent = Fcb->ParentFcb;
956
957 /* If this FCB hasn't been added to splay trees - just return */
958 if (!FlagOn( Fcb->State, FCB_STATE_HAS_NAMES ))
959 return;
960
961 /* Delete the short name link */
962 RootNew = RtlDelete(&Fcb->ShortName.Links);
963
964 /* Set the new root */
965 Parent->Dcb.SplayLinksAnsi = RootNew;
966
967 /* Deal with a unicode name if it exists */
968 if (FlagOn( Fcb->State, FCB_STATE_HAS_UNICODE_NAME ))
969 {
970 /* Delete the long unicode name link */
971 RootNew = RtlDelete(&Fcb->LongName.Links);
972
973 /* Set the new root */
974 Parent->Dcb.SplayLinksUnicode = RootNew;
975
976 /* Free the long name string's buffer*/
977 RtlFreeUnicodeString(&Fcb->LongName.Name.String);
978
979 /* Clear the "has unicode name" flag */
980 ClearFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
981 }
982
983 /* This FCB has no names added to splay trees now */
984 ClearFlag(Fcb->State, FCB_STATE_HAS_NAMES);
985 }
986
987
988 /* EOF */