Source file fat_boot_sector.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
type t = {
oem_name: string;
bytes_per_sector: int;
sectors_per_cluster: int;
reserved_sectors: int;
number_of_fats: int;
number_of_root_dir_entries: int;
total_sectors: int32;
sectors_per_fat: int;
hidden_preceeding_sectors: int32;
}
let default_oem_name = "ocamlfat"
[@@@ocaml.warning "-32"]
[%%cstruct
type t = {
jump_instruction: uint8_t [@len 3];
oem_name: uint8_t [@len 8];
bytes_per_sector: uint16_t;
sectors_per_cluster: uint8_t;
reserved_sectors: uint16_t ;
number_of_fats: uint8_t ;
number_of_root_dir_entries: uint16_t ;
total_sectors_small: uint16_t ;
media_descriptor: uint8_t ;
sectors_per_fat: uint16_t ;
sectors_per_track: uint16_t ;
heads: uint16_t ;
hidden_preceeding_sectors: uint32_t ;
total_sectors_large: uint32_t ;
boot_code: uint8_t [@len 474] ;
signature: uint16_t ;
} [@@little_endian]
]
[@@@ocaml.warning "+32"]
let sizeof = sizeof_t
let _ = assert(sizeof = 512)
let fat_id = 0xf8
let marshal (buf: Cstruct.t) t =
for i = 0 to Cstruct.len buf - 1 do
Cstruct.set_uint8 buf i 0
done;
set_t_oem_name t.oem_name 0 buf;
set_t_bytes_per_sector buf t.bytes_per_sector;
set_t_sectors_per_cluster buf t.sectors_per_cluster;
set_t_reserved_sectors buf t.reserved_sectors;
set_t_number_of_fats buf t.number_of_fats;
set_t_number_of_root_dir_entries buf t.number_of_root_dir_entries;
set_t_total_sectors_small buf 0;
set_t_media_descriptor buf fat_id;
set_t_sectors_per_fat buf t.sectors_per_fat;
set_t_sectors_per_track buf 0;
set_t_heads buf 0;
set_t_hidden_preceeding_sectors buf t.hidden_preceeding_sectors;
set_t_total_sectors_large buf t.total_sectors;
set_t_signature buf 0xaa55
let unmarshal (buf: Cstruct.t) : (t, string) result =
let open Rresult in
( if Cstruct.len buf < sizeof
then Error (Printf.sprintf "boot sector too small: %d < %d" (Cstruct.len buf) sizeof)
else Ok () ) >>= fun () ->
let signature = get_t_signature buf in
( if signature <> 0xaa55
then Error (Printf.sprintf "boot sector signature invalid: %04x <> %04x" signature 0xaa55)
else Ok () ) >>= fun () ->
let oem_name = Cstruct.to_string (get_t_oem_name buf) in
let bytes_per_sector = get_t_bytes_per_sector buf in
let sectors_per_cluster = get_t_sectors_per_cluster buf in
let reserved_sectors = get_t_reserved_sectors buf in
let number_of_fats = get_t_number_of_fats buf in
let number_of_root_dir_entries = get_t_number_of_root_dir_entries buf in
let total_sectors_small = get_t_total_sectors_small buf in
let sectors_per_fat = get_t_sectors_per_fat buf in
let hidden_preceeding_sectors = get_t_hidden_preceeding_sectors buf in
let total_sectors_large = get_t_total_sectors_large buf in
Ok {
oem_name; bytes_per_sector; sectors_per_cluster;
reserved_sectors; number_of_fats; number_of_root_dir_entries;
total_sectors = max (Int32.of_int total_sectors_small) total_sectors_large;
sectors_per_fat; hidden_preceeding_sectors;
}
let debug_print x =
Printf.printf "OEM: [%s]\n" x.oem_name;
Printf.printf "bytes_per_sector: %d\n" x.bytes_per_sector;
Printf.printf "sectors_per_cluster: %d\n" x.sectors_per_cluster;
Printf.printf "sectors_per_fat: %d\n" x.sectors_per_fat;
Printf.printf "total_sectors: %ld\n" x.total_sectors;
Printf.printf "reserved_sectors: %d\n" x.reserved_sectors;
Printf.printf "number of FATs: %d\n" x.number_of_fats;
Printf.printf "number_of_root_dir_entries: %d\n" x.number_of_root_dir_entries;
Printf.printf "hidden_preceeding_sectors: %ld\n" x.hidden_preceeding_sectors;
()
let ints start length =
let rec enumerate start length acc = match length with
| 0 -> acc
| _ -> enumerate (start + 1) (length - 1) (start :: acc) in
List.rev (enumerate start length [])
(** Return the sector number of the first cluster *)
let initial_cluster x =
let root_start = x.reserved_sectors + x.number_of_fats * x.sectors_per_fat in
root_start + (x.number_of_root_dir_entries * 32) / x.bytes_per_sector
(** Return a list of sectors corresponding to cluster n *)
let sectors_of_cluster x n =
ints (initial_cluster x + x.sectors_per_cluster * (n - 2)) x.sectors_per_cluster
(** Return the number of clusters *)
let clusters x =
let cluster_start = initial_cluster x in
2 + (Int32.to_int (Int32.div (Int32.sub x.total_sectors (Int32.of_int cluster_start)) (Int32.of_int x.sectors_per_cluster)))
let format_of_clusters number_of_clusters =
let open Fat_format in
if number_of_clusters < 4087 then Some FAT16
else if number_of_clusters < 65527 then Some FAT16
else if number_of_clusters < 268435457 then Some FAT32
else None
let detect_format x = match format_of_clusters (clusters x) with
| None -> Error "unknown cluster type"
| Some x -> Ok x
let make size =
let bytes_per_sector = 512 in
let sectors_per_cluster = 4 in
let total_sectors = Int64.(to_int32 (div (add 511L size) 512L)) in
let total_clusters =
Int32.(to_int (div (add 3l total_sectors) (of_int sectors_per_cluster)))
in
let open Fat_format in
match format_of_clusters total_clusters with
| Some FAT12 | Some FAT32 | None ->
failwith "unimplemented"
| Some FAT16 ->
let sectors_per_fat = ((total_clusters * 2) + 511) / 512 in
let reserved_sectors = 4 in
let number_of_fats = 1 in
let number_of_root_dir_entries = 512 in
let hidden_preceeding_sectors = 0l in
{ oem_name = default_oem_name;
bytes_per_sector; sectors_per_cluster; total_sectors;
sectors_per_fat; reserved_sectors; number_of_fats;
number_of_root_dir_entries; hidden_preceeding_sectors }
let sectors_of_fat x =
ints x.reserved_sectors x.sectors_per_fat
let sectors_of_root_dir x =
let start = x.reserved_sectors + x.sectors_per_fat * x.number_of_fats in
let length = (x.number_of_root_dir_entries * 32) / x.bytes_per_sector in
ints start length