source: mainline/tools/mkfat.py@ de4a1cf

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since de4a1cf was de4a1cf, checked in by Martin Decky <martin@…>, 17 years ago

remove conditional expression (for compatibility with older Python versions)

  • Property mode set to 100755
File size: 12.8 KB
Line 
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#
29"""
30FAT creator
31"""
32
33import sys
34import os
35import random
36import xstruct
37import array
38
39def align_up(size, alignment):
40 "Return size aligned up to alignment"
41
42 if (size % alignment == 0):
43 return size
44
45 return (((size / alignment) + 1) * alignment)
46
47def subtree_size(root, cluster_size, dirent_size):
48 "Recursive directory walk and calculate size"
49
50 size = 0
51 files = 2
52
53 for name in os.listdir(root):
54 canon = os.path.join(root, name)
55
56 if (os.path.isfile(canon)):
57 size += align_up(os.path.getsize(canon), cluster_size)
58 files += 1
59
60 if (os.path.isdir(canon)):
61 size += subtree_size(canon, cluster_size, dirent_size)
62 files += 1
63
64 return size + align_up(files * dirent_size, cluster_size)
65
66def root_entries(root):
67 "Return number of root directory entries"
68
69 return len(os.listdir(root))
70
71def write_file(path, outf, cluster_size, data_start, fat, reserved_clusters):
72 "Store the contents of a file"
73
74 size = os.path.getsize(path)
75 prev = -1
76 first = 0
77
78 inf = file(path, "r")
79 rd = 0;
80 while (rd < size):
81 empty_cluster = fat.index(0)
82 fat[empty_cluster] = 0xffff
83
84 if (prev != -1):
85 fat[prev] = empty_cluster
86 else:
87 first = empty_cluster
88
89 prev = empty_cluster
90
91 data = inf.read(cluster_size);
92 outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
93 outf.write(data)
94 rd += len(data)
95 inf.close()
96
97 return first, size
98
99def write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster):
100 "Store the contents of a directory"
101
102 length = len(directory)
103 size = length * dirent_size
104 prev = -1
105 first = 0
106
107 i = 0
108 rd = 0;
109 while (rd < size):
110 if (prev != -1):
111 empty_cluster = fat.index(0)
112 fat[empty_cluster] = 0xffff
113 fat[prev] = empty_cluster
114 else:
115 first = empty_cluster
116
117 prev = empty_cluster
118
119 data = ''
120 data_len = 0
121 while ((i < length) and (data_len < cluster_size)):
122 if (i == 0):
123 directory[i].cluster = empty_cluster
124
125 data += directory[i].pack()
126 data_len += dirent_size
127 i += 1
128
129 outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
130 outf.write(data)
131 rd += len(data)
132
133 return first, size
134
135DIR_ENTRY = """little:
136 char name[8] /* file name */
137 char ext[3] /* file extension */
138 uint8_t attr /* file attributes */
139 padding[1] /* reserved for NT */
140 uint8_t ctime_fine /* create time (fine resolution) */
141 uint16_t ctime /* create time */
142 uint16_t cdate /* create date */
143 uint16_t adate /* access date */
144 padding[2] /* EA-index */
145 uint16_t mtime /* modification time */
146 uint16_t mdate /* modification date */
147 uint16_t cluster /* first cluster */
148 uint32_t size /* file size */
149"""
150
151DOT_DIR_ENTRY = """little:
152 uint8_t signature /* 0x2e signature */
153 char name[7] /* empty */
154 char ext[3] /* empty */
155 uint8_t attr /* file attributes */
156 padding[1] /* reserved for NT */
157 uint8_t ctime_fine /* create time (fine resolution) */
158 uint16_t ctime /* create time */
159 uint16_t cdate /* create date */
160 uint16_t adate /* access date */
161 padding[2] /* EA-index */
162 uint16_t mtime /* modification time */
163 uint16_t mdate /* modification date */
164 uint16_t cluster /* first cluster */
165 uint32_t size /* file size */
166"""
167
168DOTDOT_DIR_ENTRY = """little:
169 uint8_t signature[2] /* 0x2e signature */
170 char name[6] /* empty */
171 char ext[3] /* empty */
172 uint8_t attr /* file attributes */
173 padding[1] /* reserved for NT */
174 uint8_t ctime_fine /* create time (fine resolution) */
175 uint16_t ctime /* create time */
176 uint16_t cdate /* create date */
177 uint16_t adate /* access date */
178 padding[2] /* EA-index */
179 uint16_t mtime /* modification time */
180 uint16_t mdate /* modification date */
181 uint16_t cluster /* first cluster */
182 uint32_t size /* file size */
183"""
184
185def mangle_fname(name):
186 # FIXME: filter illegal characters
187 parts = name.split('.')
188
189 if (len(parts) > 0):
190 fname = parts[0]
191 else:
192 fname = ''
193
194 return (fname + ' ').upper()[0:8]
195
196def mangle_ext(name):
197 # FIXME: filter illegal characters
198 parts = name.split('.')
199
200 if (len(parts) > 1):
201 ext = parts[1]
202 else:
203 ext = ''
204
205 return (ext + ' ').upper()[0:3]
206
207def create_dirent(name, directory, cluster, size):
208 dir_entry = xstruct.create(DIR_ENTRY)
209
210 dir_entry.name = mangle_fname(name)
211 dir_entry.ext = mangle_ext(name)
212
213 if (directory):
214 dir_entry.attr = 0x30
215 else:
216 dir_entry.attr = 0x20
217
218 dir_entry.ctime_fine = 0 # FIXME
219 dir_entry.ctime = 0 # FIXME
220 dir_entry.cdate = 0 # FIXME
221 dir_entry.adate = 0 # FIXME
222 dir_entry.mtime = 0 # FIXME
223 dir_entry.mdate = 0 # FIXME
224 dir_entry.cluster = cluster
225
226 if (directory):
227 dir_entry.size = 0
228 else:
229 dir_entry.size = size
230
231 return dir_entry
232
233def create_dot_dirent(empty_cluster):
234 dir_entry = xstruct.create(DOT_DIR_ENTRY)
235
236 dir_entry.signature = 0x2e
237 dir_entry.name = ' '
238 dir_entry.ext = ' '
239 dir_entry.attr = 0x10
240
241 dir_entry.ctime_fine = 0 # FIXME
242 dir_entry.ctime = 0 # FIXME
243 dir_entry.cdate = 0 # FIXME
244 dir_entry.adate = 0 # FIXME
245 dir_entry.mtime = 0 # FIXME
246 dir_entry.mdate = 0 # FIXME
247 dir_entry.cluster = empty_cluster
248 dir_entry.size = 0
249
250 return dir_entry
251
252def create_dotdot_dirent(parent_cluster):
253 dir_entry = xstruct.create(DOTDOT_DIR_ENTRY)
254
255 dir_entry.signature = [0x2e, 0x2e]
256 dir_entry.name = ' '
257 dir_entry.ext = ' '
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 = parent_cluster
267 dir_entry.size = 0
268
269 return dir_entry
270
271def recursion(head, root, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, parent_cluster):
272 "Recursive directory walk"
273
274 directory = []
275
276 if (not head):
277 # Directory cluster preallocation
278 empty_cluster = fat.index(0)
279 fat[empty_cluster] = 0xffff
280
281 directory.append(create_dot_dirent(empty_cluster))
282 directory.append(create_dotdot_dirent(parent_cluster))
283 else:
284 empty_cluster = 0
285
286 for name in os.listdir(root):
287 canon = os.path.join(root, name)
288
289 if (os.path.isfile(canon)):
290 rv = write_file(canon, outf, cluster_size, data_start, fat, reserved_clusters)
291 directory.append(create_dirent(name, False, rv[0], rv[1]))
292
293 if (os.path.isdir(canon)):
294 rv = recursion(False, canon, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
295 directory.append(create_dirent(name, True, rv[0], rv[1]))
296
297 if (head):
298 outf.seek(root_start)
299 for dir_entry in directory:
300 outf.write(dir_entry.pack())
301 else:
302 return write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
303
304BOOT_SECTOR = """little:
305 uint8_t jmp[3] /* jump instruction */
306 char oem[8] /* OEM string */
307 uint16_t sector /* bytes per sector */
308 uint8_t cluster /* sectors per cluster */
309 uint16_t reserved /* reserved sectors */
310 uint8_t fats /* number of FATs */
311 uint16_t rootdir /* root directory entries */
312 uint16_t sectors /* total number of sectors */
313 uint8_t descriptor /* media descriptor */
314 uint16_t fat_sectors /* sectors per single FAT */
315 uint16_t track_sectors /* sectors per track */
316 uint16_t heads /* number of heads */
317 uint32_t hidden /* hidden sectors */
318 uint32_t sectors_big /* total number of sectors (if sectors == 0) */
319
320 /* Extended BIOS Parameter Block */
321 uint8_t drive /* physical drive number */
322 padding[1] /* reserved (current head) */
323 uint8_t extboot_signature /* extended boot signature */
324 uint32_t serial /* serial number */
325 char label[11] /* volume label */
326 char fstype[8] /* filesystem type */
327 padding[448] /* boot code */
328 uint8_t boot_signature[2] /* boot signature */
329"""
330
331EMPTY_SECTOR = """little:
332 padding[512] /* empty sector data */
333"""
334
335FAT_ENTRY = """little:
336 uint16_t next /* FAT16 entry */
337"""
338
339def usage(prname):
340 "Print usage syntax"
341 print prname + " <PATH> <IMAGE>"
342
343def main():
344 if (len(sys.argv) < 3):
345 usage(sys.argv[0])
346 return
347
348 path = os.path.abspath(sys.argv[1])
349 if (not os.path.isdir(path)):
350 print "<PATH> must be a directory"
351 return
352
353 fat16_clusters = 4096
354 min_cluster_size = 1024
355
356 sector_size = 512
357 cluster_size = 4096
358 dirent_size = 32
359 fatent_size = 2
360 fat_count = 2
361 reserved_clusters = 2
362
363 # Make sure the filesystem is large enought for FAT16
364 size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
365 while (size / cluster_size < fat16_clusters):
366 if (cluster_size > min_cluster_size):
367 cluster_size /= 2
368 size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
369 else:
370 size = fat16_clusters * cluster_size + reserved_clusters * cluster_size
371
372 root_size = align_up(root_entries(path) * dirent_size, cluster_size)
373
374 fat_size = align_up(align_up(size, cluster_size) / cluster_size * fatent_size, sector_size)
375
376 sectors = (cluster_size + fat_count * fat_size + root_size + size) / sector_size
377 root_start = cluster_size + fat_count * fat_size
378 data_start = root_start + root_size
379
380 outf = file(sys.argv[2], "w")
381
382 boot_sector = xstruct.create(BOOT_SECTOR)
383 boot_sector.jmp = [0xEB, 0x3C, 0x90]
384 boot_sector.oem = "MSDOS5.0"
385 boot_sector.sector = sector_size
386 boot_sector.cluster = cluster_size / sector_size
387 boot_sector.reserved = cluster_size / sector_size
388 boot_sector.fats = fat_count
389 boot_sector.rootdir = root_size / dirent_size
390 if (sectors <= 65535):
391 boot_sector.sectors = sectors
392 else:
393 boot_sector.sectors = 0
394 boot_sector.descriptor = 0xF8
395 boot_sector.fat_sectors = fat_size / sector_size
396 boot_sector.track_sectors = 63
397 boot_sector.heads = 6
398 boot_sector.hidden = 0
399 if (sectors > 65535):
400 boot_sector.sectors_big = sectors
401 else:
402 boot_sector.sectors_big = 0
403
404 boot_sector.drive = 0x80
405 boot_sector.extboot_signature = 0x29
406 boot_sector.serial = random.randint(0, 0xFFFFFFFF)
407 boot_sector.label = "HELENOS"
408 boot_sector.fstype = "FAT16 "
409 boot_sector.boot_signature = [0x55, 0xAA]
410
411 outf.write(boot_sector.pack())
412
413 empty_sector = xstruct.create(EMPTY_SECTOR)
414
415 # Reserved sectors
416 for i in range(1, cluster_size / sector_size):
417 outf.write(empty_sector.pack())
418
419 # FAT tables
420 for i in range(0, fat_count):
421 for j in range(0, fat_size / sector_size):
422 outf.write(empty_sector.pack())
423
424 # Root directory
425 for i in range(0, root_size / sector_size):
426 outf.write(empty_sector.pack())
427
428 # Data
429 for i in range(0, size / sector_size):
430 outf.write(empty_sector.pack())
431
432 fat = array.array('L', [0] * (fat_size / fatent_size))
433 fat[0] = 0xfff8
434 fat[1] = 0xffff
435
436 recursion(True, path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, 0)
437
438 # Store FAT
439 fat_entry = xstruct.create(FAT_ENTRY)
440 for i in range(0, fat_count):
441 outf.seek(cluster_size + i * fat_size)
442 for j in range(0, fat_size / fatent_size):
443 fat_entry.next = fat[j]
444 outf.write(fat_entry.pack())
445
446 outf.close()
447
448if __name__ == '__main__':
449 main()
Note: See TracBrowser for help on using the repository browser.