source: mainline/tools/mkfat.py@ 609243f4

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 609243f4 was cc1a727, checked in by Martin Sucha <sucha14@…>, 14 years ago

Add support for ext2 to be used as RAM disk format

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