Rebuilt deps for x86-64
[virt-df.git] / lib / diskimage_mbr.ml
1 (* 'df' command for virtual domains.
2
3    (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
4    http://libvirt.org/
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20    Support for Master Boot Record partition scheme.
21 *)
22
23 open Printf
24 open Unix
25 open ExtList
26
27 open Diskimage_utils
28
29 open Int63.Operators
30
31 let plugin_id = "mbr"
32
33 let sector_size = ~^512
34
35 (* Maximum number of extended partitions possible. *)
36 let max_extended_partitions = 100
37
38 (* Device representing a single partition.  It just acts as an offset
39  * into the underlying device.
40  *
41  * Notes:
42  * (1) 'start'/'size' are measured in sectors.
43  * (2) 'partno' is the partition number, starting at 1
44  *     (cf. /dev/hda1 is the first partition).
45  * (3) 'dev' is the underlying block device.
46  * (4) natural blocksize to use is sector size.
47  *)
48 class partition_device partno start size dev =
49   let devname = dev#name in
50   let name = sprintf "%s%d" devname partno in
51   let start = start *^ sector_size in
52   let size = size *^ sector_size in
53 object (self)
54   inherit offset_device name start size sector_size dev
55 end
56
57 (** Probe the
58     {{:http://en.wikipedia.org/wiki/Master_boot_record}master boot record}
59     (if it is one) and read the partitions.
60
61     @raise Not_found if it is not an MBR.
62  *)
63 let rec probe dev =
64   (* Read the first sector. *)
65   let bits =
66     try dev#read_bitstring ~^0 sector_size
67     with exn -> raise Not_found in
68
69   (* Does this match a likely-looking MBR? *)
70   bitmatch bits with
71   | { _ : 3568 : bitstring;             (* padding to byte offset 446 *)
72       part0 : 128 : bitstring;          (* partitions *)
73       part1 : 128 : bitstring;
74       part2 : 128 : bitstring;
75       part3 : 128 : bitstring;
76       0x55 : 8; 0xAA : 8 } ->           (* MBR signature *)
77
78       (* Parse the partition table entries. *)
79       let primaries =
80         List.mapi (parse_mbr_entry dev) [part0;part1;part2;part3] in
81
82 (*
83       (* Read extended partition data. *)
84       let extendeds = List.map (
85         function
86         | { part_type = 0x05 } as part ->
87             probe_extended_partition
88               max_extended_partitions fd part part.part_lba_start
89         | part -> []
90       ) primaries in
91       let extendeds = List.concat extendeds in
92       primaries @ extendeds
93 *)
94       { parts_plugin_id = plugin_id; parts_dev = dev; parts = primaries }
95
96   | { _ } ->
97       raise Not_found                   (* not an MBR *)
98
99 (* Parse a single partition table entry.  See the table here:
100  * http://en.wikipedia.org/wiki/Master_boot_record
101  *)
102 and parse_mbr_entry dev i bits =
103   bitmatch bits with
104   | { 0l : 32; 0l : 32; 0l : 32; 0l : 32 } ->
105       { part_status = NullEntry; part_type = 0;
106         part_dev = null_device; part_content = `Unknown }
107
108   | { ((0|0x80) as bootable) : 8; first_chs : 24;
109       part_type : 8; last_chs : 24;
110       first_lba : 32 : unsigned, littleendian;
111       part_size : 32 : unsigned, littleendian } ->
112       let bootable = if bootable = 0 then Nonbootable else Bootable in
113       make_mbr_entry bootable dev (i+1) part_type first_lba part_size
114
115   | { _ } ->
116       { part_status = Malformed; part_type = 0;
117         part_dev = null_device; part_content = `Unknown }
118
119 and make_mbr_entry part_status dev partno part_type first_lba part_size =
120   let first_lba = Int63.of_int32 first_lba in
121   let part_size = Int63.of_int32 part_size in
122   (*
123     XXX Used to be:
124   let first_lba = uint63_of_int32 first_lba in
125   let part_size = uint63_of_int32 part_size in
126   *)
127   if !debug then
128     eprintf "make_mbr_entry: first_lba = %s part_size = %s\n%!"
129       (Int63.to_string first_lba) (Int63.to_string part_size);
130   { part_status = part_status;
131     part_type = part_type;
132     part_dev = new partition_device partno first_lba part_size dev;
133     part_content = `Unknown }
134
135 (*
136 This code worked previously, but now needs some love ...
137 XXX
138
139 (* Probe an extended partition. *)
140 and probe_extended_partition max fd epart sect =
141   if max > 0 then (
142     (* Offset of the first EBR. *)
143     let ebr_offs = sect *^ sector_size in
144     (* EBR Signature? *)
145     LargeFile.lseek fd (ebr_offs +^ 510L) SEEK_SET;
146     let str = String.create 2 in
147     if read fd str 0 2 <> 2 || str.[0] != '\x55' || str.[1] != '\xAA' then
148       [] (* Not EBR *)
149     else (
150       (* Read the extended partition table entries (just 2 of them). *)
151       LargeFile.lseek fd (ebr_offs +^ 446L) SEEK_SET;
152       let str = String.create 32 in
153       if read fd str 0 32 <> 32 then
154         failwith (s_ "error reading extended partition")
155       else (
156         (* Extract partitions from the data. *)
157         let part1, part2 =
158           match List.map (get_partition str) [ 0; 16 ] with
159           | [p1;p2] -> p1,p2
160           | _ -> failwith (s_ "probe_extended_partition: internal error") in
161         (* First partition entry has offset to the start of this partition. *)
162         let part1 = { part1 with
163                         part_lba_start = sect +^ part1.part_lba_start } in
164         (* Second partition entry is zeroes if end of list, otherwise points
165          * to the next partition.
166          *)
167         if part2.part_status = NullEntry then
168           [part1]
169         else
170           part1 :: probe_extended_partition
171                      (max-1) fd epart (sect +^ part2.part_lba_start)
172       )
173     )
174   )
175   else []
176 *)
177
178 (*
179 (* Ugh, fake a UInt32 -> UInt64 conversion without sign extension, until
180  * we get working UInt32/UInt64 modules in extlib.
181  *)
182 and uint64_of_int32 u32 =
183   let i64 = Int64.of_int32 u32 in
184   if u32 >= 0l then i64
185   else Int64.add i64 0x1_0000_0000_L
186 *)