xref: /netbsd-src/external/gpl2/lvm2/dist/lib/format_text/export.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
1 /*	$NetBSD: export.c,v 1.1.1.2 2009/02/18 11:17:05 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "lib.h"
19 #include "import-export.h"
20 #include "metadata.h"
21 #include "display.h"
22 #include "lvm-string.h"
23 #include "segtype.h"
24 #include "text_export.h"
25 #include "version.h"
26 
27 #include <stdarg.h>
28 #include <time.h>
29 #include <sys/utsname.h>
30 
31 struct formatter;
32 typedef int (*out_with_comment_fn) (struct formatter * f, const char *comment,
33 				    const char *fmt, va_list ap);
34 typedef int (*nl_fn) (struct formatter * f);
35 
36 /*
37  * Macro for formatted output.
38  * out_with_comment_fn returns -1 if data didn't fit and buffer was expanded.
39  * Then argument list is reset and out_with_comment_fn is called again.
40  */
41 #define _out_with_comment(f, buffer, fmt, ap) \
42 	do { \
43 		va_start(ap, fmt); \
44 		r = f->out_with_comment(f, buffer, fmt, ap); \
45 		va_end(ap); \
46 	} while (r == -1)
47 
48 /*
49  * The first half of this file deals with
50  * exporting the vg, ie. writing it to a file.
51  */
52 struct formatter {
53 	struct dm_pool *mem;	/* pv names allocated from here */
54 	struct dm_hash_table *pv_names;	/* dev_name -> pv_name (eg, pv1) */
55 
56 	union {
57 		FILE *fp;	/* where we're writing to */
58 		struct {
59 			char *start;
60 			uint32_t size;
61 			uint32_t used;
62 		} buf;
63 	} data;
64 
65 	out_with_comment_fn out_with_comment;
66 	nl_fn nl;
67 
68 	int indent;		/* current level of indentation */
69 	int error;
70 	int header;		/* 1 => comments at start; 0 => end */
71 };
72 
73 static struct utsname _utsname;
74 
75 static void _init(void)
76 {
77 	static int _initialised = 0;
78 
79 	if (_initialised)
80 		return;
81 
82 	if (uname(&_utsname)) {
83 		log_error("uname failed: %s", strerror(errno));
84 		memset(&_utsname, 0, sizeof(_utsname));
85 	}
86 
87 	_initialised = 1;
88 }
89 
90 /*
91  * Formatting functions.
92  */
93 
94 #define MAX_INDENT 5
95 static void _inc_indent(struct formatter *f)
96 {
97 	if (++f->indent > MAX_INDENT)
98 		f->indent = MAX_INDENT;
99 }
100 
101 static void _dec_indent(struct formatter *f)
102 {
103 	if (!f->indent--) {
104 		log_error("Internal error tracking indentation");
105 		f->indent = 0;
106 	}
107 }
108 
109 /*
110  * Newline function for prettier layout.
111  */
112 static int _nl_file(struct formatter *f)
113 {
114 	fprintf(f->data.fp, "\n");
115 
116 	return 1;
117 }
118 
119 static int _extend_buffer(struct formatter *f)
120 {
121 	char *newbuf;
122 
123 	log_debug("Doubling metadata output buffer to %" PRIu32,
124 		  f->data.buf.size * 2);
125 	if (!(newbuf = dm_realloc(f->data.buf.start,
126 				   f->data.buf.size * 2))) {
127 		log_error("Buffer reallocation failed.");
128 		return 0;
129 	}
130 	f->data.buf.start = newbuf;
131 	f->data.buf.size *= 2;
132 
133 	return 1;
134 }
135 
136 static int _nl_raw(struct formatter *f)
137 {
138 	/* If metadata doesn't fit, extend buffer */
139 	if ((f->data.buf.used + 2 > f->data.buf.size) &&
140 	    (!_extend_buffer(f)))
141 		return_0;
142 
143 	*(f->data.buf.start + f->data.buf.used) = '\n';
144 	f->data.buf.used += 1;
145 
146 	*(f->data.buf.start + f->data.buf.used) = '\0';
147 
148 	return 1;
149 }
150 
151 #define COMMENT_TAB 6
152 static int _out_with_comment_file(struct formatter *f, const char *comment,
153 				  const char *fmt, va_list ap)
154 {
155 	int i;
156 	char white_space[MAX_INDENT + 1];
157 
158 	if (ferror(f->data.fp))
159 		return 0;
160 
161 	for (i = 0; i < f->indent; i++)
162 		white_space[i] = '\t';
163 	white_space[i] = '\0';
164 	fputs(white_space, f->data.fp);
165 	i = vfprintf(f->data.fp, fmt, ap);
166 
167 	if (comment) {
168 		/*
169 		 * line comments up if possible.
170 		 */
171 		i += 8 * f->indent;
172 		i /= 8;
173 		i++;
174 
175 		do
176 			fputc('\t', f->data.fp);
177 
178 		while (++i < COMMENT_TAB);
179 
180 		fputs(comment, f->data.fp);
181 	}
182 	fputc('\n', f->data.fp);
183 
184 	return 1;
185 }
186 
187 static int _out_with_comment_raw(struct formatter *f,
188 				 const char *comment __attribute((unused)),
189 				 const char *fmt, va_list ap)
190 {
191 	int n;
192 
193 	n = vsnprintf(f->data.buf.start + f->data.buf.used,
194 		      f->data.buf.size - f->data.buf.used, fmt, ap);
195 
196 	/* If metadata doesn't fit, extend buffer */
197 	if (n < 0 || (n + f->data.buf.used + 2 > f->data.buf.size)) {
198 		if (!_extend_buffer(f))
199 			return_0;
200 		return -1; /* Retry */
201 	}
202 
203 	f->data.buf.used += n;
204 
205 	outnl(f);
206 
207 	return 1;
208 }
209 
210 /*
211  * Formats a string, converting a size specified
212  * in 512-byte sectors to a more human readable
213  * form (eg, megabytes).  We may want to lift this
214  * for other code to use.
215  */
216 static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
217 {
218 	static const char *_units[] = {
219 		"Kilobytes",
220 		"Megabytes",
221 		"Gigabytes",
222 		"Terabytes",
223 		"Petabytes",
224 		"Exabytes",
225 		NULL
226 	};
227 
228 	int i;
229 	double d = (double) sectors;
230 
231 	/* to convert to K */
232 	d /= 2.0;
233 
234 	for (i = 0; (d > 1024.0) && _units[i]; i++)
235 		d /= 1024.0;
236 
237 	return dm_snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
238 }
239 
240 /*
241  * Appends a comment giving a size in more easily
242  * readable form (eg, 4M instead of 8096).
243  */
244 int out_size(struct formatter *f, uint64_t size, const char *fmt, ...)
245 {
246 	char buffer[64];
247 	va_list ap;
248 	int r;
249 
250 	if (!_sectors_to_units(size, buffer, sizeof(buffer)))
251 		return 0;
252 
253 	_out_with_comment(f, buffer, fmt, ap);
254 
255 	return r;
256 }
257 
258 /*
259  * Appends a comment indicating that the line is
260  * only a hint.
261  */
262 int out_hint(struct formatter *f, const char *fmt, ...)
263 {
264 	va_list ap;
265 	int r;
266 
267 	_out_with_comment(f, "# Hint only", fmt, ap);
268 
269 	return r;
270 }
271 
272 /*
273  * Appends a comment
274  */
275 static int _out_comment(struct formatter *f, const char *comment, const char *fmt, ...)
276 {
277 	va_list ap;
278 	int r;
279 
280 	_out_with_comment(f, comment, fmt, ap);
281 
282 	return r;
283 }
284 
285 /*
286  * The normal output function.
287  */
288 int out_text(struct formatter *f, const char *fmt, ...)
289 {
290 	va_list ap;
291 	int r;
292 
293 	_out_with_comment(f, NULL, fmt, ap);
294 
295 	return r;
296 }
297 
298 static int _print_header(struct formatter *f,
299 			 const char *desc)
300 {
301 	char *buf;
302 	time_t t;
303 
304 	t = time(NULL);
305 
306 	outf(f, "# Generated by LVM2 version %s: %s", LVM_VERSION, ctime(&t));
307 	outf(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\"");
308 	outf(f, FORMAT_VERSION_FIELD " = %d", FORMAT_VERSION_VALUE);
309 	outnl(f);
310 
311 	if (!(buf = alloca(escaped_len(desc)))) {
312 		log_error("temporary stack allocation for description"
313 			  "string failed");
314 		return 0;
315 	}
316 	outf(f, "description = \"%s\"", escape_double_quotes(buf, desc));
317 	outnl(f);
318 	outf(f, "creation_host = \"%s\"\t# %s %s %s %s %s", _utsname.nodename,
319 	     _utsname.sysname, _utsname.nodename, _utsname.release,
320 	     _utsname.version, _utsname.machine);
321 	outf(f, "creation_time = %lu\t# %s", t, ctime(&t));
322 
323 	return 1;
324 }
325 
326 static int _print_flag_config(struct formatter *f, int status, int type)
327 {
328 	char buffer[4096];
329 	if (!print_flags(status, type | STATUS_FLAG, buffer, sizeof(buffer)))
330 		return_0;
331 	outf(f, "status = %s", buffer);
332 
333 	if (!print_flags(status, type, buffer, sizeof(buffer)))
334 		return_0;
335 	outf(f, "flags = %s", buffer);
336 
337 	return 1;
338 }
339 
340 static int _print_vg(struct formatter *f, struct volume_group *vg)
341 {
342 	char buffer[4096];
343 
344 	if (!id_write_format(&vg->id, buffer, sizeof(buffer)))
345 		return_0;
346 
347 	outf(f, "id = \"%s\"", buffer);
348 
349 	outf(f, "seqno = %u", vg->seqno);
350 
351 	if (!_print_flag_config(f, vg->status, VG_FLAGS))
352 		return_0;
353 
354 	if (!dm_list_empty(&vg->tags)) {
355 		if (!print_tags(&vg->tags, buffer, sizeof(buffer)))
356 			return_0;
357 		outf(f, "tags = %s", buffer);
358 	}
359 
360 	if (vg->system_id && *vg->system_id)
361 		outf(f, "system_id = \"%s\"", vg->system_id);
362 
363 	if (!out_size(f, (uint64_t) vg->extent_size, "extent_size = %u",
364 		      vg->extent_size))
365 		return_0;
366 	outf(f, "max_lv = %u", vg->max_lv);
367 	outf(f, "max_pv = %u", vg->max_pv);
368 
369 	/* Default policy is NORMAL; INHERIT is meaningless */
370 	if (vg->alloc != ALLOC_NORMAL && vg->alloc != ALLOC_INHERIT) {
371 		outnl(f);
372 		outf(f, "allocation_policy = \"%s\"",
373 		     get_alloc_string(vg->alloc));
374 	}
375 
376 	return 1;
377 }
378 
379 /*
380  * Get the pv%d name from the formatters hash
381  * table.
382  */
383 static const char *_get_pv_name(struct formatter *f, struct physical_volume *pv)
384 {
385 	return (pv) ? (const char *)
386 	    dm_hash_lookup(f->pv_names, pv_dev_name(pv)) : "Missing";
387 }
388 
389 static int _print_pvs(struct formatter *f, struct volume_group *vg)
390 {
391 	struct pv_list *pvl;
392 	struct physical_volume *pv;
393 	char buffer[4096];
394 	char *buf;
395 	const char *name;
396 
397 	outf(f, "physical_volumes {");
398 	_inc_indent(f);
399 
400 	dm_list_iterate_items(pvl, &vg->pvs) {
401 		pv = pvl->pv;
402 
403 		if (!(name = _get_pv_name(f, pv)))
404 			return_0;
405 
406 		outnl(f);
407 		outf(f, "%s {", name);
408 		_inc_indent(f);
409 
410 		if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
411 			return_0;
412 
413 		outf(f, "id = \"%s\"", buffer);
414 
415 		if (!(buf = alloca(escaped_len(pv_dev_name(pv))))) {
416 			log_error("temporary stack allocation for device name"
417 				  "string failed");
418 			return 0;
419 		}
420 
421 		if (!out_hint(f, "device = \"%s\"",
422 			      escape_double_quotes(buf, pv_dev_name(pv))))
423 			return_0;
424 		outnl(f);
425 
426 		if (!_print_flag_config(f, pv->status, PV_FLAGS))
427 			return_0;
428 
429 		if (!dm_list_empty(&pv->tags)) {
430 			if (!print_tags(&pv->tags, buffer, sizeof(buffer)))
431 				return_0;
432 			outf(f, "tags = %s", buffer);
433 		}
434 
435 		if (!out_size(f, pv->size, "dev_size = %" PRIu64, pv->size))
436 			return_0;
437 
438 		outf(f, "pe_start = %" PRIu64, pv->pe_start);
439 		if (!out_size(f, vg->extent_size * (uint64_t) pv->pe_count,
440 			      "pe_count = %u", pv->pe_count))
441 			return_0;
442 
443 		_dec_indent(f);
444 		outf(f, "}");
445 	}
446 
447 	_dec_indent(f);
448 	outf(f, "}");
449 	return 1;
450 }
451 
452 static int _print_segment(struct formatter *f, struct volume_group *vg,
453 			  int count, struct lv_segment *seg)
454 {
455 	char buffer[4096];
456 
457 	outf(f, "segment%u {", count);
458 	_inc_indent(f);
459 
460 	outf(f, "start_extent = %u", seg->le);
461 	if (!out_size(f, (uint64_t) seg->len * vg->extent_size,
462 		      "extent_count = %u", seg->len))
463 		return_0;
464 
465 	outnl(f);
466 	outf(f, "type = \"%s\"", seg->segtype->name);
467 
468 	if (!dm_list_empty(&seg->tags)) {
469 		if (!print_tags(&seg->tags, buffer, sizeof(buffer)))
470 			return_0;
471 		outf(f, "tags = %s", buffer);
472 	}
473 
474 	if (seg->segtype->ops->text_export &&
475 	    !seg->segtype->ops->text_export(seg, f))
476 		return_0;
477 
478 	_dec_indent(f);
479 	outf(f, "}");
480 
481 	return 1;
482 }
483 
484 int out_areas(struct formatter *f, const struct lv_segment *seg,
485 	      const char *type)
486 {
487 	const char *name;
488 	unsigned int s;
489 
490 	outnl(f);
491 
492 	outf(f, "%ss = [", type);
493 	_inc_indent(f);
494 
495 	for (s = 0; s < seg->area_count; s++) {
496 		switch (seg_type(seg, s)) {
497 		case AREA_PV:
498 			if (!(name = _get_pv_name(f, seg_pv(seg, s))))
499 				return_0;
500 
501 			outf(f, "\"%s\", %u%s", name,
502 			     seg_pe(seg, s),
503 			     (s == seg->area_count - 1) ? "" : ",");
504 			break;
505 		case AREA_LV:
506 			outf(f, "\"%s\", %u%s",
507 			     seg_lv(seg, s)->name,
508 			     seg_le(seg, s),
509 			     (s == seg->area_count - 1) ? "" : ",");
510 			break;
511 		case AREA_UNASSIGNED:
512 			return 0;
513 		}
514 	}
515 
516 	_dec_indent(f);
517 	outf(f, "]");
518 	return 1;
519 }
520 
521 static int _print_lv(struct formatter *f, struct logical_volume *lv)
522 {
523 	struct lv_segment *seg;
524 	char buffer[4096];
525 	int seg_count;
526 
527 	outnl(f);
528 	outf(f, "%s {", lv->name);
529 	_inc_indent(f);
530 
531 	/* FIXME: Write full lvid */
532 	if (!id_write_format(&lv->lvid.id[1], buffer, sizeof(buffer)))
533 		return_0;
534 
535 	outf(f, "id = \"%s\"", buffer);
536 
537 	if (!_print_flag_config(f, lv->status, LV_FLAGS))
538 		return_0;
539 
540 	if (!dm_list_empty(&lv->tags)) {
541 		if (!print_tags(&lv->tags, buffer, sizeof(buffer)))
542 			return_0;
543 		outf(f, "tags = %s", buffer);
544 	}
545 
546 	if (lv->alloc != ALLOC_INHERIT)
547 		outf(f, "allocation_policy = \"%s\"",
548 		     get_alloc_string(lv->alloc));
549 
550 	switch (lv->read_ahead) {
551 	case DM_READ_AHEAD_NONE:
552 		_out_comment(f, "# None", "read_ahead = -1");
553 		break;
554 	case DM_READ_AHEAD_AUTO:
555 		/* No output - use default */
556 		break;
557 	default:
558 		outf(f, "read_ahead = %u", lv->read_ahead);
559 	}
560 
561 	if (lv->major >= 0)
562 		outf(f, "major = %d", lv->major);
563 	if (lv->minor >= 0)
564 		outf(f, "minor = %d", lv->minor);
565 	outf(f, "segment_count = %u", dm_list_size(&lv->segments));
566 	outnl(f);
567 
568 	seg_count = 1;
569 	dm_list_iterate_items(seg, &lv->segments) {
570 		if (!_print_segment(f, lv->vg, seg_count++, seg))
571 			return_0;
572 	}
573 
574 	_dec_indent(f);
575 	outf(f, "}");
576 
577 	return 1;
578 }
579 
580 static int _print_lvs(struct formatter *f, struct volume_group *vg)
581 {
582 	struct lv_list *lvl;
583 
584 	/*
585 	 * Don't bother with an lv section if there are no lvs.
586 	 */
587 	if (dm_list_empty(&vg->lvs))
588 		return 1;
589 
590 	outf(f, "logical_volumes {");
591 	_inc_indent(f);
592 
593 	/*
594 	 * Write visible LVs first
595 	 */
596 	dm_list_iterate_items(lvl, &vg->lvs) {
597 		if (!(lv_is_displayable(lvl->lv)))
598 			continue;
599 		if (!_print_lv(f, lvl->lv))
600 			return_0;
601 	}
602 
603 	dm_list_iterate_items(lvl, &vg->lvs) {
604 		if ((lv_is_displayable(lvl->lv)))
605 			continue;
606 		if (!_print_lv(f, lvl->lv))
607 			return_0;
608 	}
609 
610 	_dec_indent(f);
611 	outf(f, "}");
612 
613 	return 1;
614 }
615 
616 /*
617  * In the text format we refer to pv's as 'pv1',
618  * 'pv2' etc.  This function builds a hash table
619  * to enable a quick lookup from device -> name.
620  */
621 static int _build_pv_names(struct formatter *f, struct volume_group *vg)
622 {
623 	int count = 0;
624 	struct pv_list *pvl;
625 	struct physical_volume *pv;
626 	char buffer[32], *name;
627 
628 	if (!(f->mem = dm_pool_create("text pv_names", 512)))
629 		return_0;
630 
631 	if (!(f->pv_names = dm_hash_create(128)))
632 		return_0;
633 
634 	dm_list_iterate_items(pvl, &vg->pvs) {
635 		pv = pvl->pv;
636 
637 		/* FIXME But skip if there's already an LV called pv%d ! */
638 		if (dm_snprintf(buffer, sizeof(buffer), "pv%d", count++) < 0)
639 			return_0;
640 
641 		if (!(name = dm_pool_strdup(f->mem, buffer)))
642 			return_0;
643 
644 		if (!dm_hash_insert(f->pv_names, pv_dev_name(pv), name))
645 			return_0;
646 	}
647 
648 	return 1;
649 }
650 
651 static int _text_vg_export(struct formatter *f,
652 			   struct volume_group *vg, const char *desc)
653 {
654 	int r = 0;
655 
656 	if (!_build_pv_names(f, vg))
657 		goto_out;
658 
659 	if (f->header && !_print_header(f, desc))
660 		goto_out;
661 
662 	if (!out_text(f, "%s {", vg->name))
663 		goto_out;
664 
665 	_inc_indent(f);
666 
667 	if (!_print_vg(f, vg))
668 		goto_out;
669 
670 	outnl(f);
671 	if (!_print_pvs(f, vg))
672 		goto_out;
673 
674 	outnl(f);
675 	if (!_print_lvs(f, vg))
676 		goto_out;
677 
678 	_dec_indent(f);
679 	if (!out_text(f, "}"))
680 		goto_out;
681 
682 	if (!f->header && !_print_header(f, desc))
683 		goto_out;
684 
685 	r = 1;
686 
687       out:
688 	if (f->mem)
689 		dm_pool_destroy(f->mem);
690 
691 	if (f->pv_names)
692 		dm_hash_destroy(f->pv_names);
693 
694 	return r;
695 }
696 
697 int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp)
698 {
699 	struct formatter *f;
700 	int r;
701 
702 	_init();
703 
704 	if (!(f = dm_malloc(sizeof(*f))))
705 		return_0;
706 
707 	memset(f, 0, sizeof(*f));
708 	f->data.fp = fp;
709 	f->indent = 0;
710 	f->header = 1;
711 	f->out_with_comment = &_out_with_comment_file;
712 	f->nl = &_nl_file;
713 
714 	r = _text_vg_export(f, vg, desc);
715 	if (r)
716 		r = !ferror(f->data.fp);
717 	dm_free(f);
718 	return r;
719 }
720 
721 /* Returns amount of buffer used incl. terminating NUL */
722 int text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf)
723 {
724 	struct formatter *f;
725 	int r = 0;
726 
727 	_init();
728 
729 	if (!(f = dm_malloc(sizeof(*f))))
730 		return_0;
731 
732 	memset(f, 0, sizeof(*f));
733 
734 	f->data.buf.size = 65536;	/* Initial metadata limit */
735 	if (!(f->data.buf.start = dm_malloc(f->data.buf.size))) {
736 		log_error("text_export buffer allocation failed");
737 		goto out;
738 	}
739 
740 	f->indent = 0;
741 	f->header = 0;
742 	f->out_with_comment = &_out_with_comment_raw;
743 	f->nl = &_nl_raw;
744 
745 	if (!_text_vg_export(f, vg, desc)) {
746 		dm_free(f->data.buf.start);
747 		goto_out;
748 	}
749 
750 	r = f->data.buf.used + 1;
751 	*buf = f->data.buf.start;
752 
753       out:
754 	dm_free(f);
755 	return r;
756 }
757 
758 int export_vg_to_buffer(struct volume_group *vg, char **buf)
759 {
760 	return text_vg_export_raw(vg, "", buf);
761 }
762 
763 #undef outf
764 #undef outnl
765