--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/imgtools/romtools/rofsbuild/fsnode.cpp Fri Jun 25 20:58:33 2010 +0800
@@ -0,0 +1,518 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+#include "fsnode.h"
+#include "fatdefines.h"
+#include "utf16string.h"
+#include <stdio.h>
+#include <iostream>
+#include <iomanip>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <ctype.h>
+
+
+#ifdef __LINUX__
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#define SPLIT_CHAR '/'
+#else
+#include <io.h>
+#include <direct.h> //TODO: check under MinGW4 + stlport 5.2
+#include <conio.h>
+#define SPLIT_CHAR '\\'
+#endif
+
+using namespace std;
+
+const TUint KBytesPerEntry = 13 ;
+//
+TFSNode::TFSNode(TFSNode* aParent, const char* aFileName, TUint8 aAttrs, const char* aPCSideName) :
+iParent(aParent),iFirstChild(0),iSibling(0),iAttrs(aAttrs), iPCSideName(0), iWideName(0){
+
+ // According to the FAT specification, short name should be inited with empty string (' ' string)
+ memset(iShortName,0x20,11);
+ iShortName[11] = 0 ;
+ if(aFileName) {
+ iFileName = strdup(aFileName);
+ GenerateBasicName() ;
+ }
+ if(aPCSideName) {
+ iPCSideName = strdup(aPCSideName);
+ }
+ iFATEntry = 0;
+ iCrtTimeTenth = 0;
+ iCrtTime.iImageTime = 0 ;
+ iCrtDate.iImageDate = 0 ;
+ iLstAccDate.iImageDate = 0 ;
+ iWrtTime.iImageTime = 0 ;
+ iWrtDate.iImageDate = 0 ;
+ iFileSize = 0;
+ if(!iParent) return ;
+
+ if(!iParent->iFirstChild)
+ iParent->iFirstChild = this ;
+ else {
+ TFSNode* sibling = iParent->iFirstChild;
+ while(sibling->iSibling)
+ sibling = sibling->iSibling ;
+ sibling->iSibling = this ;
+ }
+
+}
+TFSNode::~TFSNode(){
+ if(iFirstChild)
+ delete iFirstChild ;
+ if(iSibling)
+ delete iSibling ;
+ if(iFileName)
+ free(iFileName) ;
+ if(iWideName)
+ delete iWideName;
+}
+TFSNode* TFSNode::CreateFromFolder(const char* aPath,TFSNode* aParent) {
+ static char fileName[2048];
+ int len = strlen(aPath);
+#ifdef __LINUX__
+ DIR* dir = opendir(aPath);
+ if(dir == NULL) {
+ cout << aPath << " does not contain any subfolder/file.\n";
+ return aParent;
+ }
+ if(!aParent)
+ aParent = new TFSNode(NULL,"/",ATTR_VOLUME_ID);
+ dirent* entry;
+ struct stat statbuf ;
+ while ((entry = readdir(dir)) != NULL) {
+ if(entry->d_name[0] == '.') continue ;
+ memcpy(fileName,aPath,len);
+ fileName[len] = SPLIT_CHAR;
+ strcpy(&fileName[len+1],entry->d_name);
+ stat(fileName , &statbuf);
+ TFSNode* pNewItem = new TFSNode(aParent,fileName,S_ISDIR(statbuf.st_mode) ? ATTR_DIRECTORY : 0);
+ pNewItem->Init(statbuf.st_ctime,statbuf.st_atime,statbuf.st_mtime,statbuf.st_size);
+ if(S_ISDIR(statbuf.st_mode)){
+ CreateFromFolder(fileName,pNewItem);
+ }
+ }
+ closedir(dir);
+#else
+ struct _finddata_t data ;
+ memset(&data, 0, sizeof(data));
+ char* pattern = new char[len + 4] ;
+ memcpy(pattern,aPath,len);
+ pattern[len] = SPLIT_CHAR;
+ pattern[len+1] = '*';
+ pattern[len+2] = 0;
+
+ intptr_t hFind = _findfirst(pattern,&data);
+ delete []pattern ;
+
+ if(hFind == (intptr_t)-1 ) {
+ cout << aPath << " does not contain any subfolder/file.\n";
+ return aParent;
+ }
+ if(!aParent)
+ aParent = new TFSNode(NULL,"/",ATTR_VOLUME_ID);
+ do {
+ if(data.name[0] == '.')
+ continue ;
+ memcpy(fileName,aPath,len);
+ fileName[len] = SPLIT_CHAR;
+ strcpy(&fileName[len+1],data.name);
+ TUint8 attr = 0;
+ if(data.attrib & _A_SUBDIR)
+ attr |= ATTR_DIRECTORY;
+ if(data.attrib & _A_RDONLY)
+ attr |= ATTR_READ_ONLY ;
+ if(data.attrib & _A_HIDDEN)
+ attr |= ATTR_HIDDEN ;
+ if(data.attrib & _A_SYSTEM)
+ attr |= ATTR_SYSTEM ;
+ if(data.attrib & _A_ARCH)
+ attr |= ATTR_ARCHIVE;
+ TFSNode* pNewItem = new TFSNode(aParent,fileName,attr);
+ pNewItem->Init(data.time_create,data.time_access,data.time_write,data.size);
+ if(data.attrib & _A_SUBDIR){
+ CreateFromFolder(fileName,pNewItem);
+ }
+
+ } while(-1 != _findnext(hFind, &data));
+ _findclose(hFind);
+#endif
+
+ return aParent;
+}
+
+
+
+static const char* lbasename(const char* aFullName) {
+ const char* retval = aFullName ;
+ while(*aFullName) {
+ if('\\' == *aFullName || '/' == *aFullName )
+ retval = ++aFullName ;
+ else
+ aFullName ++ ;
+ }
+ return retval ;
+}
+/** GenerateBasicName : Generate the short name according to long name
+ *
+ * algorithm :
+ *
+ * 1. The UNICODE name passed to the file system is converted to upper case.
+ * 2. The upper cased UNICODE name is converted to OEM.
+ * if (the uppercased UNICODE glyph does not exist as an OEM glyph in the OEM code page)
+ * or (the OEM glyph is invalid in an 8.3 name)
+ * {
+ * Replace the glyph to an OEM '_' (underscore) character.
+ * Set a "lossy conversion" flag.
+ * }
+ * 3. Strip all leading and embedded spaces from the long name.
+ * 4. Strip all leading periods from the long name.
+ * 5. While (not at end of the long name)
+ * and (char is not a period)
+ * and (total chars copied < 8)
+ * {
+ * Copy characters into primary portion of the basis name
+ * }
+ * 6. Insert a dot at the end of the primary components of the basis-name
+ * if the basis name has an extension after the last period in the name.
+ *
+ * 7. Scan for the last embedded period in the long name.
+ * If (the last embedded period was found)
+ * {
+ * While (not at end of the long name) and (total chars copied < 3)
+ * {
+ * Copy characters into extension portion of the basis name
+ * }
+ * }
+ *
+ */
+void TFSNode::GenerateBasicName() {
+ const char* filename = lbasename(iFileName);
+ TUint length = strlen(filename);
+ if(0 == length)
+ return ;
+ if(0 == strcmp(filename,".")){
+ iShortName[0] = '.' ;
+ return ;
+ }
+ if(0 == strcmp(filename,"..")){
+ iShortName[0] = '.' ;
+ iShortName[1] = '.' ;
+ return ;
+ }
+#ifdef _DEBUG
+ cout << "GenericBasicName: \"" << filename ;
+#endif
+ iWideName = new UTF16String(filename,length); // The unicode string
+ char base[10];
+ const char* ext = filename + length;
+
+ //Strip all leading periods and spaces from the long name.
+ while(*filename == '.' || *filename == ' ' || *filename == '\t') {
+ filename ++ ;
+ length -- ;
+ }
+ //find the extension
+ while(ext > filename && *ext != '.')
+ ext -- ;
+ if(ext == filename){
+ ext = "" ;
+ }
+ else {
+ length = ext - filename;
+ ext ++ ;
+ }
+ bool lossyConv = false ;
+ TUint bl = 0;
+ for(TUint i = 0 ; i < length ; i++) {
+ if(filename[i] >= 'a' && filename[i] <= 'z')
+ base[bl++] = filename[i] + 'A' - 'a';
+ else if(filename[i] >= 'A' && filename[i] <= 'Z')
+ base[bl++] = filename[i];
+ else if(filename[i] == '$' || filename[i] == '%' ||
+ filename[i] == '-' || filename[i] == '_' || filename[i] == '@' ||
+ filename[i] == '~' || filename[i] == '`' || filename[i] == '!' ||
+ filename[i] == '(' || filename[i] == ')' || filename[i] == '{' ||
+ filename[i] == '}' || filename[i] == '^' || filename[i] == '#' ||
+ filename[i] == '&' ||filename[i] == '\'')
+ base[bl++] = filename[i];
+ else if(filename[i] != ' ' && filename[i] != '.'){
+ base[bl++] = '_';
+ lossyConv = true ;
+ }
+ if(bl > 8){
+ bl -- ;
+ lossyConv = true ;
+ break ;
+ }
+ }
+ if(lossyConv){
+ if(bl > 6) bl = 6 ;
+ iShortName[bl] = '~';
+ iShortName[bl+1] = '1';
+ }
+ memcpy(iShortName,base,bl);
+
+ //Copy the extension part.
+ TUint ei = 8;
+ for(TUint e = 0; ei < 11 && ext[e] != 0 ; e++){
+ if(ext[e] >= 'a' && ext[e] <= 'z')
+ iShortName[ei++] = ext[e] + 'A' - 'a';
+ else if(ext[e] >= 'A' && ext[e] <= 'Z')
+ iShortName[ei++] = ext[e] ;
+ else if(ext[e] == '$' || ext[e] == '%' || ext[e] == '-' || ext[e] == '_' ||
+ ext[e] == '@' || ext[e] == '~' || ext[e] == '`' || ext[e] == '!' ||
+ ext[e] == '(' || ext[e] == ')' || ext[e] == '{' || ext[e] == '}' ||
+ ext[e] == '^' || ext[e] == '#' || ext[e] == '&' ||ext[e] == '\'')
+ iShortName[ei++] = ext[e] ;
+ }
+
+ if(iParent)
+ iParent->MakeUniqueShortName(iShortName,bl);
+#ifdef _DEBUG
+ cout << "\" => \"" << iShortName << "\"\n";
+#endif
+}
+
+#ifdef _DEBUG
+void TFSNode::PrintTree(int nTab) {
+ for( int i = 0 ; i < nTab ; i++ )
+ cout << " " ;
+ cout << (iFileName ? iFileName : "") << " [" << hex << setw(2) << setfill('0') << (unsigned short)iAttrs << "] \n" ;
+ if(iFirstChild)
+ iFirstChild->PrintTree(nTab + 2);
+ if(iSibling)
+ iSibling->PrintTree(nTab);
+}
+#endif
+bool TFSNode::IsDirectory() const {
+ return 0 != (iAttrs & ATTR_DIRECTORY);
+}
+int TFSNode::GetWideNameLength() const {
+ if(!iWideName)
+ return 0 ;
+ return iWideName->length() ;
+}
+TUint TFSNode::GetSize() const {
+
+ if( 0 == (iAttrs & ATTR_DIRECTORY))
+ return iFileSize ;
+ TUint retVal = sizeof(TShortDirEntry) ; // the tailed entry
+ if(iParent)
+ retVal += sizeof(TShortDirEntry) * 2 ;
+ TFSNode* child = iFirstChild ;
+ while(child) {
+ TUint longNameEntries = (child->GetWideNameLength() + KBytesPerEntry) / KBytesPerEntry ;
+ retVal += longNameEntries * sizeof(TLongDirEntry) ;
+ retVal += sizeof(TShortDirEntry);
+ child = child->iSibling ;
+ }
+ return retVal ;
+}
+
+void TFSNode::Init(time_t aCreateTime, time_t aAccessTime, time_t aWriteTime, TUint aSize ) {
+
+ struct tm* temp = localtime(&aCreateTime);
+ iCrtDate.iCurrentDate.Day = temp->tm_mday;
+ iCrtDate.iCurrentDate.Month = temp->tm_mon+1; //As per FAT spec
+ iCrtDate.iCurrentDate.Year = temp->tm_year - 80;//As per FAT spec
+ iCrtTime.iCurrentTime.Hour = temp->tm_hour;
+ iCrtTime.iCurrentTime.Minute = temp->tm_min;
+ iCrtTime.iCurrentTime.Seconds = temp->tm_sec / 2;//As per FAT spec
+ iCrtTimeTenth = 0;
+
+ temp = localtime(&aAccessTime);
+ iLstAccDate.iCurrentDate.Day = temp->tm_mday;
+ iLstAccDate.iCurrentDate.Month = temp->tm_mon+1; //As per FAT spec
+ iLstAccDate.iCurrentDate.Year = temp->tm_year - 80;//As per FAT spec
+
+ temp = localtime(&aWriteTime);
+ iWrtDate.iCurrentDate.Day = temp->tm_mday;
+ iWrtDate.iCurrentDate.Month = temp->tm_mon+1; //As per FAT spec
+ iWrtDate.iCurrentDate.Year = temp->tm_year - 80;//As per FAT spec
+ iWrtTime.iCurrentTime.Hour = temp->tm_hour;
+ iWrtTime.iCurrentTime.Minute = temp->tm_min;
+ iWrtTime.iCurrentTime.Seconds = temp->tm_sec / 2;//As per FAT spec
+
+ iFileSize = aSize ;
+}
+/** WriteDirEntries : Write FAT information for this node to a cluster buffer
+ * aStartIndex : [in],the beginning index of the outputed cluster
+ * aClusterData : [in,out] the cluster buffer
+ *
+ * notice, aClusterData is only required if node is a directory node.
+ * for a file node, no data will be written out.
+ * in this case, only corresponding cluster index information is updated.
+ */
+void TFSNode::WriteDirEntries(TUint aStartIndex,TUint8* aClusterData){
+ if(iFATEntry){
+ *((TUint16*)iFATEntry->DIR_FstClusHI) = (aStartIndex >> 16) ;
+ *((TUint16*)iFATEntry->DIR_FstClusLO) = (aStartIndex & 0xFFFF) ;
+ }
+
+ if(iAttrs & ATTR_DIRECTORY) { // Directory , write dir entries ;
+ TShortDirEntry* entry = reinterpret_cast<TShortDirEntry*>(aClusterData);
+ if(iParent != NULL) {
+ //Make
+ GetShortEntry(entry);
+ //TODO: Add comments to avoid mistaken deleting.
+ memcpy(entry->DIR_Name,". ",sizeof(entry->DIR_Name));
+ entry ++ ;
+ iParent->GetShortEntry(entry);
+ memcpy(entry->DIR_Name,".. ",sizeof(entry->DIR_Name));
+ entry ++ ;
+ }
+ TFSNode* child = iFirstChild ;
+ while(child){
+ int items = child->GetLongEntries(reinterpret_cast<TLongDirEntry*>(entry));
+ entry += items ;
+ child->GetShortEntry(entry);
+ child->iFATEntry = entry ;
+ entry ++ ;
+ child = child->iSibling ;
+
+ }
+
+ }
+}
+/** GetShortEntry : Make a short directory entry (FAT16/32 conception)
+ * aEntry : the entry buffer
+ */
+void TFSNode::GetShortEntry(TShortDirEntry* aEntry) {
+ if(!aEntry) return ;
+ if(iFATEntry){
+ if(iFATEntry != aEntry)
+ memcpy(aEntry,iFATEntry,sizeof(TShortDirEntry));
+ return ;
+ }
+ memcpy(aEntry->DIR_Name,iShortName,sizeof(aEntry->DIR_Name));
+ aEntry->DIR_Attr = iAttrs;
+ aEntry->DIR_NTRes = 0 ;
+ aEntry->DIR_CrtTimeTenth = 0 ;
+ memcpy(aEntry->DIR_CrtTime,&iCrtTime,sizeof(aEntry->DIR_CrtTime));
+ memcpy(aEntry->DIR_CrtDate,&iCrtDate,sizeof(aEntry->DIR_CrtDate));
+ memcpy(aEntry->DIR_LstAccDate,&iLstAccDate,sizeof(aEntry->DIR_LstAccDate));
+ memset(aEntry->DIR_FstClusHI,0,sizeof(aEntry->DIR_FstClusHI));
+ memcpy(aEntry->DIR_WrtTime,&iWrtTime,sizeof(aEntry->DIR_WrtTime));
+ memcpy(aEntry->DIR_WrtDate,&iWrtDate,sizeof(aEntry->DIR_WrtDate));
+ memset(aEntry->DIR_FstClusLO,0,sizeof(aEntry->DIR_FstClusLO));
+ memcpy(aEntry->DIR_FileSize,&iFileSize,sizeof(aEntry->DIR_FileSize));
+}
+TUint8 FATChkSum(const char* pFcbName) {
+ short fcbNameLen ;
+ TUint8 sum = 0 ;
+ for(fcbNameLen = 11 ; fcbNameLen != 0 ; fcbNameLen --) {
+ sum = ((sum & 1) ? 0x80 : 0 ) + (sum >> 1 ) + *pFcbName++ ;
+ }
+ return sum ;
+}
+/** GetLongEntries : Make a series of long directory entries (FAT16/32 conception)
+ * aEntries : the start addr of the long directory entries buffer
+ *
+ * return value : actual entris count.
+ */
+int TFSNode::GetLongEntries(TLongDirEntry* aEntries) {
+
+ if(!aEntries) return 0;
+ int packs = (GetWideNameLength() + KBytesPerEntry) / KBytesPerEntry ;
+
+ TUint buflen = packs * KBytesPerEntry;
+ TUint16* buffer = new(std::nothrow) TUint16[buflen];
+ if(!buffer)
+ return 0 ;
+ memset(buffer,0xff,(buflen << 1));
+ if(iWideName) {
+ memcpy(buffer,iWideName->c_str(),iWideName->bytes());
+ buffer[iWideName->length()] = 0;
+ }
+ TUint8 chkSum = FATChkSum(iShortName);;
+
+ TUint16* ptr = buffer ;
+ TLongDirEntry* entry = aEntries +(packs - 1);
+ for(int i = 1 ; i <= packs ; i++, entry--) {
+ entry->LDIR_Ord = i ;
+ entry->LDIR_Chksum = chkSum ;
+ entry->LDIR_Attr = (TUint8)ATTR_LONG_NAME;
+ *((TUint16*)(entry->LDIR_FstClusLO)) = 0;
+ entry->LDIR_Type = 0;
+ memcpy(entry->LDIR_Name1,ptr,10);
+ memcpy(entry->LDIR_Name2,&ptr[5],12);
+ memcpy(entry->LDIR_Name3,&ptr[11],4);
+ ptr += 13;
+ }
+ aEntries->LDIR_Ord |= 0x40 ;
+
+ delete []buffer ;
+ return packs ;
+}
+/** Make a unique name for a new child which has not been added.
+ * to avoid same short names under a directory
+ * rShortName : [in,out] , The new short name to be checked and changed.
+ * baseNameLength: [in], the length of the base part of the short name
+ * not including the "~n"
+ * for example,
+ * "ABC.LOG" => baseNameLength == 3 ("ABC")
+ * "AB~1.TXT" => baseNameLength == 2 ("AB")
+ *
+ *
+ *The Numeric-Tail Generation Algorithm
+
+ * If (a "lossy conversion" was not flagged)
+ * and (the long name fits within the 8.3 naming conventions)
+ * and (the basis-name does not collide with any existing short name)
+ * {
+ * The short name is only the basis-name without the numeric tail.
+ * }
+ * else {
+ * Insert a numeric-tail "~n" to the end of the primary name such that the value of
+ * the "~n" is chosen so that the name thus formed does not collide with
+ * any existing short name and that the primary name does not exceed eight
+ * characters in length.
+ * }
+ * The "~n" string can range from "~1" to "~999999".
+ *
+ */
+
+void TFSNode::MakeUniqueShortName(char rShortName[12],TUint baseNameLength) const {
+ bool dup ;
+ char nstring[10];
+ int n = 0 ;
+ do {
+ TFSNode* child = iFirstChild ;
+ dup = false ;
+ while(child){
+ if(0 == memcmp(rShortName,child->iShortName,11)) {
+ dup = true ;
+ break ;
+ }
+ child = child->iSibling ;
+ }
+ if(dup){ //duplex , increase the index , make a new name
+ int nlen = sprintf(nstring,"~%u",++n);
+ while((baseNameLength + nlen > 8) && baseNameLength > 1)
+ baseNameLength -- ;
+ memcpy(&rShortName[baseNameLength],nstring,nlen);
+
+ }
+ }while(dup) ;
+
+}
+