source: mainline/tools/mkfat.py@ 336d2faa

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

Add shell command for creating zero-filled files (mkfile).

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