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.
27 open Virt_df_gettext.Gettext
31 let sector_size64 = 512L
33 (* Maximum number of extended partitions possible. *)
34 let max_extended_partitions = 100
36 (* Device representing a single partition. It just acts as an offset
37 * into the underlying device.
40 * (1) 'start'/'size' are measured in sectors.
41 * (2) 'partno' is the partition number, starting at 1
42 * (cf. /dev/hda1 is the first partition).
43 * (3) 'dev' is the underlying block device.
45 class partition_device dev partno start size =
46 let devname = dev#name in
47 let name = sprintf "%s%d" devname partno in
48 let start = start *^ sector_size64 in
49 let size = size *^ sector_size64 in
54 method read offset len =
55 if offset < 0L || len < 0 || offset +^ Int64.of_int len > size then
57 sprintf "%s: tried to read outside partition boundaries (%Ld/%d/%Ld)"
60 dev#read (start+^offset) len
64 {{:http://en.wikipedia.org/wiki/Master_boot_record}master boot record}
65 (if it is one) and read the partitions.
67 @raise Not_found if it is not an MBR.
69 let rec probe_mbr dev =
70 (* Adjust size to sectors. *)
71 let size = dev#size /^ sector_size64 in
73 (* Read the first sector. *)
75 try dev#read_bitstring 0L sector_size
76 with exn -> raise Not_found in
78 (* Does this match a likely-looking MBR? *)
80 | padding : 3568 : bitstring; (* padding to byte offset 446 *)
81 part0 : 128 : bitstring; (* partitions *)
82 part1 : 128 : bitstring;
83 part2 : 128 : bitstring;
84 part3 : 128 : bitstring;
85 0x55 : 8; 0xAA : 8 -> (* MBR signature *)
87 (* Parse the partition table entries. *)
89 List.mapi (parse_mbr_entry dev) [part0;part1;part2;part3] in
92 (* Read extended partition data. *)
93 let extendeds = List.map (
95 | { part_type = 0x05 } as part ->
96 probe_extended_partition
97 max_extended_partitions fd part part.part_lba_start
100 let extendeds = List.concat extendeds in
101 primaries @ extendeds
103 { parts_name = "MBR"; parts = primaries }
106 raise Not_found (* not an MBR *)
108 (* Parse a single partition table entry. See the table here:
109 * http://en.wikipedia.org/wiki/Master_boot_record
111 and parse_mbr_entry dev i bits =
113 | 0l : 32; 0l : 32; 0l : 32; 0l : 32 ->
114 { part_status = NullEntry; part_type = 0;
115 part_dev = null_device; part_content = `Unknown }
117 | 0 : 8; first_chs : 24;
118 part_type : 8; last_chs : 24;
119 first_lba : 32 : unsigned, littleendian;
120 part_size : 32 : unsigned, littleendian ->
121 make_mbr_entry Nonbootable dev (i+1) part_type first_lba part_size
123 | 0x80 : 8; first_chs : 24;
124 part_type : 8; last_chs : 24;
125 first_lba : 32 : unsigned, littleendian;
126 part_size : 32 : unsigned, littleendian ->
127 make_mbr_entry Bootable dev (i+1) part_type first_lba part_size
130 { part_status = Malformed; part_type = 0;
131 part_dev = null_device; part_content = `Unknown }
133 and make_mbr_entry part_status dev partno part_type first_lba part_size =
134 let first_lba = uint64_of_int32 first_lba in
135 let part_size = uint64_of_int32 part_size in
136 eprintf "first_lba = %Lx\n" first_lba;
137 eprintf "part_size = %Lx\n" part_size;
138 { part_status = part_status;
139 part_type = part_type;
140 part_dev = new partition_device dev partno first_lba part_size;
141 part_content = `Unknown }
144 This code worked previously, but now needs some love ...
147 (* Probe an extended partition. *)
148 and probe_extended_partition max fd epart sect =
150 (* Offset of the first EBR. *)
151 let ebr_offs = sect *^ sector_size in
153 LargeFile.lseek fd (ebr_offs +^ 510L) SEEK_SET;
154 let str = String.create 2 in
155 if read fd str 0 2 <> 2 || str.[0] != '\x55' || str.[1] != '\xAA' then
158 (* Read the extended partition table entries (just 2 of them). *)
159 LargeFile.lseek fd (ebr_offs +^ 446L) SEEK_SET;
160 let str = String.create 32 in
161 if read fd str 0 32 <> 32 then
162 failwith (s_ "error reading extended partition")
164 (* Extract partitions from the data. *)
166 match List.map (get_partition str) [ 0; 16 ] with
168 | _ -> failwith (s_ "probe_extended_partition: internal error") in
169 (* First partition entry has offset to the start of this partition. *)
170 let part1 = { part1 with
171 part_lba_start = sect +^ part1.part_lba_start } in
172 (* Second partition entry is zeroes if end of list, otherwise points
173 * to the next partition.
175 if part2.part_status = NullEntry then
178 part1 :: probe_extended_partition
179 (max-1) fd epart (sect +^ part2.part_lba_start)
186 (* Ugh, fake a UInt32 -> UInt64 conversion without sign extension, until
187 * we get working UInt32/UInt64 modules in extlib.
189 and uint64_of_int32 u32 =
190 let i64 = Int64.of_int32 u32 in
191 if u32 >= 0l then i64
192 else Int64.add i64 0x1_0000_0000_L
194 (* Register with main code. *)
195 let () = partition_type_register "MBR" probe_mbr