- Merge from trunk up to r45543
[reactos.git] / drivers / filesystems / fastfat_new / fcb.c
1 /*
2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GNU GPLv3 as published by the Free Software Foundation
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, StatusPrev;
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();
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 /* Remember previous status */
387 StatusPrev = Iosb.Status;
388
389 // TODO: Check system security access
390
391 /* Perform overwrite operation */
392 Iosb = FatiOverwriteFile(IrpContext,
393 FileObject,
394 Fcb,
395 AllocationSize,
396 EaBuffer,
397 EaLength,
398 FileAttributes,
399 CreateDisposition,
400 NoEaKnowledge);
401
402 /* Restore previous status in case of success */
403 if (Iosb.Status == STATUS_SUCCESS)
404 Iosb.Status = StatusPrev;
405
406 /* Fall down to completion */
407 }
408 else
409 {
410 /* We can't get here */
411 KeBugCheckEx(FAT_FILE_SYSTEM, CreateDisposition, 0, 0, 0);
412 }
413
414
415 SuccComplete:
416 /* If all is fine */
417 if (Iosb.Status != STATUS_PENDING &&
418 NT_SUCCESS(Iosb.Status))
419 {
420 /* Update access if needed */
421 if (AddedAccess)
422 {
423 /* Remove added access flags from desired access */
424 ClearFlag(*DesiredAccess, AddedAccess);
425
426 /* Check share access */
427 Status = IoCheckShareAccess(*DesiredAccess,
428 ShareAccess,
429 FileObject,
430 &Fcb->ShareAccess,
431 TRUE);
432
433 /* Make sure it's success */
434 ASSERT(Status == STATUS_SUCCESS);
435 }
436 else
437 {
438 /* Update the share access */
439 IoUpdateShareAccess(FileObject, &Fcb->ShareAccess);
440 }
441
442 /* Clear the delay close */
443 ClearFlag(Fcb->State, FCB_STATE_DELAY_CLOSE);
444
445 /* Increase counters */
446 Fcb->OpenCount++;
447 Vcb->OpenFileCount++;
448
449 // TODO: Handle DeleteOnClose and OpenedAsDos by storing those flags in CCB
450 }
451
452 return Iosb;
453 }
454
455 VOID
456 NTAPI
457 FatGetFcbUnicodeName(IN PFAT_IRP_CONTEXT IrpContext,
458 IN PFCB Fcb,
459 OUT PUNICODE_STRING LongName)
460 {
461 FF_DIRENT DirEnt;
462 FF_ERROR Err;
463 OEM_STRING ShortName;
464 CHAR ShortNameBuf[13];
465 UCHAR EntryBuffer[32];
466 UCHAR NumLFNs;
467 OEM_STRING LongNameOem;
468 NTSTATUS Status;
469
470 /* Make sure this FCB has a FullFAT handle associated with it */
471 if (Fcb->FatHandle == NULL &&
472 FatNodeType(Fcb) == FAT_NTC_DCB)
473 {
474 /* Open the dir with FullFAT */
475 Fcb->FatHandle = FF_OpenW(Fcb->Vcb->Ioman, &Fcb->FullFileName, FF_MODE_DIR, NULL);
476 if (!Fcb->FatHandle)
477 {
478 ASSERT(FALSE);
479 }
480 }
481
482 /* Get the dir entry */
483 Err = FF_GetEntry(Fcb->Vcb->Ioman,
484 Fcb->FatHandle->DirEntry,
485 Fcb->FatHandle->DirCluster,
486 &DirEnt);
487
488 if (Err != FF_ERR_NONE)
489 {
490 DPRINT1("Error %d getting dirent of a file\n", Err);
491 return;
492 }
493
494 /* Read the dirent to fetch the raw short name */
495 FF_FetchEntry(Fcb->Vcb->Ioman,
496 Fcb->FatHandle->DirCluster,
497 Fcb->FatHandle->DirEntry,
498 EntryBuffer);
499 NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
500
501 /* Check if we only have a short name.
502 Convert it to unicode and return if that's the case */
503 if (NumLFNs == 0)
504 {
505 /* Initialize short name string */
506 ShortName.Buffer = ShortNameBuf;
507 ShortName.Length = 0;
508 ShortName.MaximumLength = 12;
509
510 /* Convert raw short name to a proper string */
511 Fati8dot3ToString((PCHAR)EntryBuffer, FALSE, &ShortName);
512
513 /* Convert it to unicode */
514 Status = RtlOemStringToCountedUnicodeString(LongName,
515 &ShortName,
516 FALSE);
517
518 /* Ensure conversion was successful */
519 ASSERT(Status == STATUS_SUCCESS);
520
521 /* Exit */
522 return;
523 }
524
525 /* Convert LFN from OEM to unicode and return */
526 LongNameOem.Buffer = DirEnt.FileName;
527 LongNameOem.MaximumLength = FF_MAX_FILENAME;
528 LongNameOem.Length = strlen(DirEnt.FileName);
529
530 /* Convert it to unicode */
531 Status = RtlOemStringToUnicodeString(LongName, &LongNameOem, FALSE);
532
533 /* Ensure conversion was successful */
534 ASSERT(Status == STATUS_SUCCESS);
535 }
536
537
538 VOID
539 NTAPI
540 FatSetFullNameInFcb(PFCB Fcb,
541 PUNICODE_STRING Name)
542 {
543 PUNICODE_STRING ParentName;
544
545 /* Make sure this FCB's name wasn't already set */
546 ASSERT(Fcb->FullFileName.Buffer == NULL);
547
548 /* First of all, check exact case name */
549 if (Fcb->ExactCaseLongName.Buffer)
550 {
551 ASSERT(Fcb->ExactCaseLongName.Length != 0);
552
553 /* Use exact case name */
554 Name = &Fcb->ExactCaseLongName;
555 }
556
557 /* Treat root dir different */
558 if (FatNodeType(Fcb->ParentFcb) == FAT_NTC_ROOT_DCB)
559 {
560 /* Set lengths */
561 Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + Name->Length;
562 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
563
564 /* Allocate a buffer */
565 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
566 Fcb->FullFileName.Length,
567 TAG_FILENAME);
568
569 /* Prefix with a backslash */
570 Fcb->FullFileName.Buffer[0] = L'\\';
571
572 /* Copy the name here */
573 RtlCopyMemory(&Fcb->FullFileName.Buffer[1],
574 &Name->Buffer[0],
575 Name->Length );
576 }
577 else
578 {
579 ParentName = &Fcb->ParentFcb->FullFileName;
580
581 /* Check if parent's name is set */
582 if (!ParentName->Buffer)
583 return;
584
585 /* Set lengths */
586 Fcb->FullFileName.MaximumLength =
587 ParentName->Length + sizeof(WCHAR) + Name->Length;
588 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
589
590 /* Allocate a buffer */
591 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
592 Fcb->FullFileName.Length,
593 TAG_FILENAME );
594
595 /* Copy parent's name here */
596 RtlCopyMemory(&Fcb->FullFileName.Buffer[0],
597 &ParentName->Buffer[0],
598 ParentName->Length );
599
600 /* Add a backslash */
601 Fcb->FullFileName.Buffer[ParentName->Length / sizeof(WCHAR)] = L'\\';
602
603 /* Copy given name here */
604 RtlCopyMemory(&Fcb->FullFileName.Buffer[(ParentName->Length / sizeof(WCHAR)) + 1],
605 &Name->Buffer[0],
606 Name->Length );
607 }
608 }
609
610 VOID
611 NTAPI
612 FatSetFullFileNameInFcb(IN PFAT_IRP_CONTEXT IrpContext,
613 IN PFCB Fcb)
614 {
615 UNICODE_STRING LongName;
616 PFCB CurFcb = Fcb;
617 PFCB StopFcb;
618 PWCHAR TmpBuffer;
619 ULONG PathLength = 0;
620
621 /* Do nothing if it's already set */
622 if (Fcb->FullFileName.Buffer) return;
623
624 /* Allocate a temporary buffer */
625 LongName.Length = 0;
626 LongName.MaximumLength = FF_MAX_FILENAME * sizeof(WCHAR);
627 LongName.Buffer =
628 FsRtlAllocatePoolWithTag(PagedPool,
629 FF_MAX_FILENAME * sizeof(WCHAR),
630 TAG_FILENAME);
631
632 /* Go through all parents to calculate needed length */
633 while (CurFcb != Fcb->Vcb->RootDcb)
634 {
635 /* Does current FCB have FullFileName set? */
636 if (CurFcb != Fcb &&
637 CurFcb->FullFileName.Buffer)
638 {
639 /* Yes, just use it! */
640 PathLength += CurFcb->FullFileName.Length;
641
642 Fcb->FullFileName.Buffer =
643 FsRtlAllocatePoolWithTag(PagedPool,
644 PathLength,
645 TAG_FILENAME);
646
647 RtlCopyMemory(Fcb->FullFileName.Buffer,
648 CurFcb->FullFileName.Buffer,
649 CurFcb->FullFileName.Length);
650
651 break;
652 }
653
654 /* Sum up length of a current item */
655 PathLength += CurFcb->FileNameLength + sizeof(WCHAR);
656
657 /* Go to the parent */
658 CurFcb = CurFcb->ParentFcb;
659 }
660
661 /* Allocate FullFileName if it wasn't already allocated above */
662 if (!Fcb->FullFileName.Buffer)
663 {
664 Fcb->FullFileName.Buffer =
665 FsRtlAllocatePoolWithTag(PagedPool,
666 PathLength,
667 TAG_FILENAME);
668 }
669
670 StopFcb = CurFcb;
671
672 CurFcb = Fcb;
673 TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR);
674
675 /* Set lengths */
676 Fcb->FullFileName.Length = PathLength;
677 Fcb->FullFileName.MaximumLength = PathLength;
678
679 while (CurFcb != StopFcb)
680 {
681 /* Get its unicode name */
682 FatGetFcbUnicodeName(IrpContext,
683 CurFcb,
684 &LongName);
685
686 /* Copy it */
687 TmpBuffer -= LongName.Length / sizeof(WCHAR);
688 RtlCopyMemory(TmpBuffer, LongName.Buffer, LongName.Length);
689
690 /* Append with a backslash */
691 TmpBuffer -= 1;
692 *TmpBuffer = L'\\';
693
694 /* Go to the parent */
695 CurFcb = CurFcb->ParentFcb;
696 }
697
698 /* Free the temp buffer */
699 ExFreePool(LongName.Buffer);
700 }
701
702
703 VOID
704 NTAPI
705 FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext,
706 IN PFCB Fcb)
707 {
708 FF_DIRENT DirEnt;
709 FF_ERROR Err;
710 POEM_STRING ShortName;
711 CHAR ShortNameRaw[13];
712 UCHAR EntryBuffer[32];
713 UCHAR NumLFNs;
714 PUNICODE_STRING UnicodeName;
715 OEM_STRING LongNameOem;
716 NTSTATUS Status;
717
718 /* Get the dir entry */
719 Err = FF_GetEntry(Fcb->Vcb->Ioman,
720 Fcb->FatHandle->DirEntry,
721 Fcb->FatHandle->DirCluster,
722 &DirEnt);
723
724 if (Err != FF_ERR_NONE)
725 {
726 DPRINT1("Error %d getting dirent of a file\n", Err);
727 return;
728 }
729
730 /* Read the dirent to fetch the raw short name */
731 FF_FetchEntry(Fcb->Vcb->Ioman,
732 Fcb->FatHandle->DirCluster,
733 Fcb->FatHandle->DirEntry,
734 EntryBuffer);
735 NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
736 RtlCopyMemory(ShortNameRaw, EntryBuffer, 11);
737
738 /* Initialize short name string */
739 ShortName = &Fcb->ShortName.Name.Ansi;
740 ShortName->Buffer = Fcb->ShortNameBuffer;
741 ShortName->Length = 0;
742 ShortName->MaximumLength = sizeof(Fcb->ShortNameBuffer);
743
744 /* Convert raw short name to a proper string */
745 Fati8dot3ToString(ShortNameRaw, FALSE, ShortName);
746
747 /* Add the short name link */
748 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksAnsi, &Fcb->ShortName);
749 Fcb->ShortName.Fcb = Fcb;
750
751 /* Get the long file name (if any) */
752 if (NumLFNs > 0)
753 {
754 /* Prepare the oem string */
755 LongNameOem.Buffer = DirEnt.FileName;
756 LongNameOem.MaximumLength = FF_MAX_FILENAME;
757 LongNameOem.Length = strlen(DirEnt.FileName);
758
759 /* Prepare the unicode string */
760 UnicodeName = &Fcb->LongName.Name.String;
761 UnicodeName->Length = (LongNameOem.Length + 1) * sizeof(WCHAR);
762 UnicodeName->MaximumLength = UnicodeName->Length;
763 UnicodeName->Buffer = FsRtlAllocatePool(PagedPool, UnicodeName->Length);
764
765 /* Convert it to unicode */
766 Status = RtlOemStringToUnicodeString(UnicodeName, &LongNameOem, FALSE);
767 if (!NT_SUCCESS(Status))
768 {
769 ASSERT(FALSE);
770 }
771
772 /* Set its length */
773 Fcb->FileNameLength = UnicodeName->Length;
774
775 /* Save case-preserved copy */
776 Fcb->ExactCaseLongName.Length = UnicodeName->Length;
777 Fcb->ExactCaseLongName.MaximumLength = UnicodeName->Length;
778 Fcb->ExactCaseLongName.Buffer =
779 FsRtlAllocatePoolWithTag(PagedPool, UnicodeName->Length, TAG_FILENAME);
780
781 RtlCopyMemory(Fcb->ExactCaseLongName.Buffer,
782 UnicodeName->Buffer,
783 UnicodeName->Length);
784
785 /* Perform a trick which is done by MS's FASTFAT driver to monocase
786 the filename */
787 RtlDowncaseUnicodeString(UnicodeName, UnicodeName, FALSE);
788 RtlUpcaseUnicodeString(UnicodeName, UnicodeName, FALSE);
789
790 DPRINT("Converted long name: %wZ\n", UnicodeName);
791
792 /* Add the long unicode name link */
793 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksUnicode, &Fcb->LongName);
794 Fcb->LongName.Fcb = Fcb;
795
796 /* Indicate that this FCB has a unicode long name */
797 SetFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
798 }
799 else
800 {
801 /* No LFN, set exact case name to 0 length */
802 Fcb->ExactCaseLongName.Length = 0;
803 Fcb->ExactCaseLongName.MaximumLength = 0;
804
805 /* Set the length based on the short name */
806 Fcb->FileNameLength = RtlOemStringToCountedUnicodeSize(ShortName);
807 }
808
809 /* Mark the fact that names were added to splay trees*/
810 SetFlag(Fcb->State, FCB_STATE_HAS_NAMES);
811 }
812
813 VOID
814 NTAPI
815 Fati8dot3ToString(IN PCHAR FileName,
816 IN BOOLEAN DownCase,
817 OUT POEM_STRING OutString)
818 {
819 ULONG BaseLen, ExtLen;
820 CHAR *cString = OutString->Buffer;
821 ULONG i;
822
823 /* Calc base and ext lens */
824 for (BaseLen = 8; BaseLen > 0; BaseLen--)
825 {
826 if (FileName[BaseLen - 1] != ' ') break;
827 }
828
829 for (ExtLen = 3; ExtLen > 0; ExtLen--)
830 {
831 if (FileName[8 + ExtLen - 1] != ' ') break;
832 }
833
834 /* Process base name */
835 if (BaseLen)
836 {
837 RtlCopyMemory(cString, FileName, BaseLen);
838
839 /* Substitute the e5 thing */
840 if (cString[0] == 0x05) cString[0] = 0xe5;
841
842 /* Downcase if asked to */
843 if (DownCase)
844 {
845 /* Do it manually */
846 for (i = 0; i < BaseLen; i++)
847 {
848 if (cString[i] >= 'A' &&
849 cString[i] <= 'Z')
850 {
851 /* Lowercase it */
852 cString[i] += 'a' - 'A';
853 }
854
855 }
856 }
857 }
858
859 /* Process extension */
860 if (ExtLen)
861 {
862 /* Add the dot */
863 cString[BaseLen] = '.';
864 BaseLen++;
865
866 /* Copy the extension */
867 for (i = 0; i < ExtLen; i++)
868 {
869 cString[BaseLen + i] = FileName[8 + i];
870 }
871
872 /* Lowercase the extension if asked to */
873 if (DownCase)
874 {
875 /* Do it manually */
876 for (i = BaseLen; i < BaseLen + ExtLen; i++)
877 {
878 if (cString[i] >= 'A' &&
879 cString[i] <= 'Z')
880 {
881 /* Lowercase it */
882 cString[i] += 'a' - 'A';
883 }
884 }
885 }
886 }
887
888 /* Set the length */
889 OutString->Length = BaseLen + ExtLen;
890
891 DPRINT("'%s', len %d\n", OutString->Buffer, OutString->Length);
892 }
893
894 VOID
895 NTAPI
896 FatInsertName(IN PFAT_IRP_CONTEXT IrpContext,
897 IN PRTL_SPLAY_LINKS *RootNode,
898 IN PFCB_NAME_LINK Name)
899 {
900 PFCB_NAME_LINK NameLink;
901 FSRTL_COMPARISON_RESULT Comparison;
902
903 /* Initialize the splay links */
904 RtlInitializeSplayLinks(&Name->Links);
905
906 /* Is this the first entry? */
907 if (*RootNode == NULL)
908 {
909 /* Yes, become root and return */
910 *RootNode = &Name->Links;
911 return;
912 }
913
914 /* Get the name link */
915 NameLink = CONTAINING_RECORD(*RootNode, FCB_NAME_LINK, Links);
916 while (TRUE)
917 {
918 /* Compare the prefix */
919 if (*(PUCHAR)NameLink->Name.Ansi.Buffer != *(PUCHAR)&Name->Name.Ansi.Buffer)
920 {
921 if (*(PUCHAR)NameLink->Name.Ansi.Buffer < *(PUCHAR)&Name->Name.Ansi.Buffer)
922 Comparison = LessThan;
923 else
924 Comparison = GreaterThan;
925 }
926 else
927 {
928 /* Perform real comparison */
929 Comparison = FatiCompareNames(&NameLink->Name.Ansi, &Name->Name.Ansi);
930 }
931
932 /* Check the bad case first */
933 if (Comparison == EqualTo)
934 {
935 /* Must not happen */
936 ASSERT(FALSE);
937 }
938
939 /* Check comparison result */
940 if (Comparison == GreaterThan)
941 {
942 /* Go to the left child */
943 if (!RtlLeftChild(&NameLink->Links))
944 {
945 /* It's absent, insert here and break */
946 RtlInsertAsLeftChild(&NameLink->Links, &Name->Links);
947 break;
948 }
949 else
950 {
951 /* It's present, go inside it */
952 NameLink = CONTAINING_RECORD(RtlLeftChild(&NameLink->Links),
953 FCB_NAME_LINK,
954 Links);
955 }
956 }
957 else
958 {
959 /* Go to the right child */
960 if (!RtlRightChild(&NameLink->Links))
961 {
962 /* It's absent, insert here and break */
963 RtlInsertAsRightChild(&NameLink->Links, &Name->Links);
964 break;
965 }
966 else
967 {
968 /* It's present, go inside it */
969 NameLink = CONTAINING_RECORD(RtlRightChild(&NameLink->Links),
970 FCB_NAME_LINK,
971 Links);
972 }
973 }
974 }
975 }
976
977 VOID
978 NTAPI
979 FatRemoveNames(IN PFAT_IRP_CONTEXT IrpContext,
980 IN PFCB Fcb)
981 {
982 PRTL_SPLAY_LINKS RootNew;
983 PFCB Parent;
984
985 /* Reference the parent for simplicity */
986 Parent = Fcb->ParentFcb;
987
988 /* If this FCB hasn't been added to splay trees - just return */
989 if (!FlagOn( Fcb->State, FCB_STATE_HAS_NAMES ))
990 return;
991
992 /* Delete the short name link */
993 RootNew = RtlDelete(&Fcb->ShortName.Links);
994
995 /* Set the new root */
996 Parent->Dcb.SplayLinksAnsi = RootNew;
997
998 /* Deal with a unicode name if it exists */
999 if (FlagOn( Fcb->State, FCB_STATE_HAS_UNICODE_NAME ))
1000 {
1001 /* Delete the long unicode name link */
1002 RootNew = RtlDelete(&Fcb->LongName.Links);
1003
1004 /* Set the new root */
1005 Parent->Dcb.SplayLinksUnicode = RootNew;
1006
1007 /* Free the long name string's buffer*/
1008 RtlFreeUnicodeString(&Fcb->LongName.Name.String);
1009
1010 /* Clear the "has unicode name" flag */
1011 ClearFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
1012 }
1013
1014 /* This FCB has no names added to splay trees now */
1015 ClearFlag(Fcb->State, FCB_STATE_HAS_NAMES);
1016 }
1017
1018
1019 /* EOF */