1 (* 'df' command for virtual domains.
3 (C) Copyright 2007 Richard W.M. Jones, Red Hat Inc.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 open Diskimage_lvm2_metadata
33 let sector_size_int = 512
34 let sector_size = ~^sector_size_int
36 (*let attach_private_data, get_private_data =
37 private_data_functions (fun {lvm_cb = {lvm_cb_uq = u}} -> u)*)
39 (*----------------------------------------------------------------------*)
40 (* Block device which can do linear maps, same as the kernel dm-linear.c *)
41 class linear_map_device name extent_size segments =
42 (* The segments are passed containing (start_extent, extent_count, ...)
43 * but it's easier to deal with (start_extent, end_extent, ...) so
46 let segments = List.map
47 (fun (start_extent, extent_count, dev, pvoffset) ->
48 (start_extent, start_extent +^ extent_count, dev, pvoffset)
51 (* Calculate the size of the device (in bytes). Note that because
52 * of the random nature of the mapping this doesn't imply that we can
53 * satisfy any read request up to the full size.
56 List.fold_left max ~^0
57 (List.map (fun (_, end_extent, _, _) -> end_extent) segments) in
58 let size = size_in_extents *^ extent_size in
64 (* The natural blocksize for LVM devices is the extent size.
65 * NB. Throws a runtime exception if the extent size is bigger
66 * than an int (only likely to matter on 32 bit).
68 method blocksize = extent_size
70 method private map i =
71 if i < ~^0 || i >= size_in_extents then
72 invalid_arg "linear_map_device: read outside device";
74 let rec loop = function
77 | (start_extent, end_extent, dev, pvoffset) :: rest ->
78 if start_extent <= i && i < end_extent then (
79 let dev_offset = (pvoffset +^ i) *^ extent_size in
80 Some (start_extent, end_extent, dev, dev_offset, pvoffset)
86 (* Map block (extent) i to the underlying device. *)
89 | Some (_, _, dev, dev_offset, _) -> [dev, dev_offset]
92 (* Continguous span. *)
93 method contiguous offset =
94 let offset_in_extents = offset /^ extent_size in
96 (* Get the segment that this offset lies in. *)
97 match self#map offset_in_extents with
98 | Some (_, end_extent, dev, dev_offset, _) ->
99 (* Contiguous bytes up to the end of this extent. *)
100 end_extent *^ extent_size -^ offset
103 (* NB. Use the superclass #read method. *)
106 (*----------------------------------------------------------------------*)
107 (* Probe to see if it's an LVM2 PV. *)
110 let uuid, _, _ = read_pv_label dev in
112 eprintf "LVM2 detected PV UUID %s\n%!" uuid;
113 { pv_cb = callbacks (); pv_uuid = uuid; pv_dev = dev }
115 if !debug then prerr_endline (Printexc.to_string exn);
118 and read_pv_label dev =
119 (* Load the first 8 sectors. I found by experimentation that
120 * the second sector contains the header ("LABELONE" etc) and
121 * the nineth sector contains some additional information about
122 * the location of the current metadata.
124 let bits = dev#read_bitstring ~^0 (~^9 *^ sector_size) in
129 sector0 : sector_size_int*8 : bitstring;
132 "LABELONE" : 64 : string; (* "LABELONE" *)
133 _ : 128 : bitstring; (* Seems to contain something. *)
134 "LVM2 001" : 64 : string; (* "LVM2 001" *)
135 uuid : 256 : string; (* PV UUID *)
136 endsect : (sector_size_int-64)*8 : bitstring;(* to end of second sector *)
139 sectors234567 : sector_size_int*8 * 6 : bitstring;
142 _ : 320 : bitstring; (* start of sector 8 *)
143 metadata_offset : 32 : littleendian; (* metadata offset *)
145 metadata_length : 32 : littleendian (* length of metadata (bytes) *)
148 (* Metadata offset is relative to end of PV label. *)
149 let metadata_offset = Int63.of_int32 metadata_offset +^ ~^0x1000 in
150 (* Metadata length appears to include the trailing \000 which
151 * we don't want, so subtract 1 to get the true length.
153 let metadata_length = Int63.of_int32 metadata_length -^ ~^1 in
155 (* Check the metadata offset and length are sensible. *)
156 if metadata_offset <= ~^0x1200 || metadata_offset >= dev#size
157 || metadata_length <= ~^0
158 || metadata_offset +^ metadata_length >= dev#size then
159 invalid_arg "LVM2: read_metadata: bad metadata offset or length";
161 uuid, metadata_offset, metadata_length
165 (sprintf "LVM2: read_pv_label: %s: not an LVM2 physical volume"
168 (*----------------------------------------------------------------------*)
169 (* We are passed a list of devices which we previously identified
170 * as PVs belonging to us. From these produce a list of all LVs
171 * (as devices) and return them. Note that we don't try to detect
172 * what is on these LVs - that will be done in the main code.
175 (* Read the PV label (again) for each PV, and this time also
176 * read out the metadata, which is a big block of text.
178 let pvsmap = List.map (
179 fun { pv_dev = dev } ->
180 let uuid, metadata_offset, metadata_length = read_pv_label dev in
181 let metadata = dev#read metadata_offset metadata_length in
184 eprintf "list_lvs: metadata for PV %s (offset %s len %s):\n%s\n%!"
186 (Int63.to_string metadata_offset) (Int63.to_string metadata_length)
189 (uuid, (metadata, dev))
192 (* Parse the metadata using the external lexer/parser. *)
193 let pvsmap = List.map (
194 fun (uuid, (metadata, dev)) ->
195 uuid, (Diskimage_lvm2_lexer.parse_lvm2_metadata_from_string metadata,
199 (* Print the parsed metadata. *)
202 fun (uuid, (metadata, dev)) ->
203 eprintf "metadata for PV UUID %s on %s:\n" uuid dev#name;
204 output_metadata stderr metadata
207 (* Scan for volume groups. The first entry in the metadata
208 * appears to be the volume group name. This gives us a
209 * list of VGs and the metadata for each underlying PV.
214 | pvuuid, (((vgname, Metadata vgmeta) :: _), dev) ->
215 Some (vgname, (pvuuid, vgmeta))
219 let cmp ((a:string),_) ((b:string),_) = compare a b in
220 let vgnames = List.sort ~cmp vgnames in
221 let vgs = group_by vgnames in
223 (* Note that the metadata is supposed to be duplicated
224 * identically across all PVs (for redundancy purposes).
225 * In theory we should check this and use the 'seqno'
226 * field to find the latest metadata if it doesn't match,
227 * but in fact we don't check this.
230 fun (vgname, metas) ->
231 let pvuuids = List.map fst metas in
232 let _, vgmeta = List.hd metas in (* just pick any metadata *)
233 vgname, (pvuuids, vgmeta)) vgs in
238 fun (vgname, (pvuuids, vgmeta)) ->
239 eprintf "VG %s is on PVs: %s\n%!" vgname (String.concat "," pvuuids)
242 (* Some useful getter functions. If these can't get a value
243 * from the metadata or if the type is wrong they raise Not_found.
245 let rec get_int63 field meta =
246 match List.assoc field meta with
248 | _ -> raise Not_found
249 and get_int_bounded field meta max =
250 match List.assoc field meta with
251 | Int i when i >= ~^0 && i <= Int63.of_int max -> Int63.to_int i
252 | _ -> raise Not_found
253 and get_string field meta =
254 match List.assoc field meta with
256 | _ -> raise Not_found
257 and get_meta field meta =
258 match List.assoc field meta with
260 | _ -> raise Not_found
261 and get_stripes field meta = (* List of (string,int) pairs. *)
262 match List.assoc field meta with
264 let rec loop = function
266 | String pvname :: Int offset :: xs ->
267 (pvname, offset) :: loop xs
268 | _ -> raise Not_found
271 | _ -> raise Not_found
274 (* The volume groups refer to the physical volumes using their
275 * own naming system ("pv0", "pv1", etc.) instead of PV UUIDs.
277 * Each PV also has a start (in sectors) & count (in extents)
278 * of the writable area (the bit after the superblock and metadata)
279 * which normally starts at sector 384.
281 * Create a PV device (simple offset + size) and a map from PV
282 * names to these devices.
285 fun (vgname, (pvuuids, vgmeta)) ->
286 let pvdevs, extent_size =
288 (* NB: extent_size is in sectors here - we convert to bytes. *)
290 get_int_bounded "extent_size" vgmeta (1024*1024) in
291 let extent_size = Int63.of_int extent_size in
292 let extent_size = extent_size *^ sector_size in
294 (* Get the physical_volumes section of the metadata. *)
295 let pvdevs = get_meta "physical_volumes" vgmeta in
299 | (pvname, Metadata meta) ->
301 let pvuuid = get_string "id" meta in
302 let pvuuid = canonical_uuid pvuuid in
304 (* Get the underlying physical device. *)
305 let _, dev = List.assoc pvuuid pvsmap in
307 (* Construct a PV device. *)
308 let pe_start = get_int63 "pe_start" meta in
309 let pe_start = pe_start *^ sector_size in
310 let pe_count = get_int63 "pe_count" meta in
311 let pe_count = pe_count *^ extent_size in
315 pe_start pe_count (* start, size in bytes *)
316 (* don't really have a natural block size ... *)
318 dev (* underlying device *) in
323 ) pvdevs, extent_size
325 (* Something went wrong - just return an empty map. *)
326 Not_found -> [], ~^0 in
327 (vgname, (pvuuids, vgmeta, pvdevs, extent_size))
330 (* Scan for logical volumes. Each VG contains several LVs.
331 * This gives us a list of LVs within each VG (hence extends
335 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size)) ->
338 let lvs = get_meta "logical_volumes" vgmeta in
339 let lvs = List.filter_map (
341 | lvname, Metadata lvmeta ->
344 get_int_bounded "segment_count" lvmeta 1024 in
346 (* Get the segments for this LV. *)
347 let segments = range 1 (segment_count+1) in
350 (fun i -> get_meta ("segment" ^ string_of_int i) lvmeta)
357 get_int63 "start_extent" segmeta in
359 get_int63 "extent_count" segmeta in
360 let segtype = get_string "type" segmeta in
362 (* Can only handle striped segments at the
365 if segtype <> "striped" then raise Not_found;
368 get_int_bounded "stripe_count" segmeta 1024 in
369 let stripes = get_stripes "stripes" segmeta in
371 if List.length stripes <> stripe_count then
374 (* Can only handle linear striped segments at
377 if stripe_count <> 1 then raise Not_found;
378 let pvname, pvoffset = List.hd stripes in
380 (start_extent, extent_count, pvname, pvoffset)
383 Some (lvname, segments)
385 (* Something went wrong with segments - omit this LV. *)
393 (* Something went wrong - assume no LVs found. *)
395 (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs))
401 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
402 eprintf "VG %s: (extent_size = %s bytes)\n" vgname
403 (Int63.to_string extent_size);
405 fun (lvname, segments) ->
406 eprintf " %s/%s:\n" vgname lvname;
408 fun (start_extent, extent_count, pvname, pvoffset) ->
409 eprintf " start %s count %s at %s:%s\n"
410 (Int63.to_string start_extent)
411 (Int63.to_string extent_count)
412 pvname (Int63.to_string pvoffset)
419 (* List.iter (fun pv -> attach_private_data pv vgs) pvs; *)
421 (* Finally we can set up devices for the LVs. *)
424 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
427 fun (lvname, segments) ->
428 let name = vgname ^ "/" ^ lvname in
429 let segments = List.map (
430 fun (start_extent, extent_count, pvname, pvoffset) ->
431 (* Get the PV device. *)
432 let pvdev = List.assoc pvname pvdevs in
434 (* Extents mapped to: *)
435 (start_extent, extent_count, pvdev, pvoffset)
438 (* Create a linear mapping device. *)
439 let lv_dev = new linear_map_device name extent_size segments in
446 let lvs = List.concat lvs in
448 (* Return the list of LV devices. *)
451 (* XXX We need to reparse the metadata in a different way in
452 * order to calculate this. Need to generalize metadata handling.
454 and offset_is_free _ _ = false
459 lvm_cb_uq = (incr i; !i);
461 lvm_cb_list_lvs = list_lvs;
462 lvm_cb_offset_is_free = offset_is_free;
465 (* Register the plugin. *)
466 let () = register_plugin ~lvm:probe id