source: mainline/tools/mkfat.py@ fc840d9

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

root directory population

  • Property mode set to 100755
File size: 8.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 = 0
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):
72 "Store the contents of a file"
73
74 size = os.path.getsize(path)
75 prev = -1
76 first = -1
77
78 inf = file(path, "r")
79 rd = 0;
80 while (rd < size):
81 data = inf.read(cluster_size);
82
83 empty_cluster = fat.index(0)
84
85 fat[empty_cluster] = 0xffff
86 if (prev != -1):
87 fat[prev] = empty_cluster
88 else:
89 first = empty_cluster
90
91 prev = empty_cluster
92
93 outf.seek(data_start + empty_cluster * cluster_size)
94 outf.write(data)
95 rd += len(data)
96 inf.close()
97
98 return first, size
99
100DIR_ENTRY = """little:
101 char name[8] /* file name */
102 char ext[3] /* file extension */
103 uint8_t attr /* file attributes */
104 padding[1] /* reserved for NT */
105 uint8_t ctime_fine /* create time (fine resolution) */
106 uint16_t ctime /* create time */
107 uint16_t cdate /* create date */
108 uint16_t adate /* access date */
109 padding[2] /* EA-index */
110 uint16_t mtime /* modification time */
111 uint16_t mdate /* modification date */
112 uint16_t cluster /* first cluster */
113 uint32_t size /* file size */
114"""
115
116def mangle_fname(name):
117 # FIXME: filter illegal characters
118 fname = (name.split('.')[0] + ' ').upper()[0:8]
119 return fname
120
121def mangle_ext(name):
122 # FIXME: filter illegal characters
123 ext = (name.split('.')[1] + ' ').upper()[0:3]
124 return ext
125
126def create_dirent(name, directory, cluster, size):
127 dir_entry = xstruct.create(DIR_ENTRY)
128
129 dir_entry.name = mangle_fname(name)
130 dir_entry.ext = mangle_ext(name)
131
132 if (directory):
133 dir_entry.attr = 0x30
134 else:
135 dir_entry.attr = 0x20
136
137 dir_entry.ctime_fine = 0 # FIXME
138 dir_entry.ctime = 0 # FIXME
139 dir_entry.cdate = 0 # FIXME
140 dir_entry.adate = 0 # FIXME
141 dir_entry.mtime = 0 # FIXME
142 dir_entry.mdate = 0 # FIXME
143 dir_entry.cluster = cluster
144 dir_entry.size = size
145
146 return dir_entry
147
148def recursion(head, root, outf, cluster_size, root_start, data_start, fat):
149 "Recursive directory walk"
150
151 directory = []
152 for name in os.listdir(root):
153 canon = os.path.join(root, name)
154
155 if (os.path.isfile(canon)):
156 rv = write_file(canon, outf, cluster_size, data_start, fat)
157 directory.append(create_dirent(name, False, rv[0], rv[1]))
158
159 if (head):
160 outf.seek(root_start)
161 for dir_entry in directory:
162 outf.write(dir_entry.pack())
163
164BOOT_SECTOR = """little:
165 uint8_t jmp[3] /* jump instruction */
166 char oem[8] /* OEM string */
167 uint16_t sector /* bytes per sector */
168 uint8_t cluster /* sectors per cluster */
169 uint16_t reserved /* reserved sectors */
170 uint8_t fats /* number of FATs */
171 uint16_t rootdir /* root directory entries */
172 uint16_t sectors /* total number of sectors */
173 uint8_t descriptor /* media descriptor */
174 uint16_t fat_sectors /* sectors per single FAT */
175 uint16_t track_sectors /* sectors per track */
176 uint16_t heads /* number of heads */
177 uint32_t hidden /* hidden sectors */
178 uint32_t sectors_big /* total number of sectors (if sectors == 0) */
179
180 /* Extended BIOS Parameter Block */
181 uint8_t drive /* physical drive number */
182 padding[1] /* reserved (current head) */
183 uint8_t extboot_signature /* extended boot signature */
184 uint32_t serial /* serial number */
185 char label[11] /* volume label */
186 char fstype[8] /* filesystem type */
187 padding[448] /* boot code */
188 uint8_t boot_signature[2] /* boot signature */
189"""
190
191EMPTY_SECTOR = """little:
192 padding[512] /* empty sector data */
193"""
194
195FAT_ENTRY = """little:
196 uint16_t next /* FAT16 entry */
197"""
198
199def usage(prname):
200 "Print usage syntax"
201 print prname + " <PATH> <IMAGE>"
202
203def main():
204 if (len(sys.argv) < 3):
205 usage(sys.argv[0])
206 return
207
208 path = os.path.abspath(sys.argv[1])
209 if (not os.path.isdir(path)):
210 print "<PATH> must be a directory"
211 return
212
213 fat16_clusters = 4096
214
215 sector_size = 512
216 cluster_size = 4096
217 dirent_size = 32
218 fatent_size = 2
219 fat_count = 2
220 reserved_clusters = 2
221
222 # Make sure the filesystem is large enought for FAT16
223 size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
224 while (size / cluster_size < fat16_clusters):
225 if (cluster_size > sector_size):
226 cluster_size /= 2
227 size = subtree_size(path, cluster_size, dirent_size) + reserved_clusters * cluster_size
228 else:
229 size = fat16_clusters * cluster_size + reserved_clusters * cluster_size
230
231 root_size = align_up(root_entries(path) * dirent_size, cluster_size)
232
233 fat_size = align_up(align_up(size, cluster_size) / cluster_size * fatent_size, sector_size)
234
235 sectors = (cluster_size + fat_count * fat_size + root_size + size) / sector_size
236 root_start = cluster_size + fat_count * fat_size
237 data_start = root_start + root_size
238
239 outf = file(sys.argv[2], "w")
240
241 boot_sector = xstruct.create(BOOT_SECTOR)
242 boot_sector.jmp = [0xEB, 0x3C, 0x90]
243 boot_sector.oem = "MSDOS5.0"
244 boot_sector.sector = sector_size
245 boot_sector.cluster = cluster_size / sector_size
246 boot_sector.reserved = cluster_size / sector_size
247 boot_sector.fats = fat_count
248 boot_sector.rootdir = root_size / dirent_size
249 boot_sector.sectors = (sectors if (sectors <= 65535) else 0)
250 boot_sector.descriptor = 0xF8
251 boot_sector.fat_sectors = fat_size / sector_size
252 boot_sector.track_sectors = 63
253 boot_sector.heads = 6
254 boot_sector.hidden = 0
255 boot_sector.sectors_big = (sectors if (sectors > 65535) else 0)
256
257 boot_sector.drive = 0x80
258 boot_sector.extboot_signature = 0x29
259 boot_sector.serial = random.randint(0, 0xFFFFFFFF)
260 boot_sector.label = "HELENOS"
261 boot_sector.fstype = "FAT16 "
262 boot_sector.boot_signature = [0x55, 0xAA]
263
264 outf.write(boot_sector.pack())
265
266 empty_sector = xstruct.create(EMPTY_SECTOR)
267
268 # Reserved sectors
269 for i in range(1, cluster_size / sector_size):
270 outf.write(empty_sector.pack())
271
272 # FAT tables
273 for i in range(0, fat_count):
274 for j in range(0, boot_sector.fat_sectors):
275 outf.write(empty_sector.pack())
276
277 # Root directory
278 for i in range(0, root_size / sector_size):
279 outf.write(empty_sector.pack())
280
281 # Data
282 for i in range(0, size / sector_size):
283 outf.write(empty_sector.pack())
284
285 fat = array.array('L', [0] * (fat_size / fatent_size))
286 fat[0] = 0xfff8
287 fat[1] = 0xffff
288
289 recursion(True, path, outf, cluster_size, root_start, data_start, fat)
290
291 # Store FAT
292 fat_entry = xstruct.create(FAT_ENTRY)
293 for i in range(0, fat_count):
294 outf.seek(cluster_size + i * fat_size)
295 for j in range(0, fat_size / fatent_size):
296 fat_entry.next = fat[j]
297 outf.write(fat_entry.pack())
298
299 outf.close()
300
301if __name__ == '__main__':
302 main()
Note: See TracBrowser for help on using the repository browser.