Subversion Repositories HelenOS

Rev

Rev 3511 | Rev 3523 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3264 decky 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
FAT creator
31
"""
32
 
33
import sys
34
import os
35
import random
36
import xstruct
3520 decky 37
import array
3264 decky 38
 
3509 decky 39
def 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
 
3520 decky 47
def subtree_size(root, cluster_size, dirent_size):
3509 decky 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)):
3520 decky 61
			size += subtree_size(canon, cluster_size, dirent_size)
3509 decky 62
			files += 1
63
 
3520 decky 64
	return size + align_up(files * dirent_size, cluster_size)
3509 decky 65
 
66
def root_entries(root):
67
	"Return number of root directory entries"
68
 
69
	return len(os.listdir(root))
70
 
3520 decky 71
def 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
 
100
DIR_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
 
116
def mangle_fname(name):
117
	# FIXME: filter illegal characters
118
	fname = (name.split('.')[0] + '          ').upper()[0:8]
119
	return fname
120
 
121
def mangle_ext(name):
122
	# FIXME: filter illegal characters
123
	ext = (name.split('.')[1] + '  ').upper()[0:3]
124
	return ext
125
 
126
def 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
 
148
def 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
 
3264 decky 164
BOOT_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
 
3511 decky 191
EMPTY_SECTOR = """little:
3520 decky 192
	padding[512]               /* empty sector data */
3511 decky 193
"""
194
 
3520 decky 195
FAT_ENTRY = """little:
196
	uint16_t next              /* FAT16 entry */
197
"""
198
 
3264 decky 199
def usage(prname):
200
	"Print usage syntax"
201
	print prname + " <PATH> <IMAGE>"
202
 
203
def 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
 
3520 decky 213
	fat16_clusters = 4096
214
 
3509 decky 215
	sector_size = 512
216
	cluster_size = 4096
3520 decky 217
	dirent_size = 32
218
	fatent_size = 2
219
	fat_count = 2
220
	reserved_clusters = 2
3509 decky 221
 
3520 decky 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
3509 decky 230
 
3520 decky 231
	root_size = align_up(root_entries(path) * dirent_size, cluster_size)
3509 decky 232
 
3520 decky 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
 
3264 decky 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"
3509 decky 244
	boot_sector.sector = sector_size
245
	boot_sector.cluster = cluster_size / sector_size
246
	boot_sector.reserved = cluster_size / sector_size
3520 decky 247
	boot_sector.fats = fat_count
248
	boot_sector.rootdir = root_size / dirent_size
3509 decky 249
	boot_sector.sectors = (sectors if (sectors <= 65535) else 0)
3264 decky 250
	boot_sector.descriptor = 0xF8
3509 decky 251
	boot_sector.fat_sectors = fat_size / sector_size
252
	boot_sector.track_sectors = 63
253
	boot_sector.heads = 6
3264 decky 254
	boot_sector.hidden = 0
3509 decky 255
	boot_sector.sectors_big = (sectors if (sectors > 65535) else 0)
3264 decky 256
 
3509 decky 257
	boot_sector.drive = 0x80
3264 decky 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
 
3511 decky 266
	empty_sector = xstruct.create(EMPTY_SECTOR)
267
 
3520 decky 268
	# Reserved sectors
269
	for i in range(1, cluster_size / sector_size):
3511 decky 270
		outf.write(empty_sector.pack())
271
 
272
	# FAT tables
3520 decky 273
	for i in range(0, fat_count):
3511 decky 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
 
3520 decky 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
 
3264 decky 299
	outf.close()
300
 
301
if __name__ == '__main__':
302
	main()