diff options
Diffstat (limited to 'virt-df/virt_df_mbr.ml')
-rw-r--r-- | virt-df/virt_df_mbr.ml | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/virt-df/virt_df_mbr.ml b/virt-df/virt_df_mbr.ml new file mode 100644 index 0000000..b9a6cb7 --- /dev/null +++ b/virt-df/virt_df_mbr.ml @@ -0,0 +1,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 : device) = + (* 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 |