Home | History | Annotate | Download | only in EnhancedFatDxe
      1 /** @file
      2   Functions for manipulating file names.
      3 
      4 Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials are licensed and made available
      6 under the terms and conditions of the BSD License which accompanies this
      7 distribution. The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "Fat.h"
     16 
     17 /**
     18 
     19   This function checks whether the input FileName is a valid 8.3 short name.
     20   If the input FileName is a valid 8.3, the output is the 8.3 short name;
     21   otherwise, the output is the base tag of 8.3 short name.
     22 
     23   @param  FileName              - The input unicode filename.
     24   @param  File8Dot3Name         - The output ascii 8.3 short name or base tag of 8.3 short name.
     25 
     26   @retval TRUE                  - The input unicode filename is a valid 8.3 short name.
     27   @retval FALSE                 - The input unicode filename is not a valid 8.3 short name.
     28 
     29 **/
     30 BOOLEAN
     31 FatCheckIs8Dot3Name (
     32   IN  CHAR16    *FileName,
     33   OUT CHAR8     *File8Dot3Name
     34   )
     35 {
     36   BOOLEAN PossibleShortName;
     37   CHAR16  *TempName;
     38   CHAR16  *ExtendName;
     39   CHAR16  *SeparateDot;
     40   UINTN   MainNameLen;
     41   UINTN   ExtendNameLen;
     42 
     43   PossibleShortName = TRUE;
     44   SeparateDot       = NULL;
     45   SetMem (File8Dot3Name, FAT_NAME_LEN, ' ');
     46   for (TempName = FileName; *TempName != '\0'; TempName++) {
     47     if (*TempName == L'.') {
     48       SeparateDot = TempName;
     49     }
     50   }
     51 
     52   if (SeparateDot == NULL) {
     53     //
     54     // Extended filename is not detected
     55     //
     56     MainNameLen   = TempName - FileName;
     57     ExtendName    = TempName;
     58     ExtendNameLen = 0;
     59   } else {
     60     //
     61     // Extended filename is detected
     62     //
     63     MainNameLen   = SeparateDot - FileName;
     64     ExtendName    = SeparateDot + 1;
     65     ExtendNameLen = TempName - ExtendName;
     66   }
     67   //
     68   // We scan the filename for the second time
     69   // to check if there exists any extra blanks and dots
     70   //
     71   while (--TempName >= FileName) {
     72     if ((*TempName == L'.' || *TempName == L' ') && (TempName != SeparateDot)) {
     73       //
     74       // There exist extra blanks and dots
     75       //
     76       PossibleShortName = FALSE;
     77     }
     78   }
     79 
     80   if (MainNameLen == 0) {
     81     PossibleShortName = FALSE;
     82   }
     83 
     84   if (MainNameLen > FAT_MAIN_NAME_LEN) {
     85     PossibleShortName = FALSE;
     86     MainNameLen       = FAT_MAIN_NAME_LEN;
     87   }
     88 
     89   if (ExtendNameLen > FAT_EXTEND_NAME_LEN) {
     90     PossibleShortName = FALSE;
     91     ExtendNameLen     = FAT_EXTEND_NAME_LEN;
     92   }
     93 
     94   if (FatStrToFat (FileName, MainNameLen, File8Dot3Name)) {
     95     PossibleShortName = FALSE;
     96   }
     97 
     98   if (FatStrToFat (ExtendName, ExtendNameLen, File8Dot3Name + FAT_MAIN_NAME_LEN)) {
     99     PossibleShortName = FALSE;
    100   }
    101 
    102   return PossibleShortName;
    103 }
    104 
    105 /**
    106 
    107   Trim the trailing blanks of fat name.
    108 
    109   @param  Name                  - The Char8 string needs to be trimed.
    110   @param  Len                   - The length of the fat name.
    111 
    112   The real length of the fat name after the trailing blanks are trimmed.
    113 
    114 **/
    115 STATIC
    116 UINTN
    117 FatTrimAsciiTrailingBlanks (
    118   IN CHAR8        *Name,
    119   IN UINTN        Len
    120   )
    121 {
    122   while (Len > 0 && Name[Len - 1] == ' ') {
    123     Len--;
    124   }
    125 
    126   return Len;
    127 }
    128 
    129 /**
    130 
    131   Convert the ascii fat name to the unicode string and strip trailing spaces,
    132   and if necessary, convert the unicode string to lower case.
    133 
    134   @param  FatName               - The Char8 string needs to be converted.
    135   @param  Len                   - The length of the fat name.
    136   @param  LowerCase             - Indicate whether to convert the string to lower case.
    137   @param  Str                   - The result of the convertion.
    138 
    139 **/
    140 VOID
    141 FatNameToStr (
    142   IN  CHAR8            *FatName,
    143   IN  UINTN            Len,
    144   IN  UINTN            LowerCase,
    145   OUT CHAR16           *Str
    146   )
    147 {
    148   //
    149   // First, trim the trailing blanks
    150   //
    151   Len = FatTrimAsciiTrailingBlanks (FatName, Len);
    152   //
    153   // Convert fat string to unicode string
    154   //
    155   FatFatToStr (Len, FatName, Str);
    156 
    157   //
    158   // If the name is to be lower cased, do it now
    159   //
    160   if (LowerCase != 0) {
    161     FatStrLwr (Str);
    162   }
    163 }
    164 
    165 /**
    166 
    167   This function generates 8Dot3 name from user specified name for a newly created file.
    168 
    169   @param  Parent                - The parent directory.
    170   @param  DirEnt                - The directory entry whose 8Dot3Name needs to be generated.
    171 
    172 **/
    173 VOID
    174 FatCreate8Dot3Name (
    175   IN FAT_OFILE    *Parent,
    176   IN FAT_DIRENT   *DirEnt
    177   )
    178 {
    179   CHAR8 *ShortName;
    180   CHAR8 *ShortNameChar;
    181   UINTN BaseTagLen;
    182   UINTN Index;
    183   UINTN Retry;
    184   UINT8 Segment;
    185   union {
    186     UINT32  Crc;
    187     struct HEX_DATA {
    188       UINT8 Segment : HASH_VALUE_TAG_LEN;
    189     } Hex[HASH_VALUE_TAG_LEN];
    190   } HashValue;
    191   //
    192   // Make sure the whole directory has been loaded
    193   //
    194   ASSERT (Parent->ODir->EndOfDir);
    195   ShortName = DirEnt->Entry.FileName;
    196 
    197   //
    198   // Trim trailing blanks of 8.3 name
    199   //
    200   BaseTagLen = FatTrimAsciiTrailingBlanks (ShortName, FAT_MAIN_NAME_LEN);
    201   if (BaseTagLen > SPEC_BASE_TAG_LEN) {
    202     BaseTagLen = SPEC_BASE_TAG_LEN;
    203   }
    204   //
    205   // We first use the algorithm described by spec.
    206   //
    207   ShortNameChar     = ShortName + BaseTagLen;
    208   *ShortNameChar++  = '~';
    209   *ShortNameChar    = '1';
    210   Retry = 0;
    211   while (*FatShortNameHashSearch (Parent->ODir, ShortName) != NULL) {
    212     *ShortNameChar = (CHAR8)(*ShortNameChar + 1);
    213     if (++Retry == MAX_SPEC_RETRY) {
    214       //
    215       // We use new algorithm to generate 8.3 name
    216       //
    217       ASSERT (DirEnt->FileString != NULL);
    218       gBS->CalculateCrc32 (DirEnt->FileString, StrSize (DirEnt->FileString), &HashValue.Crc);
    219 
    220       if (BaseTagLen > HASH_BASE_TAG_LEN) {
    221         BaseTagLen = HASH_BASE_TAG_LEN;
    222       }
    223 
    224       ShortNameChar = ShortName + BaseTagLen;
    225       for (Index = 0; Index < HASH_VALUE_TAG_LEN; Index++) {
    226         Segment = HashValue.Hex[Index].Segment;
    227         if (Segment > 9) {
    228           *ShortNameChar++ = (CHAR8)(Segment - 10 + 'A');
    229         } else {
    230           *ShortNameChar++ = (CHAR8)(Segment + '0');
    231         }
    232       }
    233 
    234       *ShortNameChar++  = '~';
    235       *ShortNameChar    = '1';
    236     }
    237   }
    238 }
    239 
    240 /**
    241 
    242   Check the string is lower case or upper case
    243   and it is used by fatname to dir entry count
    244 
    245   @param Str                   - The string which needs to be checked.
    246   @param InCaseFlag            - The input case flag which is returned when the string is lower case.
    247 
    248   @retval OutCaseFlag           - The output case flag.
    249 
    250 **/
    251 STATIC
    252 UINT8
    253 FatCheckNameCase (
    254   IN CHAR16           *Str,
    255   IN UINT8            InCaseFlag
    256   )
    257 {
    258   CHAR16  Buffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1];
    259   UINT8   OutCaseFlag;
    260 
    261   //
    262   // Assume the case of input string is mixed
    263   //
    264   OutCaseFlag = FAT_CASE_MIXED;
    265   //
    266   // Lower case a copy of the string, if it matches the
    267   // original then the string is lower case
    268   //
    269   StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str);
    270   FatStrLwr (Buffer);
    271   if (StrCmp (Str, Buffer) == 0) {
    272     OutCaseFlag = InCaseFlag;
    273   }
    274   //
    275   // Upper case a copy of the string, if it matches the
    276   // original then the string is upper case
    277   //
    278   StrCpyS (Buffer, ARRAY_SIZE (Buffer), Str);
    279   FatStrUpr (Buffer);
    280   if (StrCmp (Str, Buffer) == 0) {
    281     OutCaseFlag = 0;
    282   }
    283 
    284   return OutCaseFlag;
    285 }
    286 
    287 /**
    288 
    289   Set the caseflag value for the directory entry.
    290 
    291   @param DirEnt                - The logical directory entry whose caseflag value is to be set.
    292 
    293 **/
    294 VOID
    295 FatSetCaseFlag (
    296   IN FAT_DIRENT   *DirEnt
    297   )
    298 {
    299   CHAR16  LfnBuffer[FAT_MAIN_NAME_LEN + 1 + FAT_EXTEND_NAME_LEN + 1];
    300   CHAR16  *TempCharPtr;
    301   CHAR16  *ExtendName;
    302   CHAR16  *FileNameCharPtr;
    303   UINT8   CaseFlag;
    304 
    305   ExtendName      = NULL;
    306   TempCharPtr     = LfnBuffer;
    307   FileNameCharPtr = DirEnt->FileString;
    308   ASSERT (StrSize (DirEnt->FileString) <= sizeof (LfnBuffer));
    309   while ((*TempCharPtr = *FileNameCharPtr) != 0) {
    310     if (*TempCharPtr == L'.') {
    311       ExtendName = TempCharPtr;
    312     }
    313 
    314     TempCharPtr++;
    315     FileNameCharPtr++;
    316   }
    317 
    318   CaseFlag = 0;
    319   if (ExtendName != NULL) {
    320     *ExtendName = 0;
    321     ExtendName++;
    322     CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (ExtendName, FAT_CASE_EXT_LOWER));
    323   }
    324 
    325   CaseFlag = (UINT8)(CaseFlag | FatCheckNameCase (LfnBuffer, FAT_CASE_NAME_LOWER));
    326   if ((CaseFlag & FAT_CASE_MIXED) == 0) {
    327     //
    328     // We just need one directory entry to store this file name entry
    329     //
    330     DirEnt->Entry.CaseFlag = CaseFlag;
    331   } else {
    332     //
    333     // We need one extra directory entry to store the mixed case entry
    334     //
    335     DirEnt->Entry.CaseFlag = 0;
    336     DirEnt->EntryCount++;
    337   }
    338 }
    339 
    340 /**
    341 
    342   Convert the 8.3 ASCII fat name to cased Unicode string according to case flag.
    343 
    344   @param  DirEnt                - The corresponding directory entry.
    345   @param  FileString            - The output Unicode file name.
    346   @param  FileStringMax           The max length of FileString.
    347 
    348 **/
    349 VOID
    350 FatGetFileNameViaCaseFlag (
    351   IN     FAT_DIRENT   *DirEnt,
    352   IN OUT CHAR16       *FileString,
    353   IN     UINTN        FileStringMax
    354   )
    355 {
    356   UINT8   CaseFlag;
    357   CHAR8   *File8Dot3Name;
    358   CHAR16  TempExt[1 + FAT_EXTEND_NAME_LEN + 1];
    359   //
    360   // Store file extension like ".txt"
    361   //
    362   CaseFlag      = DirEnt->Entry.CaseFlag;
    363   File8Dot3Name = DirEnt->Entry.FileName;
    364 
    365   FatNameToStr (File8Dot3Name, FAT_MAIN_NAME_LEN, CaseFlag & FAT_CASE_NAME_LOWER, FileString);
    366   FatNameToStr (File8Dot3Name + FAT_MAIN_NAME_LEN, FAT_EXTEND_NAME_LEN, CaseFlag & FAT_CASE_EXT_LOWER, &TempExt[1]);
    367   if (TempExt[1] != 0) {
    368     TempExt[0] = L'.';
    369     StrCatS (FileString, FileStringMax, TempExt);
    370   }
    371 }
    372 
    373 /**
    374 
    375   Get the Check sum for a short name.
    376 
    377   @param  ShortNameString       - The short name for a file.
    378 
    379   @retval Sum                   - UINT8 checksum.
    380 
    381 **/
    382 UINT8
    383 FatCheckSum (
    384   IN CHAR8  *ShortNameString
    385   )
    386 {
    387   UINTN ShortNameLen;
    388   UINT8 Sum;
    389   Sum = 0;
    390   for (ShortNameLen = FAT_NAME_LEN; ShortNameLen != 0; ShortNameLen--) {
    391     Sum = (UINT8)((((Sum & 1) != 0) ? 0x80 : 0) + (Sum >> 1) + *ShortNameString++);
    392   }
    393 
    394   return Sum;
    395 }
    396 
    397 /**
    398 
    399   Takes Path as input, returns the next name component
    400   in Name, and returns the position after Name (e.g., the
    401   start of the next name component)
    402 
    403   @param  Path                  - The path of one file.
    404   @param  Name                  - The next name component in Path.
    405 
    406   The position after Name in the Path
    407 
    408 **/
    409 CHAR16 *
    410 FatGetNextNameComponent (
    411   IN  CHAR16      *Path,
    412   OUT CHAR16      *Name
    413   )
    414 {
    415   while (*Path != 0 && *Path != PATH_NAME_SEPARATOR) {
    416     *Name++ = *Path++;
    417   }
    418   *Name = 0;
    419   //
    420   // Get off of trailing path name separator
    421   //
    422   while (*Path == PATH_NAME_SEPARATOR) {
    423     Path++;
    424   }
    425 
    426   return Path;
    427 }
    428 
    429 /**
    430 
    431   Check whether the IFileName is valid long file name. If the IFileName is a valid
    432   long file name, then we trim the possible leading blanks and leading/trailing dots.
    433   the trimmed filename is stored in OutputFileName
    434 
    435   @param  InputFileName         - The input file name.
    436   @param  OutputFileName        - The output file name.
    437 
    438   @retval TRUE                  - The InputFileName is a valid long file name.
    439   @retval FALSE                 - The InputFileName is not a valid long file name.
    440 
    441 **/
    442 BOOLEAN
    443 FatFileNameIsValid (
    444   IN  CHAR16  *InputFileName,
    445   OUT CHAR16  *OutputFileName
    446   )
    447 {
    448   CHAR16  *TempNamePointer;
    449   CHAR16  TempChar;
    450   //
    451   // Trim Leading blanks
    452   //
    453   while (*InputFileName == L' ') {
    454     InputFileName++;
    455   }
    456 
    457   TempNamePointer = OutputFileName;
    458   while (*InputFileName != 0) {
    459     *TempNamePointer++ = *InputFileName++;
    460   }
    461   //
    462   // Trim Trailing blanks and dots
    463   //
    464   while (TempNamePointer > OutputFileName) {
    465     TempChar = *(TempNamePointer - 1);
    466     if (TempChar != L' ' && TempChar != L'.') {
    467       break;
    468     }
    469 
    470     TempNamePointer--;
    471   }
    472 
    473   *TempNamePointer = 0;
    474 
    475   //
    476   // Per FAT Spec the file name should meet the following criteria:
    477   //   C1. Length (FileLongName) <= 255
    478   //   C2. Length (X:FileFullPath<NUL>) <= 260
    479   // Here we check C1.
    480   //
    481   if (TempNamePointer - OutputFileName > EFI_FILE_STRING_LENGTH) {
    482     return FALSE;
    483   }
    484   //
    485   // See if there is any illegal characters within the name
    486   //
    487   do {
    488     if (*OutputFileName < 0x20 ||
    489         *OutputFileName == '\"' ||
    490         *OutputFileName == '*' ||
    491         *OutputFileName == '/' ||
    492         *OutputFileName == ':' ||
    493         *OutputFileName == '<' ||
    494         *OutputFileName == '>' ||
    495         *OutputFileName == '?' ||
    496         *OutputFileName == '\\' ||
    497         *OutputFileName == '|'
    498         ) {
    499       return FALSE;
    500     }
    501 
    502     OutputFileName++;
    503   } while (*OutputFileName != 0);
    504   return TRUE;
    505 }
    506