source: mainline/tools/mkfat.py@ 042fbe0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 042fbe0 was 042fbe0, checked in by Jiri Svoboda <jiri@…>, 14 years ago

mkfat.py - check for 8+3 filename limit violations.
Work around FAT 8+3 limit vs. library filenames by using .so0 extension

  • Property mode set to 100755
File size: 13.6 KB
RevLine 
[5749372]1#!/usr/bin/env python
2#
3# Copyright (c) 2008 Martin Decky
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9#
10# - Redistributions of source code must retain the above copyright
11# notice, this list of conditions and the following disclaimer.
12# - Redistributions in binary form must reproduce the above copyright
13# notice, this list of conditions and the following disclaimer in the
14# documentation and/or other materials provided with the distribution.
15# - The name of the author may not be used to endorse or promote products
16# derived from this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#
[3c80f2b]29
[5749372]30"""
31FAT creator
32"""
33
34import sys
35import os
36import random
37import xstruct
[04619ba]38import array
[5749372]39
[24edc18]40exclude_names = set(['.svn', '.bzr'])
[9df7918]41
[700eaa9]42def align_up(size, alignment):
43 "Return size aligned up to alignment"
44
45 if (size % alignment == 0):
46 return size
47
[28f4adb]48 return ((size // alignment) + 1) * alignment
[700eaa9]49
[04619ba]50def subtree_size(root, cluster_size, dirent_size):
[700eaa9]51 "Recursive directory walk and calculate size"
52
53 size = 0
[95b730c2]54 files = 2
[700eaa9]55
56 for name in os.listdir(root):
57 canon = os.path.join(root, name)
58
[ab579fa]59 if (os.path.isfile(canon) and (not name in exclude_names)):
[700eaa9]60 size += align_up(os.path.getsize(canon), cluster_size)
61 files += 1
62
[ab579fa]63 if (os.path.isdir(canon) and (not name in exclude_names)):
[04619ba]64 size += subtree_size(canon, cluster_size, dirent_size)
[700eaa9]65 files += 1
66
[04619ba]67 return size + align_up(files * dirent_size, cluster_size)
[700eaa9]68
69def root_entries(root):
70 "Return number of root directory entries"
71
72 return len(os.listdir(root))
73
[95b730c2]74def write_file(path, outf, cluster_size, data_start, fat, reserved_clusters):
[04619ba]75 "Store the contents of a file"
76
77 size = os.path.getsize(path)
78 prev = -1
[95b730c2]79 first = 0
[04619ba]80
[28f4adb]81 inf = open(path, "rb")
[04619ba]82 rd = 0;
83 while (rd < size):
84 empty_cluster = fat.index(0)
85 fat[empty_cluster] = 0xffff
[95b730c2]86
[04619ba]87 if (prev != -1):
88 fat[prev] = empty_cluster
89 else:
90 first = empty_cluster
91
92 prev = empty_cluster
93
[28f4adb]94 data = bytes(inf.read(cluster_size));
[95b730c2]95 outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
[04619ba]96 outf.write(data)
97 rd += len(data)
98 inf.close()
99
100 return first, size
101
[95b730c2]102def write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster):
103 "Store the contents of a directory"
104
105 length = len(directory)
106 size = length * dirent_size
107 prev = -1
108 first = 0
109
110 i = 0
111 rd = 0;
112 while (rd < size):
113 if (prev != -1):
114 empty_cluster = fat.index(0)
115 fat[empty_cluster] = 0xffff
116 fat[prev] = empty_cluster
117 else:
118 first = empty_cluster
119
120 prev = empty_cluster
121
[28f4adb]122 data = bytes()
[95b730c2]123 data_len = 0
124 while ((i < length) and (data_len < cluster_size)):
125 if (i == 0):
126 directory[i].cluster = empty_cluster
127
128 data += directory[i].pack()
129 data_len += dirent_size
130 i += 1
131
132 outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
133 outf.write(data)
134 rd += len(data)
135
136 return first, size
137
[04619ba]138DIR_ENTRY = """little:
139 char name[8] /* file name */
140 char ext[3] /* file extension */
141 uint8_t attr /* file attributes */
[a248234]142 uint8_t lcase /* file name case (NT extension) */
[04619ba]143 uint8_t ctime_fine /* create time (fine resolution) */
144 uint16_t ctime /* create time */
145 uint16_t cdate /* create date */
146 uint16_t adate /* access date */
147 padding[2] /* EA-index */
148 uint16_t mtime /* modification time */
149 uint16_t mdate /* modification date */
150 uint16_t cluster /* first cluster */
151 uint32_t size /* file size */
152"""
153
[95b730c2]154DOT_DIR_ENTRY = """little:
155 uint8_t signature /* 0x2e signature */
156 char name[7] /* empty */
157 char ext[3] /* empty */
158 uint8_t attr /* file attributes */
159 padding[1] /* reserved for NT */
160 uint8_t ctime_fine /* create time (fine resolution) */
161 uint16_t ctime /* create time */
162 uint16_t cdate /* create date */
163 uint16_t adate /* access date */
164 padding[2] /* EA-index */
165 uint16_t mtime /* modification time */
166 uint16_t mdate /* modification date */
167 uint16_t cluster /* first cluster */
168 uint32_t size /* file size */
169"""
170
171DOTDOT_DIR_ENTRY = """little:
172 uint8_t signature[2] /* 0x2e signature */
173 char name[6] /* empty */
174 char ext[3] /* empty */
175 uint8_t attr /* file attributes */
176 padding[1] /* reserved for NT */
177 uint8_t ctime_fine /* create time (fine resolution) */
178 uint16_t ctime /* create time */
179 uint16_t cdate /* create date */
180 uint16_t adate /* access date */
181 padding[2] /* EA-index */
182 uint16_t mtime /* modification time */
183 uint16_t mdate /* modification date */
184 uint16_t cluster /* first cluster */
185 uint32_t size /* file size */
186"""
187
[04619ba]188def mangle_fname(name):
189 # FIXME: filter illegal characters
[95b730c2]190 parts = name.split('.')
191
[042fbe0]192 if len(parts) > 0:
[95b730c2]193 fname = parts[0]
194 else:
195 fname = ''
[042fbe0]196
197 if len(fname) > 8:
198 sys.stdout.write("mkfat.py: error: Directory entry " + name +
199 " base name is longer than 8 characters\n")
200 sys.exit(1);
201
[95b730c2]202 return (fname + ' ').upper()[0:8]
[04619ba]203
204def mangle_ext(name):
205 # FIXME: filter illegal characters
[95b730c2]206 parts = name.split('.')
207
[042fbe0]208 if len(parts) > 1:
[95b730c2]209 ext = parts[1]
210 else:
211 ext = ''
212
[042fbe0]213 if len(parts) > 2:
214 sys.stdout.write("mkfat.py: error: Directory entry " + name +
215 " has more than one extension\n")
216 sys.exit(1);
217
218 if len(ext) > 3:
219 sys.stdout.write("mkfat.py: error: Directory entry " + name +
220 " extension is longer than 3 characters\n")
221 sys.exit(1);
222
[95b730c2]223 return (ext + ' ').upper()[0:3]
[04619ba]224
225def create_dirent(name, directory, cluster, size):
226 dir_entry = xstruct.create(DIR_ENTRY)
227
[432f68a]228 dir_entry.name = mangle_fname(name).encode('ascii')
229 dir_entry.ext = mangle_ext(name).encode('ascii')
[04619ba]230
231 if (directory):
232 dir_entry.attr = 0x30
233 else:
234 dir_entry.attr = 0x20
235
[a248234]236 dir_entry.lcase = 0x18
[04619ba]237 dir_entry.ctime_fine = 0 # FIXME
238 dir_entry.ctime = 0 # FIXME
239 dir_entry.cdate = 0 # FIXME
240 dir_entry.adate = 0 # FIXME
241 dir_entry.mtime = 0 # FIXME
242 dir_entry.mdate = 0 # FIXME
243 dir_entry.cluster = cluster
[95b730c2]244
245 if (directory):
246 dir_entry.size = 0
247 else:
248 dir_entry.size = size
249
250 return dir_entry
251
252def create_dot_dirent(empty_cluster):
253 dir_entry = xstruct.create(DOT_DIR_ENTRY)
254
255 dir_entry.signature = 0x2e
[432f68a]256 dir_entry.name = b' '
257 dir_entry.ext = b' '
[95b730c2]258 dir_entry.attr = 0x10
259
260 dir_entry.ctime_fine = 0 # FIXME
261 dir_entry.ctime = 0 # FIXME
262 dir_entry.cdate = 0 # FIXME
263 dir_entry.adate = 0 # FIXME
264 dir_entry.mtime = 0 # FIXME
265 dir_entry.mdate = 0 # FIXME
266 dir_entry.cluster = empty_cluster
267 dir_entry.size = 0
268
269 return dir_entry
270
271def create_dotdot_dirent(parent_cluster):
272 dir_entry = xstruct.create(DOTDOT_DIR_ENTRY)
273
274 dir_entry.signature = [0x2e, 0x2e]
[432f68a]275 dir_entry.name = b' '
276 dir_entry.ext = b' '
[95b730c2]277 dir_entry.attr = 0x10
278
279 dir_entry.ctime_fine = 0 # FIXME
280 dir_entry.ctime = 0 # FIXME
281 dir_entry.cdate = 0 # FIXME
282 dir_entry.adate = 0 # FIXME
283 dir_entry.mtime = 0 # FIXME
284 dir_entry.mdate = 0 # FIXME
285 dir_entry.cluster = parent_cluster
286 dir_entry.size = 0
[04619ba]287
288 return dir_entry
289
[95b730c2]290def recursion(head, root, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, parent_cluster):
[04619ba]291 "Recursive directory walk"
292
293 directory = []
[95b730c2]294
295 if (not head):
296 # Directory cluster preallocation
297 empty_cluster = fat.index(0)
298 fat[empty_cluster] = 0xffff
299
300 directory.append(create_dot_dirent(empty_cluster))
301 directory.append(create_dotdot_dirent(parent_cluster))
302 else:
303 empty_cluster = 0
304
[04619ba]305 for name in os.listdir(root):
306 canon = os.path.join(root, name)
307
[9df7918]308 if (os.path.isfile(canon) and (not name in exclude_names)):
[95b730c2]309 rv = write_file(canon, outf, cluster_size, data_start, fat, reserved_clusters)
[04619ba]310 directory.append(create_dirent(name, False, rv[0], rv[1]))
[95b730c2]311
[9df7918]312 if (os.path.isdir(canon) and (not name in exclude_names)):
[95b730c2]313 rv = recursion(False, canon, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
314 directory.append(create_dirent(name, True, rv[0], rv[1]))
[04619ba]315
316 if (head):
317 outf.seek(root_start)
318 for dir_entry in directory:
319 outf.write(dir_entry.pack())
[95b730c2]320 else:
321 return write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
[04619ba]322
[5749372]323BOOT_SECTOR = """little:
324 uint8_t jmp[3] /* jump instruction */
325 char oem[8] /* OEM string */
326 uint16_t sector /* bytes per sector */
327 uint8_t cluster /* sectors per cluster */
328 uint16_t reserved /* reserved sectors */
329 uint8_t fats /* number of FATs */
330 uint16_t rootdir /* root directory entries */
331 uint16_t sectors /* total number of sectors */
332 uint8_t descriptor /* media descriptor */
333 uint16_t fat_sectors /* sectors per single FAT */
334 uint16_t track_sectors /* sectors per track */
335 uint16_t heads /* number of heads */
336 uint32_t hidden /* hidden sectors */
337 uint32_t sectors_big /* total number of sectors (if sectors == 0) */
338
339 /* Extended BIOS Parameter Block */
340 uint8_t drive /* physical drive number */
341 padding[1] /* reserved (current head) */
342 uint8_t extboot_signature /* extended boot signature */
343 uint32_t serial /* serial number */
344 char label[11] /* volume label */
345 char fstype[8] /* filesystem type */
346 padding[448] /* boot code */
347 uint8_t boot_signature[2] /* boot signature */
348"""
349
[8bc6fcf]350EMPTY_SECTOR = """little:
[04619ba]351 padding[512] /* empty sector data */
352"""
353
354FAT_ENTRY = """little:
355 uint16_t next /* FAT16 entry */
[8bc6fcf]356"""
357
[5749372]358def usage(prname):
359 "Print usage syntax"
[28f4adb]360 print(prname + " <EXTRA_BYTES> <PATH> <IMAGE>")
[5749372]361
362def main():
[14f2100]363 if (len(sys.argv) < 4):
[5749372]364 usage(sys.argv[0])
365 return
366
[14f2100]367 if (not sys.argv[1].isdigit()):
[28f4adb]368 print("<EXTRA_BYTES> must be a number")
[14f2100]369 return
370
371 extra_bytes = int(sys.argv[1])
372
373 path = os.path.abspath(sys.argv[2])
[5749372]374 if (not os.path.isdir(path)):
[28f4adb]375 print("<PATH> must be a directory")
[5749372]376 return
377
[c4702798]378 fat16_clusters = 4096
[04619ba]379
[700eaa9]380 sector_size = 512
381 cluster_size = 4096
[04619ba]382 dirent_size = 32
383 fatent_size = 2
384 fat_count = 2
385 reserved_clusters = 2
386
[f4057f5]387 # Make sure the filesystem is large enough for FAT16
[14f2100]388 size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size + extra_bytes
[28f4adb]389 while (size // cluster_size < fat16_clusters):
[ab579fa]390 if (cluster_size > sector_size):
[28f4adb]391 cluster_size = cluster_size // 2
[14f2100]392 size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size + extra_bytes
[04619ba]393 else:
394 size = fat16_clusters * cluster_size + reserved_clusters * cluster_size
[700eaa9]395
[04619ba]396 root_size = align_up(root_entries(path) * dirent_size, cluster_size)
[700eaa9]397
[28f4adb]398 fat_size = align_up(align_up(size, cluster_size) // cluster_size * fatent_size, sector_size)
[04619ba]399
[28f4adb]400 sectors = (cluster_size + fat_count * fat_size + root_size + size) // sector_size
[04619ba]401 root_start = cluster_size + fat_count * fat_size
402 data_start = root_start + root_size
[700eaa9]403
[28f4adb]404 outf = open(sys.argv[3], "wb")
[5749372]405
406 boot_sector = xstruct.create(BOOT_SECTOR)
407 boot_sector.jmp = [0xEB, 0x3C, 0x90]
[28f4adb]408 boot_sector.oem = b'MSDOS5.0'
[700eaa9]409 boot_sector.sector = sector_size
[28f4adb]410 boot_sector.cluster = cluster_size // sector_size
411 boot_sector.reserved = cluster_size // sector_size
[04619ba]412 boot_sector.fats = fat_count
[28f4adb]413 boot_sector.rootdir = root_size // dirent_size
[de4a1cf]414 if (sectors <= 65535):
415 boot_sector.sectors = sectors
416 else:
417 boot_sector.sectors = 0
[5749372]418 boot_sector.descriptor = 0xF8
[28f4adb]419 boot_sector.fat_sectors = fat_size // sector_size
[700eaa9]420 boot_sector.track_sectors = 63
421 boot_sector.heads = 6
[5749372]422 boot_sector.hidden = 0
[de4a1cf]423 if (sectors > 65535):
424 boot_sector.sectors_big = sectors
425 else:
426 boot_sector.sectors_big = 0
[5749372]427
[700eaa9]428 boot_sector.drive = 0x80
[5749372]429 boot_sector.extboot_signature = 0x29
[0951495]430 boot_sector.serial = random.randint(0, 0x7fffffff)
[28f4adb]431 boot_sector.label = b'HELENOS'
432 boot_sector.fstype = b'FAT16 '
[5749372]433 boot_sector.boot_signature = [0x55, 0xAA]
434
435 outf.write(boot_sector.pack())
436
[8bc6fcf]437 empty_sector = xstruct.create(EMPTY_SECTOR)
438
[04619ba]439 # Reserved sectors
[28f4adb]440 for i in range(1, cluster_size // sector_size):
[8bc6fcf]441 outf.write(empty_sector.pack())
442
443 # FAT tables
[04619ba]444 for i in range(0, fat_count):
[28f4adb]445 for j in range(0, fat_size // sector_size):
[8bc6fcf]446 outf.write(empty_sector.pack())
447
448 # Root directory
[28f4adb]449 for i in range(0, root_size // sector_size):
[8bc6fcf]450 outf.write(empty_sector.pack())
451
452 # Data
[28f4adb]453 for i in range(0, size // sector_size):
[8bc6fcf]454 outf.write(empty_sector.pack())
455
[28f4adb]456 fat = array.array('L', [0] * (fat_size // fatent_size))
[04619ba]457 fat[0] = 0xfff8
458 fat[1] = 0xffff
459
[95b730c2]460 recursion(True, path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, 0)
[04619ba]461
462 # Store FAT
463 fat_entry = xstruct.create(FAT_ENTRY)
464 for i in range(0, fat_count):
465 outf.seek(cluster_size + i * fat_size)
[28f4adb]466 for j in range(0, fat_size // fatent_size):
[04619ba]467 fat_entry.next = fat[j]
468 outf.write(fat_entry.pack())
469
[5749372]470 outf.close()
471
472if __name__ == '__main__':
473 main()
Note: See TracBrowser for help on using the repository browser.