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
31 let plugin_id = "LVM2"
33 let sector_size_int = 512
34 let sector_size = ~^sector_size_int
36 (*----------------------------------------------------------------------*)
37 (* Block device which can do linear maps, same as the kernel dm-linear.c *)
38 class linear_map_device name extent_size segments =
39 (* The segments are passed containing (start_extent, extent_count, ...)
40 * but it's easier to deal with (start_extent, end_extent, ...) so
43 let segments = List.map
44 (fun (start_extent, extent_count, dev, pvoffset) ->
45 (start_extent, start_extent +^ extent_count, dev, pvoffset)
48 (* Calculate the size of the device (in bytes). Note that because
49 * of the random nature of the mapping this doesn't imply that we can
50 * satisfy any read request up to the full size.
53 List.fold_left max ~^0
54 (List.map (fun (_, end_extent, _, _) -> end_extent) segments) in
55 let size = size_in_extents *^ extent_size in
61 (* The natural blocksize for LVM devices is the extent size.
62 * NB. Throws a runtime exception if the extent size is bigger
63 * than an int (only likely to matter on 32 bit).
65 method blocksize = extent_size
67 method private map i =
68 if i < ~^0 || i >= size_in_extents then
69 invalid_arg "linear_map_device: read outside device";
71 let rec loop = function
74 | (start_extent, end_extent, dev, pvoffset) :: rest ->
75 if start_extent <= i && i < end_extent then (
76 let dev_offset = (pvoffset +^ i) *^ extent_size in
77 Some (start_extent, end_extent, dev, dev_offset, pvoffset)
83 (* Map block (extent) i to the underlying device. *)
86 | Some (_, _, dev, dev_offset, _) -> [dev, dev_offset]
89 (* Continguous span. *)
90 method contiguous offset =
91 let offset_in_extents = offset /^ extent_size in
93 (* Get the segment that this offset lies in. *)
94 match self#map offset_in_extents with
95 | Some (_, end_extent, dev, dev_offset, _) ->
96 (* Contiguous bytes up to the end of this extent. *)
97 end_extent *^ extent_size -^ offset
100 (* NB. Use the superclass #read method. *)
103 (*----------------------------------------------------------------------*)
104 (* Probe to see if it's an LVM2 PV. *)
105 let rec probe lvm_plugin_id dev =
107 let uuid, _ = read_pv_label dev in
109 eprintf "LVM2 detected PV UUID %s\n%!" uuid;
110 { lvm_plugin_id = lvm_plugin_id; pv_uuid = uuid; pv_dev = dev }
112 if !debug then prerr_endline (Printexc.to_string exn);
115 and read_pv_label dev =
116 (* Load the first 8 sectors. I found by experimentation that
117 * the second sector contains the header ("LABELONE" etc) and
118 * the nineth sector contains some additional information about
119 * the location of the current metadata.
121 let bits = dev#read_bitstring ~^0 (~^9 *^ sector_size) in
123 (*Bitmatch.hexdump_bitstring stdout bits;*)
128 sector0 : sector_size_int*8 : bitstring;
131 "LABELONE" : 64 : string; (* "LABELONE" *)
132 _ : 128 : bitstring; (* Seems to contain something. *)
133 "LVM2 001" : 64 : string; (* "LVM2 001" *)
134 uuid : 256 : string; (* UUID *)
135 endsect : (sector_size_int-64)*8 : bitstring;(* to end of second sector *)
138 sectors234567 : sector_size_int*8 * 6 : bitstring;
141 _ : 320 : bitstring; (* start of sector 8 *)
142 metadata_offset : 32 : littleendian; (* metadata offset *)
144 metadata_length : 32 : littleendian (* length of metadata (bytes) *)
147 (* Metadata offset is relative to end of PV label. *)
148 let metadata_offset = Int63.of_int32 metadata_offset +^ ~^0x1000 in
149 (* Metadata length appears to include the trailing \000 which
152 let metadata_length = Int63.of_int32 metadata_length -^ ~^1 in
154 let metadata = read_metadata dev metadata_offset metadata_length in
160 (sprintf "LVM2: read_pv_label: %s: not an LVM2 physical volume"
163 and read_metadata dev offset len =
165 eprintf "metadata: offset %s len %s bytes\n%!"
166 (Int63.to_string offset) (Int63.to_string len);
168 (* Check the offset and length are sensible. *)
169 if offset <= ~^0x1200 || offset >= dev#size
170 || len <= ~^0 || offset +^ len >= dev#size then
171 invalid_arg "LVM2: read_metadata: bad metadata offset or length";
173 (* If it is outside the disk boundaries, this will throw an exception,
174 * otherwise it will read and return the metadata string.
178 (*----------------------------------------------------------------------*)
179 (* We are passed a list of devices which we previously identified
180 * as PVs belonging to us. From these produce a list of all LVs
181 * (as devices) and return them. Note that we don't try to detect
182 * what is on these LVs - that will be done in the main code.
185 (* Read the UUID and metadata (again) from each device to end up with
186 * an assoc list of PVs, keyed on the UUID.
190 let uuid, metadata = read_pv_label dev in
191 (uuid, (metadata, dev))
194 (* Parse the metadata using the external lexer/parser. *)
196 fun (uuid, (metadata, dev)) ->
197 uuid, (Diskimage_lvm2_lexer.parse_lvm2_metadata_from_string metadata,
201 (* Print the parsed metadata. *)
204 fun (uuid, (metadata, dev)) ->
205 eprintf "metadata for PV UUID %s on %s:\n" uuid dev#name;
206 output_metadata stderr metadata
209 (* Scan for volume groups. The first entry in the metadata
210 * appears to be the volume group name. This gives us a
211 * list of VGs and the metadata for each underlying PV.
216 | pvuuid, (((vgname, Metadata vgmeta) :: _), dev) ->
217 Some (vgname, (pvuuid, vgmeta))
221 let cmp ((a:string),_) ((b:string),_) = compare a b in
222 let vgnames = List.sort ~cmp vgnames in
223 let vgs = group_by vgnames in
225 (* Note that the metadata is supposed to be duplicated
226 * identically across all PVs (for redundancy purposes).
227 * In theory we should check this and use the 'seqno'
228 * field to find the latest metadata if it doesn't match,
229 * but in fact we don't check this.
232 fun (vgname, metas) ->
233 let pvuuids = List.map fst metas in
234 let _, vgmeta = List.hd metas in (* just pick any metadata *)
235 vgname, (pvuuids, vgmeta)) vgs in
240 fun (vgname, (pvuuids, vgmeta)) ->
241 eprintf "VG %s is on PVs: %s\n%!" vgname (String.concat "," pvuuids)
244 (* Some useful getter functions. If these can't get a value
245 * from the metadata or if the type is wrong they raise Not_found.
247 let rec get_int63 field meta =
248 match List.assoc field meta with
250 | _ -> raise Not_found
251 and get_int_bounded field meta max =
252 match List.assoc field meta with
253 | Int i when i >= ~^0 && i <= Int63.of_int max -> Int63.to_int i
254 | _ -> raise Not_found
255 and get_string field meta =
256 match List.assoc field meta with
258 | _ -> raise Not_found
259 and get_meta field meta =
260 match List.assoc field meta with
262 | _ -> raise Not_found
263 and get_stripes field meta = (* List of (string,int) pairs. *)
264 match List.assoc field meta with
266 let rec loop = function
268 | String pvname :: Int offset :: xs ->
269 (pvname, offset) :: loop xs
270 | _ -> raise Not_found
273 | _ -> raise Not_found
276 (* The volume groups refer to the physical volumes using their
277 * own naming system ("pv0", "pv1", etc.) instead of PV UUIDs.
279 * Each PV also has a start (in sectors) & count (in extents)
280 * of the writable area (the bit after the superblock and metadata)
281 * which normally starts at sector 384.
283 * Create a PV device (simple offset + size) and a map from PV
284 * names to these devices.
287 fun (vgname, (pvuuids, vgmeta)) ->
288 let pvdevs, extent_size =
290 (* NB: extent_size is in sectors here - we convert to bytes. *)
292 get_int_bounded "extent_size" vgmeta (1024*1024) in
293 let extent_size = Int63.of_int extent_size in
294 let extent_size = extent_size *^ sector_size in
296 (* Get the physical_volumes section of the metadata. *)
297 let pvdevs = get_meta "physical_volumes" vgmeta in
301 | (pvname, Metadata meta) ->
303 let pvuuid = get_string "id" meta in
304 let pvuuid = canonical_uuid pvuuid in
306 (* Get the underlying physical device. *)
307 let _, dev = List.assoc pvuuid pvs in
309 (* Construct a PV device. *)
310 let pe_start = get_int63 "pe_start" meta in
311 let pe_start = pe_start *^ sector_size in
312 let pe_count = get_int63 "pe_count" meta in
313 let pe_count = pe_count *^ extent_size in
317 pe_start pe_count (* start, size in bytes *)
318 (* don't really have a natural block size ... *)
320 dev (* underlying device *) in
325 ) pvdevs, extent_size
327 (* Something went wrong - just return an empty map. *)
328 Not_found -> [], ~^0 in
329 (vgname, (pvuuids, vgmeta, pvdevs, extent_size))
332 (* Scan for logical volumes. Each VG contains several LVs.
333 * This gives us a list of LVs within each VG (hence extends
337 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size)) ->
340 let lvs = get_meta "logical_volumes" vgmeta in
341 let lvs = List.filter_map (
343 | lvname, Metadata lvmeta ->
346 get_int_bounded "segment_count" lvmeta 1024 in
348 (* Get the segments for this LV. *)
349 let segments = range 1 (segment_count+1) in
352 (fun i -> get_meta ("segment" ^ string_of_int i) lvmeta)
359 get_int63 "start_extent" segmeta in
361 get_int63 "extent_count" segmeta in
362 let segtype = get_string "type" segmeta in
364 (* Can only handle striped segments at the
367 if segtype <> "striped" then raise Not_found;
370 get_int_bounded "stripe_count" segmeta 1024 in
371 let stripes = get_stripes "stripes" segmeta in
373 if List.length stripes <> stripe_count then
376 (* Can only handle linear striped segments at
379 if stripe_count <> 1 then raise Not_found;
380 let pvname, pvoffset = List.hd stripes in
382 (start_extent, extent_count, pvname, pvoffset)
385 Some (lvname, segments)
387 (* Something went wrong with segments - omit this LV. *)
395 (* Something went wrong - assume no LVs found. *)
397 (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs))
403 fun (vgname, (pvuuids, vgmeta, pvdevs, extent_size, lvs)) ->
404 eprintf "VG %s: (extent_size = %s bytes)\n" vgname
405 (Int63.to_string extent_size);
407 fun (lvname, segments) ->
408 eprintf " %s/%s:\n" vgname lvname;
410 fun (start_extent, extent_count, pvname, pvoffset) ->
411 eprintf " start %s count %s at %s:%s\n"
412 (Int63.to_string start_extent)
413 (Int63.to_string extent_count)
414 pvname (Int63.to_string pvoffset)
421 (* Finally we can set up devices for the LVs. *)
424 fun (vgname, (pvuuid, 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. *)