Subversion Repositories HelenOS

Rev

Rev 3520 | Rev 3524 | 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
3523 decky 51
    files = 2
3509 decky 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
 
3523 decky 71
def write_file(path, outf, cluster_size, data_start, fat, reserved_clusters):
3520 decky 72
    "Store the contents of a file"
73
 
74
    size = os.path.getsize(path)
75
    prev = -1
3523 decky 76
    first = 0
3520 decky 77
 
78
    inf = file(path, "r")
79
    rd = 0;
80
    while (rd < size):
81
        empty_cluster = fat.index(0)
3523 decky 82
        fat[empty_cluster] = 0xffff
3520 decky 83
 
84
        if (prev != -1):
85
            fat[prev] = empty_cluster
86
        else:
87
            first = empty_cluster
88
 
89
        prev = empty_cluster
90
 
3523 decky 91
        data = inf.read(cluster_size);
92
        outf.seek(data_start + (empty_cluster - reserved_clusters) * cluster_size)
3520 decky 93
        outf.write(data)
94
        rd += len(data)
95
    inf.close()
96
 
97
    return first, size
98
 
3523 decky 99
def 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
 
3520 decky 135
DIR_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
 
3523 decky 151
DOT_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
 
168
DOTDOT_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
 
3520 decky 185
def mangle_fname(name):
186
    # FIXME: filter illegal characters
3523 decky 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]
3520 decky 195
 
196
def mangle_ext(name):
197
    # FIXME: filter illegal characters
3523 decky 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]
3520 decky 206
 
207
def 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
 
3523 decky 226
    if (directory):
227
        dir_entry.size = 0
228
    else:
229
        dir_entry.size = size
230
 
3520 decky 231
    return dir_entry
232
 
3523 decky 233
def 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
 
252
def 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
 
271
def recursion(head, root, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, parent_cluster):
3520 decky 272
    "Recursive directory walk"
273
 
274
    directory = []
3523 decky 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
 
3520 decky 286
    for name in os.listdir(root):
287
        canon = os.path.join(root, name)
288
 
289
        if (os.path.isfile(canon)):
3523 decky 290
            rv = write_file(canon, outf, cluster_size, data_start, fat, reserved_clusters)
3520 decky 291
            directory.append(create_dirent(name, False, rv[0], rv[1]))
3523 decky 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]))
3520 decky 296
 
297
    if (head):
298
        outf.seek(root_start)
299
        for dir_entry in directory:
300
            outf.write(dir_entry.pack())
3523 decky 301
    else:
302
        return write_directory(directory, outf, cluster_size, data_start, fat, reserved_clusters, dirent_size, empty_cluster)
3520 decky 303
 
3264 decky 304
BOOT_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
 
3511 decky 331
EMPTY_SECTOR = """little:
3520 decky 332
    padding[512]               /* empty sector data */
3511 decky 333
"""
334
 
3520 decky 335
FAT_ENTRY = """little:
336
    uint16_t next              /* FAT16 entry */
337
"""
338
 
3264 decky 339
def usage(prname):
340
    "Print usage syntax"
341
    print prname + " <PATH> <IMAGE>"
342
 
343
def 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
 
3520 decky 353
    fat16_clusters = 4096
3523 decky 354
    min_cluster_size = 1024
3520 decky 355
 
3509 decky 356
    sector_size = 512
357
    cluster_size = 4096
3520 decky 358
    dirent_size = 32
359
    fatent_size = 2
360
    fat_count = 2
361
    reserved_clusters = 2
3509 decky 362
 
3520 decky 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):
3523 decky 366
        if (cluster_size > min_cluster_size):
3520 decky 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
3509 decky 371
 
3520 decky 372
    root_size = align_up(root_entries(path) * dirent_size, cluster_size)
3509 decky 373
 
3520 decky 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
 
3264 decky 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"
3509 decky 385
    boot_sector.sector = sector_size
386
    boot_sector.cluster = cluster_size / sector_size
387
    boot_sector.reserved = cluster_size / sector_size
3520 decky 388
    boot_sector.fats = fat_count
389
    boot_sector.rootdir = root_size / dirent_size
3509 decky 390
    boot_sector.sectors = (sectors if (sectors <= 65535) else 0)
3264 decky 391
    boot_sector.descriptor = 0xF8
3509 decky 392
    boot_sector.fat_sectors = fat_size / sector_size
393
    boot_sector.track_sectors = 63
394
    boot_sector.heads = 6
3264 decky 395
    boot_sector.hidden = 0
3509 decky 396
    boot_sector.sectors_big = (sectors if (sectors > 65535) else 0)
3264 decky 397
 
3509 decky 398
    boot_sector.drive = 0x80
3264 decky 399
    boot_sector.extboot_signature = 0x29
400
    boot_sector.serial = random.randint(0, 0xFFFFFFFF)
401
    boot_sector.label = "HELENOS"
402
    boot_sector.fstype = "FAT16   "
403
    boot_sector.boot_signature = [0x55, 0xAA]
404
 
405
    outf.write(boot_sector.pack())
406
 
3511 decky 407
    empty_sector = xstruct.create(EMPTY_SECTOR)
408
 
3520 decky 409
    # Reserved sectors
410
    for i in range(1, cluster_size / sector_size):
3511 decky 411
        outf.write(empty_sector.pack())
412
 
413
    # FAT tables
3520 decky 414
    for i in range(0, fat_count):
3523 decky 415
        for j in range(0, fat_size / sector_size):
3511 decky 416
            outf.write(empty_sector.pack())
417
 
418
    # Root directory
419
    for i in range(0, root_size / sector_size):
420
        outf.write(empty_sector.pack())
421
 
422
    # Data
423
    for i in range(0, size / sector_size):
424
        outf.write(empty_sector.pack())
425
 
3520 decky 426
    fat = array.array('L', [0] * (fat_size / fatent_size))
427
    fat[0] = 0xfff8
428
    fat[1] = 0xffff
429
 
3523 decky 430
    recursion(True, path, outf, cluster_size, root_start, data_start, fat, reserved_clusters, dirent_size, 0)
3520 decky 431
 
432
    # Store FAT
433
    fat_entry = xstruct.create(FAT_ENTRY)
434
    for i in range(0, fat_count):
435
        outf.seek(cluster_size + i * fat_size)
436
        for j in range(0, fat_size / fatent_size):
437
            fat_entry.next = fat[j]
438
            outf.write(fat_entry.pack())
439
 
3264 decky 440
    outf.close()
441
 
442
if __name__ == '__main__':
443
    main()