1 (* 'df' command for virtual domains.
3 (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
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.
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.
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.
20 Support for Master Boot Record partition scheme.
33 let sector_size = ~^512
35 (* Maximum number of extended partitions possible. *)
36 let max_extended_partitions = 100
38 (* The private data attached to a partitions structure. *)
39 type private_t = mbr_part list (* list of partitions that we found *)
41 mbr_part_start : int63; (* start of partition in bytes *)
42 mbr_part_size : int63; (* size of partition in bytes *)
45 let attach_private_data, get_private_data =
46 private_data_functions (fun {parts_cb = {parts_cb_uq = u}} -> u)
48 (* Device representing a single partition. It just acts as an offset
49 * into the underlying device.
52 * (1) 'start'/'size' are measured in sectors.
53 * (2) 'partno' is the partition number, starting at 1
54 * (cf. /dev/hda1 is the first partition).
55 * (3) 'dev' is the underlying block device.
56 * (4) natural blocksize to use is sector size.
58 class partition_device partno start size dev =
59 let devname = dev#name in
60 let name = sprintf "%s%d" devname partno in
61 let start = start *^ sector_size in
62 let size = size *^ sector_size in
64 inherit offset_device name start size sector_size dev
68 {{:http://en.wikipedia.org/wiki/Master_boot_record}master boot record}
69 (if it is one) and read the partitions.
71 @raise Not_found if it is not an MBR.
74 (* Read the first sector. *)
76 try dev#read_bitstring ~^0 sector_size
77 with exn -> raise Not_found in
79 (* Does this match a likely-looking MBR? *)
81 | { _ : 3568 : bitstring; (* padding to byte offset 446 *)
82 part0 : 128 : bitstring; (* partitions *)
83 part1 : 128 : bitstring;
84 part2 : 128 : bitstring;
85 part3 : 128 : bitstring;
86 0x55 : 8; 0xAA : 8 } -> (* MBR signature *)
88 (* Parse the partition table entries. *)
90 List.mapi (parse_mbr_entry dev) [part0;part1;part2;part3] in
93 (* Read extended partition data. *)
94 let extendeds = List.map (
96 | { part_type = 0x05 } as part ->
97 probe_extended_partition
98 max_extended_partitions fd part part.part_lba_start
101 let extendeds = List.concat extendeds in
104 let parts = primaries (* @ extendeds *) in
105 let privs = List.concat (List.map snd parts) in
106 let parts = List.map fst parts in
108 let r = { parts_cb = callbacks (); parts_dev = dev; parts = parts } in
109 attach_private_data r privs;
113 raise Not_found (* not an MBR *)
115 (* Parse a single partition table entry. See the table here:
116 * http://en.wikipedia.org/wiki/Master_boot_record
118 and parse_mbr_entry dev i bits =
120 | { 0l : 32; 0l : 32; 0l : 32; 0l : 32 } ->
121 { part_status = NullEntry; part_type = 0;
122 part_dev = null_device; part_content = `Unknown }, []
124 | { ((0|0x80) as bootable) : 8; first_chs : 24;
125 part_type : 8; last_chs : 24;
126 first_lba : 32 : unsigned, littleendian;
127 part_size : 32 : unsigned, littleendian } ->
129 let bootable = if bootable = 0 then Nonbootable else Bootable in
130 let first_lba = Int63.of_int32 first_lba in
131 let part_size = Int63.of_int32 part_size in
135 eprintf "parse_mbr_entry: first_lba = %s part_size = %s\n%!"
136 (Int63.to_string first_lba) (Int63.to_string part_size);
139 part_status = bootable;
140 part_type = part_type;
141 part_dev = new partition_device partno first_lba part_size dev;
142 part_content = `Unknown;
145 (* Extra private data which we'll use to calculate free offsets. *)
147 mbr_part_start = first_lba *^ sector_size;
148 mbr_part_size = part_size *^ sector_size;
154 { part_status = Malformed; part_type = 0;
155 part_dev = null_device; part_content = `Unknown }, []
158 This code worked previously, but now needs some love ...
161 (* Probe an extended partition. *)
162 and probe_extended_partition max fd epart sect =
164 (* Offset of the first EBR. *)
165 let ebr_offs = sect *^ sector_size in
167 LargeFile.lseek fd (ebr_offs +^ 510L) SEEK_SET;
168 let str = String.create 2 in
169 if read fd str 0 2 <> 2 || str.[0] != '\x55' || str.[1] != '\xAA' then
172 (* Read the extended partition table entries (just 2 of them). *)
173 LargeFile.lseek fd (ebr_offs +^ 446L) SEEK_SET;
174 let str = String.create 32 in
175 if read fd str 0 32 <> 32 then
176 failwith (s_ "error reading extended partition")
178 (* Extract partitions from the data. *)
180 match List.map (get_partition str) [ 0; 16 ] with
182 | _ -> failwith (s_ "probe_extended_partition: internal error") in
183 (* First partition entry has offset to the start of this partition. *)
184 let part1 = { part1 with
185 part_lba_start = sect +^ part1.part_lba_start } in
186 (* Second partition entry is zeroes if end of list, otherwise points
187 * to the next partition.
189 if part2.part_status = NullEntry then
192 part1 :: probe_extended_partition
193 (max-1) fd epart (sect +^ part2.part_lba_start)
201 (* Ugh, fake a UInt32 -> UInt64 conversion without sign extension, until
202 * we get working UInt32/UInt64 modules in extlib.
204 and uint64_of_int32 u32 =
205 let i64 = Int64.of_int32 u32 in
206 if u32 >= 0l then i64
207 else Int64.add i64 0x1_0000_0000_L
210 and offset_is_free parts offset =
211 let privs = get_private_data parts in
213 (* The first partition is somehow privileged in that we assume
214 * everything before this is not free. Usually this is the first
215 * 63 sectors containing the MBR itself and sectors which should
216 * be blank but in reality contain all sorts of stupid hacks like
217 * alternate partitioning schemes.
221 | { mbr_part_start = start; mbr_part_size = size } :: rest ->
222 if offset < start +^ size then
225 let rec loop = function
226 | [] -> true (* not in a partition, must be free *)
227 | { mbr_part_start = start; mbr_part_size = size } :: rest ->
228 if start <= offset && offset < start +^ size then
239 parts_cb_uq = (incr i; !i);
241 parts_cb_offset_is_free = offset_is_free;
244 (* Register the plugin. *)
245 let () = register_plugin ~partitioner:probe id