From 81593022de32f72e6dd7430519009cb70659eab6 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Thu, 1 Jan 1970 00:00:00 +0000 Subject: [PATCH] LVM2 parsing complete and working. --- virt-df/virt_df_lvm2.ml | 207 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 188 insertions(+), 19 deletions(-) diff --git a/virt-df/virt_df_lvm2.ml b/virt-df/virt_df_lvm2.ml index 314586e..6a8f573 100644 --- a/virt-df/virt_df_lvm2.ml +++ b/virt-df/virt_df_lvm2.ml @@ -33,6 +33,63 @@ let plugin_name = "LVM2" let sector_size = 512 let sector_size64 = 512L +(*----------------------------------------------------------------------*) +(* Block device which can do linear maps, same as the kernel dm-linear.c *) +class linear_map_device name extent_size segments = + (* The segments are passed containing (start_extent, extent_count, ...) + * but it's easier to deal with (start_extent, end_extent, ...) so + * rewrite them. + *) + let segments = List.map + (fun (start_extent, extent_count, dev, pvoffset) -> + (start_extent, start_extent +^ extent_count, dev, pvoffset) + ) segments in + + (* Calculate the size of the device (in bytes). Note that because + * of the random nature of the mapping this doesn't imply that we can + * satisfy any read request up to the full size. + *) + let size_in_extents = + List.fold_left max 0L + (List.map (fun (_, end_extent, _, _) -> end_extent) segments) in + let size = size_in_extents *^ extent_size in +object + 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). + *) + method read offset len = + let offset_in_extents = offset /^ extent_size in + + (* 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 + invalid_arg "linear_map_device: read outside device"; + + let rec loop = function + | [] -> + invalid_arg "linear_map_device: offset not mapped" + | (start_extent, end_extent, dev, pvoffset) :: rest -> + eprintf "pvoffset = %Ld\n" pvoffset; + if start_extent <= offset_in_extents && + offset_in_extents < end_extent + then dev#read (offset +^ pvoffset *^ extent_size) len + else loop rest + in + loop segments +end + +(*----------------------------------------------------------------------*) (* Probe to see if it's an LVM2 PV. *) let rec probe_pv lvm_plugin_id dev = try @@ -107,6 +164,7 @@ and read_metadata dev offset32 len32 = *) dev#read offset64 (Int64.to_int len64) +(*----------------------------------------------------------------------*) (* We are passed a list of devices which we previously identified * as PVs belonging to us. From these produce a list of all LVs * (as devices) and return them. Note that we don't try to detect @@ -129,13 +187,13 @@ let rec list_lvs devs = dev) ) pvs in - (* Print the parsed metadata. - List.iter ( - fun (uuid, (metadata, dev)) -> - eprintf "metadata for UUID %s:\n" uuid; - output_metadata stderr metadata - ) pvs; - *) + (* Print the parsed metadata. *) + if !debug then + List.iter ( + fun (uuid, (metadata, dev)) -> + eprintf "metadata for PV UUID %s on %s:\n" uuid dev#name; + output_metadata stderr metadata + ) pvs; (* Scan for volume groups. The first entry in the metadata * appears to be the volume group name. This gives us a @@ -191,18 +249,76 @@ let rec list_lvs devs = and get_meta field meta = match List.assoc field meta with | Metadata md -> md - | _ -> raise Not_found in + | _ -> raise Not_found + and get_stripes field meta = (* List of (string,int) pairs. *) + match List.assoc field meta with + | List xs -> + let rec loop = function + | [] -> [] + | String pvname :: Int offset :: xs -> + (pvname, offset) :: loop xs + | _ -> raise Not_found + in + loop xs + | _ -> raise Not_found in + (* The volume groups refer to the physical volumes using their + * own naming system ("pv0", "pv1", etc.) instead of PV UUIDs. + * + * Each PV also has a start (in sectors) & count (in extents) + * of the writable area (the bit after the superblock and metadata) + * which normally starts at sector 384. + * + * Create a PV device (simple offset + size) and a map from PV + * names to these devices. + *) + let vgs = List.map ( + fun (vgname, (pvuuids, vgmeta)) -> + 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 + + (* Get the physical_volumes section of the metadata. *) + let pvdevs = get_meta "physical_volumes" vgmeta in + + List.filter_map ( + function + | (pvname, Metadata meta) -> + (* Get the UUID. *) + let pvuuid = get_string "id" meta in + let pvuuid = canonical_uuid pvuuid in + + (* Get the underlying physical device. *) + 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_count = pe_count *^ extent_size in + let pvdev = new offset_device pvuuid pe_start pe_count dev in + + Some (pvname, pvdev) + | _ -> + None + ) pvdevs, extent_size + with + (* Something went wrong - just return an empty map. *) + Not_found -> [], 0L in + (vgname, (pvuuids, vgmeta, pvdevs, extent_size)) + ) vgs in + (* Scan for logical volumes. Each VG contains several LVs. * This gives us a list of LVs within each VG (hence extends * the vgs variable). *) let vgs = List.map ( - fun (vgname, (pvuuids, vgmeta)) -> + fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size)) -> let lvs = try - let extent_size = get_int "extent_size" vgmeta 0 (256*1024) in let lvs = get_meta "logical_volumes" vgmeta in let lvs = List.filter_map ( function @@ -225,15 +341,29 @@ let rec list_lvs devs = let extent_count = get_int64 "extent_count" segmeta in let segtype = get_string "type" segmeta in + + (* Can only handle striped segments at the + * moment. XXX + *) if segtype <> "striped" then raise Not_found; + let stripe_count = get_int "stripe_count" segmeta 0 1024 in - (* let stripes = in *) + let stripes = get_stripes "stripes" segmeta in + + if List.length stripes <> stripe_count then + raise Not_found; - (start_extent, extent_count, stripe_count) + (* Can only handle linear striped segments at + * the moment. XXX + *) + if stripe_count <> 1 then raise Not_found; + let pvname, pvoffset = List.hd stripes in + + (start_extent, extent_count, pvname, pvoffset) ) segments in - Some (lvname, (lvmeta, segments)) + Some (lvname, segments) with (* Something went wrong with segments - omit this LV. *) Not_found -> None) @@ -245,19 +375,58 @@ let rec list_lvs devs = Not_found -> (* Something went wrong - assume no LVs found. *) [] in - (vgname, (pvuuids, vgmeta, lvs)) + (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ) vgs in (* Print the LVs. *) - if !debug then + if !debug then ( List.iter ( - fun (vgname, (pvuuids, vgmeta, lvs)) -> - let lvnames = List.map fst lvs in - eprintf "VG %s contains LVs: %s\n%!" vgname (String.concat ", " lvnames) + fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) -> + eprintf "VG %s: (extent_size = %Ld bytes)\n" vgname 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 + ) segments + ) lvs ) vgs; + flush stderr + ); + + (* Finally we can set up devices for the LVs. *) + let lvs = + List.map ( + fun (vgname, (pvuuid, vgmeta, pvdevs, extent_size, lvs)) -> + try + List.map ( + fun (lvname, segments) -> + let name = vgname ^ "/" ^ lvname in + let segments = List.map ( + fun (start_extent, extent_count, pvname, pvoffset) -> + (* Get the PV device. *) + let pvdev = List.assoc pvname pvdevs in + + (* Extents mapped to: *) + (start_extent, extent_count, pvdev, pvoffset) + ) segments in + + (* Create a linear mapping device. *) + let lv_dev = new linear_map_device name extent_size segments in + + { lv_dev = lv_dev } + ) lvs + with + Not_found -> [] + ) vgs in + let lvs = List.concat lvs in - [] + (* Return the list of LV devices. *) + lvs +(*----------------------------------------------------------------------*) (* Register with main code. *) let () = lvm_type_register plugin_name probe_pv list_lvs -- 1.8.3.1