1 (* 'df' command for virtual domains.
3 (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version,
10 with the OCaml linking exception described in ../COPYING.LIB.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28 open Diskimage_lvm2_metadata
34 let sector_size_int = 512
35 let sector_size = ~^sector_size_int
37 (*let attach_private_data, get_private_data =
38 private_data_functions (fun {lvm_cb = {lvm_cb_uq = u}} -> u)*)
40 (*----------------------------------------------------------------------*)
41 (* Block device which can do linear maps, same as the kernel dm-linear.c *)
42 class linear_map_device name extent_size segments =
43 (* The segments are passed containing (start_extent, extent_count, ...)
44 * but it's easier to deal with (start_extent, end_extent, ...) so
47 let segments = List.map
48 (fun (start_extent, extent_count, dev, pvoffset) ->
49 (start_extent, start_extent +^ extent_count, dev, pvoffset)
52 (* Calculate the size of the device (in bytes). Note that because
53 * of the random nature of the mapping this doesn't imply that we can
54 * satisfy any read request up to the full size.
57 List.fold_left max ~^0
58 (List.map (fun (_, end_extent, _, _) -> end_extent) segments) in
59 let size = size_in_extents *^ extent_size in
65 (* The natural blocksize for LVM devices is the extent size.
66 * NB. Throws a runtime exception if the extent size is bigger
67 * than an int (only likely to matter on 32 bit).
69 method blocksize = extent_size
71 method private map i =
72 if i < ~^0 || i >= size_in_extents then
73 invalid_arg "linear_map_device: read outside device";
75 let rec loop = function
78 | (start_extent, end_extent, dev, pvoffset) :: rest ->
79 if start_extent <= i && i < end_extent then (
80 let dev_offset = (pvoffset +^ i) *^ extent_size in
81 Some (start_extent, end_extent, dev, dev_offset, pvoffset)
87 (* Map block (extent) i to the underlying device. *)
90 | Some (_, _, dev, dev_offset, _) -> [dev, dev_offset]
93 (* Continguous span. *)
94 method contiguous offset =
95 let offset_in_extents = offset /^ extent_size in
97 (* Get the segment that this offset lies in. *)
98 match self#map offset_in_extents with
99 | Some (_, end_extent, dev, dev_offset, _) ->
100 (* Contiguous bytes up to the end of this extent. *)
101 end_extent *^ extent_size -^ offset
104 (* NB. Use the superclass #read method. *)
107 (*----------------------------------------------------------------------*)
108 (* Probe to see if it's an LVM2 PV. *)
111 let uuid, _, _ = read_pv_label dev in
113 eprintf "LVM2 detected PV UUID %s\n%!" uuid;
114 { pv_cb = callbacks (); pv_uuid = uuid; pv_dev = dev }
116 if !debug then prerr_endline (Printexc.to_string exn);
119 and read_pv_label dev =
120 (* Load the first 8 sectors. I found by experimentation that
121 * the second sector contains the header ("LABELONE" etc) and
122 * the nineth sector contains some additional information about
123 * the location of the current metadata.
125 let bits = dev#read_bitstring ~^0 (~^9 *^ sector_size) in
130 _ : sector_size_int*8 : bitstring;
133 "LABELONE" : 64 : string; (* "LABELONE" *)
134 _ : 128 : bitstring; (* Seems to contain something. *)
135 "LVM2 001" : 64 : string; (* "LVM2 001" *)
136 uuid : 256 : string; (* PV UUID *)
137 _ : (sector_size_int-64)*8 : bitstring;(* to end of second sector *)
140 _ : sector_size_int*8 * 6 : bitstring;
143 _ : 320 : bitstring; (* start of sector 8 *)
144 metadata_offset : 32 : littleendian; (* metadata offset *)
146 metadata_length : 32 : littleendian (* length of metadata (bytes) *)
149 (* Metadata offset is relative to end of PV label. *)
150 let metadata_offset = Int63.of_int32 metadata_offset +^ ~^0x1000 in
151 (* Metadata length appears to include the trailing \000 which
152 * we don't want, so subtract 1 to get the true length.
154 let metadata_length = Int63.of_int32 metadata_length -^ ~^1 in
156 (* Check the metadata offset and length are sensible. *)
157 if metadata_offset <= ~^0x1200 || metadata_offset >= dev#size
158 || metadata_length <= ~^0
159 || metadata_offset +^ metadata_length >= dev#size then
160 invalid_arg "LVM2: read_metadata: bad metadata offset or length";
162 uuid, metadata_offset, metadata_length
166 (sprintf "LVM2: read_pv_label: %s: not an LVM2 physical volume"
169 (*----------------------------------------------------------------------*)
170 (* We are passed a list of devices which we previously identified
171 * as PVs belonging to us. From these produce a list of all LVs
172 * (as devices) and return them. Note that we don't try to detect
173 * what is on these LVs - that will be done in the main code.
176 (* Read the PV label (again) for each PV, and this time also
177 * read out the metadata, which is a big block of text.
179 let pvsmap = List.map (
180 fun { pv_dev = dev } ->
181 let uuid, metadata_offset, metadata_length = read_pv_label dev in
182 let metadata = dev#read metadata_offset metadata_length in
185 eprintf "list_lvs: metadata for PV %s (offset %s len %s):\n%s\n%!"
187 (Int63.to_string metadata_offset) (Int63.to_string metadata_length)
190 (uuid, (metadata, dev))
193 (* Parse the metadata using the external lexer/parser. *)
194 let pvsmap = List.map (
195 fun (uuid, (metadata, dev)) ->
196 uuid, (Diskimage_lvm2_lexer.parse_lvm2_metadata_from_string metadata,
200 (* Print the parsed metadata. *)
203 fun (uuid, (metadata, dev)) ->
204 eprintf "metadata for PV UUID %s on %s:\n" uuid dev#name;
205 output_metadata stderr metadata
208 (* Scan for volume groups. The first entry in the metadata
209 * appears to be the volume group name. This gives us a
210 * list of VGs and the metadata for each underlying PV.
215 | pvuuid, (((vgname, Metadata vgmeta) :: _), dev) ->
216 Some (vgname, (pvuuid, vgmeta))
220 let cmp ((a:string),_) ((b:string),_) = compare a b in
221 let vgnames = List.sort ~cmp vgnames in
222 let vgs = group_by vgnames in
224 (* Note that the metadata is supposed to be duplicated
225 * identically across all PVs (for redundancy purposes).
226 * In theory we should check this and use the 'seqno'
227 * field to find the latest metadata if it doesn't match,
228 * but in fact we don't check this.
231 fun (vgname, metas) ->
232 let pvuuids = List.map fst metas in
233 let _, vgmeta = List.hd metas in (* just pick any metadata *)
234 vgname, (pvuuids, vgmeta)) vgs in
239 fun (vgname, (pvuuids, vgmeta)) ->
240 eprintf "VG %s is on PVs: %s\n%!" vgname (String.concat "," pvuuids)
243 (* Some useful getter functions. If these can't get a value
244 * from the metadata or if the type is wrong they raise Not_found.
246 let rec get_int63 field meta =
247 match List.assoc field meta with
249 | _ -> raise Not_found
250 and get_int_bounded field meta max =
251 match List.assoc field meta with
252 | Int i when i >= ~^0 && i <= Int63.of_int max -> Int63.to_int i
253 | _ -> raise Not_found
254 and get_string field meta =
255 match List.assoc field meta with
257 | _ -> raise Not_found
258 and get_meta field meta =
259 match List.assoc field meta with
261 | _ -> raise Not_found
262 and get_stripes field meta = (* List of (string,int) pairs. *)
263 match List.assoc field meta with
265 let rec loop = function
267 | String pvname :: Int offset :: xs ->
268 (pvname, offset) :: loop xs
269 | _ -> raise Not_found
272 | _ -> raise Not_found
275 (* The volume groups refer to the physical volumes using their
276 * own naming system ("pv0", "pv1", etc.) instead of PV UUIDs.
278 * Each PV also has a start (in sectors) & count (in extents)
279 * of the writable area (the bit after the superblock and metadata)
280 * which normally starts at sector 384.
282 * Create a PV device (simple offset + size) and a map from PV
283 * names to these devices.
286 fun (vgname, (pvuuids, vgmeta)) ->
287 let pvdevs, extent_size =
289 (* NB: extent_size is in sectors here - we convert to bytes. *)
291 get_int_bounded "extent_size" vgmeta (1024*1024) in
292 let extent_size = Int63.of_int extent_size in
293 let extent_size = extent_size *^ sector_size in
295 (* Get the physical_volumes section of the metadata. *)
296 let pvdevs = get_meta "physical_volumes" vgmeta in
300 | (pvname, Metadata meta) ->
302 let pvuuid = get_string "id" meta in
303 let pvuuid = canonical_uuid pvuuid in
305 (* Get the underlying physical device. *)
306 let _, dev = List.assoc pvuuid pvsmap in
308 (* Construct a PV device. *)
309 let pe_start = get_int63 "pe_start" meta in
310 let pe_start = pe_start *^ sector_size in
311 let pe_count = get_int63 "pe_count" meta in
312 let pe_count = pe_count *^ extent_size in
316 pe_start pe_count (* start, size in bytes *)
317 (* don't really have a natural block size ... *)
319 dev (* underlying device *) in
324 ) pvdevs, extent_size
326 (* Something went wrong - just return an empty map. *)
327 Not_found -> [], ~^0 in
328 (vgname, (pvuuids, vgmeta, pvdevs, extent_size))
331 (* Scan for logical volumes. Each VG contains several LVs.
332 * This gives us a list of LVs within each VG (hence extends
336 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size)) ->
339 let lvs = get_meta "logical_volumes" vgmeta in
340 let lvs = List.filter_map (
342 | lvname, Metadata lvmeta ->
345 get_int_bounded "segment_count" lvmeta 1024 in
347 (* Get the segments for this LV. *)
348 let segments = range 1 (segment_count+1) in
351 (fun i -> get_meta ("segment" ^ string_of_int i) lvmeta)
358 get_int63 "start_extent" segmeta in
360 get_int63 "extent_count" segmeta in
361 let segtype = get_string "type" segmeta in
363 (* Can only handle striped segments at the
366 if segtype <> "striped" then raise Not_found;
369 get_int_bounded "stripe_count" segmeta 1024 in
370 let stripes = get_stripes "stripes" segmeta in
372 if List.length stripes <> stripe_count then
375 (* Can only handle linear striped segments at
378 if stripe_count <> 1 then raise Not_found;
379 let pvname, pvoffset = List.hd stripes in
381 (start_extent, extent_count, pvname, pvoffset)
384 Some (lvname, segments)
386 (* Something went wrong with segments - omit this LV. *)
394 (* Something went wrong - assume no LVs found. *)
396 (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs))
402 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
403 eprintf "VG %s: (extent_size = %s bytes)\n" vgname
404 (Int63.to_string extent_size);
406 fun (lvname, segments) ->
407 eprintf " %s/%s:\n" vgname lvname;
409 fun (start_extent, extent_count, pvname, pvoffset) ->
410 eprintf " start %s count %s at %s:%s\n"
411 (Int63.to_string start_extent)
412 (Int63.to_string extent_count)
413 pvname (Int63.to_string pvoffset)
420 (* List.iter (fun pv -> attach_private_data pv vgs) pvs; *)
422 (* Finally we can set up devices for the LVs. *)
425 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
428 fun (lvname, segments) ->
429 let name = vgname ^ "/" ^ lvname in
430 let segments = List.map (
431 fun (start_extent, extent_count, pvname, pvoffset) ->
432 (* Get the PV device. *)
433 let pvdev = List.assoc pvname pvdevs in
435 (* Extents mapped to: *)
436 (start_extent, extent_count, pvdev, pvoffset)
439 (* Create a linear mapping device. *)
440 let lv_dev = new linear_map_device name extent_size segments in
447 let lvs = List.concat lvs in
449 (* Return the list of LV devices. *)
452 (* XXX We need to reparse the metadata in a different way in
453 * order to calculate this. Need to generalize metadata handling.
455 and offset_is_free _ _ = false
460 lvm_cb_uq = (incr i; !i);
462 lvm_cb_list_lvs = list_lvs;
463 lvm_cb_offset_is_free = offset_is_free;
466 (* Register the plugin. *)
467 let () = register_plugin ~lvm:probe id