Sync with trunk (r48545)
[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 /* Initialize locks */
153 FsRtlInitializeFileLock(&Fcb->Fcb.Lock, NULL, NULL);
154 FsRtlInitializeOplock(&Fcb->Fcb.Oplock);
155
156 /* Set names */
157 FatSetFcbNames(IrpContext, Fcb);
158
159 return Fcb;
160 }
161
162 VOID
163 NTAPI
164 FatDeleteFcb(IN PFAT_IRP_CONTEXT IrpContext,
165 IN PFCB Fcb)
166 {
167 DPRINT("FatDeleteFcb %p\n", Fcb);
168
169 if (Fcb->OpenCount != 0)
170 {
171 DPRINT1("Trying to delete FCB with OpenCount %d\n", Fcb->OpenCount);
172 ASSERT(FALSE);
173 }
174
175 if ((Fcb->Header.NodeTypeCode == FAT_NTC_DCB) ||
176 (Fcb->Header.NodeTypeCode == FAT_NTC_ROOT_DCB))
177 {
178 /* Make sure it's a valid deletion */
179 ASSERT(Fcb->Dcb.DirectoryFileOpenCount == 0);
180 ASSERT(IsListEmpty(&Fcb->Dcb.ParentDcbList));
181 ASSERT(Fcb->Dcb.DirectoryFile == NULL);
182 }
183 else
184 {
185 /* Free locks */
186 FsRtlUninitializeFileLock(&Fcb->Fcb.Lock);
187 FsRtlUninitializeOplock(&Fcb->Fcb.Oplock);
188 }
189
190 /* Release any possible filter contexts */
191 FsRtlTeardownPerStreamContexts(&Fcb->Header);
192
193 /* Remove from parents queue */
194 if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB)
195 {
196 RemoveEntryList(&(Fcb->ParentDcbLinks));
197 }
198
199 /* Free FullFAT handle */
200 if (Fcb->FatHandle) FF_Close(Fcb->FatHandle);
201
202 /* Remove from the splay table */
203 if (FlagOn(Fcb->State, FCB_STATE_HAS_NAMES))
204 FatRemoveNames(IrpContext, Fcb);
205
206 /* Free file name buffers */
207 if (Fcb->Header.NodeTypeCode != FAT_NTC_ROOT_DCB)
208 {
209 if (Fcb->FullFileName.Buffer)
210 ExFreePool(Fcb->FullFileName.Buffer);
211 }
212
213 if (Fcb->ExactCaseLongName.Buffer)
214 ExFreePool(Fcb->ExactCaseLongName.Buffer);
215
216 /* Free this FCB, finally */
217 ExFreePool(Fcb);
218 }
219
220 PCCB
221 NTAPI
222 FatCreateCcb()
223 {
224 PCCB Ccb;
225
226 /* Allocate the CCB and zero it */
227 Ccb = ExAllocatePoolWithTag(NonPagedPool, sizeof(CCB), TAG_CCB);
228 RtlZeroMemory(Ccb, sizeof(CCB));
229
230 /* Set mandatory header */
231 Ccb->NodeTypeCode = FAT_NTC_FCB;
232 Ccb->NodeByteSize = sizeof(CCB);
233
234 return Ccb;
235 }
236
237 VOID
238 NTAPI
239 FatDeleteCcb(IN PFAT_IRP_CONTEXT IrpContext,
240 IN PCCB Ccb)
241 {
242 // TODO: Deallocate CCB strings, if any
243
244 /* Free the CCB */
245 ExFreePool(Ccb);
246 }
247
248 IO_STATUS_BLOCK
249 NTAPI
250 FatiOpenExistingFcb(IN PFAT_IRP_CONTEXT IrpContext,
251 IN PFILE_OBJECT FileObject,
252 IN PVCB Vcb,
253 IN PFCB Fcb,
254 IN PACCESS_MASK DesiredAccess,
255 IN USHORT ShareAccess,
256 IN ULONG AllocationSize,
257 IN PFILE_FULL_EA_INFORMATION EaBuffer,
258 IN ULONG EaLength,
259 IN UCHAR FileAttributes,
260 IN ULONG CreateDisposition,
261 IN BOOLEAN NoEaKnowledge,
262 IN BOOLEAN DeleteOnClose,
263 IN BOOLEAN OpenedAsDos,
264 OUT PBOOLEAN OplockPostIrp)
265 {
266 IO_STATUS_BLOCK Iosb = {{0}};
267 ACCESS_MASK AddedAccess = 0;
268 BOOLEAN Hidden;
269 BOOLEAN System;
270 PCCB Ccb = NULL;
271 NTSTATUS Status, StatusPrev;
272
273 /* Acquire exclusive FCB lock */
274 (VOID)FatAcquireExclusiveFcb(IrpContext, Fcb);
275
276 *OplockPostIrp = FALSE;
277
278 /* Check if there is a batch oplock */
279 if (FsRtlCurrentBatchOplock(&Fcb->Fcb.Oplock))
280 {
281 /* Return with a special information field */
282 Iosb.Information = FILE_OPBATCH_BREAK_UNDERWAY;
283
284 /* Check the oplock */
285 Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
286 IrpContext->Irp,
287 IrpContext,
288 FatOplockComplete,
289 FatPrePostIrp);
290
291 if (Iosb.Status != STATUS_SUCCESS &&
292 Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
293 {
294 /* The Irp needs to be queued */
295 *OplockPostIrp = TRUE;
296
297 /* Release the FCB and return */
298 FatReleaseFcb(IrpContext, Fcb);
299 return Iosb;
300 }
301 }
302
303 /* Validate parameters and modify access */
304 if (CreateDisposition == FILE_CREATE)
305 {
306 Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
307
308 /* Release the FCB and return */
309 FatReleaseFcb(IrpContext, Fcb);
310 return Iosb;
311 }
312 else if (CreateDisposition == FILE_SUPERSEDE)
313 {
314 SetFlag(AddedAccess, DELETE & ~(*DesiredAccess));
315 *DesiredAccess |= DELETE;
316 }
317 else if ((CreateDisposition == FILE_OVERWRITE) ||
318 (CreateDisposition == FILE_OVERWRITE_IF))
319 {
320 SetFlag(AddedAccess,
321 (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
322 & ~(*DesiredAccess) );
323
324 *DesiredAccess |= FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
325 }
326
327 // TODO: Check desired access
328
329 // TODO: Check if this file is readonly and DeleteOnClose is set
330
331 /* Validate disposition information */
332 if ((CreateDisposition == FILE_SUPERSEDE) ||
333 (CreateDisposition == FILE_OVERWRITE) ||
334 (CreateDisposition == FILE_OVERWRITE_IF))
335 {
336 // TODO: Get this attributes from the dirent
337 Hidden = FALSE;
338 System = FALSE;
339
340 if ((Hidden && !FlagOn(FileAttributes, FILE_ATTRIBUTE_HIDDEN)) ||
341 (System && !FlagOn(FileAttributes, FILE_ATTRIBUTE_SYSTEM)))
342 {
343 DPRINT1("Hidden/system attributes don't match\n");
344
345 Iosb.Status = STATUS_ACCESS_DENIED;
346
347 /* Release the FCB and return */
348 FatReleaseFcb(IrpContext, Fcb);
349 return Iosb;
350 }
351
352 // TODO: Check for write protected volume
353 }
354
355 /* Check share access */
356 Iosb.Status = IoCheckShareAccess(*DesiredAccess,
357 ShareAccess,
358 FileObject,
359 &Fcb->ShareAccess,
360 FALSE);
361 if (!NT_SUCCESS(Iosb.Status))
362 {
363 /* Release the FCB and return */
364 FatReleaseFcb(IrpContext, Fcb);
365 return Iosb;
366 }
367
368 /* Check the oplock status after checking for share access */
369 Iosb.Status = FsRtlCheckOplock(&Fcb->Fcb.Oplock,
370 IrpContext->Irp,
371 IrpContext,
372 FatOplockComplete,
373 FatPrePostIrp );
374
375 if (Iosb.Status != STATUS_SUCCESS &&
376 Iosb.Status != STATUS_OPLOCK_BREAK_IN_PROGRESS)
377 {
378 /* The Irp needs to be queued */
379 *OplockPostIrp = TRUE;
380
381 /* Release the FCB and return */
382 FatReleaseFcb(IrpContext, Fcb);
383 return Iosb;
384 }
385
386 /* Set Fast I/O flag */
387 Fcb->Header.IsFastIoPossible = FALSE; //FatiIsFastIoPossible(Fcb);
388
389 /* Make sure image is not mapped */
390 if (DeleteOnClose || FlagOn(*DesiredAccess, FILE_WRITE_DATA))
391 {
392 /* Try to flush the image section */
393 if (!MmFlushImageSection(&Fcb->SectionObjectPointers, MmFlushForWrite))
394 {
395 /* Yes, image section exists, set correct status code */
396 if (DeleteOnClose)
397 Iosb.Status = STATUS_CANNOT_DELETE;
398 else
399 Iosb.Status = STATUS_SHARING_VIOLATION;
400
401 /* Release the FCB and return */
402 FatReleaseFcb(IrpContext, Fcb);
403 return Iosb;
404 }
405 }
406
407 /* Flush the cache if it's non-cached non-pagefile access */
408 if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING) &&
409 Fcb->SectionObjectPointers.DataSectionObject &&
410 !FlagOn(Fcb->State, FCB_STATE_PAGEFILE))
411 {
412 /* Set the flag that create is in progress */
413 SetFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
414
415 /* Flush the cache */
416 CcFlushCache(&Fcb->SectionObjectPointers, NULL, 0, NULL);
417
418 /* Acquire and release Paging I/O resource before purging the cache section
419 to let lazy writer finish */
420 ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
421 ExReleaseResourceLite( Fcb->Header.PagingIoResource );
422
423 /* Delete the cache section */
424 CcPurgeCacheSection(&Fcb->SectionObjectPointers, NULL, 0, FALSE);
425
426 /* Clear the flag */
427 ClearFlag(Fcb->Vcb->State, VCB_STATE_CREATE_IN_PROGRESS);
428 }
429
430 /* Check create disposition flags and branch accordingly */
431 if (CreateDisposition == FILE_OPEN ||
432 CreateDisposition == FILE_OPEN_IF)
433 {
434 DPRINT("Opening a file\n");
435
436 /* Check if we need to bother with EA */
437 if (NoEaKnowledge && FALSE /* FatIsFat32(Vcb)*/)
438 {
439 UNIMPLEMENTED;
440 }
441
442 /* Set up file object */
443 Ccb = FatCreateCcb();
444 FatSetFileObject(FileObject,
445 UserFileOpen,
446 Fcb,
447 Ccb);
448
449 FileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
450
451 /* The file is opened */
452 Iosb.Information = FILE_OPENED;
453 goto SuccComplete;
454 }
455 else if ((CreateDisposition == FILE_SUPERSEDE) ||
456 (CreateDisposition == FILE_OVERWRITE) ||
457 (CreateDisposition == FILE_OVERWRITE_IF))
458 {
459 /* Remember previous status */
460 StatusPrev = Iosb.Status;
461
462 // TODO: Check system security access
463
464 /* Perform overwrite operation */
465 Iosb = FatiOverwriteFile(IrpContext,
466 FileObject,
467 Fcb,
468 AllocationSize,
469 EaBuffer,
470 EaLength,
471 FileAttributes,
472 CreateDisposition,
473 NoEaKnowledge);
474
475 /* Restore previous status in case of success */
476 if (Iosb.Status == STATUS_SUCCESS)
477 Iosb.Status = StatusPrev;
478
479 /* Fall down to completion */
480 }
481 else
482 {
483 /* We can't get here */
484 KeBugCheckEx(FAT_FILE_SYSTEM, CreateDisposition, 0, 0, 0);
485 }
486
487
488 SuccComplete:
489 /* If all is fine */
490 if (Iosb.Status != STATUS_PENDING &&
491 NT_SUCCESS(Iosb.Status))
492 {
493 /* Update access if needed */
494 if (AddedAccess)
495 {
496 /* Remove added access flags from desired access */
497 ClearFlag(*DesiredAccess, AddedAccess);
498
499 /* Check share access */
500 Status = IoCheckShareAccess(*DesiredAccess,
501 ShareAccess,
502 FileObject,
503 &Fcb->ShareAccess,
504 TRUE);
505
506 /* Make sure it's success */
507 ASSERT(Status == STATUS_SUCCESS);
508 }
509 else
510 {
511 /* Update the share access */
512 IoUpdateShareAccess(FileObject, &Fcb->ShareAccess);
513 }
514
515 /* Clear the delay close */
516 ClearFlag(Fcb->State, FCB_STATE_DELAY_CLOSE);
517
518 /* Increase counters */
519 Fcb->UncleanCount++;
520 Fcb->OpenCount++;
521 Vcb->OpenFileCount++;
522 if (IsFileObjectReadOnly(FileObject)) Vcb->ReadOnlyCount++;
523 if (FlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING)) Fcb->NonCachedUncleanCount++;
524
525 // TODO: Handle DeleteOnClose and OpenedAsDos by storing those flags in CCB
526 }
527
528 return Iosb;
529 }
530
531 VOID
532 NTAPI
533 FatGetFcbUnicodeName(IN PFAT_IRP_CONTEXT IrpContext,
534 IN PFCB Fcb,
535 OUT PUNICODE_STRING LongName)
536 {
537 FF_DIRENT DirEnt;
538 FF_ERROR Err;
539 OEM_STRING ShortName;
540 CHAR ShortNameBuf[13];
541 UCHAR EntryBuffer[32];
542 UCHAR NumLFNs;
543 OEM_STRING LongNameOem;
544 NTSTATUS Status;
545
546 /* Make sure this FCB has a FullFAT handle associated with it */
547 if (Fcb->FatHandle == NULL &&
548 FatNodeType(Fcb) == FAT_NTC_DCB)
549 {
550 /* Open the dir with FullFAT */
551 Fcb->FatHandle = FF_OpenW(Fcb->Vcb->Ioman, &Fcb->FullFileName, FF_MODE_DIR, NULL);
552 if (!Fcb->FatHandle)
553 {
554 ASSERT(FALSE);
555 }
556 }
557
558 /* Get the dir entry */
559 Err = FF_GetEntry(Fcb->Vcb->Ioman,
560 Fcb->FatHandle->DirEntry,
561 Fcb->FatHandle->DirCluster,
562 &DirEnt);
563
564 if (Err != FF_ERR_NONE)
565 {
566 DPRINT1("Error %d getting dirent of a file\n", Err);
567 return;
568 }
569
570 /* Read the dirent to fetch the raw short name */
571 FF_FetchEntry(Fcb->Vcb->Ioman,
572 Fcb->FatHandle->DirCluster,
573 Fcb->FatHandle->DirEntry,
574 EntryBuffer);
575 NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
576
577 /* Check if we only have a short name.
578 Convert it to unicode and return if that's the case */
579 if (NumLFNs == 0)
580 {
581 /* Initialize short name string */
582 ShortName.Buffer = ShortNameBuf;
583 ShortName.Length = 0;
584 ShortName.MaximumLength = 12;
585
586 /* Convert raw short name to a proper string */
587 Fati8dot3ToString((PCHAR)EntryBuffer, FALSE, &ShortName);
588
589 /* Convert it to unicode */
590 Status = RtlOemStringToCountedUnicodeString(LongName,
591 &ShortName,
592 FALSE);
593
594 /* Ensure conversion was successful */
595 ASSERT(Status == STATUS_SUCCESS);
596
597 /* Exit */
598 return;
599 }
600
601 /* Convert LFN from OEM to unicode and return */
602 LongNameOem.Buffer = DirEnt.FileName;
603 LongNameOem.MaximumLength = FF_MAX_FILENAME;
604 LongNameOem.Length = strlen(DirEnt.FileName);
605
606 /* Convert it to unicode */
607 Status = RtlOemStringToUnicodeString(LongName, &LongNameOem, FALSE);
608
609 /* Ensure conversion was successful */
610 ASSERT(Status == STATUS_SUCCESS);
611 }
612
613
614 VOID
615 NTAPI
616 FatSetFullNameInFcb(PFCB Fcb,
617 PUNICODE_STRING Name)
618 {
619 PUNICODE_STRING ParentName;
620
621 /* Make sure this FCB's name wasn't already set */
622 ASSERT(Fcb->FullFileName.Buffer == NULL);
623
624 /* First of all, check exact case name */
625 if (Fcb->ExactCaseLongName.Buffer)
626 {
627 ASSERT(Fcb->ExactCaseLongName.Length != 0);
628
629 /* Use exact case name */
630 Name = &Fcb->ExactCaseLongName;
631 }
632
633 /* Treat root dir different */
634 if (FatNodeType(Fcb->ParentFcb) == FAT_NTC_ROOT_DCB)
635 {
636 /* Set lengths */
637 Fcb->FullFileName.MaximumLength = sizeof(WCHAR) + Name->Length;
638 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
639
640 /* Allocate a buffer */
641 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
642 Fcb->FullFileName.Length,
643 TAG_FILENAME);
644
645 /* Prefix with a backslash */
646 Fcb->FullFileName.Buffer[0] = L'\\';
647
648 /* Copy the name here */
649 RtlCopyMemory(&Fcb->FullFileName.Buffer[1],
650 &Name->Buffer[0],
651 Name->Length );
652 }
653 else
654 {
655 ParentName = &Fcb->ParentFcb->FullFileName;
656
657 /* Check if parent's name is set */
658 if (!ParentName->Buffer)
659 return;
660
661 /* Set lengths */
662 Fcb->FullFileName.MaximumLength =
663 ParentName->Length + sizeof(WCHAR) + Name->Length;
664 Fcb->FullFileName.Length = Fcb->FullFileName.MaximumLength;
665
666 /* Allocate a buffer */
667 Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag(PagedPool,
668 Fcb->FullFileName.Length,
669 TAG_FILENAME );
670
671 /* Copy parent's name here */
672 RtlCopyMemory(&Fcb->FullFileName.Buffer[0],
673 &ParentName->Buffer[0],
674 ParentName->Length );
675
676 /* Add a backslash */
677 Fcb->FullFileName.Buffer[ParentName->Length / sizeof(WCHAR)] = L'\\';
678
679 /* Copy given name here */
680 RtlCopyMemory(&Fcb->FullFileName.Buffer[(ParentName->Length / sizeof(WCHAR)) + 1],
681 &Name->Buffer[0],
682 Name->Length );
683 }
684 }
685
686 VOID
687 NTAPI
688 FatSetFullFileNameInFcb(IN PFAT_IRP_CONTEXT IrpContext,
689 IN PFCB Fcb)
690 {
691 UNICODE_STRING LongName;
692 PFCB CurFcb = Fcb;
693 PFCB StopFcb;
694 PWCHAR TmpBuffer;
695 ULONG PathLength = 0;
696
697 /* Do nothing if it's already set */
698 if (Fcb->FullFileName.Buffer) return;
699
700 /* Allocate a temporary buffer */
701 LongName.Length = 0;
702 LongName.MaximumLength = FF_MAX_FILENAME * sizeof(WCHAR);
703 LongName.Buffer =
704 FsRtlAllocatePoolWithTag(PagedPool,
705 FF_MAX_FILENAME * sizeof(WCHAR),
706 TAG_FILENAME);
707
708 /* Go through all parents to calculate needed length */
709 while (CurFcb != Fcb->Vcb->RootDcb)
710 {
711 /* Does current FCB have FullFileName set? */
712 if (CurFcb != Fcb &&
713 CurFcb->FullFileName.Buffer)
714 {
715 /* Yes, just use it! */
716 PathLength += CurFcb->FullFileName.Length;
717
718 Fcb->FullFileName.Buffer =
719 FsRtlAllocatePoolWithTag(PagedPool,
720 PathLength,
721 TAG_FILENAME);
722
723 RtlCopyMemory(Fcb->FullFileName.Buffer,
724 CurFcb->FullFileName.Buffer,
725 CurFcb->FullFileName.Length);
726
727 break;
728 }
729
730 /* Sum up length of a current item */
731 PathLength += CurFcb->FileNameLength + sizeof(WCHAR);
732
733 /* Go to the parent */
734 CurFcb = CurFcb->ParentFcb;
735 }
736
737 /* Allocate FullFileName if it wasn't already allocated above */
738 if (!Fcb->FullFileName.Buffer)
739 {
740 Fcb->FullFileName.Buffer =
741 FsRtlAllocatePoolWithTag(PagedPool,
742 PathLength,
743 TAG_FILENAME);
744 }
745
746 StopFcb = CurFcb;
747
748 CurFcb = Fcb;
749 TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR);
750
751 /* Set lengths */
752 Fcb->FullFileName.Length = PathLength;
753 Fcb->FullFileName.MaximumLength = PathLength;
754
755 while (CurFcb != StopFcb)
756 {
757 /* Get its unicode name */
758 FatGetFcbUnicodeName(IrpContext,
759 CurFcb,
760 &LongName);
761
762 /* Copy it */
763 TmpBuffer -= LongName.Length / sizeof(WCHAR);
764 RtlCopyMemory(TmpBuffer, LongName.Buffer, LongName.Length);
765
766 /* Append with a backslash */
767 TmpBuffer -= 1;
768 *TmpBuffer = L'\\';
769
770 /* Go to the parent */
771 CurFcb = CurFcb->ParentFcb;
772 }
773
774 /* Free the temp buffer */
775 ExFreePool(LongName.Buffer);
776 }
777
778
779 VOID
780 NTAPI
781 FatSetFcbNames(IN PFAT_IRP_CONTEXT IrpContext,
782 IN PFCB Fcb)
783 {
784 FF_DIRENT DirEnt;
785 FF_ERROR Err;
786 POEM_STRING ShortName;
787 CHAR ShortNameRaw[13];
788 UCHAR EntryBuffer[32];
789 UCHAR NumLFNs;
790 PUNICODE_STRING UnicodeName;
791 OEM_STRING LongNameOem;
792 NTSTATUS Status;
793
794 /* Get the dir entry */
795 Err = FF_GetEntry(Fcb->Vcb->Ioman,
796 Fcb->FatHandle->DirEntry,
797 Fcb->FatHandle->DirCluster,
798 &DirEnt);
799
800 if (Err != FF_ERR_NONE)
801 {
802 DPRINT1("Error %d getting dirent of a file\n", Err);
803 return;
804 }
805
806 /* Read the dirent to fetch the raw short name */
807 FF_FetchEntry(Fcb->Vcb->Ioman,
808 Fcb->FatHandle->DirCluster,
809 Fcb->FatHandle->DirEntry,
810 EntryBuffer);
811 NumLFNs = (UCHAR)(EntryBuffer[0] & ~0x40);
812 RtlCopyMemory(ShortNameRaw, EntryBuffer, 11);
813
814 /* Initialize short name string */
815 ShortName = &Fcb->ShortName.Name.Ansi;
816 ShortName->Buffer = Fcb->ShortNameBuffer;
817 ShortName->Length = 0;
818 ShortName->MaximumLength = sizeof(Fcb->ShortNameBuffer);
819
820 /* Convert raw short name to a proper string */
821 Fati8dot3ToString(ShortNameRaw, FALSE, ShortName);
822
823 /* Add the short name link */
824 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksAnsi, &Fcb->ShortName);
825 Fcb->ShortName.Fcb = Fcb;
826
827 /* Get the long file name (if any) */
828 if (NumLFNs > 0)
829 {
830 /* Prepare the oem string */
831 LongNameOem.Buffer = DirEnt.FileName;
832 LongNameOem.MaximumLength = FF_MAX_FILENAME;
833 LongNameOem.Length = strlen(DirEnt.FileName);
834
835 /* Prepare the unicode string */
836 UnicodeName = &Fcb->LongName.Name.String;
837 UnicodeName->Length = (LongNameOem.Length + 1) * sizeof(WCHAR);
838 UnicodeName->MaximumLength = UnicodeName->Length;
839 UnicodeName->Buffer = FsRtlAllocatePool(PagedPool, UnicodeName->Length);
840
841 /* Convert it to unicode */
842 Status = RtlOemStringToUnicodeString(UnicodeName, &LongNameOem, FALSE);
843 if (!NT_SUCCESS(Status))
844 {
845 ASSERT(FALSE);
846 }
847
848 /* Set its length */
849 Fcb->FileNameLength = UnicodeName->Length;
850
851 /* Save case-preserved copy */
852 Fcb->ExactCaseLongName.Length = UnicodeName->Length;
853 Fcb->ExactCaseLongName.MaximumLength = UnicodeName->Length;
854 Fcb->ExactCaseLongName.Buffer =
855 FsRtlAllocatePoolWithTag(PagedPool, UnicodeName->Length, TAG_FILENAME);
856
857 RtlCopyMemory(Fcb->ExactCaseLongName.Buffer,
858 UnicodeName->Buffer,
859 UnicodeName->Length);
860
861 /* Perform a trick which is done by MS's FASTFAT driver to monocase
862 the filename */
863 RtlDowncaseUnicodeString(UnicodeName, UnicodeName, FALSE);
864 RtlUpcaseUnicodeString(UnicodeName, UnicodeName, FALSE);
865
866 DPRINT("Converted long name: %wZ\n", UnicodeName);
867
868 /* Add the long unicode name link */
869 FatInsertName(IrpContext, &Fcb->ParentFcb->Dcb.SplayLinksUnicode, &Fcb->LongName);
870 Fcb->LongName.Fcb = Fcb;
871
872 /* Indicate that this FCB has a unicode long name */
873 SetFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
874 }
875 else
876 {
877 /* No LFN, set exact case name to 0 length */
878 Fcb->ExactCaseLongName.Length = 0;
879 Fcb->ExactCaseLongName.MaximumLength = 0;
880
881 /* Set the length based on the short name */
882 Fcb->FileNameLength = RtlOemStringToCountedUnicodeSize(ShortName);
883 }
884
885 /* Mark the fact that names were added to splay trees*/
886 SetFlag(Fcb->State, FCB_STATE_HAS_NAMES);
887 }
888
889 VOID
890 NTAPI
891 Fati8dot3ToString(IN PCHAR FileName,
892 IN BOOLEAN DownCase,
893 OUT POEM_STRING OutString)
894 {
895 ULONG BaseLen, ExtLen;
896 CHAR *cString = OutString->Buffer;
897 ULONG i;
898
899 /* Calc base and ext lens */
900 for (BaseLen = 8; BaseLen > 0; BaseLen--)
901 {
902 if (FileName[BaseLen - 1] != ' ') break;
903 }
904
905 for (ExtLen = 3; ExtLen > 0; ExtLen--)
906 {
907 if (FileName[8 + ExtLen - 1] != ' ') break;
908 }
909
910 /* Process base name */
911 if (BaseLen)
912 {
913 RtlCopyMemory(cString, FileName, BaseLen);
914
915 /* Substitute the e5 thing */
916 if (cString[0] == 0x05) cString[0] = 0xe5;
917
918 /* Downcase if asked to */
919 if (DownCase)
920 {
921 /* Do it manually */
922 for (i = 0; i < BaseLen; i++)
923 {
924 if (cString[i] >= 'A' &&
925 cString[i] <= 'Z')
926 {
927 /* Lowercase it */
928 cString[i] += 'a' - 'A';
929 }
930
931 }
932 }
933 }
934
935 /* Process extension */
936 if (ExtLen)
937 {
938 /* Add the dot */
939 cString[BaseLen] = '.';
940 BaseLen++;
941
942 /* Copy the extension */
943 for (i = 0; i < ExtLen; i++)
944 {
945 cString[BaseLen + i] = FileName[8 + i];
946 }
947
948 /* Lowercase the extension if asked to */
949 if (DownCase)
950 {
951 /* Do it manually */
952 for (i = BaseLen; i < BaseLen + ExtLen; i++)
953 {
954 if (cString[i] >= 'A' &&
955 cString[i] <= 'Z')
956 {
957 /* Lowercase it */
958 cString[i] += 'a' - 'A';
959 }
960 }
961 }
962 }
963
964 /* Set the length */
965 OutString->Length = BaseLen + ExtLen;
966
967 DPRINT("'%s', len %d\n", OutString->Buffer, OutString->Length);
968 }
969
970 VOID
971 NTAPI
972 FatInsertName(IN PFAT_IRP_CONTEXT IrpContext,
973 IN PRTL_SPLAY_LINKS *RootNode,
974 IN PFCB_NAME_LINK Name)
975 {
976 PFCB_NAME_LINK NameLink;
977 FSRTL_COMPARISON_RESULT Comparison;
978
979 /* Initialize the splay links */
980 RtlInitializeSplayLinks(&Name->Links);
981
982 /* Is this the first entry? */
983 if (*RootNode == NULL)
984 {
985 /* Yes, become root and return */
986 *RootNode = &Name->Links;
987 return;
988 }
989
990 /* Get the name link */
991 NameLink = CONTAINING_RECORD(*RootNode, FCB_NAME_LINK, Links);
992 while (TRUE)
993 {
994 /* Compare the prefix */
995 if (*(PUCHAR)NameLink->Name.Ansi.Buffer != *(PUCHAR)&Name->Name.Ansi.Buffer)
996 {
997 if (*(PUCHAR)NameLink->Name.Ansi.Buffer < *(PUCHAR)&Name->Name.Ansi.Buffer)
998 Comparison = LessThan;
999 else
1000 Comparison = GreaterThan;
1001 }
1002 else
1003 {
1004 /* Perform real comparison */
1005 Comparison = FatiCompareNames(&NameLink->Name.Ansi, &Name->Name.Ansi);
1006 }
1007
1008 /* Check the bad case first */
1009 if (Comparison == EqualTo)
1010 {
1011 /* Must not happen */
1012 ASSERT(FALSE);
1013 }
1014
1015 /* Check comparison result */
1016 if (Comparison == GreaterThan)
1017 {
1018 /* Go to the left child */
1019 if (!RtlLeftChild(&NameLink->Links))
1020 {
1021 /* It's absent, insert here and break */
1022 RtlInsertAsLeftChild(&NameLink->Links, &Name->Links);
1023 break;
1024 }
1025 else
1026 {
1027 /* It's present, go inside it */
1028 NameLink = CONTAINING_RECORD(RtlLeftChild(&NameLink->Links),
1029 FCB_NAME_LINK,
1030 Links);
1031 }
1032 }
1033 else
1034 {
1035 /* Go to the right child */
1036 if (!RtlRightChild(&NameLink->Links))
1037 {
1038 /* It's absent, insert here and break */
1039 RtlInsertAsRightChild(&NameLink->Links, &Name->Links);
1040 break;
1041 }
1042 else
1043 {
1044 /* It's present, go inside it */
1045 NameLink = CONTAINING_RECORD(RtlRightChild(&NameLink->Links),
1046 FCB_NAME_LINK,
1047 Links);
1048 }
1049 }
1050 }
1051 }
1052
1053 VOID
1054 NTAPI
1055 FatRemoveNames(IN PFAT_IRP_CONTEXT IrpContext,
1056 IN PFCB Fcb)
1057 {
1058 PRTL_SPLAY_LINKS RootNew;
1059 PFCB Parent;
1060
1061 /* Reference the parent for simplicity */
1062 Parent = Fcb->ParentFcb;
1063
1064 /* If this FCB hasn't been added to splay trees - just return */
1065 if (!FlagOn( Fcb->State, FCB_STATE_HAS_NAMES ))
1066 return;
1067
1068 /* Delete the short name link */
1069 RootNew = RtlDelete(&Fcb->ShortName.Links);
1070
1071 /* Set the new root */
1072 Parent->Dcb.SplayLinksAnsi = RootNew;
1073
1074 /* Deal with a unicode name if it exists */
1075 if (FlagOn( Fcb->State, FCB_STATE_HAS_UNICODE_NAME ))
1076 {
1077 /* Delete the long unicode name link */
1078 RootNew = RtlDelete(&Fcb->LongName.Links);
1079
1080 /* Set the new root */
1081 Parent->Dcb.SplayLinksUnicode = RootNew;
1082
1083 /* Free the long name string's buffer*/
1084 RtlFreeUnicodeString(&Fcb->LongName.Name.String);
1085
1086 /* Clear the "has unicode name" flag */
1087 ClearFlag(Fcb->State, FCB_STATE_HAS_UNICODE_NAME);
1088 }
1089
1090 /* This FCB has no names added to splay trees now */
1091 ClearFlag(Fcb->State, FCB_STATE_HAS_NAMES);
1092 }
1093
1094
1095 /* EOF */