File: pv.ml

package info (click to toggle)
xen-api-libs 0.5.2-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 1,940 kB
  • sloc: ml: 13,925; sh: 2,930; ansic: 1,699; makefile: 1,240; python: 83
file content (556 lines) | stat: -rw-r--r-- 18,981 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
(** Physical Volume module *)

open Absty

(** Start with the meta-metadata - this is how we actually locate the 
    metadata on disk. It's a bit backwards, because a PV is part of a 
    volume group, but it's the PV that contains the volume group info *)

open Lvmmarshal

(** Dummy mode stuff *)

let dummy_fname dev ty =
  let fname = Printf.sprintf "%s/%s/%s" (!Constants.dummy_base) dev ty in
  let basedir = Filename.dirname fname in
  Unixext.mkdir_rec basedir 0o755;
  fname

    
(** Label bits and pieces *)

module Label = struct 
  type label_header = {
    id : string; (* 8 bytes, equal to label_id in Constants *)
    sector : int64;
    crc : int32;
    offset : int32;
    ty : string (* 8 bytes, equal to "LVM2 001" - Constants.label_type*)
  }
      
  and disk_locn = {
    dl_offset : int64;
    dl_size : int64;
  }

  and pv_header = {
    pvh_id : string; (* 32 bytes, 'uuid' *)
    pvh_device_size : int64;
    pvh_extents: disk_locn list;
    pvh_metadata_areas: disk_locn list;
  }

  and t = {
    device : string;
    label_header : label_header;
    pv_header : pv_header;
  } with rpc 

  let unmarshal_header b0 =
    let id,b = unmarshal_string 8 b0 in
    let sector_xl,b = unmarshal_uint64 b in
    let crc_xl,b = unmarshal_uint32 b in
    let offset,b = unmarshal_uint32 b in
    let ty,b = unmarshal_string 8 b in
    let crc_check = String.sub (fst b0) (20 + snd b0) (Constants.label_size - 20) in (* wtf? *)
    let calculated_crc = Crc.crc crc_check Crc.initial_crc in
    if calculated_crc <> crc_xl then
      failwith "Bad checksum in PV Label";
    {id=id;
     sector=sector_xl;
     crc=crc_xl;
     offset=offset;
     ty=ty}
      
  let find_label dev =
    let fd = Unix.openfile (if !Constants.dummy_mode then dummy_fname dev "pvh" else dev) [Unix.O_RDONLY] 0o000 in
    let buf = really_read fd (if !Constants.dummy_mode then (Constants.sector_size * 2) else Constants.label_scan_size) in
    Unix.close fd;
    let rec find n =
      if n>3 then failwith "No label found" else begin
	let b = (buf,n*Constants.sector_size) in
	let (s,b') = unmarshal_string 8 b in
	Printf.fprintf stderr "String='%s' (looking for %s)\n" s Constants.label_id;
	if s=Constants.label_id then (unmarshal_header b,b) else find (n+1)
      end
    in find 0
    
  let label_header_to_ascii label =
    Printf.sprintf "id: %s\nsector: %Ld\ncrc: %ld\noffset: %ld\nty: %s\n" label.id label.sector label.crc label.offset label.ty
      
  let read_pv_header label b =
    let b = skip (Int32.to_int label.offset) b in
    let id,b = unmarshal_string 32 b in
    let size,b = unmarshal_uint64 b in
    let rec do_disk_locn b acc =
      let offset,b = unmarshal_uint64 b in
      if offset=0L 
      then (List.rev acc,skip 8 b) 
      else 
	let size,b = unmarshal_uint64 b in
	do_disk_locn b ({dl_offset=offset; dl_size=size}::acc)
    in 
    let disk_areas,b = do_disk_locn b [] in
    let disk_areas2,b = do_disk_locn b [] in
    { pvh_id=Lvm_uuid.add_hyphens id;
      pvh_device_size=size;
      pvh_extents=disk_areas;
      pvh_metadata_areas=disk_areas2},b

  let pvh_to_ascii pvh =
    let disk_area_list_to_ascii l =
      (String.concat "," (List.map (fun da -> Printf.sprintf "{offset=%Ld,size=%Ld}" da.dl_offset da.dl_size) l)) in  
    Printf.sprintf "pvh_id: %s\npvh_device_size: %Ld\npvh_areas1: %s\npvh_areas2: %s\n"
      pvh.pvh_id pvh.pvh_device_size 
      (disk_area_list_to_ascii pvh.pvh_extents)
      (disk_area_list_to_ascii pvh.pvh_metadata_areas)

  let write_label_and_pv_header l =
    let label = l.label_header in
    let pvh = l.pv_header in
    let dev = l.device in

    let header = (String.make Constants.sector_size '\000', 0) in (* label header is 1 sector long *)
    let pos = Int64.mul label.sector (Int64.of_int Constants.sector_size) in
    assert(label.offset=32l);
    
    let header = marshal_string header label.id in
    let header = marshal_int64  header label.sector in
    
    let crc_pos = header in (* Set later! *)
    let header = marshal_int32  header 0l in
    
    let (do_crc_str,do_crc_from) = header in
    let header = marshal_int32  header label.offset in
    let header = marshal_string header label.ty in
    
    assert(snd header = 32);

    Debug.debug (Printf.sprintf "write_label_and_pv_header:\nPV header:\n%s" (pvh_to_ascii pvh));

    (* PV header *)
    let header = marshal_string header (Lvm_uuid.remove_hyphens pvh.pvh_id) in
    let header = marshal_int64 header pvh.pvh_device_size in
    
    let do_disk_locn header l =       
      let header = List.fold_left (fun header e ->
        let header = marshal_int64 header e.dl_offset in
        marshal_int64 header e.dl_size) header l
      in
      marshal_int64 (marshal_int64 header 0L) 0L
    in
    
    let header = do_disk_locn header pvh.pvh_extents in
    let header = do_disk_locn header pvh.pvh_metadata_areas in
    
    (* Now calc CRC *)
    let crc = Crc.crc (String.sub do_crc_str do_crc_from (Constants.label_size - do_crc_from)) Crc.initial_crc in
    ignore(marshal_int32 crc_pos crc);
    
    let fd = 
      if !Constants.dummy_mode then begin
	let fname = dummy_fname dev "pvh" in 
	Unix.openfile fname [Unix.O_RDWR; Unix.O_DSYNC; Unix.O_CREAT] 0o644
      end else begin
	let fd = Unix.openfile dev [Unix.O_RDWR; Unix.O_DSYNC] 0o000 in
	fd
      end
    in

    ignore(Unix.LargeFile.lseek fd pos Unix.SEEK_SET);
      
    let str = fst header in
    let len = String.length str in
    if len <> (Unix.write fd str 0 len) then failwith "Short write!";
    
    Unix.close fd

  let get_metadata_locations label = 
    label.pv_header.pvh_metadata_areas

  let get_pv_id label =
    label.pv_header.pvh_id

  let get_device label = 
    label.device

  let find device =
    let label,b = find_label device in
    let pvh,b' = read_pv_header label b in    
    { device = device;
      label_header = label;
      pv_header = pvh; }
      
  let create device id size extents_start extents_size mda_start mda_size =
    let label = {
      id=Constants.label_id;
      sector=1L;
      crc=0l;
      offset=32l;
      ty=Constants.label_type;
    } in
    let pvh = {
      pvh_id=id;
      pvh_device_size=size;
      pvh_extents=[{dl_offset=(Int64.add mda_start mda_size); dl_size=0L}];
      pvh_metadata_areas=[{dl_offset=mda_start; dl_size=mda_size}];
    } in
    { device = device;
      label_header = label;
      pv_header = pvh }

  let to_ascii label =
    Printf.sprintf "Label header:\n%s\nPV Header:\n%s\n" 
      (label_header_to_ascii label.label_header)
      (pvh_to_ascii label.pv_header)

end
    
module MDAHeader = struct
  let mda_header_size = Constants.sector_size

  type mda_raw_locn = {
    mrl_offset: int64;
    mrl_size: int64;
    mrl_checksum: int32;
    mrl_filler: int32;
  }

  and mda_header = {
    mdah_checksum : int32;
    mdah_magic : string;
    mdah_version : int32;
    mdah_start: int64;
    mdah_size: int64;
    mdah_raw_locns : mda_raw_locn list;
  } with rpc

  let unmarshal_mda_header device location =
    let offset,fd = 
      if !Constants.dummy_mode 
      then (0L,Unix.openfile (dummy_fname device "mdah") [Unix.O_RDONLY] 0o000) 
      else (location.Label.dl_offset,Unix.openfile device [Unix.O_RDONLY] 0o000) in
    ignore(Unix.LargeFile.lseek fd offset Unix.SEEK_SET);
    let buf = really_read fd (mda_header_size) in
    Unix.close fd;
    let checksum,b = unmarshal_uint32 (buf,0) in
    let magic,b = unmarshal_string 16 b in
    let version,b = unmarshal_uint32 b in
    let start,b = unmarshal_uint64 b in
    let size,b = unmarshal_uint64 b in
    let rec read_raw_locns b acc =
      let offset,b = unmarshal_uint64 b in
      let size,b = unmarshal_uint64 b in
      let checksum,b = unmarshal_uint32 b in
      let filler,b = unmarshal_uint32 b in
      if (offset=0L) 
      then (List.rev acc),b
      else 
	read_raw_locns b ({mrl_offset=offset;mrl_size=size;mrl_checksum=checksum;mrl_filler=filler}::acc)
    in
    let raw_locns,b = read_raw_locns b [] in
    let crc_to_check = String.sub buf 4 (mda_header_size - 4) in
    let crc = Crc.crc crc_to_check Crc.initial_crc in
    if crc <> checksum then
      failwith "Bad checksum in MDA header";
    {mdah_checksum=checksum;
     mdah_magic=magic;
     mdah_version=version;
     mdah_start=start;
     mdah_size=size;
     mdah_raw_locns=raw_locns}

  let to_ascii mdah =
    let rl2ascii r = Printf.sprintf "{offset:%Ld,size:%Ld,checksum:%ld,filler:%ld}" r.mrl_offset r.mrl_size r.mrl_checksum r.mrl_filler in
    Printf.sprintf "checksum: %ld\nmagic: %s\nversion: %ld\nstart: %Ld\nsize: %Ld\nraw_locns:[%s]\n"
      mdah.mdah_checksum mdah.mdah_magic mdah.mdah_version mdah.mdah_start mdah.mdah_size (String.concat "," (List.map rl2ascii mdah.mdah_raw_locns))
          
  let write_mda_header mdah device  =
    Debug.debug "Writing MDA header";
    Debug.debug (Printf.sprintf "Writing: %s" (to_ascii mdah));
    let realheader = (String.make mda_header_size '\000', 0) in (* Mda header is 1 sector long *)
    let header = marshal_int32 realheader 0l in (* Write the checksum later *)
    let header = marshal_string header mdah.mdah_magic in
    let header = marshal_int32 header mdah.mdah_version in
    let header = marshal_int64 header mdah.mdah_start in
    let header = marshal_int64 header mdah.mdah_size in
    let write_raw_locn header locn =
      let header = marshal_int64 header locn.mrl_offset in
      let header = marshal_int64 header locn.mrl_size in
      let header = marshal_int32 header locn.mrl_checksum in
      let header = marshal_int32 header locn.mrl_filler in
      header
    in
    let header = List.fold_left write_raw_locn header mdah.mdah_raw_locns in
    let header = write_raw_locn header {mrl_offset=0L; mrl_size=0L; mrl_checksum=0l; mrl_filler=0l} in
    let crcable = String.sub (fst realheader) 4 (mda_header_size - 4) in
    let crc = Crc.crc crcable Crc.initial_crc in
    let _ = marshal_int32 realheader crc in
    
    let fd = 
      if !Constants.dummy_mode then begin
	Unix.openfile (dummy_fname device "mdah") [Unix.O_RDWR; Unix.O_DSYNC; Unix.O_CREAT] 0o644
      end else begin
        let fd = Unix.openfile device [Unix.O_RDWR; Unix.O_DSYNC] 0o000 in
	ignore(Unix.LargeFile.lseek fd mdah.mdah_start Unix.SEEK_SET);
	fd
      end
    in
    let written = Unix.write fd (fst header) 0 mda_header_size in
    if written <> Constants.sector_size then failwith "Wrote short!";
    Unix.close fd
      
	let read_md dev mdah n =
		(* debug *)
		let oc = open_out "/tmp/hdr" in
		Printf.fprintf oc "%s\n%!" (to_ascii mdah);
		close_out oc;

		let locn = List.nth mdah.mdah_raw_locns n in
		let fd =
			if !Constants.dummy_mode then begin
				Unix.openfile (dummy_fname dev "md") [Unix.O_RDONLY] 0o000
			end else begin
				let fd = Unix.openfile dev [Unix.O_RDONLY] 0o000 in
				ignore(Unix.LargeFile.lseek fd (Int64.add mdah.mdah_start locn.mrl_offset) Unix.SEEK_SET);
				fd
			end
		in
		let md =
			(* Include terminating \0 in this string.
			 * The checksum calculation in original lvm does so, too.*)
			if(Int64.add locn.mrl_offset locn.mrl_size > mdah.mdah_size)
			then (* wrap around *)
				let firstbit = Int64.to_int (Int64.sub mdah.mdah_size locn.mrl_offset) in
				let firstbitstr = really_read fd firstbit in
				let secondbit = (Int64.to_int locn.mrl_size) - firstbit in
				if not !Constants.dummy_mode then ignore(Unix.LargeFile.lseek fd (Int64.add mdah.mdah_start 512L) Unix.SEEK_SET);
				let secondbitstr = really_read fd secondbit in
				firstbitstr ^ secondbitstr
			else
				really_read fd (Int64.to_int locn.mrl_size) in
		let checksum = Crc.crc md Crc.initial_crc in
		Unix.close fd;
		if checksum <> locn.mrl_checksum then
			Printf.fprintf stderr "Checksum invalid in metadata: Found %lx, expecting %lx\n" checksum locn.mrl_checksum;
		md
      
  let write_md device mdah md =
    (* Find the current raw location of the metadata, assuming there's only one copy *)
    let current = List.hd mdah.mdah_raw_locns in
    
    (* Find the new place to write (as an offset from the position of the metadata area)
     * LVM always rounds up to the next sector, so we'll do the same. *)
    let newpos = Utils.int64_round_up (Int64.add current.mrl_offset current.mrl_size) 512L in

    (* Check if we've gone outside the mda *)
    let newpos = 
      if newpos >= mdah.mdah_size then
	(Int64.add 512L (Int64.sub newpos mdah.mdah_size))
      else 
	newpos
    in

    (* debug *)
    let oc = open_out "/tmp/hdr" in
    Printf.fprintf oc "%s\n%!" (to_ascii mdah);
    close_out oc;

    (* Add on the position of the metadata area *)
    let absnewpos = Int64.add newpos mdah.mdah_start in

    let size = String.length md in

    let fd = 
      if !Constants.dummy_mode then begin
	Unix.openfile (dummy_fname device "md") [Unix.O_RDWR; Unix.O_DSYNC; Unix.O_CREAT] 0o644
      end else begin
	let fd = Unix.openfile device [Unix.O_RDWR; Unix.O_DSYNC] 0o000 in
	ignore(Unix.LargeFile.lseek fd absnewpos Unix.SEEK_SET);          
	fd
      end
    in

    (* Check whether we're going to wrap or not *)
    if Int64.add newpos (Int64.of_int size) > mdah.mdah_size 
    then begin
      let firstbit = Int64.to_int (Int64.sub mdah.mdah_size newpos) in
      if (Unix.write fd md 0 firstbit) <> firstbit then failwith "Wrote short!";
      let secondbit = size - firstbit in
      if not !Constants.dummy_mode then ignore(Unix.LargeFile.lseek fd (Int64.add 512L mdah.mdah_start) Unix.SEEK_SET);
      if (Unix.write fd md firstbit secondbit) <> secondbit then failwith "Wrote short!"; 
      Unix.close fd;
    end else begin    
      if (Unix.write fd md 0 size) <> size then 
	failwith "Wrote short!";
      Unix.close fd;
    end;


    (* Now we have to update the crc and pointer to the metadata *)
    
    let checksum = Crc.crc md Crc.initial_crc in
    let new_raw_locn = {
      mrl_offset=newpos;
      mrl_size=Int64.of_int size;
      mrl_checksum=checksum;
      mrl_filler=0l;
    } in

    let mdah = {mdah with mdah_raw_locns=[new_raw_locn]} in
    write_mda_header mdah device;
    mdah


  let create_blank () =
    let mda_raw_locn = {
      mrl_offset = 512L;
      mrl_size = 0L;
      mrl_checksum = 0l;
      mrl_filler=0l;
    } in
    let mda_header = {
      mdah_checksum = 0l;
      mdah_magic = Constants.fmtt_magic;
      mdah_version = 1l;
      mdah_start = Constants.mdah_start;
      mdah_size = Constants.mdah_size;
      mdah_raw_locns = [mda_raw_locn]
    } in
    mda_header
end

  (** Here's the actual PV data that's part of the volume group *)
  
type status = 
    | Allocatable
	
and physical_volume = {
  name : string;
  id : string;
  dev : string;
  real_device : string; (* Actual device we're reading/writing to/from *)
  status : status list;
  dev_size : int64;
  pe_start : int64;
  pe_count : int64;
  label : Label.t;  (* The one label for this PV *)
  mda_headers : MDAHeader.mda_header list; 
} with rpc 

let status_to_string s =
  match s with
    | Allocatable -> "ALLOCATABLE"

let status_of_string s =
  match s with
    | "ALLOCATABLE" -> Allocatable
    | _ -> failwith "Bad status string"

let write_to_buffer b pv =
  let bprintf = Printf.bprintf in
  bprintf b "\n%s {\nid = \"%s\"\ndevice = \"%s\"\n\n" pv.name pv.id pv.dev;
  bprintf b "status = [%s]\ndev_size = %Ld\npe_start = %Ld\npe_count = %Ld\n}\n" 
    (String.concat ", " (List.map (o quote status_to_string) pv.status))
    pv.dev_size pv.pe_start pv.pe_count

let of_metadata name config pvdatas =
  let id = expect_mapped_string "id" config in
  let device = expect_mapped_string "device" config in
  let status = map_expected_mapped_array "status" 
    (fun a -> status_of_string (expect_string "status" a)) config in
  let dev_size = expect_mapped_int "dev_size" config in
  let pe_start = expect_mapped_int "pe_start" config in
  let pe_count = expect_mapped_int "pe_count" config in
  let label,mdahs = 
    try 
      let res = List.find (fun (label,mdahs) -> id=Label.get_pv_id label) pvdatas in
      Printf.fprintf stderr "Found cached PV label data\n";
      res
    with Not_found -> 
      try
	Printf.fprintf stderr "No cached PV data found - loading from device '%s'\n" device;
	let label = Label.find device in
	let mda_locs = Label.get_metadata_locations label in
	let mdahs = List.map (MDAHeader.unmarshal_mda_header device) mda_locs in
	(label,mdahs)
      with e ->
	Printf.fprintf stderr "Error: Could not find label and/or MDA headers on device '%s'\n" 
	  device;
	raise e
  in
  let real_device = Label.get_device label in
  if real_device <> device then
    Printf.fprintf stderr "WARNING: PV.device and real_device are not the same";
  {name=name;
   id=id;
   dev=device;
   real_device=real_device;
   status=status;
   dev_size=dev_size;
   pe_start=pe_start;
   pe_count=pe_count;
   label=label;
   mda_headers=mdahs;
  }

(** Find the metadata area on a device and return the text of the metadata *)
let find_metadata device =
  let label = Label.find device in
  Debug.debug (Printf.sprintf "Label found: \n%s\n" 
    (Label.to_ascii label));
  let mda_locs = Label.get_metadata_locations label in
  let mdahs = List.map (MDAHeader.unmarshal_mda_header device) mda_locs in
  let mdt = MDAHeader.read_md device (List.hd mdahs) 0 in  
  (mdt, (label, mdahs))

let human_readable pv =
  let label=pv.label in
  let b=Buffer.create 1000 in
  let label_str=Label.to_ascii label in
  let mdah_ascii = String.concat "\n" (List.map MDAHeader.to_ascii pv.mda_headers) in
  write_to_buffer b pv;
  Printf.sprintf "Label:\n%s\nMDA Headers:\n%s\n%s\n" 
    label_str mdah_ascii (Buffer.contents b)

let create_new dev name =
  let size = 
    if !Constants.dummy_mode then 
      Constants.tib
    else 
      let fd = Unix.openfile dev [Unix.O_RDONLY] 0 in
      let size = Unixext.blkgetsize64 fd in
      Unix.close fd;
      size
  in
  (* Arbitrarily put the MDA at 4096. We'll have a 10 meg MDA too *)
  let dev_size = Int64.div size (Int64.of_int Constants.sector_size) in
  let mda_pos = Constants.mdah_start in
  let mda_len = Constants.mdah_size in
  let pe_start_byte = 
    Utils.int64_round_up (Int64.add mda_pos mda_len) Constants.pe_align in
  let pe_start_sector = Int64.div pe_start_byte 
    (Int64.of_int Constants.sector_size) in
  let pe_count = Int64.div (Int64.sub size pe_start_byte) Constants.extent_size in
  let mda_len = Int64.sub pe_start_byte mda_pos in
  let id=Lvm_uuid.create () in
  let label = Label.create dev id size pe_start_sector 
    (Int64.mul pe_count Constants.extent_size)
    mda_pos mda_len in
  let mda_header = MDAHeader.create_blank () in
  Label.write_label_and_pv_header label;
  MDAHeader.write_mda_header mda_header dev;
  let pv = { name=name;
	     id=id;
	     dev=dev;
	     real_device=dev;
	     status=[Allocatable];
	     dev_size = dev_size;
	     pe_start=pe_start_sector;
	     pe_count=pe_count;
	     label = label;
	     mda_headers = [mda_header]; }
  in
  pv