Changeset 7751ff1 in mainline for tools/mkfat.py


Ignore:
Timestamp:
2012-04-13T14:50:33Z (12 years ago)
Author:
Martin Decky <martin@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
3e72e41
Parents:
4f4cae1
Message:

improve FAT16 LFN creation support

  • add proper support for non-ASCII characters (by removing them from the legacy 8+3 names and by proper UTF-16 encoding in LFN names)
  • do not use the ugly global 8+3 disambiguation list, but create the legacy 8+3 names on directory basis
  • improve cstyle and code readability
File:
1 edited

Legend:

Unmodified
Added
Removed
  • tools/mkfat.py

    r4f4cae1 r7751ff1  
    168168"""
    169169
    170 LFN_ENTRY = """little:
    171         uint8_t pos
    172         uint16_t name1[5]
    173         uint8_t attr
    174         uint8_t type
    175         uint8_t csum
    176         uint16_t name2[6]
    177         uint16_t fc
    178         uint16_t name3[2]
    179 """
    180 
    181 # Global variable to hold the file names in 8.3 format. Needed to
    182 # keep track of "number" when creating a short fname from a LFN.
    183 name83_list = []
    184 
    185 def name83(fname):
    186         "Create a 8.3 name for the given fname"
    187 
    188         # FIXME: filter illegal characters
    189         parts = fname.split('.')
    190        
    191         name = ''
    192         ext = ''
     170LFN_DIR_ENTRY = """little:
     171        uint8_t seq                /* sequence number */
     172        char name1[10]             /* first part of the name */
     173        uint8_t attr               /* attributes */
     174        uint8_t rec_type           /* LFN record type */
     175        uint8_t checksum           /* LFN checksum */
     176        char name2[12]             /* second part of the name */
     177        uint16_t cluster           /* cluster */
     178        char name3[4]              /* third part of the name */
     179"""
     180
     181lchars = set(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
     182              'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
     183              'U', 'V', 'W', 'X', 'Y', 'Z',
     184              '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
     185              '!', '#', '$', '%', '&', '\'', '(', ')', '-', '@',
     186              '^', '_', '`', '{', '}', '~', '.'])
     187
     188def fat_lchars(name):
     189        "Filter FAT legal characters"
     190       
     191        filtered_name = ''
     192       
     193        for char in name.encode('ascii', 'ignore').upper():
     194                if not char in lchars:
     195                        continue
     196               
     197                filtered_name += char
     198       
     199        return filtered_name
     200
     201def fat_name83(name, name83_list):
     202        "Create a 8.3 name for the given name"
     203       
     204        ascii_name = fat_lchars(name)
     205        ascii_parts = ascii_name.split('.')
     206       
     207        short_name = ''
     208        short_ext = ''
    193209        lfn = False
    194 
    195         if len(fname) > 11 :
     210       
     211        if len(ascii_name) > 11:
    196212                lfn = True
    197 
    198         if len(parts) > 0:
    199                 name = parts[0]
    200                 if len(name) > 8 :
     213       
     214        if len(ascii_parts) > 0:
     215                short_name = ascii_parts[0]
     216                if len(short_name) > 8:
    201217                        lfn = True
    202 
    203         if len(parts) > 1 :
    204                 ext = parts[-1]
    205                 if len(ext) > 3 :
     218       
     219        if len(ascii_parts) > 1:
     220                short_ext = ascii_parts[-1]
     221                if len(short_ext) > 3:
    206222                        lfn = True
    207 
    208         if len(parts) > 2 :
     223       
     224        if len(ascii_parts) > 2:
    209225                lfn = True
    210 
    211         if (lfn == False) :
    212                 return (name.ljust(8)[0:8], ext.ljust(3)[0:3], False)
    213 
     226       
     227        if lfn == False:
     228                name83_list.append(short_name + '.' + short_ext)
     229                return (short_name.ljust(8)[0:8], short_ext.ljust(3)[0:3], False)
     230       
    214231        # For filenames with multiple extensions, we treat the last one
    215232        # as the actual extension. The rest of the filename is stripped
    216233        # of dots and concatenated to form the short name
    217         for _name in parts[1:-1]:
    218                 name = name + _name             
    219 
    220         global name83_list
    221         for number in range(1, 10000) :
    222                 number_str = '~' + str(number)
    223 
    224                 if len(name) + len(number_str) > 8 :
    225                         name = name[0:8 - len(number_str)]
    226 
    227                 name = name + number_str;
    228 
    229                 if (name + ext) not in name83_list :
     234        for part in ascii_parts[1:-1]:
     235                short_name += part
     236       
     237        for number in range(1, 999999):
     238                number_str = ('~' + str(number)).upper()
     239               
     240                if len(short_name) + len(number_str) > 8:
     241                        short_name = short_name[0:8 - len(number_str)]
     242               
     243                short_name += number_str;
     244               
     245                if not (short_name + '.' + short_ext) in name83_list:
    230246                        break
    231                        
    232         name83_list.append(name + ext) 
    233 
    234         return (name.ljust(8)[0:8], ext.ljust(3)[0:3], True)
    235 
    236 def get_utf16(name, l) :
    237         "Create a int array out of a string which we can store in uint16_t arrays"
    238 
    239         bs = [0xFFFF for i in range(l)]
    240 
    241         for i in range(len(name)) :
    242                 bs[i] = ord(name[i])
    243        
    244         if (len(name) < l) :
    245                 bs[len(name)] = 0;
    246        
    247         return bs
    248 
    249 def create_lfn_entry((name, index)) :
    250         entry = xstruct.create(LFN_ENTRY)
    251 
    252         entry.name1 = get_utf16(name[0:5], 5)
    253         entry.name2 = get_utf16(name[5:11], 6)
    254         entry.name3 = get_utf16(name[11:13], 2)
    255         entry.pos = index
    256 
    257         entry.attr = 0xF
    258         entry.fc = 0
    259         entry.type = 0
    260 
    261         return entry
    262 
    263 def create_dirent(name, directory, cluster, size):
     247       
     248        name83_list.append(short_name + '.' + short_ext)
     249        return (short_name.ljust(8)[0:8], short_ext.ljust(3)[0:3], True)
     250
     251def create_lfn_dirent(name, seq, checksum):
     252        "Create LFN directory entry"
     253       
     254        entry = xstruct.create(LFN_DIR_ENTRY)
     255        name_rest = name[26:]
     256       
     257        if len(name_rest) > 0:
     258                entry.seq = seq
     259        else:
     260                entry.seq = seq | 0x40
     261       
     262        entry.name1 = name[0:10]
     263        entry.name2 = name[10:22]
     264        entry.name3 = name[22:26]
     265       
     266        entry.attr = 0x0F
     267        entry.rec_type = 0
     268        entry.checksum = checksum
     269        entry.cluster = 0
     270       
     271        return (entry, name_rest)
     272
     273def lfn_checksum(name):
     274        "Calculate LFN checksum"
     275       
     276        checksum = 0
     277        for i in range(0, 11):
     278                checksum = (((checksum & 1) << 7) + (checksum >> 1) + ord(name[i])) & 0xFF
     279       
     280        return checksum
     281
     282def create_dirent(name, name83_list, directory, cluster, size):
     283        short_name, short_ext, lfn = fat_name83(name, name83_list)
    264284       
    265285        dir_entry = xstruct.create(DIR_ENTRY)
    266286       
    267         dir_entry.name, dir_entry.ext, lfn = name83(name)
    268 
    269         dir_entry.name = dir_entry.name.upper().encode('ascii')
    270         dir_entry.ext = dir_entry.ext.upper().encode('ascii')
    271 
     287        dir_entry.name = short_name
     288        dir_entry.ext = short_ext
     289       
    272290        if (directory):
    273291                dir_entry.attr = 0x30
     
    289307                dir_entry.size = size
    290308       
    291 
    292309        if not lfn:
    293310                return [dir_entry]
    294 
    295         n = len(name) / 13 + 1
    296         names = [(name[i * 13: (i + 1) * 13 + 1], i + 1) for i in range(n)]
    297 
    298         entries = sorted(map (create_lfn_entry, names), reverse = True, key = lambda e : e.pos)
    299         entries[0].pos |= 0x40
    300 
    301         fname11 = dir_entry.name + dir_entry.ext
    302 
    303         csum = 0
    304         for i in range(0, 11) :
    305                 csum = ((csum & 1) << 7) + (csum  >> 1) + ord(fname11[i])
    306                 csum = csum & 0xFF
    307        
    308         for e in entries :
    309                 e.csum = csum;
    310        
    311         entries.append(dir_entry)
    312 
     311       
     312        long_name = name.encode('utf_16_le')
     313        entries = [dir_entry]
     314       
     315        seq = 1
     316        checksum = lfn_checksum(dir_entry.name + dir_entry.ext)
     317       
     318        while len(long_name) > 0:
     319                long_entry, long_name = create_lfn_dirent(long_name, seq, checksum)
     320                entries.append(long_entry)
     321                seq += 1
     322       
     323        entries.reverse()
    313324        return entries
    314325
     
    355366       
    356367        directory = []
    357        
    358         if (not head):
     368        name83_list = []
     369       
     370        if not head:
    359371                # Directory cluster preallocation
    360372                empty_cluster = fat.index(0)
    361                 fat[empty_cluster] = 0xffff
     373                fat[empty_cluster] = 0xFFFF
    362374               
    363375                directory.append(create_dot_dirent(empty_cluster))
     
    366378                empty_cluster = 0
    367379       
    368         for item in listdir_items(root):               
     380        for item in listdir_items(root):
    369381                if item.is_file:
    370382                        rv = write_file(item, outf, cluster_size, data_start, fat, reserved_clusters)
    371                         directory.extend(create_dirent(item.name, False, rv[0], rv[1]))
     383                        directory.extend(create_dirent(item.name, name83_list, False, rv[0], rv[1]))
    372384                elif item.is_dir:
    373385                        rv = recursion(False, item.path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
    374                         directory.extend(create_dirent(item.name, True, rv[0], rv[1]))
    375        
    376         if (head):
     386                        directory.extend(create_dirent(item.name, name83_list, True, rv[0], rv[1]))
     387       
     388        if head:
    377389                outf.seek(root_start)
    378390                for dir_entry in directory:
     
    431443        extra_bytes = int(sys.argv[1])
    432444       
    433         path = os.path.abspath(sys.argv[2])
     445        path = os.path.abspath(sys.argv[2].decode())
    434446        if (not os.path.isdir(path)):
    435447                print("<PATH> must be a directory")
     
    529541       
    530542        outf.close()
    531        
     543
    532544if __name__ == '__main__':
    533545        main()
Note: See TracChangeset for help on using the changeset viewer.