Subversion Repositories HelenOS

Rev

Rev 3511 | Rev 3523 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 3511 Rev 3520
1
#!/usr/bin/env python
1
#!/usr/bin/env python
2
#
2
#
3
# Copyright (c) 2008 Martin Decky
3
# Copyright (c) 2008 Martin Decky
4
# All rights reserved.
4
# All rights reserved.
5
#
5
#
6
# Redistribution and use in source and binary forms, with or without
6
# Redistribution and use in source and binary forms, with or without
7
# modification, are permitted provided that the following conditions
7
# modification, are permitted provided that the following conditions
8
# are met:
8
# are met:
9
#
9
#
10
# - Redistributions of source code must retain the above copyright
10
# - Redistributions of source code must retain the above copyright
11
#   notice, this list of conditions and the following disclaimer.
11
#   notice, this list of conditions and the following disclaimer.
12
# - Redistributions in binary form must reproduce the above copyright
12
# - Redistributions in binary form must reproduce the above copyright
13
#   notice, this list of conditions and the following disclaimer in the
13
#   notice, this list of conditions and the following disclaimer in the
14
#   documentation and/or other materials provided with the distribution.
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
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.
16
#   derived from this software without specific prior written permission.
17
#
17
#
18
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
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
19
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
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
24
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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
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.
27
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
#
28
#
29
"""
29
"""
30
FAT creator
30
FAT creator
31
"""
31
"""
32
 
32
 
33
import sys
33
import sys
34
import os
34
import os
35
import random
35
import random
36
import xstruct
36
import xstruct
-
 
37
import array
37
 
38
 
38
def align_up(size, alignment):
39
def align_up(size, alignment):
39
    "Return size aligned up to alignment"
40
    "Return size aligned up to alignment"
40
   
41
   
41
    if (size % alignment == 0):
42
    if (size % alignment == 0):
42
        return size
43
        return size
43
   
44
   
44
    return (((size / alignment) + 1) * alignment)
45
    return (((size / alignment) + 1) * alignment)
45
 
46
 
46
def subtree_size(root, cluster_size):
47
def subtree_size(root, cluster_size, dirent_size):
47
    "Recursive directory walk and calculate size"
48
    "Recursive directory walk and calculate size"
48
   
49
   
49
    size = 0
50
    size = 0
50
    files = 0
51
    files = 0
51
   
52
   
52
    for name in os.listdir(root):
53
    for name in os.listdir(root):
53
        canon = os.path.join(root, name)
54
        canon = os.path.join(root, name)
54
       
55
       
55
        if (os.path.isfile(canon)):
56
        if (os.path.isfile(canon)):
56
            size += align_up(os.path.getsize(canon), cluster_size)
57
            size += align_up(os.path.getsize(canon), cluster_size)
57
            files += 1
58
            files += 1
58
       
59
       
59
        if (os.path.isdir(canon)):
60
        if (os.path.isdir(canon)):
60
            size += subtree_size(canon, cluster_size)
61
            size += subtree_size(canon, cluster_size, dirent_size)
61
            files += 1
62
            files += 1
62
   
63
   
63
    return size + align_up(files * 32, cluster_size)
64
    return size + align_up(files * dirent_size, cluster_size)
64
 
65
 
65
def root_entries(root):
66
def root_entries(root):
66
    "Return number of root directory entries"
67
    "Return number of root directory entries"
67
   
68
   
68
    return len(os.listdir(root))
69
    return len(os.listdir(root))
69
 
70
 
-
 
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
 
70
BOOT_SECTOR = """little:
164
BOOT_SECTOR = """little:
71
    uint8_t jmp[3]             /* jump instruction */
165
    uint8_t jmp[3]             /* jump instruction */
72
    char oem[8]                /* OEM string */
166
    char oem[8]                /* OEM string */
73
    uint16_t sector            /* bytes per sector */
167
    uint16_t sector            /* bytes per sector */
74
    uint8_t cluster            /* sectors per cluster */
168
    uint8_t cluster            /* sectors per cluster */
75
    uint16_t reserved          /* reserved sectors */
169
    uint16_t reserved          /* reserved sectors */
76
    uint8_t fats               /* number of FATs */
170
    uint8_t fats               /* number of FATs */
77
    uint16_t rootdir           /* root directory entries */
171
    uint16_t rootdir           /* root directory entries */
78
    uint16_t sectors           /* total number of sectors */
172
    uint16_t sectors           /* total number of sectors */
79
    uint8_t descriptor         /* media descriptor */
173
    uint8_t descriptor         /* media descriptor */
80
    uint16_t fat_sectors       /* sectors per single FAT */
174
    uint16_t fat_sectors       /* sectors per single FAT */
81
    uint16_t track_sectors     /* sectors per track */
175
    uint16_t track_sectors     /* sectors per track */
82
    uint16_t heads             /* number of heads */
176
    uint16_t heads             /* number of heads */
83
    uint32_t hidden            /* hidden sectors */
177
    uint32_t hidden            /* hidden sectors */
84
    uint32_t sectors_big       /* total number of sectors (if sectors == 0) */
178
    uint32_t sectors_big       /* total number of sectors (if sectors == 0) */
85
   
179
   
86
    /* Extended BIOS Parameter Block */
180
    /* Extended BIOS Parameter Block */
87
    uint8_t drive              /* physical drive number */
181
    uint8_t drive              /* physical drive number */
88
    padding[1]                 /* reserved (current head) */
182
    padding[1]                 /* reserved (current head) */
89
    uint8_t extboot_signature  /* extended boot signature */
183
    uint8_t extboot_signature  /* extended boot signature */
90
    uint32_t serial            /* serial number */
184
    uint32_t serial            /* serial number */
91
    char label[11]             /* volume label */
185
    char label[11]             /* volume label */
92
    char fstype[8]             /* filesystem type */
186
    char fstype[8]             /* filesystem type */
93
    padding[448]               /* boot code */
187
    padding[448]               /* boot code */
94
    uint8_t boot_signature[2]  /* boot signature */
188
    uint8_t boot_signature[2]  /* boot signature */
95
"""
189
"""
96
 
190
 
97
EMPTY_SECTOR = """little:
191
EMPTY_SECTOR = """little:
98
    padding[512]
192
    padding[512]               /* empty sector data */
-
 
193
"""
-
 
194
 
-
 
195
FAT_ENTRY = """little:
-
 
196
    uint16_t next              /* FAT16 entry */
99
"""
197
"""
100
 
198
 
101
def usage(prname):
199
def usage(prname):
102
    "Print usage syntax"
200
    "Print usage syntax"
103
    print prname + " <PATH> <IMAGE>"
201
    print prname + " <PATH> <IMAGE>"
104
 
202
 
105
def main():
203
def main():
106
    if (len(sys.argv) < 3):
204
    if (len(sys.argv) < 3):
107
        usage(sys.argv[0])
205
        usage(sys.argv[0])
108
        return
206
        return
109
   
207
   
110
    path = os.path.abspath(sys.argv[1])
208
    path = os.path.abspath(sys.argv[1])
111
    if (not os.path.isdir(path)):
209
    if (not os.path.isdir(path)):
112
        print "<PATH> must be a directory"
210
        print "<PATH> must be a directory"
113
        return
211
        return
114
   
212
   
-
 
213
    fat16_clusters = 4096
-
 
214
   
115
    sector_size = 512
215
    sector_size = 512
116
    cluster_size = 4096
216
    cluster_size = 4096
-
 
217
    dirent_size = 32
-
 
218
    fatent_size = 2
-
 
219
    fat_count = 2
-
 
220
    reserved_clusters = 2
117
   
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:
118
    root_size = align_up(root_entries(sys.argv[1]) * 32, sector_size)
229
            size = fat16_clusters * cluster_size + reserved_clusters * cluster_size
-
 
230
   
119
    size = subtree_size(sys.argv[1], cluster_size)
231
    root_size = align_up(root_entries(path) * dirent_size, cluster_size)
-
 
232
   
120
    fat_size = align_up(size / cluster_size * 2, sector_size)
233
    fat_size = align_up(align_up(size, cluster_size) / cluster_size * fatent_size, sector_size)
121
   
234
   
122
    sectors = (cluster_size + 2 * fat_size + root_size + size) / sector_size
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
123
   
238
   
124
    outf = file(sys.argv[2], "w")
239
    outf = file(sys.argv[2], "w")
125
   
240
   
126
    boot_sector = xstruct.create(BOOT_SECTOR)
241
    boot_sector = xstruct.create(BOOT_SECTOR)
127
    boot_sector.jmp = [0xEB, 0x3C, 0x90]
242
    boot_sector.jmp = [0xEB, 0x3C, 0x90]
128
    boot_sector.oem = "MSDOS5.0"
243
    boot_sector.oem = "MSDOS5.0"
129
    boot_sector.sector = sector_size
244
    boot_sector.sector = sector_size
130
    boot_sector.cluster = cluster_size / sector_size
245
    boot_sector.cluster = cluster_size / sector_size
131
    boot_sector.reserved = cluster_size / sector_size
246
    boot_sector.reserved = cluster_size / sector_size
132
    boot_sector.fats = 2
247
    boot_sector.fats = fat_count
133
    boot_sector.rootdir = root_size / 32
248
    boot_sector.rootdir = root_size / dirent_size
134
    boot_sector.sectors = (sectors if (sectors <= 65535) else 0)
249
    boot_sector.sectors = (sectors if (sectors <= 65535) else 0)
135
    boot_sector.descriptor = 0xF8
250
    boot_sector.descriptor = 0xF8
136
    boot_sector.fat_sectors = fat_size / sector_size
251
    boot_sector.fat_sectors = fat_size / sector_size
137
    boot_sector.track_sectors = 63
252
    boot_sector.track_sectors = 63
138
    boot_sector.heads = 6
253
    boot_sector.heads = 6
139
    boot_sector.hidden = 0
254
    boot_sector.hidden = 0
140
    boot_sector.sectors_big = (sectors if (sectors > 65535) else 0)
255
    boot_sector.sectors_big = (sectors if (sectors > 65535) else 0)
141
   
256
   
142
    boot_sector.drive = 0x80
257
    boot_sector.drive = 0x80
143
    boot_sector.extboot_signature = 0x29
258
    boot_sector.extboot_signature = 0x29
144
    boot_sector.serial = random.randint(0, 0xFFFFFFFF)
259
    boot_sector.serial = random.randint(0, 0xFFFFFFFF)
145
    boot_sector.label = "HELENOS"
260
    boot_sector.label = "HELENOS"
146
    boot_sector.fstype = "FAT16   "
261
    boot_sector.fstype = "FAT16   "
147
    boot_sector.boot_signature = [0x55, 0xAA]
262
    boot_sector.boot_signature = [0x55, 0xAA]
148
   
263
   
149
    outf.write(boot_sector.pack())
264
    outf.write(boot_sector.pack())
150
   
265
   
151
    empty_sector = xstruct.create(EMPTY_SECTOR)
266
    empty_sector = xstruct.create(EMPTY_SECTOR)
152
   
267
   
153
    # Reserved sectors (boot_sector.reserved - boot_sector)
268
    # Reserved sectors
154
    for i in range(1, boot_sector.reserved):
269
    for i in range(1, cluster_size / sector_size):
155
        outf.write(empty_sector.pack())
270
        outf.write(empty_sector.pack())
156
   
271
   
157
    # FAT tables
272
    # FAT tables
158
    for i in range(0, boot_sector.fats):
273
    for i in range(0, fat_count):
159
        for j in range(0, boot_sector.fat_sectors):
274
        for j in range(0, boot_sector.fat_sectors):
160
            outf.write(empty_sector.pack())
275
            outf.write(empty_sector.pack())
161
   
276
   
162
    # Root directory
277
    # Root directory
163
    for i in range(0, root_size / sector_size):
278
    for i in range(0, root_size / sector_size):
164
        outf.write(empty_sector.pack())
279
        outf.write(empty_sector.pack())
165
   
280
   
166
    # Data
281
    # Data
167
    for i in range(0, size / sector_size):
282
    for i in range(0, size / sector_size):
168
        outf.write(empty_sector.pack())
283
        outf.write(empty_sector.pack())
169
   
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
   
170
    outf.close()
299
    outf.close()
171
   
300
   
172
if __name__ == '__main__':
301
if __name__ == '__main__':
173
    main()
302
    main()
174
 
303