upstream: https://github.com/mirage/ocaml-mbr
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 444 lines 14 kB view raw
1(* 2 * Copyright (C) 2013 Citrix Inc 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 *) 16 17open Result.Syntax 18 19(* Error helpers *) 20let err_lba_max x = Error (Fmt.str "sector count exceeds LBA max: %Ld" x) 21 22let err_bad_signature s1 s2 = 23 Error (Fmt.str "Invalid signature: %02x %02x <> 0x55 0xaa" s1 s2) 24 25let err_too_small n = Error (Fmt.str "MBR too small: %d < 512" n) 26 27module Reader = Bytesrw.Bytes.Reader 28module Writer = Bytesrw.Bytes.Writer 29module Slice = Bytesrw.Bytes.Slice 30 31(* Read exactly n bytes from reader *) 32let read_exactly reader n = 33 if n = 0 then Ok Bytes.empty 34 else 35 let buf = Bytes.create n in 36 let rec loop pos remaining = 37 if remaining = 0 then Ok buf 38 else 39 match Reader.read reader with 40 | slice when Slice.is_eod slice -> Error "unexpected end of data" 41 | slice -> 42 let str = Slice.to_string slice in 43 let available = String.length str in 44 let to_copy = min available remaining in 45 Bytes.blit_string str 0 buf pos to_copy; 46 if to_copy < available then begin 47 let leftover = String.sub str to_copy (available - to_copy) in 48 Reader.push_back reader (Slice.of_string leftover) 49 end; 50 loop (pos + to_copy) (remaining - to_copy) 51 in 52 loop 0 n 53 54module Geometry = struct 55 type t = { cylinders : int; heads : int; sectors : int } 56 57 let kib = 1024L 58 let mib = Int64.mul kib 1024L 59 60 let of_lba_size x = 61 let sectors = 63 in 62 let* heads = 63 if x < Int64.(mul 504L mib) then Ok 16 64 else if x < Int64.(mul 1008L mib) then Ok 64 65 else if x < Int64.(mul 4032L mib) then Ok 128 66 else if x < Int64.(add (mul 8032L mib) (mul 512L kib)) then Ok 255 67 else err_lba_max x 68 in 69 let cylinders = 70 Int64.(to_int (div (div x (of_int sectors)) (of_int heads))) 71 in 72 Ok { cylinders; heads; sectors } 73 74 let to_chs g x = 75 let open Int64 in 76 let cylinders = to_int (div x (mul (of_int g.sectors) (of_int g.heads))) in 77 let heads = to_int (rem (div x (of_int g.sectors)) (of_int g.heads)) in 78 let sectors = to_int (succ (rem x (of_int g.sectors))) in 79 { cylinders; heads; sectors } 80end 81 82module Partition = struct 83 type t = { 84 active : bool; 85 first_absolute_sector_chs : Geometry.t; 86 ty : int; 87 last_absolute_sector_chs : Geometry.t; 88 first_absolute_sector_lba : int32; 89 sectors : int32; 90 } 91 92 let sector_start t = 93 Int64.(logand (of_int32 t.first_absolute_sector_lba) 0xFFFF_FFFFL) 94 95 let size_sectors t = Int64.(logand (of_int32 t.sectors) 0xFFFF_FFFFL) 96 97 let make ?(active = false) ~partition_type:(ty : int) 98 first_absolute_sector_lba sectors = 99 let* () = 100 if ty > 0 && ty < 256 then Ok () 101 else Error "Mbr.Partition.make: ty must be between 1 and 255" 102 in 103 let first_absolute_sector_chs = 104 { Geometry.cylinders = 0; heads = 0; sectors = 0 } 105 in 106 let last_absolute_sector_chs = first_absolute_sector_chs in 107 Ok 108 { 109 active; 110 first_absolute_sector_chs; 111 ty; 112 last_absolute_sector_chs; 113 first_absolute_sector_lba; 114 sectors; 115 } 116 117 let make' ?active ~partition_type:(ty : int) sector_start size_sectors = 118 if 119 Int64.( 120 logand sector_start 0xFFFF_FFFFL = sector_start 121 && logand size_sectors 0xFFFF_FFFFL = size_sectors) 122 then 123 let sector_start = Int64.to_int32 sector_start in 124 let size_sectors = Int64.to_int32 size_sectors in 125 make ?active ~partition_type:ty sector_start size_sectors 126 else Error "partition parameters do not fit in int32" 127 128 let decode_chs s = 129 let heads = Char.code s.[0] in 130 let y = Char.code s.[1] in 131 let z = Char.code s.[2] in 132 { Geometry.cylinders = (y lsl 2) lor z; heads; sectors = y land 0x3F } 133 134 let f_status = Wire.Field.v "status" Wire.uint8 135 136 let f_first_chs = 137 Wire.Field.v "first_chs" (Wire.byte_array ~size:(Wire.int 3)) 138 139 let f_ty = Wire.Field.v "type" Wire.uint8 140 let f_last_chs = Wire.Field.v "last_chs" (Wire.byte_array ~size:(Wire.int 3)) 141 let f_lba_start = Wire.Field.v "lba_start" Wire.uint32 142 let f_sectors = Wire.Field.v "sectors" Wire.uint32 143 144 let codec = 145 Wire.Codec.v "MbrPartition" 146 (fun status first_chs ty last_chs lba sectors -> 147 { 148 active = status = 0x80; 149 first_absolute_sector_chs = decode_chs first_chs; 150 ty; 151 last_absolute_sector_chs = decode_chs last_chs; 152 first_absolute_sector_lba = 153 Int32.of_int (Wire.Private.UInt32.to_int lba); 154 sectors = Int32.of_int (Wire.Private.UInt32.to_int sectors); 155 }) 156 Wire.Codec. 157 [ 158 (f_status $ fun t -> if t.active then 0x80 else 0); 159 (f_first_chs $ fun _ -> "\000\000\000"); 160 (f_ty $ fun t -> t.ty); 161 (f_last_chs $ fun _ -> "\000\000\000"); 162 ( f_lba_start $ fun t -> 163 Wire.Private.UInt32.of_int 164 (Int32.to_int t.first_absolute_sector_lba) ); 165 ( f_sectors $ fun t -> 166 Wire.Private.UInt32.of_int (Int32.to_int t.sectors) ); 167 ] 168end 169 170type t = { 171 bootstrap_code : string; 172 original_physical_drive : int; 173 seconds : int; 174 minutes : int; 175 hours : int; 176 disk_signature : int32; 177 partitions : Partition.t list; 178} 179 180let pp ppf t = 181 Fmt.pf ppf "@[<v>MBR(sig=%ld, %d partitions)@]" t.disk_signature 182 (List.length t.partitions) 183 184let partitions t = t.partitions 185 186(* Security helper: check for int32 addition overflow *) 187let int32_add_overflow a b = 188 let sum = Int32.add a b in 189 (* Overflow if: a > 0 && b > 0 && sum < 0, or a < 0 && b < 0 && sum > 0 *) 190 (* For unsigned interpretation, check if sum < a when both are positive *) 191 Int32.unsigned_compare sum a < 0 192 193let validate_partitions partitions = 194 let* () = 195 if List.length partitions <= 4 then Ok () else Error "Too many partitions" 196 in 197 let num_active = 198 List.fold_left 199 (fun acc p -> if p.Partition.active then succ acc else acc) 200 0 partitions 201 in 202 let* () = 203 if num_active <= 1 then Ok () 204 else Error "More than one active/boot partitions is not advisable" 205 in 206 let* () = 207 List.fold_left 208 (fun r p -> 209 let* () = r in 210 if 211 int32_add_overflow p.Partition.first_absolute_sector_lba 212 p.Partition.sectors 213 then 214 Error 215 (Fmt.str "Partition start %lu + size %lu overflows int32" 216 p.Partition.first_absolute_sector_lba p.Partition.sectors) 217 else Ok ()) 218 (Ok ()) partitions 219 in 220 let sorted = 221 List.sort 222 (fun p1 p2 -> 223 Int32.unsigned_compare p1.Partition.first_absolute_sector_lba 224 p2.Partition.first_absolute_sector_lba) 225 partitions 226 in 227 let* (_ : int32) = 228 List.fold_left 229 (fun r p -> 230 let* offset = r in 231 if 232 Int32.unsigned_compare offset p.Partition.first_absolute_sector_lba 233 <= 0 234 then 235 Ok 236 (Int32.add p.Partition.first_absolute_sector_lba p.Partition.sectors) 237 else Error "Partitions overlap") 238 (Ok 1l) sorted 239 in 240 Ok sorted 241 242let v ?(disk_signature = 0l) partitions = 243 let* partitions = validate_partitions partitions in 244 Ok 245 { 246 bootstrap_code = String.init (218 + 216) (Fun.const '\000'); 247 original_physical_drive = 0; 248 seconds = 0; 249 minutes = 0; 250 hours = 0; 251 disk_signature; 252 partitions; 253 } 254 255(* MBR layout: Wire codec for the full 512-byte sector. 256 The codec decodes/encodes a raw record with all 4 partition slots; 257 conversion to/from the public [t] type filters empty entries. *) 258 259let sizeof = 512 260let default_partition_start = 2048l 261let bootstrap_code1_len = 218 262let bootstrap_code2_len = 216 263 264(* Wire fields for the MBR header *) 265let f_bootstrap1 = 266 Wire.Field.v "bootstrap1" 267 (Wire.byte_array ~size:(Wire.int bootstrap_code1_len)) 268 269let funused1 = Wire.Field.v "unused1" (Wire.byte_array ~size:(Wire.int 2)) 270let f_drive = Wire.Field.v "drive" Wire.uint8 271let f_seconds = Wire.Field.v "seconds" Wire.uint8 272let f_minutes = Wire.Field.v "minutes" Wire.uint8 273let f_hours = Wire.Field.v "hours" Wire.uint8 274 275let f_bootstrap2 = 276 Wire.Field.v "bootstrap2" 277 (Wire.byte_array ~size:(Wire.int bootstrap_code2_len)) 278 279let f_disk_sig = Wire.Field.v "disk_signature" Wire.uint32 280let funused2 = Wire.Field.v "unused2" (Wire.byte_array ~size:(Wire.int 2)) 281let f_part1 = Wire.Field.v "partition1" (Wire.codec Partition.codec) 282let f_part2 = Wire.Field.v "partition2" (Wire.codec Partition.codec) 283let f_part3 = Wire.Field.v "partition3" (Wire.codec Partition.codec) 284let f_part4 = Wire.Field.v "partition4" (Wire.codec Partition.codec) 285let f_sig1 = Wire.Field.v "signature1" Wire.uint8 286let f_sig2 = Wire.Field.v "signature2" Wire.uint8 287 288(* A partition entry that encodes as 16 zero bytes *) 289let empty_partition = 290 { 291 Partition.active = false; 292 first_absolute_sector_chs = 293 { Geometry.cylinders = 0; heads = 0; sectors = 0 }; 294 ty = 0; 295 last_absolute_sector_chs = 296 { Geometry.cylinders = 0; heads = 0; sectors = 0 }; 297 first_absolute_sector_lba = 0l; 298 sectors = 0l; 299 } 300 301(* Raw decoded MBR: all 4 partition slots present, signature bytes included *) 302type raw = { 303 bootstrap1 : string; 304 unused1 : string; 305 drive : int; 306 seconds : int; 307 minutes : int; 308 hours : int; 309 bootstrap2 : string; 310 disk_signature_raw : int; 311 unused2 : string; 312 part1 : Partition.t; 313 part2 : Partition.t; 314 part3 : Partition.t; 315 part4 : Partition.t; 316 sig1 : int; 317 sig2 : int; 318} 319 320let raw_codec = 321 Wire.Codec.v "Mbr" 322 (fun bootstrap1 unused1 drive seconds minutes hours bootstrap2 323 disk_signature_raw unused2 part1 part2 part3 part4 sig1 sig2 -> 324 { 325 bootstrap1; 326 unused1; 327 drive; 328 seconds; 329 minutes; 330 hours; 331 bootstrap2; 332 disk_signature_raw; 333 unused2; 334 part1; 335 part2; 336 part3; 337 part4; 338 sig1; 339 sig2; 340 }) 341 Wire.Codec. 342 [ 343 (f_bootstrap1 $ fun r -> r.bootstrap1); 344 (funused1 $ fun r -> r.unused1); 345 (f_drive $ fun r -> r.drive); 346 (f_seconds $ fun r -> r.seconds); 347 (f_minutes $ fun r -> r.minutes); 348 (f_hours $ fun r -> r.hours); 349 (f_bootstrap2 $ fun r -> r.bootstrap2); 350 (f_disk_sig $ fun r -> r.disk_signature_raw); 351 (funused2 $ fun r -> r.unused2); 352 (f_part1 $ fun r -> r.part1); 353 (f_part2 $ fun r -> r.part2); 354 (f_part3 $ fun r -> r.part3); 355 (f_part4 $ fun r -> r.part4); 356 (f_sig1 $ fun r -> r.sig1); 357 (f_sig2 $ fun r -> r.sig2); 358 ] 359 360(* Filter a decoded partition: ty=0 means empty slot *) 361let filter_partition (p : Partition.t) = 362 if p.ty <> 0 then Ok (Some p) 363 else if (not p.active) && p.first_absolute_sector_lba = 0l && p.sectors = 0l 364 then Ok None 365 else Error "Non-zero empty partition type" 366 367(* Internal unmarshal from bytes buffer *) 368let unmarshal_bytes (buf : bytes) : (t, string) result = 369 let* () = 370 if Bytes.length buf < sizeof then err_too_small (Bytes.length buf) 371 else Ok () 372 in 373 let* raw = 374 Wire.Codec.decode raw_codec buf 0 375 |> Result.map_error (Fmt.str "%a" Wire.pp_parse_error) 376 in 377 let* () = 378 if raw.sig1 = 0x55 && raw.sig2 = 0xaa then Ok () 379 else err_bad_signature raw.sig1 raw.sig2 380 in 381 let* p1 = filter_partition raw.part1 in 382 let* p2 = filter_partition raw.part2 in 383 let* p3 = filter_partition raw.part3 in 384 let* p4 = filter_partition raw.part4 in 385 let partitions = List.filter_map Fun.id [ p1; p2; p3; p4 ] in 386 Ok 387 { 388 bootstrap_code = raw.bootstrap1 ^ raw.bootstrap2; 389 original_physical_drive = raw.drive; 390 seconds = raw.seconds; 391 minutes = raw.minutes; 392 hours = raw.hours; 393 disk_signature = 394 Int32.of_int (Wire.Private.UInt32.to_int raw.disk_signature_raw); 395 partitions; 396 } 397 398(* Internal marshal to bytes buffer *) 399let marshal_bytes (buf : bytes) t = 400 let parts = t.partitions in 401 let nth i = 402 try List.nth parts i 403 with Failure _ | Invalid_argument _ -> empty_partition 404 in 405 let raw = 406 { 407 bootstrap1 = String.sub t.bootstrap_code 0 bootstrap_code1_len; 408 unused1 = "\000\000"; 409 drive = t.original_physical_drive; 410 seconds = t.seconds; 411 minutes = t.minutes; 412 hours = t.hours; 413 bootstrap2 = 414 String.sub t.bootstrap_code bootstrap_code1_len bootstrap_code2_len; 415 disk_signature_raw = 416 Wire.Private.UInt32.of_int (Int32.to_int t.disk_signature); 417 unused2 = "\000\000"; 418 part1 = nth 0; 419 part2 = nth 1; 420 part3 = nth 2; 421 part4 = nth 3; 422 sig1 = 0x55; 423 sig2 = 0xaa; 424 } 425 in 426 Wire.Codec.encode raw_codec raw buf 0 427 428(* Streaming API *) 429 430let of_string s = unmarshal_bytes (Bytes.of_string s) 431 432let to_string t = 433 let buf = Bytes.make sizeof '\x00' in 434 marshal_bytes buf t; 435 Bytes.to_string buf 436 437let read reader = 438 match read_exactly reader sizeof with 439 | Error e -> Error e 440 | Ok buf -> unmarshal_bytes buf 441 442let write writer t = 443 let s = to_string t in 444 Writer.write writer (Slice.of_string s)