summaryrefslogtreecommitdiffstats
path: root/virt-df/virt_df_mbr.ml
blob: b56189c45a86d342dcbc1f42e36232916748d7cd (plain)
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
184
185
186
187
188
189
190
191
192
193
194
195
(* 'df' command for virtual domains.

   (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
   http://libvirt.org/

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   Support for Master Boot Record partition scheme.
*)

open Printf
open Unix
open ExtList

open Virt_df_gettext.Gettext
open Virt_df

let sector_size = 512
let sector_size64 = 512L

(* Maximum number of extended partitions possible. *)
let max_extended_partitions = 100

(* Device representing a single partition.  It just acts as an offset
 * into the underlying device.
 *
 * Notes:
 * (1) 'start'/'size' are measured in sectors.
 * (2) 'partno' is the partition number, starting at 1
 *     (cf. /dev/hda1 is the first partition).
 * (3) 'dev' is the underlying block device.
 *)
class partition_device dev partno start size =
  let devname = dev#name in
  let name = sprintf "%s%d" devname partno in
  let start = start *^ sector_size64 in
  let size = size *^ sector_size64 in
object (self)
  inherit device
  method name = name
  method size = size
  method read offset len =
    if offset < 0L || len < 0 || offset +^ Int64.of_int len > size then
      invalid_arg (
	sprintf "%s: tried to read outside partition boundaries (%Ld/%d/%Ld)"
	  name offset len size
      );
    dev#read (start+^offset) len
end

(** Probe the
    {{:http://en.wikipedia.org/wiki/Master_boot_record}master boot record}
    (if it is one) and read the partitions.

    @raise Not_found if it is not an MBR.
 *)
let rec probe_mbr dev =
  (* Adjust size to sectors. *)
  let size = dev#size /^ sector_size64 in

  (* Read the first sector. *)
  let bits =
    try dev#read_bitstring 0L sector_size
    with exn -> raise Not_found in

  (* Does this match a likely-looking MBR? *)
  bitmatch bits with
  | padding : 3568 : bitstring;		(* padding to byte offset 446 *)
    part0 : 128 : bitstring;		(* partitions *)
    part1 : 128 : bitstring;
    part2 : 128 : bitstring;
    part3 : 128 : bitstring;
    0x55 : 8; 0xAA : 8 ->		(* MBR signature *)

      (* Parse the partition table entries. *)
      let primaries =
	List.mapi (parse_mbr_entry dev) [part0;part1;part2;part3] in

(*
      (* Read extended partition data. *)
      let extendeds = List.map (
	function
	| { part_type = 0x05 } as part ->
	    probe_extended_partition
	      max_extended_partitions fd part part.part_lba_start
	| part -> []
      ) primaries in
      let extendeds = List.concat extendeds in
      primaries @ extendeds
*)
      { parts_name = "MBR"; parts = primaries }

  | _ ->
      raise Not_found			(* not an MBR *)

(* Parse a single partition table entry.  See the table here:
 * http://en.wikipedia.org/wiki/Master_boot_record
 *)
and parse_mbr_entry dev i bits =
  bitmatch bits with
  | 0l : 32; 0l : 32; 0l : 32; 0l : 32 ->
    { part_status = NullEntry; part_type = 0;
      part_dev = null_device; part_content = `Unknown }

  | 0 : 8; first_chs : 24;
    part_type : 8; last_chs : 24;
    first_lba : 32 : unsigned, littleendian;
    part_size : 32 : unsigned, littleendian ->
    make_mbr_entry Nonbootable dev (i+1) part_type first_lba part_size

  | 0x80 : 8; first_chs : 24;
    part_type : 8; last_chs : 24;
    first_lba : 32 : unsigned, littleendian;
    part_size : 32 : unsigned, littleendian ->
    make_mbr_entry Bootable dev (i+1) part_type first_lba part_size

  | _ ->
      { part_status = Malformed; part_type = 0;
	part_dev = null_device; part_content = `Unknown }

and make_mbr_entry part_status dev partno part_type first_lba part_size =
  let first_lba = uint64_of_int32 first_lba in
  let part_size = uint64_of_int32 part_size in
  eprintf "first_lba = %Lx\n" first_lba;
  eprintf "part_size = %Lx\n" part_size;
  { part_status = part_status;
    part_type = part_type;
    part_dev = new partition_device dev partno first_lba part_size;
    part_content = `Unknown }

(*
This code worked previously, but now needs some love ...
XXX

(* Probe an extended partition. *)
and probe_extended_partition max fd epart sect =
  if max > 0 then (
    (* Offset of the first EBR. *)
    let ebr_offs = sect *^ sector_size in
    (* EBR Signature? *)
    LargeFile.lseek fd (ebr_offs +^ 510L) SEEK_SET;
    let str = String.create 2 in
    if read fd str 0 2 <> 2 || str.[0] != '\x55' || str.[1] != '\xAA' then
      [] (* Not EBR *)
    else (
      (* Read the extended partition table entries (just 2 of them). *)
      LargeFile.lseek fd (ebr_offs +^ 446L) SEEK_SET;
      let str = String.create 32 in
      if read fd str 0 32 <> 32 then
	failwith (s_ "error reading extended partition")
      else (
	(* Extract partitions from the data. *)
	let part1, part2 =
	  match List.map (get_partition str) [ 0; 16 ] with
	  | [p1;p2] -> p1,p2
	  | _ -> failwith (s_ "probe_extended_partition: internal error") in
	(* First partition entry has offset to the start of this partition. *)
	let part1 = { part1 with
			part_lba_start = sect +^ part1.part_lba_start } in
	(* Second partition entry is zeroes if end of list, otherwise points
	 * to the next partition.
	 *)
	if part2.part_status = NullEntry then
	  [part1]
	else
	  part1 :: probe_extended_partition
	             (max-1) fd epart (sect +^ part2.part_lba_start)
      )
    )
  )
  else []
*)

(* Ugh, fake a UInt32 -> UInt64 conversion without sign extension, until
 * we get working UInt32/UInt64 modules in extlib.
 *)
and uint64_of_int32 u32 =
  let i64 = Int64.of_int32 u32 in
  if u32 >= 0l then i64
  else Int64.add i64 0x1_0000_0000_L

(* Register with main code. *)
let () = partition_type_register "MBR" probe_mbr