544 } |
561 } |
545 |
562 |
546 |
563 |
547 } |
564 } |
548 |
565 |
549 void TFatDirEntry::InitializeAsVFat(TUint8 aCheckSum) |
566 |
550 // |
567 //----------------------------------------------------------------------------- |
551 // Initialize a FAT entry as a VFAT filename |
568 /** |
552 // |
569 Write up to KMaxVFatEntryName unicode chars from aName to the entry |
553 { |
570 @param aName long file name part that will be converted into the VFAT entryset |
554 |
571 @param aLen length of the remaining name |
555 Mem::Fill(this,sizeof(SFatDirEntry),0xFF); |
572 @param aCheckSum DOS entry name checksum. |
|
573 */ |
|
574 void TFatDirEntry::SetVFatEntry(const TDesC& aName, TUint aLen, TUint8 aCheckSum) |
|
575 { |
|
576 //-- initialise some VFAT entry specific fields |
556 iData[0x0B]=0x0F; |
577 iData[0x0B]=0x0F; |
557 iData[0x0C]=0x00; iData[0x0D]=aCheckSum; |
578 iData[0x0C]=0x00; iData[0x0D]=aCheckSum; |
558 iData[0x1A]=0x00; iData[0x1B]=0x00; |
579 iData[0x1A]=0x00; iData[0x1B]=0x00; |
559 } |
|
560 |
|
561 void TFatDirEntry::SetVFatEntry(const TDesC& aName,TInt aLen) |
|
562 // |
|
563 // Write up to KMaxVFatEntryName unicode chars from aName to the entry |
|
564 // |
|
565 { |
|
566 |
580 |
567 TInt rem=aName.Length()-aLen; |
581 TInt rem=aName.Length()-aLen; |
568 TPtrC section(aName.Ptr()+aLen,Min(rem,KMaxVFatEntryName)); |
582 TPtrC section(aName.Ptr()+aLen,Min(rem,KMaxVFatEntryName)); |
569 TBuf16<KMaxVFatEntryName> buf16; |
583 TBuf16<KMaxVFatEntryName> buf16; |
570 buf16.Copy(section); |
584 buf16.Copy(section); |
|
585 |
571 if (rem<KMaxVFatEntryName) |
586 if (rem<KMaxVFatEntryName) |
572 { |
587 { |
573 rem++; |
588 rem++; |
574 buf16.ZeroTerminate(); |
589 buf16.ZeroTerminate(); |
575 buf16.SetLength(rem); // Zero termination doesn't increase the buf length |
590 buf16.SetLength(rem); // Zero termination doesn't increase the buf length |
576 } |
591 } |
|
592 |
577 TUint8 orderNo=(TUint8)(aLen/KMaxVFatEntryName+1); |
593 TUint8 orderNo=(TUint8)(aLen/KMaxVFatEntryName+1); |
578 TInt s=Min(rem,5); |
594 TInt s=Min(rem,5); |
579 Mem::Copy(&iData[0x01],buf16.Ptr(),s*2);//Copy up to 10 bytes of buf16 into iData |
595 Mem::Copy(&iData[0x01],buf16.Ptr(),s*2);//Copy up to 10 bytes of buf16 into iData |
|
596 |
580 TInt offset=s; |
597 TInt offset=s; |
581 rem-=s; |
598 rem-=s; |
582 s=Min(rem,6); |
599 s=Min(rem,6); |
583 Mem::Copy(&iData[0x0E],buf16.Ptr()+offset,s*2); |
600 Mem::Copy(&iData[0x0E],buf16.Ptr()+offset,s*2); |
|
601 |
584 offset+=s; |
602 offset+=s; |
585 rem-=s; |
603 rem-=s; |
|
604 |
586 s=Min(rem,2); |
605 s=Min(rem,2); |
587 Mem::Copy(&iData[0x1C],buf16.Ptr()+offset,s*2); |
606 Mem::Copy(&iData[0x1C],buf16.Ptr()+offset,s*2); |
588 rem-=s; |
607 rem-=s; |
|
608 |
589 if (rem==0) |
609 if (rem==0) |
590 orderNo|=0x40; |
610 orderNo|=0x40; |
|
611 |
591 iData[0]=orderNo; |
612 iData[0]=orderNo; |
592 } |
613 } |
593 |
614 |
|
615 |
|
616 //----------------------------------------------------------------------------- |
|
617 /** |
|
618 Read KMaxVFatEntryName unicode chars from the entry |
|
619 */ |
594 void TFatDirEntry::ReadVFatEntry(TDes16& aBuf) const |
620 void TFatDirEntry::ReadVFatEntry(TDes16& aBuf) const |
595 // |
621 { |
596 // Read KMaxVFatEntryName unicode chars from the entry |
|
597 // |
|
598 { |
|
599 |
|
600 aBuf.SetLength(KMaxVFatEntryName); |
622 aBuf.SetLength(KMaxVFatEntryName); |
601 Mem::Copy(&aBuf[0],&iData[0x01],5*2); |
623 Mem::Copy(&aBuf[0],&iData[0x01],5*2); |
602 Mem::Copy(&aBuf[5],&iData[0x0E],6*2); |
624 Mem::Copy(&aBuf[5],&iData[0x0E],6*2); |
603 Mem::Copy(&aBuf[11],&iData[0x1C],2*2); |
625 Mem::Copy(&aBuf[11],&iData[0x1C],2*2); |
604 } |
626 } |
605 |
627 |
606 void CFatMountCB::WriteDirEntryL(TEntryPos& aPos,const TFatDirEntry& aFatDirEntry,const TDesC& aLongName) |
628 //----------------------------------------------------------------------------- |
607 // |
629 /** |
608 // Write a VFAT directory entry to disk at position aPos - leave aPos refering to the dos entry |
630 Write a VFAT directory entry set to disk at position aPos - leave aPos refering to the dos entry |
609 // Assumes sufficient space has been created for it by AddDirEntry. |
631 Assumes sufficient space has been created for it by AddDirEntry. |
610 // |
632 For Rugged FAT mode bulk writing of the whole entryset is OK. If the entryset fits into media atomic write unit, the |
611 { |
633 write is transactional anyway. if the entryset is split between media atomic write units, the part of it with the DOS |
612 |
634 entry is written last; if this write operation fails, the artifact would be just several orphaned VFAT entries; |
613 __PRINT(_L("VFAT::CFatMountCB::WriteDirEntryL")); |
635 |
|
636 @param aPos in: specifies the entryste start position. out: points to the last (DOS) entry in the created entryset |
|
637 @param aFatDirEntry aDosEntry DOS entry |
|
638 @param aLongName VFAT entry long name |
|
639 */ |
|
640 void CFatMountCB::WriteDirEntryL(TEntryPos& aPos, const TFatDirEntry& aDosEntry, const TDesC& aLongName) |
|
641 { |
|
642 __PRINT2(_L("CFatMountCB::WriteDirEntryL() cl:%d, pos:%d"), aPos.Cluster(), aPos.Pos()); |
614 __ASSERT_DEBUG(aLongName.Length(),Fault(EVFatNoLongName)); |
643 __ASSERT_DEBUG(aLongName.Length(),Fault(EVFatNoLongName)); |
615 TEntryPos startPos(aPos.iCluster,aPos.iPos); |
644 |
616 TUint8 localBuf[KDefaultSectorSize]; |
645 //-- scratch buffer for whole VFAT entryset. Max number of entries in it is 21 entry or 672 bytes. |
617 TUint8 cksum=CalculateShortNameCheckSum(aFatDirEntry.Name()); |
646 //-- in the worst case the entryset can span across 3 clusters (512 bytes per cluster) |
618 TInt numEntries=NumberOfVFatEntries(aLongName.Length())-1; // Excluding dos entry |
647 //-- Using the scratch buffer is not ideal, but write-back directory cache isn't in place yet |
619 // see if all entries written to one sector |
648 const TUint KBufSize = 680; |
620 // single sector writes not supported if sector size>default size |
649 TUint8 scratchBuf[KBufSize]; |
621 TInt dosOffset=numEntries<<KSizeOfFatDirEntryLog2; |
650 |
622 TInt absolutePos=(aPos.iCluster<<ClusterSizeLog2())+ClusterRelativePos(aPos.iPos); |
651 const TUint8 cksum=CalculateShortNameCheckSum(aDosEntry.Name()); |
623 TBool isSameSector=(((absolutePos^(absolutePos+dosOffset))>>SectorSizeLog2())==0 && ((TUint)(1<<SectorSizeLog2())<=KDefaultSectorSize)); |
652 TUint numEntries=NumberOfVFatEntries(aLongName.Length())-1; // Excluding dos entry |
624 TFatDirEntry vFatEntry; |
653 |
625 vFatEntry.InitializeAsVFat(cksum); |
654 ASSERT(KBufSize >= ((numEntries+1)<<KSizeOfFatDirEntryLog2)); |
626 TInt offset=0; |
655 TEntryPos startPos; |
|
656 |
|
657 for(;;) |
|
658 { |
|
659 TInt posInBuf = 0; |
|
660 startPos = aPos; |
|
661 TBool movedCluster = EFalse; |
|
662 |
|
663 while(numEntries) |
|
664 { |
|
665 TFatDirEntry* pEntry = (TFatDirEntry*)(&scratchBuf[posInBuf]); |
|
666 pEntry->SetVFatEntry(aLongName, KMaxVFatEntryName*(numEntries-1), cksum); //KMaxVFatEntryName=13 |
|
667 |
|
668 posInBuf += KSizeOfFatDirEntry; |
|
669 MoveToNextEntryL(aPos); |
|
670 |
|
671 numEntries--; |
|
672 movedCluster = (startPos.Cluster() != aPos.Cluster()); //-- if moved to another cluser, need to flush buffer |
|
673 |
|
674 if(!numEntries || movedCluster) |
|
675 break; //-- VFAT entryset is completed |
|
676 } |
|
677 |
|
678 if(movedCluster) |
|
679 { |
|
680 DirWriteL(startPos, TPtrC8(&scratchBuf[0], posInBuf)); |
|
681 continue; |
|
682 } |
|
683 |
|
684 if(!numEntries) |
|
685 {//-- need to append DOS entry |
|
686 Mem::Copy(&scratchBuf[posInBuf], &aDosEntry, KSizeOfFatDirEntry); |
|
687 posInBuf+= KSizeOfFatDirEntry; |
|
688 DirWriteL(startPos, TPtrC8(&scratchBuf[0], posInBuf)); |
|
689 break; |
|
690 } |
|
691 |
|
692 }//for(;;) |
|
693 } |
|
694 |
|
695 |
|
696 |
|
697 //--------------------------------------------------------------------------------- |
|
698 |
|
699 void CFatMountCB::DoEraseEntrySetChunkL(const TEntrySetChunkInfo& aEntrySetChunk) |
|
700 { |
|
701 |
|
702 //-- scratch buffer for whole VFAT entryset. Max number of entries in it is 21 entry or 672 bytes. |
|
703 //-- in the worst case the entryset can span across 3 clusters (512 bytes per cluster) |
|
704 //-- Using the scratch buffer is not ideal, but write-back directory cache isn't in place yet |
|
705 |
|
706 const TUint KBufSize = 680; |
|
707 TBuf8<KBufSize> scratchBuf; |
|
708 |
|
709 TUint numEntries = aEntrySetChunk.iNumEntries; |
|
710 |
|
711 ASSERT(numEntries >0 && numEntries <= KMaxVFatEntries); |
|
712 const TUint32 KChunkLen = numEntries << KSizeOfFatDirEntryLog2; |
|
713 |
|
714 DirReadL(aEntrySetChunk.iEntryPos, KChunkLen, scratchBuf); |
|
715 |
|
716 TInt posInBuf = 0; |
627 while (numEntries--) |
717 while (numEntries--) |
628 { |
718 { |
629 vFatEntry.SetVFatEntry(aLongName,KMaxVFatEntryName*numEntries);// KMaxVFatEntryName=13 |
719 TFatDirEntry* pEntry = (TFatDirEntry*)(scratchBuf.Ptr()+posInBuf); |
630 if(isSameSector) |
720 pEntry->SetErased(); |
631 { |
721 posInBuf += KSizeOfFatDirEntry; |
632 Mem::Copy(&localBuf[offset],&vFatEntry,KSizeOfFatDirEntry); |
|
633 offset+=KSizeOfFatDirEntry; |
|
634 MoveToNextEntryL(aPos); |
|
635 } |
|
636 else |
|
637 { |
|
638 WriteDirEntryL(aPos,vFatEntry); |
|
639 MoveToNextEntryL(aPos); |
|
640 } |
|
641 } |
|
642 if(isSameSector) |
|
643 { |
|
644 Mem::Copy(&localBuf[offset],&aFatDirEntry,KSizeOfFatDirEntry); |
|
645 |
|
646 //-- use special interface to access FAT directory file |
|
647 DirWriteL(startPos,TPtrC8(&localBuf[0],dosOffset+KSizeOfFatDirEntry)); |
|
648 } |
722 } |
|
723 |
|
724 DirWriteL(aEntrySetChunk.iEntryPos, scratchBuf); |
|
725 } |
|
726 |
|
727 //--------------------------------------------------------------------------------- |
|
728 /** |
|
729 Erase whole VFAT entryset. |
|
730 For Rugged FAT the situation is more complicated: we need firstly delete the DOS entry _atomically_ i.e. if this operation fails, |
|
731 the whole VFAT entryset won't be broken. Deleting VFAT entries doesn't require the atomic media writes; DOS entry contains necessary |
|
732 information about data stream. |
|
733 |
|
734 @param aPos position of the entryset start in the directory. |
|
735 @param aFirstEntry first entry in the entryset, it can be DOS entry |
|
736 |
|
737 */ |
|
738 void CFatMountCB::EraseDirEntryL(TEntryPos aPos,const TFatDirEntry& aFirstEntry) |
|
739 { |
|
740 __PRINT2(_L("CFatMountCB::EraseDirEntryL() cl:%d, offset:%d"), aPos.Cluster(), aPos.Pos()); |
|
741 |
|
742 TUint numEntries=0; |
|
743 if (aFirstEntry.IsVFatEntry()) |
|
744 { |
|
745 numEntries=aFirstEntry.NumFollowing(); |
|
746 numEntries++; //-- take into account the last DOS entry |
|
747 } |
|
748 else |
|
749 {//-- we are deleting a single DOS entry. This is an atomic operation. |
|
750 EraseDirEntryL(aPos); |
|
751 return; |
|
752 } |
|
753 |
|
754 ASSERT(numEntries > 1 && numEntries <= KMaxVFatEntries); |
|
755 |
|
756 TEntrySetChunkInfo chunksInfo[TEntrySetChunkInfo::KMaxChunks]; |
|
757 |
|
758 //-- 1. check if the entryset fits into a unit of write ganularity. This will be 1 sector for rugged FAT or 1 cluster otherwise |
|
759 |
|
760 TUint32 MaxWriteGranularityLog2; |
|
761 |
|
762 if(IsRuggedFSys()) |
|
763 { |
|
764 MaxWriteGranularityLog2 = AtomicWriteGranularityLog2(); |
|
765 } |
|
766 else if(IsRootDir(aPos)) |
|
767 {//-- root dir. for FAT12/16 is a special case, it is not made of clusters. it's unit is 1 sector. |
|
768 MaxWriteGranularityLog2 = KDefSectorSzLog2; |
|
769 } |
|
770 else |
|
771 {//-- minimal unit size will be a cluster |
|
772 MaxWriteGranularityLog2 = ClusterSizeLog2(); |
|
773 } |
|
774 |
|
775 |
|
776 { |
|
777 const TUint64 KEntrySetStartPos = MakeLinAddrL(aPos); |
|
778 const TUint64 KEntrySetLogicalEndPos = KEntrySetStartPos + (numEntries << KSizeOfFatDirEntryLog2); |
|
779 |
|
780 const TUint64 KBlockEndPos = ((KEntrySetLogicalEndPos-1) >> MaxWriteGranularityLog2) << MaxWriteGranularityLog2; |
|
781 const TUint64 KBlockStartPos = (KEntrySetStartPos >> MaxWriteGranularityLog2) << MaxWriteGranularityLog2; |
|
782 |
|
783 if(KBlockEndPos == KBlockStartPos) |
|
784 {//-- whole entryet is in the same block; the whole entryset erase operation will be atomic for Rugged/non-rugged FAT |
|
785 chunksInfo[0].iEntryPos = aPos; |
|
786 chunksInfo[0].iNumEntries = numEntries; |
|
787 DoEraseEntrySetChunkL(chunksInfo[0]); |
|
788 return; |
|
789 } |
|
790 |
|
791 } |
|
792 |
|
793 //-- the entryset is split on max. 3 parts between units of write granularity (see MaxWriteGranularityLog2). |
|
794 ASSERT(numEntries > 1 && numEntries <= KMaxVFatEntries); |
|
795 |
|
796 TInt cntChunk = 1; //-- there is at least 1 entries chunk |
|
797 TEntrySetChunkInfo* pChunkInfo = chunksInfo; |
|
798 |
|
799 //-- collect information about dir. entry chunks that reside in different units of write granularity |
|
800 for(;;) |
|
801 { |
|
802 TBool movedUnit = EFalse; |
|
803 |
|
804 pChunkInfo->iEntryPos = aPos; |
|
805 pChunkInfo->iNumEntries = 0; |
|
806 |
|
807 const TUint64 KChunkStartPos = MakeLinAddrL(aPos); |
|
808 const TUint64 KChunkBlockStartPos = (KChunkStartPos >> MaxWriteGranularityLog2) << MaxWriteGranularityLog2; |
|
809 const TUint64 KChunkBlockEndPos = (KChunkBlockStartPos-1) + (1<<MaxWriteGranularityLog2); |
|
810 |
|
811 while(numEntries) |
|
812 { |
|
813 pChunkInfo->iNumEntries++; |
|
814 MoveToNextEntryL(aPos); |
|
815 |
|
816 numEntries--; |
|
817 const TUint64 currPos = MakeLinAddrL(aPos); |
|
818 movedUnit = !(currPos >= KChunkBlockStartPos && currPos <= KChunkBlockEndPos); |
|
819 |
|
820 if(!numEntries || movedUnit) |
|
821 { |
|
822 break; |
|
823 } |
|
824 |
|
825 } |
|
826 |
|
827 if(movedUnit && numEntries) |
|
828 {//-- move to the next unit of write granularity |
|
829 ++pChunkInfo; |
|
830 ++cntChunk; |
|
831 ASSERT(cntChunk <= TEntrySetChunkInfo::KMaxChunks); |
|
832 continue; |
|
833 } |
|
834 |
|
835 |
|
836 ASSERT(!numEntries); |
|
837 break; |
|
838 } |
|
839 |
|
840 //-- now do bulk deletion, write data based on collected entries chunks. |
|
841 ASSERT(cntChunk > 0); |
|
842 |
|
843 //-- if it is a rugged FAT, we need to delete DOS entry first; it will be in the last chunk. |
|
844 if(IsRuggedFSys()) |
|
845 { |
|
846 const TInt dosEntryChunk = cntChunk-1; |
|
847 DoEraseEntrySetChunkL(chunksInfo[dosEntryChunk]); |
|
848 cntChunk--; |
|
849 } |
|
850 |
|
851 //-- it is also possible to joint entryset chunks together here if they belong to the same cluster. |
|
852 //-- the atomic write here is not required. |
|
853 |
|
854 //-- erase the rest of entries in reamining chunks. |
|
855 for(TInt i=0; i<cntChunk; ++i) |
|
856 { |
|
857 DoEraseEntrySetChunkL(chunksInfo[i]); |
|
858 } |
|
859 |
|
860 } |
|
861 |
|
862 //--------------------------------------------------------------------------------- |
|
863 /** |
|
864 Convert the volume label using the algorithm specified in the current locale-DLL. |
|
865 */ |
|
866 void LocaleUtils::ConvertFromUnicodeL(TDes8& aForeign, const TDesC16& aUnicode, TFatUtilityFunctions::TOverflowAction aOverflowAction) |
|
867 { |
|
868 if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave) |
|
869 { |
|
870 GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionLeave); |
|
871 } |
649 else |
872 else |
650 WriteDirEntryL(aPos,aFatDirEntry); |
873 { |
651 } |
874 GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionTruncate); |
652 |
875 } |
653 void CFatMountCB::EraseDirEntryL(TEntryPos aPos,const TFatDirEntry& aFirstEntry) |
876 } |
654 // |
877 //--------------------------------------------------------------------------------- |
655 // Mark all entries in a VFat directory entry as erased |
878 /** |
656 // |
879 Convert the volume label using the algorithm specified in the current locale-DLL. |
657 { |
880 */ |
658 __PRINT(_L("VFAT::CFatMountCB::EraseDirEntryL")); |
881 void LocaleUtils::ConvertToUnicodeL(TDes16& aUnicode, const TDesC8& aForeign, TFatUtilityFunctions::TOverflowAction aOverflowAction) |
659 TInt numEntries=0; |
|
660 if (aFirstEntry.IsVFatEntry()) |
|
661 numEntries=aFirstEntry.NumFollowing(); |
|
662 if(IsRuggedFSys()&&numEntries) |
|
663 { |
|
664 TInt count=numEntries; |
|
665 TEntryPos pos=aPos; |
|
666 while(count--) |
|
667 MoveToNextEntryL(pos); |
|
668 EraseDirEntryL(pos); |
|
669 numEntries--; |
|
670 } |
|
671 FOREVER |
|
672 { |
|
673 EraseDirEntryL(aPos); |
|
674 if (!numEntries--) |
|
675 break; |
|
676 MoveToNextEntryL(aPos); |
|
677 } |
|
678 } |
|
679 |
|
680 |
|
681 void LocaleUtils::ConvertFromUnicodeL(TDes8& aForeign, const TDesC16& aUnicode, TFatUtilityFunctions::TOverflowAction aOverflowAction) |
|
682 // |
|
683 // Convert the volume label using the algorithm specified in the current locale-DLL. |
|
684 // |
|
685 { |
882 { |
686 if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave) |
883 if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave) |
687 { |
884 { |
688 GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionLeave); |
885 GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionLeave); |
689 } |
886 } |
690 else |
887 else |
691 { |
888 { |
692 GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionTruncate); |
|
693 } |
|
694 } |
|
695 |
|
696 void LocaleUtils::ConvertToUnicodeL(TDes16& aUnicode, const TDesC8& aForeign, TFatUtilityFunctions::TOverflowAction aOverflowAction) |
|
697 // |
|
698 // Convert the volume label using the algorithm specified in the current locale-DLL. |
|
699 // |
|
700 { |
|
701 if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave) |
|
702 { |
|
703 GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionLeave); |
|
704 } |
|
705 else |
|
706 { |
|
707 GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionTruncate); |
889 GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionTruncate); |
708 } |
890 } |
709 } |
891 } |
710 |
892 |
|
893 //--------------------------------------------------------------------------------- |
|
894 /** |
|
895 Convert the volume label using the algorithm specified in the current locale-DLL. |
|
896 */ |
711 TBool LocaleUtils::IsLegalShortNameCharacter(TUint aCharacter,TBool aUseExtendedChars) |
897 TBool LocaleUtils::IsLegalShortNameCharacter(TUint aCharacter,TBool aUseExtendedChars) |
712 // |
|
713 // Convert the volume label using the algorithm specified in the current locale-DLL. |
|
714 // |
|
715 { |
898 { |
716 return GetCodePage().IsLegalShortNameCharacter(aCharacter, aUseExtendedChars); |
899 return GetCodePage().IsLegalShortNameCharacter(aCharacter, aUseExtendedChars); |
717 } |
900 } |
|
901 |
|
902 |
|
903 |
|
904 |
|
905 |
|
906 |
|
907 |
|
908 |
|
909 |