Generate uniquifier so that private data functions will work.
[virt-df.git] / lib / diskimage_lvm2.ml
index 0f00e1f..da61600 100644 (file)
 open Printf
 open ExtList
 
-open Diskimage_utils
+open Diskimage_impl
 open Diskimage_lvm2_metadata
 
-let plugin_id = "LVM2"
+open Int63.Operators
 
-let sector_size = 512
-let sector_size64 = 512L
+let 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 *)
@@ -48,7 +50,7 @@ class linear_map_device name extent_size segments =
    * 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 (self)
@@ -60,35 +62,52 @@ object (self)
    * NB. Throws a runtime exception if the extent size is bigger
    * than an int (only likely to matter on 32 bit).
    *)
-  method blocksize = Int64.to_int extent_size
+  method blocksize = extent_size
 
-  (* Map block (extent) i to the underlying device. *)
-  method mapblock i =
-    if i < 0L || i >= 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
       | [] ->
-         []
+         None
       | (start_extent, end_extent, dev, pvoffset) :: rest ->
-         if start_extent <= i && i < end_extent then
-           [dev, (pvoffset +^ i) *^ extent_size]
-         else
+         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 lvm_plugin_id dev =
+let rec probe 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 }
+    { pv_cb = callbacks (); pv_uuid = uuid; pv_dev = dev }
   with exn ->
     if !debug then prerr_endline (Printexc.to_string exn);
     raise Not_found
@@ -99,24 +118,24 @@ and read_pv_label dev =
    * 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 *)
@@ -126,11 +145,11 @@ and read_pv_label dev =
     } ->
 
       (* 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
 
@@ -141,26 +160,20 @@ and read_pv_label dev =
        (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
@@ -168,15 +181,17 @@ and read_metadata dev offset32 len32 =
  * (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 devs =
+and list_lvs pvs =
   (* Read the UUID and metadata (again) from each device to end up with
    * an assoc list of PVs, keyed on the UUID.
+   *
+   * XXX We've already read this - we should save it in the pv struct.
    *)
   let pvs = List.map (
-    fun dev ->
+    fun { pv_dev = dev } ->
       let uuid, metadata = read_pv_label dev in
       (uuid, (metadata, dev))
-  ) devs in
+  ) pvs in
 
   (* Parse the metadata using the external lexer/parser. *)
   let pvs = List.map (
@@ -231,14 +246,13 @@ let rec list devs =
   (* 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
@@ -276,8 +290,10 @@ let rec list devs =
       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
@@ -293,16 +309,16 @@ let rec list devs =
                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 (* name *)
                    pe_start pe_count (* start, size in bytes *)
                    (* don't really have a natural block size ... *)
-                   (Int64.to_int extent_size)
+                   extent_size
                    dev (* underlying device *) in
 
                Some (pvname, pvdev)
@@ -311,7 +327,7 @@ let rec list devs =
          ) 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
 
@@ -328,7 +344,8 @@ let rec list devs =
            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
@@ -341,9 +358,9 @@ let rec list devs =
                     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
@@ -352,7 +369,7 @@ let rec list devs =
                         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
@@ -386,14 +403,17 @@ let rec list devs =
   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;
@@ -403,7 +423,7 @@ let rec list devs =
   (* Finally we can set up devices for the LVs. *)
   let lvs =
     List.map (
-      fun (vgname, (pvuuid, vgmeta, pvdevs, extent_size, lvs)) ->
+      fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
        try
          List.map (
            fun (lvname, segments) ->
@@ -429,3 +449,21 @@ let rec list devs =
 
   (* Return the list of LV devices. *)
   lvs
+
+(* XXX We don't currently have enough information in the PV
+ * structure to determine quickly which blocks are used.  Need
+ * to store the parsed metadata in the structure ...
+ *)
+and offset_is_free _ _ = false
+
+and callbacks =
+  let i = ref 0 in
+  fun () -> {
+    lvm_cb_uq = (incr i; !i);
+    lvm_cb_name = id;
+    lvm_cb_list_lvs = list_lvs;
+    lvm_cb_offset_is_free = offset_is_free;
+  }
+
+(* Register the plugin. *)
+let () = register_plugin ~lvm:probe id