1 (* 'df' command for virtual domains.
3 (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version,
10 with the OCaml linking exception described in ../COPYING.LIB.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 Support for Master Boot Record partition scheme.
34 let sector_size = ~^512
36 (* Maximum number of extended partitions possible. *)
37 let max_extended_partitions = 100
39 (* The private data attached to a partitions structure. *)
41 mbr_part_start : int63; (* start of partition in SECTORS *)
42 mbr_part_size : int63; (* size of partition in SECTORS *)
44 let null_priv = { mbr_part_start = ~^0; mbr_part_size = ~^0 }
46 let attach_private_data, get_private_data =
47 private_data_functions (fun {parts_cb = {parts_cb_uq = u}} -> u)
49 (* Device representing a single partition. It just acts as an offset
50 * into the underlying device.
53 * (1) 'start'/'size' are measured in sectors.
54 * (2) 'partno' is the partition number, starting at 1
55 * (cf. /dev/hda1 is the first partition).
56 * (3) 'dev' is the underlying block device.
57 * (4) natural blocksize to use is sector size.
59 class partition_device partno start size dev =
60 let devname = dev#name in
61 let name = sprintf "%s%d" devname partno in
62 let start = start *^ sector_size in
63 let size = size *^ sector_size in
65 inherit offset_device name start size sector_size dev
69 {{:http://en.wikipedia.org/wiki/Master_boot_record}master boot record}
70 (if it is one) and read the partitions.
72 @raise Not_found if it is not an MBR.
75 (* Read the first sector. *)
77 try dev#read_bitstring ~^0 sector_size
78 with exn -> raise Not_found in
80 (* Does this match a likely-looking MBR? *)
82 | { _ : 3568 : bitstring; (* padding to byte offset 446 *)
83 part0 : 128 : bitstring; (* partitions *)
84 part1 : 128 : bitstring;
85 part2 : 128 : bitstring;
86 part3 : 128 : bitstring;
87 0x55 : 8; 0xAA : 8 } -> (* MBR signature *)
89 (* Parse the partition table entries. *)
90 let primaries = List.map parse_mbr_entry [part0;part1;part2;part3] in
92 (* Extended partitions are primary partitions with part_type 0x05,
93 * containing extended partition data.
95 let extendeds = List.map (
97 | ({ part_type = 0x05 }, { mbr_part_start = start }) ->
98 probe_extended_partition max_extended_partitions dev start
101 let extendeds = List.concat extendeds in
103 let parts : (partition * mbr_priv) list = primaries @ extendeds in
105 (* Create partition devices for all partitions that aren't null. *)
106 let parts = List.mapi (
107 fun partno (part, priv) ->
108 let partno = partno+1 in
110 | { part_status = (Bootable|Nonbootable) } as part ->
111 let start = priv.mbr_part_start and size = priv.mbr_part_size in
112 let dev = new partition_device partno start size dev in
113 { part with part_dev = dev }, priv
117 (* Separate out the private data and attach it. *)
118 let privs = List.map snd parts in
119 let parts = List.map fst parts in
121 let r = { parts_cb = callbacks (); parts_dev = dev; parts = parts } in
122 attach_private_data r privs;
126 raise Not_found (* not an MBR *)
128 (* Parse a single partition table entry. See the table here:
129 * http://en.wikipedia.org/wiki/Master_boot_record
131 and parse_mbr_entry bits =
133 | { 0l : 32; 0l : 32; 0l : 32; 0l : 32 } ->
134 { part_status = NullEntry; part_type = 0;
135 part_dev = null_device; part_content = `Unknown }, null_priv
137 | { ((0|0x80) as bootable) : 8; first_chs : 24;
138 part_type : 8; last_chs : 24;
139 first_lba : 32 : unsigned, littleendian;
140 part_size : 32 : unsigned, littleendian } ->
142 let bootable = if bootable = 0 then Nonbootable else Bootable in
143 let first_lba = Int63.of_int32 first_lba in
144 let part_size = Int63.of_int32 part_size in
147 eprintf "parse_mbr_entry: first_lba = %s part_size = %s\n%!"
148 (Int63.to_string first_lba) (Int63.to_string part_size);
151 part_status = bootable; part_type = part_type;
152 part_dev = null_device; (* This gets overwritten by probe. *)
153 part_content = `Unknown;
156 (* Extra private data which we'll use to calculate free offsets. *)
158 mbr_part_start = first_lba;
159 mbr_part_size = part_size;
165 { part_status = Malformed; part_type = 0;
166 part_dev = null_device; part_content = `Unknown }, null_priv
168 (* Probe an extended partition. *)
169 and probe_extended_partition max dev start =
172 (* Get the partition table (like a boot sector). *)
173 let bits = dev#read_bitstring (start *^ sector_size) sector_size in
176 | { _ : 3568 : bitstring; (* padding to byte offset 446 *)
177 part : 128 : bitstring; (* this partition *)
178 next : 128 : bitstring; (* pointer to next extended partition *)
179 _ : 128 : bitstring; (* ignored - should be zero *)
181 0x55 : 8; 0xAA : 8 } -> (* MBR signature *)
183 let (part, ppriv) = parse_mbr_entry part in
185 (* The first partition's LBA is actually offset relative
186 * to the current sector.
189 { ppriv with mbr_part_start = ppriv.mbr_part_start +^ start } in
191 let (next, npriv) = parse_mbr_entry next in
193 if next.part_status = NullEntry then
194 [ part, ppriv ] (* End of list. *)
196 let start_of_next = start +^ npriv.mbr_part_start in
198 probe_extended_partition (max-1) dev start_of_next
202 invalid_arg "mbr: invalid extended partition table"
205 prerr_endline (Printexc.to_string exn);
208 prerr_endline "mbr: too many extended partitions";
213 (* Ugh, fake a UInt32 -> UInt64 conversion without sign extension, until
214 * we get working UInt32/UInt64 modules in extlib.
216 and uint64_of_int32 u32 =
217 let i64 = Int64.of_int32 u32 in
218 if u32 >= 0l then i64
219 else Int64.add i64 0x1_0000_0000_L
222 and offset_is_free parts offset =
223 let privs = get_private_data parts in
225 (* The first partition is somehow privileged in that we assume
226 * everything before this is not free. Usually this is the first
227 * 63 sectors containing the MBR itself and sectors which should
228 * be blank but in reality contain all sorts of stupid hacks like
229 * alternate partitioning schemes.
233 | { mbr_part_start = start; mbr_part_size = size } :: rest ->
234 let start = start *^ sector_size in
235 let size = size *^ sector_size in
236 if offset < start +^ size then
239 let rec loop = function
240 | [] -> true (* not in a partition, must be free *)
241 | { mbr_part_start = start; mbr_part_size = size } :: rest ->
242 let start = start *^ sector_size in
243 let size = size *^ sector_size in
244 if start <= offset && offset < start +^ size then
255 parts_cb_uq = (incr i; !i);
257 parts_cb_offset_is_free = offset_is_free;
260 (* Register the plugin. *)
261 let () = register_plugin ~partitioner:probe id