open Diskimage_utils
open Diskimage_lvm2_metadata
-let plugin_name = "LVM2"
+open Int63.Operators
-let sector_size = 512
-let sector_size64 = 512L
+let plugin_id = "LVM2"
+
+let sector_size_int = 512
+let sector_size = ~^sector_size_int
(*----------------------------------------------------------------------*)
(* Block device which can do linear maps, same as the kernel dm-linear.c *)
* satisfy any read request up to the full size.
*)
let size_in_extents =
- List.fold_left max 0L
+ List.fold_left max ~^0
(List.map (fun (_, end_extent, _, _) -> end_extent) segments) in
let size = size_in_extents *^ extent_size in
-object
+object (self)
inherit device
method name = name
method size = size
- (* Read method checks which segment the request lies inside and
- * maps it to the underlying device. If there is no mapping then
- * we have to return an error.
- *
- * The request must lie inside a single extent, otherwise this is
- * also an error (XXX - should lift this restriction, however default
- * extent size is 4 MB so we probably won't hit this very often).
+ (* The natural blocksize for LVM devices is the extent size.
+ * NB. Throws a runtime exception if the extent size is bigger
+ * than an int (only likely to matter on 32 bit).
*)
- method read offset len =
- let offset_in_extents = offset /^ extent_size in
+ method blocksize = extent_size
- (* Check we don't cross an extent boundary. *)
- if (offset +^ Int64.of_int (len-1)) /^ extent_size <> offset_in_extents
- then invalid_arg "linear_map_device: request crosses extent boundary";
-
- if offset_in_extents < 0L || offset_in_extents >= size_in_extents then
+ method private map i =
+ if i < ~^0 || i >= size_in_extents then
invalid_arg "linear_map_device: read outside device";
let rec loop = function
| [] ->
- invalid_arg "linear_map_device: offset not mapped"
+ None
| (start_extent, end_extent, dev, pvoffset) :: rest ->
- if start_extent <= offset_in_extents &&
- offset_in_extents < end_extent
- then dev#read (offset +^ pvoffset *^ extent_size) len
- else loop rest
+ if start_extent <= i && i < end_extent then (
+ let dev_offset = (pvoffset +^ i) *^ extent_size in
+ Some (start_extent, end_extent, dev, dev_offset, pvoffset)
+ ) else
+ loop rest
in
loop segments
+
+ (* Map block (extent) i to the underlying device. *)
+ method map_block i =
+ match self#map i with
+ | Some (_, _, dev, dev_offset, _) -> [dev, dev_offset]
+ | None -> []
+
+ (* Continguous span. *)
+ method contiguous offset =
+ let offset_in_extents = offset /^ extent_size in
+
+ (* Get the segment that this offset lies in. *)
+ match self#map offset_in_extents with
+ | Some (_, end_extent, dev, dev_offset, _) ->
+ (* Contiguous bytes up to the end of this extent. *)
+ end_extent *^ extent_size -^ offset
+ | None -> ~^0
+
+ (* NB. Use the superclass #read method. *)
end
(*----------------------------------------------------------------------*)
(* Probe to see if it's an LVM2 PV. *)
-let rec probe_pv lvm_plugin_id dev =
+let rec probe lvm_plugin_id dev =
try
let uuid, _ = read_pv_label dev in
if !debug then
eprintf "LVM2 detected PV UUID %s\n%!" uuid;
- { lvm_plugin_id = lvm_plugin_id; pv_uuid = uuid }
+ { lvm_plugin_id = lvm_plugin_id; pv_uuid = uuid; pv_dev = dev }
with exn ->
if !debug then prerr_endline (Printexc.to_string exn);
raise Not_found
* the nineth sector contains some additional information about
* the location of the current metadata.
*)
- let bits = dev#read_bitstring 0L (9 * sector_size) in
+ let bits = dev#read_bitstring ~^0 (~^9 *^ sector_size) in
(*Bitmatch.hexdump_bitstring stdout bits;*)
bitmatch bits with
| {
(* sector 0 *)
- sector0 : sector_size*8 : bitstring;
+ sector0 : sector_size_int*8 : bitstring;
(* sector 1 *)
"LABELONE" : 64 : string; (* "LABELONE" *)
_ : 128 : bitstring; (* Seems to contain something. *)
"LVM2 001" : 64 : string; (* "LVM2 001" *)
uuid : 256 : string; (* UUID *)
- endsect : (sector_size-64)*8 : bitstring; (* to end of second sector *)
+ endsect : (sector_size_int-64)*8 : bitstring;(* to end of second sector *)
(* sectors 2-7 *)
- sectors234567 : sector_size*8 * 6 : bitstring;
+ sectors234567 : sector_size_int*8 * 6 : bitstring;
(* sector 8 *)
_ : 320 : bitstring; (* start of sector 8 *)
} ->
(* Metadata offset is relative to end of PV label. *)
- let metadata_offset = metadata_offset +* 0x1000_l in
+ let metadata_offset = Int63.of_int32 metadata_offset +^ ~^0x1000 in
(* Metadata length appears to include the trailing \000 which
* we don't want.
*)
- let metadata_length = metadata_length -* 1_l in
+ let metadata_length = Int63.of_int32 metadata_length -^ ~^1 in
let metadata = read_metadata dev metadata_offset metadata_length in
(sprintf "LVM2: read_pv_label: %s: not an LVM2 physical volume"
dev#name)
-and read_metadata dev offset32 len32 =
+and read_metadata dev offset len =
if !debug then
- eprintf "metadata: offset 0x%lx len %ld bytes\n%!" offset32 len32;
+ eprintf "metadata: offset %s len %s bytes\n%!"
+ (Int63.to_string offset) (Int63.to_string len);
(* Check the offset and length are sensible. *)
- let offset64 =
- if offset32 <= Int32.max_int then Int64.of_int32 offset32
- else invalid_arg "LVM2: read_metadata: metadata offset too large" in
- let len64 =
- if len32 <= 2_147_483_647_l then Int64.of_int32 len32
- else invalid_arg "LVM2: read_metadata: metadata length too large" in
-
- if offset64 <= 0x1200L || offset64 >= dev#size
- || len64 <= 0L || offset64 +^ len64 >= dev#size then
+ if offset <= ~^0x1200 || offset >= dev#size
+ || len <= ~^0 || offset +^ len >= dev#size then
invalid_arg "LVM2: read_metadata: bad metadata offset or length";
(* If it is outside the disk boundaries, this will throw an exception,
* otherwise it will read and return the metadata string.
*)
- dev#read offset64 (Int64.to_int len64)
+ dev#read offset len
(*----------------------------------------------------------------------*)
(* We are passed a list of devices which we previously identified
* (as devices) and return them. Note that we don't try to detect
* what is on these LVs - that will be done in the main code.
*)
-let rec list_lvs devs =
+let rec list devs =
(* Read the UUID and metadata (again) from each device to end up with
* an assoc list of PVs, keyed on the UUID.
*)
(* Some useful getter functions. If these can't get a value
* from the metadata or if the type is wrong they raise Not_found.
*)
- let rec get_int64 field meta =
+ let rec get_int63 field meta =
match List.assoc field meta with
| Int i -> i
| _ -> raise Not_found
- and get_int field meta min max =
+ and get_int_bounded field meta max =
match List.assoc field meta with
- | Int i when Int64.of_int min <= i && i <= Int64.of_int max ->
- Int64.to_int i
+ | Int i when i >= ~^0 && i <= Int63.of_int max -> Int63.to_int i
| _ -> raise Not_found
and get_string field meta =
match List.assoc field meta with
let pvdevs, extent_size =
try
(* NB: extent_size is in sectors here - we convert to bytes. *)
- let extent_size = get_int "extent_size" vgmeta 0 (1024*1024) in
- let extent_size = Int64.of_int extent_size *^ sector_size64 in
+ let extent_size =
+ get_int_bounded "extent_size" vgmeta (1024*1024) in
+ let extent_size = Int63.of_int extent_size in
+ let extent_size = extent_size *^ sector_size in
(* Get the physical_volumes section of the metadata. *)
let pvdevs = get_meta "physical_volumes" vgmeta in
let _, dev = List.assoc pvuuid pvs in
(* Construct a PV device. *)
- let pe_start = get_int64 "pe_start" meta in
- let pe_start = pe_start *^ sector_size64 in
- let pe_count = get_int64 "pe_count" meta in
+ let pe_start = get_int63 "pe_start" meta in
+ let pe_start = pe_start *^ sector_size in
+ let pe_count = get_int63 "pe_count" meta in
let pe_count = pe_count *^ extent_size in
- let pvdev = new offset_device pvuuid pe_start pe_count dev in
+ let pvdev =
+ new offset_device
+ pvuuid (* name *)
+ pe_start pe_count (* start, size in bytes *)
+ (* don't really have a natural block size ... *)
+ extent_size
+ dev (* underlying device *) in
Some (pvname, pvdev)
| _ ->
) pvdevs, extent_size
with
(* Something went wrong - just return an empty map. *)
- Not_found -> [], 0L in
+ Not_found -> [], ~^0 in
(vgname, (pvuuids, vgmeta, pvdevs, extent_size))
) vgs in
function
| lvname, Metadata lvmeta ->
(try
- let segment_count = get_int "segment_count" lvmeta 0 1024 in
+ let segment_count =
+ get_int_bounded "segment_count" lvmeta 1024 in
(* Get the segments for this LV. *)
let segments = range 1 (segment_count+1) in
List.map (
fun segmeta ->
let start_extent =
- get_int64 "start_extent" segmeta in
+ get_int63 "start_extent" segmeta in
let extent_count =
- get_int64 "extent_count" segmeta in
+ get_int63 "extent_count" segmeta in
let segtype = get_string "type" segmeta in
(* Can only handle striped segments at the
if segtype <> "striped" then raise Not_found;
let stripe_count =
- get_int "stripe_count" segmeta 0 1024 in
+ get_int_bounded "stripe_count" segmeta 1024 in
let stripes = get_stripes "stripes" segmeta in
if List.length stripes <> stripe_count then
if !debug then (
List.iter (
fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
- eprintf "VG %s: (extent_size = %Ld bytes)\n" vgname extent_size;
+ eprintf "VG %s: (extent_size = %s bytes)\n" vgname
+ (Int63.to_string extent_size);
List.iter (
fun (lvname, segments) ->
eprintf " %s/%s:\n" vgname lvname;
List.iter (
fun (start_extent, extent_count, pvname, pvoffset) ->
- eprintf " start %Ld count %Ld at %s:%Ld\n"
- start_extent extent_count pvname pvoffset
+ eprintf " start %s count %s at %s:%s\n"
+ (Int63.to_string start_extent)
+ (Int63.to_string extent_count)
+ pvname (Int63.to_string pvoffset)
) segments
) lvs
) vgs;