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>
9 /* INCLUDES *****************************************************************/
14 #define TAG_FILENAME 'fBnF'
16 /* FUNCTIONS ****************************************************************/
18 FSRTL_COMPARISON_RESULT
20 FatiCompareNames(PSTRING NameA
,
25 /* Calc the minimum length */
26 MinimumLen
= NameA
->Length
< NameB
->Length
? NameA
->Length
:
29 /* Actually compare them */
30 i
= (ULONG
)RtlCompareMemory( NameA
->Buffer
, NameB
->Buffer
, MinimumLen
);
34 /* Compare prefixes */
35 if (NameA
->Buffer
[i
] < NameB
->Buffer
[i
])
41 /* Final comparison */
42 if (NameA
->Length
< NameB
->Length
)
44 else if (NameA
->Length
> NameB
->Length
)
52 FatFindFcb(PFAT_IRP_CONTEXT IrpContext
,
53 PRTL_SPLAY_LINKS
*RootNode
,
58 FSRTL_COMPARISON_RESULT Comparison
;
59 PRTL_SPLAY_LINKS Links
;
65 Node
= CONTAINING_RECORD(Links
, FCB_NAME_LINK
, Links
);
67 /* Compare the prefix */
68 if (*(PUCHAR
)Node
->Name
.Ansi
.Buffer
!= *(PUCHAR
)AnsiName
->Buffer
)
70 if (*(PUCHAR
)Node
->Name
.Ansi
.Buffer
< *(PUCHAR
)AnsiName
->Buffer
)
71 Comparison
= LessThan
;
73 Comparison
= GreaterThan
;
77 /* Perform real comparison */
78 Comparison
= FatiCompareNames(&Node
->Name
.Ansi
, AnsiName
);
82 if (Comparison
== GreaterThan
)
84 /* No, it's greater, go to the left child */
85 Links
= RtlLeftChild(Links
);
87 else if (Comparison
== LessThan
)
89 /* No, it's lesser, go to the right child */
90 Links
= RtlRightChild(Links
);
94 /* Exact match, balance the tree */
95 *RootNode
= RtlSplay(Links
);
97 /* Save type of the name, if needed */
99 *IsDosName
= Node
->IsDosName
;
101 /* Return the found fcb */
112 FatCreateFcb(IN PFAT_IRP_CONTEXT IrpContext
,
115 IN FF_FILE
*FileHandle
)
119 /* Allocate it and zero it */
120 Fcb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(FCB
), TAG_FCB
);
121 RtlZeroMemory(Fcb
, sizeof(FCB
));
124 Fcb
->Header
.NodeTypeCode
= FAT_NTC_FCB
;
125 Fcb
->Header
.NodeByteSize
= sizeof(FCB
);
126 Fcb
->Condition
= FcbGood
;
128 /* Initialize resources */
129 Fcb
->Header
.Resource
= &Fcb
->Resource
;
130 ExInitializeResourceLite(Fcb
->Header
.Resource
);
132 Fcb
->Header
.PagingIoResource
= &Fcb
->PagingIoResource
;
133 ExInitializeResourceLite(Fcb
->Header
.PagingIoResource
);
135 /* Initialize mutexes */
136 Fcb
->Header
.FastMutex
= &Fcb
->HeaderMutex
;
137 ExInitializeFastMutex(&Fcb
->HeaderMutex
);
138 FsRtlSetupAdvancedHeader(&Fcb
->Header
, &Fcb
->HeaderMutex
);
140 /* Insert into parent's DCB list */
141 InsertTailList(&ParentDcb
->Dcb
.ParentDcbList
, &Fcb
->ParentDcbLinks
);
144 Fcb
->ParentFcb
= ParentDcb
;
147 /* Set file handle and sizes */
148 Fcb
->Header
.FileSize
.LowPart
= FileHandle
->Filesize
;
149 Fcb
->Header
.ValidDataLength
.LowPart
= FileHandle
->Filesize
;
150 Fcb
->FatHandle
= FileHandle
;
153 FatSetFcbNames(IrpContext
, Fcb
);
164 /* Allocate the CCB and zero it */
165 Ccb
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(CCB
), TAG_CCB
);
166 RtlZeroMemory(Ccb
, sizeof(CCB
));
168 /* Set mandatory header */
169 Ccb
->NodeTypeCode
= FAT_NTC_FCB
;
170 Ccb
->NodeByteSize
= sizeof(CCB
);
177 FatGetFcbUnicodeName(IN PFAT_IRP_CONTEXT IrpContext
,
179 OUT PUNICODE_STRING LongName
)
183 OEM_STRING ShortName
;
184 CHAR ShortNameBuf
[13];
185 UCHAR EntryBuffer
[32];
187 OEM_STRING LongNameOem
;
190 /* We support only files now, not directories */
191 if (Fcb
->Header
.NodeTypeCode
!= FAT_NTC_FCB
)
197 /* Get the dir entry */
198 Err
= FF_GetEntry(Fcb
->Vcb
->Ioman
,
199 Fcb
->FatHandle
->DirEntry
,
200 Fcb
->FatHandle
->DirCluster
,
203 if (Err
!= FF_ERR_NONE
)
205 DPRINT1("Error %d getting dirent of a file\n", Err
);
209 /* Read the dirent to fetch the raw short name */
210 FF_FetchEntry(Fcb
->Vcb
->Ioman
,
211 Fcb
->FatHandle
->DirCluster
,
212 Fcb
->FatHandle
->DirEntry
,
214 NumLFNs
= (UCHAR
)(EntryBuffer
[0] & ~0x40);
216 /* Check if we only have a short name.
217 Convert it to unicode and return if that's the case */
220 /* Initialize short name string */
221 ShortName
.Buffer
= ShortNameBuf
;
222 ShortName
.Length
= 0;
223 ShortName
.MaximumLength
= 12;
225 /* Convert raw short name to a proper string */
226 Fati8dot3ToString((PCHAR
)EntryBuffer
, FALSE
, &ShortName
);
228 /* Convert it to unicode */
229 Status
= RtlOemStringToCountedUnicodeString(LongName
,
233 /* Ensure conversion was successful */
234 ASSERT(Status
== STATUS_SUCCESS
);
240 /* Convert LFN from OEM to unicode and return */
241 LongNameOem
.Buffer
= DirEnt
.FileName
;
242 LongNameOem
.MaximumLength
= FF_MAX_FILENAME
;
243 LongNameOem
.Length
= strlen(DirEnt
.FileName
);
245 /* Convert it to unicode */
246 Status
= RtlOemStringToUnicodeString(LongName
, &LongNameOem
, FALSE
);
248 /* Ensure conversion was successful */
249 ASSERT(Status
== STATUS_SUCCESS
);
255 FatSetFullNameInFcb(PFCB Fcb
,
256 PUNICODE_STRING Name
)
258 PUNICODE_STRING ParentName
;
260 /* Make sure this FCB's name wasn't already set */
261 ASSERT(Fcb
->FullFileName
.Buffer
== NULL
);
263 /* First of all, check exact case name */
264 if (Fcb
->ExactCaseLongName
.Buffer
)
266 ASSERT(Fcb
->ExactCaseLongName
.Length
!= 0);
268 /* Use exact case name */
269 Name
= &Fcb
->ExactCaseLongName
;
272 /* Treat root dir different */
273 if (FatNodeType(Fcb
->ParentFcb
) == FAT_NTC_ROOT_DCB
)
276 Fcb
->FullFileName
.MaximumLength
= sizeof(WCHAR
) + Name
->Length
;
277 Fcb
->FullFileName
.Length
= Fcb
->FullFileName
.MaximumLength
;
279 /* Allocate a buffer */
280 Fcb
->FullFileName
.Buffer
= FsRtlAllocatePoolWithTag(PagedPool
,
281 Fcb
->FullFileName
.Length
,
284 /* Prefix with a backslash */
285 Fcb
->FullFileName
.Buffer
[0] = L
'\\';
287 /* Copy the name here */
288 RtlCopyMemory(&Fcb
->FullFileName
.Buffer
[1],
294 ParentName
= &Fcb
->ParentFcb
->FullFileName
;
296 /* Check if parent's name is set */
297 if (!ParentName
->Buffer
)
301 Fcb
->FullFileName
.MaximumLength
=
302 ParentName
->Length
+ sizeof(WCHAR
) + Name
->Length
;
303 Fcb
->FullFileName
.Length
= Fcb
->FullFileName
.MaximumLength
;
305 /* Allocate a buffer */
306 Fcb
->FullFileName
.Buffer
= FsRtlAllocatePoolWithTag(PagedPool
,
307 Fcb
->FullFileName
.Length
,
310 /* Copy parent's name here */
311 RtlCopyMemory(&Fcb
->FullFileName
.Buffer
[0],
312 &ParentName
->Buffer
[0],
313 ParentName
->Length
);
315 /* Add a backslash */
316 Fcb
->FullFileName
.Buffer
[ParentName
->Length
/ sizeof(WCHAR
)] = L
'\\';
318 /* Copy given name here */
319 RtlCopyMemory(&Fcb
->FullFileName
.Buffer
[(ParentName
->Length
/ sizeof(WCHAR
)) + 1],
327 FatSetFullFileNameInFcb(IN PFAT_IRP_CONTEXT IrpContext
,
330 UNICODE_STRING LongName
;
334 ULONG PathLength
= 0;
336 /* Do nothing if it's already set */
337 if (Fcb
->FullFileName
.Buffer
) return;
339 /* Allocate a temporary buffer */
341 LongName
.MaximumLength
= FF_MAX_FILENAME
* sizeof(WCHAR
);
343 FsRtlAllocatePoolWithTag(PagedPool
,
344 FF_MAX_FILENAME
* sizeof(WCHAR
),
347 /* Go through all parents to calculate needed length */
348 while (CurFcb
!= Fcb
->Vcb
->RootDcb
)
350 /* Does current FCB have FullFileName set? */
352 CurFcb
->FullFileName
.Buffer
)
354 /* Yes, just use it! */
355 PathLength
+= CurFcb
->FullFileName
.Length
;
357 Fcb
->FullFileName
.Buffer
=
358 FsRtlAllocatePoolWithTag(PagedPool
,
362 RtlCopyMemory(Fcb
->FullFileName
.Buffer
,
363 CurFcb
->FullFileName
.Buffer
,
364 CurFcb
->FullFileName
.Length
);
369 /* Sum up length of a current item */
370 PathLength
+= CurFcb
->FileNameLength
+ sizeof(WCHAR
);
372 /* Go to the parent */
373 CurFcb
= CurFcb
->ParentFcb
;
376 /* Allocate FullFileName if it wasn't already allocated above */
377 if (!Fcb
->FullFileName
.Buffer
)
379 Fcb
->FullFileName
.Buffer
=
380 FsRtlAllocatePoolWithTag(PagedPool
,
388 TmpBuffer
= Fcb
->FullFileName
.Buffer
+ PathLength
/ sizeof(WCHAR
);
391 Fcb
->FullFileName
.Length
= PathLength
;
392 Fcb
->FullFileName
.MaximumLength
= PathLength
;
394 while (CurFcb
!= StopFcb
)
396 /* Get its unicode name */
397 FatGetFcbUnicodeName(IrpContext
,
402 TmpBuffer
-= LongName
.Length
/ sizeof(WCHAR
);
403 RtlCopyMemory(TmpBuffer
, LongName
.Buffer
, LongName
.Length
);
405 /* Append with a backslash */
409 /* Go to the parent */
410 CurFcb
= CurFcb
->ParentFcb
;
413 /* Free the temp buffer */
414 ExFreePool(LongName
.Buffer
);
420 FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext
,
425 POEM_STRING ShortName
;
426 CHAR ShortNameRaw
[13];
427 UCHAR EntryBuffer
[32];
429 PUNICODE_STRING UnicodeName
;
430 OEM_STRING LongNameOem
;
433 /* Get the dir entry */
434 Err
= FF_GetEntry(Fcb
->Vcb
->Ioman
,
435 Fcb
->FatHandle
->DirEntry
,
436 Fcb
->FatHandle
->DirCluster
,
439 if (Err
!= FF_ERR_NONE
)
441 DPRINT1("Error %d getting dirent of a file\n", Err
);
445 /* Read the dirent to fetch the raw short name */
446 FF_FetchEntry(Fcb
->Vcb
->Ioman
,
447 Fcb
->FatHandle
->DirCluster
,
448 Fcb
->FatHandle
->DirEntry
,
450 NumLFNs
= (UCHAR
)(EntryBuffer
[0] & ~0x40);
451 RtlCopyMemory(ShortNameRaw
, EntryBuffer
, 11);
453 /* Initialize short name string */
454 ShortName
= &Fcb
->ShortName
.Name
.Ansi
;
455 ShortName
->Buffer
= Fcb
->ShortNameBuffer
;
456 ShortName
->Length
= 0;
457 ShortName
->MaximumLength
= sizeof(Fcb
->ShortNameBuffer
);
459 /* Convert raw short name to a proper string */
460 Fati8dot3ToString(ShortNameRaw
, FALSE
, ShortName
);
462 /* Add the short name link */
463 FatInsertName(IrpContext
, &Fcb
->ParentFcb
->Dcb
.SplayLinksAnsi
, &Fcb
->ShortName
);
464 Fcb
->ShortName
.Fcb
= Fcb
;
466 /* Get the long file name (if any) */
469 /* Prepare the oem string */
470 LongNameOem
.Buffer
= DirEnt
.FileName
;
471 LongNameOem
.MaximumLength
= FF_MAX_FILENAME
;
472 LongNameOem
.Length
= strlen(DirEnt
.FileName
);
474 /* Prepare the unicode string */
475 UnicodeName
= &Fcb
->LongName
.Name
.String
;
476 UnicodeName
->Length
= (LongNameOem
.Length
+ 1) * sizeof(WCHAR
);
477 UnicodeName
->MaximumLength
= UnicodeName
->Length
;
478 UnicodeName
->Buffer
= FsRtlAllocatePool(PagedPool
, UnicodeName
->Length
);
480 /* Convert it to unicode */
481 Status
= RtlOemStringToUnicodeString(UnicodeName
, &LongNameOem
, FALSE
);
482 if (!NT_SUCCESS(Status
))
488 Fcb
->FileNameLength
= UnicodeName
->Length
;
490 /* Save case-preserved copy */
491 Fcb
->ExactCaseLongName
.Length
= UnicodeName
->Length
;
492 Fcb
->ExactCaseLongName
.MaximumLength
= UnicodeName
->Length
;
493 Fcb
->ExactCaseLongName
.Buffer
=
494 FsRtlAllocatePoolWithTag(PagedPool
, UnicodeName
->Length
, TAG_FILENAME
);
496 RtlCopyMemory(Fcb
->ExactCaseLongName
.Buffer
,
498 UnicodeName
->Length
);
500 /* Perform a trick which is done by MS's FASTFAT driver to monocase
502 RtlDowncaseUnicodeString(UnicodeName
, UnicodeName
, FALSE
);
503 RtlUpcaseUnicodeString(UnicodeName
, UnicodeName
, FALSE
);
505 DPRINT("Converted long name: %wZ\n", UnicodeName
);
507 /* Add the long unicode name link */
508 FatInsertName(IrpContext
, &Fcb
->ParentFcb
->Dcb
.SplayLinksUnicode
, &Fcb
->LongName
);
509 Fcb
->LongName
.Fcb
= Fcb
;
511 /* Indicate that this FCB has a unicode long name */
512 SetFlag(Fcb
->State
, FCB_STATE_HAS_UNICODE_NAME
);
516 /* No LFN, set exact case name to 0 length */
517 Fcb
->ExactCaseLongName
.Length
= 0;
518 Fcb
->ExactCaseLongName
.MaximumLength
= 0;
520 /* Set the length based on the short name */
521 Fcb
->FileNameLength
= RtlOemStringToCountedUnicodeSize(ShortName
);
524 /* Mark the fact that names were added to splay trees*/
525 SetFlag(Fcb
->State
, FCB_STATE_HAS_NAMES
);
530 Fati8dot3ToString(IN PCHAR FileName
,
532 OUT POEM_STRING OutString
)
534 ULONG BaseLen
, ExtLen
;
535 CHAR
*cString
= OutString
->Buffer
;
538 /* Calc base and ext lens */
539 for (BaseLen
= 8; BaseLen
> 0; BaseLen
--)
541 if (FileName
[BaseLen
- 1] != ' ') break;
544 for (ExtLen
= 3; ExtLen
> 0; ExtLen
--)
546 if (FileName
[8 + ExtLen
- 1] != ' ') break;
549 /* Process base name */
552 RtlCopyMemory(cString
, FileName
, BaseLen
);
554 /* Substitute the e5 thing */
555 if (cString
[0] == 0x05) cString
[0] = 0xe5;
557 /* Downcase if asked to */
561 for (i
= 0; i
< BaseLen
; i
++)
563 if (cString
[i
] >= 'A' &&
567 cString
[i
] += 'a' - 'A';
574 /* Process extension */
578 cString
[BaseLen
] = '.';
581 /* Copy the extension */
582 for (i
= 0; i
< ExtLen
; i
++)
584 cString
[BaseLen
+ i
] = FileName
[8 + i
];
587 /* Lowercase the extension if asked to */
591 for (i
= BaseLen
; i
< BaseLen
+ ExtLen
; i
++)
593 if (cString
[i
] >= 'A' &&
597 cString
[i
] += 'a' - 'A';
604 OutString
->Length
= BaseLen
+ ExtLen
;
606 DPRINT("'%s', len %d\n", OutString
->Buffer
, OutString
->Length
);
611 FatInsertName(IN PFAT_IRP_CONTEXT IrpContext
,
612 IN PRTL_SPLAY_LINKS
*RootNode
,
613 IN PFCB_NAME_LINK Name
)
615 PFCB_NAME_LINK NameLink
;
616 FSRTL_COMPARISON_RESULT Comparison
;
618 /* Initialize the splay links */
619 RtlInitializeSplayLinks(&Name
->Links
);
621 /* Is this the first entry? */
622 if (*RootNode
== NULL
)
624 /* Yes, become root and return */
625 *RootNode
= &Name
->Links
;
629 /* Get the name link */
630 NameLink
= CONTAINING_RECORD(*RootNode
, FCB_NAME_LINK
, Links
);
633 /* Compare prefixes */
634 Comparison
= FatiCompareNames(&NameLink
->Name
.Ansi
, &Name
->Name
.Ansi
);
636 /* Check the bad case first */
637 if (Comparison
== EqualTo
)
639 /* Must not happen */
643 /* Check comparison result */
644 if (Comparison
== GreaterThan
)
646 /* Go to the left child */
647 if (!RtlLeftChild(&NameLink
->Links
))
649 /* It's absent, insert here and break */
650 RtlInsertAsLeftChild(&NameLink
->Links
, &NameLink
->Links
);
655 /* It's present, go inside it */
656 NameLink
= CONTAINING_RECORD(RtlLeftChild(&NameLink
->Links
),
663 /* Go to the right child */
664 if (!RtlRightChild(&NameLink
->Links
))
666 /* It's absent, insert here and break */
667 RtlInsertAsRightChild(&NameLink
->Links
, &Name
->Links
);
672 /* It's present, go inside it */
673 NameLink
= CONTAINING_RECORD(RtlRightChild(&NameLink
->Links
),
683 FatRemoveNames(IN PFAT_IRP_CONTEXT IrpContext
,
686 PRTL_SPLAY_LINKS RootNew
;
689 /* Reference the parent for simplicity */
690 Parent
= Fcb
->ParentFcb
;
692 /* If this FCB hasn't been added to splay trees - just return */
693 if (!FlagOn( Fcb
->State
, FCB_STATE_HAS_NAMES
))
696 /* Delete the short name link */
697 RootNew
= RtlDelete(&Fcb
->ShortName
.Links
);
699 /* Set the new root */
700 Parent
->Dcb
.SplayLinksAnsi
= RootNew
;
702 /* Deal with a unicode name if it exists */
703 if (FlagOn( Fcb
->State
, FCB_STATE_HAS_UNICODE_NAME
))
705 /* Delete the long unicode name link */
706 RootNew
= RtlDelete(&Fcb
->LongName
.Links
);
708 /* Set the new root */
709 Parent
->Dcb
.SplayLinksUnicode
= RootNew
;
711 /* Free the long name string's buffer*/
712 RtlFreeUnicodeString(&Fcb
->LongName
.Name
.String
);
714 /* Clear the "has unicode name" flag */
715 ClearFlag(Fcb
->State
, FCB_STATE_HAS_UNICODE_NAME
);
718 /* This FCB has no names added to splay trees now */
719 ClearFlag(Fcb
->State
, FCB_STATE_HAS_NAMES
);