Home | History | Annotate | Line # | Download | only in format_text
      1 /*	$NetBSD: export.c,v 1.2 2011/05/24 15:51:00 joerg Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
      5  * Copyright (C) 2004-2009 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 "lvm-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 /* increment indention level */
    241 void out_inc_indent(struct formatter *f)
    242 {
    243 	_inc_indent(f);
    244 }
    245 
    246 /* decrement indention level */
    247 void out_dec_indent(struct formatter *f)
    248 {
    249 	_dec_indent(f);
    250 }
    251 
    252 /* insert new line */
    253 int out_newline(struct formatter *f)
    254 {
    255 	return f->nl(f);
    256 }
    257 
    258 /*
    259  * Appends a comment giving a size in more easily
    260  * readable form (eg, 4M instead of 8096).
    261  */
    262 int out_size(struct formatter *f, uint64_t size, const char *fmt, ...)
    263 {
    264 	char buffer[64];
    265 	va_list ap;
    266 	int r;
    267 
    268 	if (!_sectors_to_units(size, buffer, sizeof(buffer)))
    269 		return 0;
    270 
    271 	_out_with_comment(f, buffer, fmt, ap);
    272 
    273 	return r;
    274 }
    275 
    276 /*
    277  * Appends a comment indicating that the line is
    278  * only a hint.
    279  */
    280 int out_hint(struct formatter *f, const char *fmt, ...)
    281 {
    282 	va_list ap;
    283 	int r;
    284 
    285 	_out_with_comment(f, "# Hint only", fmt, ap);
    286 
    287 	return r;
    288 }
    289 
    290 /*
    291  * Appends a comment
    292  */
    293 static int _out_comment(struct formatter *f, const char *comment, const char *fmt, ...)
    294 {
    295 	va_list ap;
    296 	int r;
    297 
    298 	_out_with_comment(f, comment, fmt, ap);
    299 
    300 	return r;
    301 }
    302 
    303 /*
    304  * The normal output function.
    305  */
    306 int out_text(struct formatter *f, const char *fmt, ...)
    307 {
    308 	va_list ap;
    309 	int r;
    310 
    311 	_out_with_comment(f, NULL, fmt, ap);
    312 
    313 	return r;
    314 }
    315 
    316 static int _out_line(const char *line, void *_f) {
    317 	struct formatter *f = (struct formatter *) _f;
    318 	return out_text(f, "%s", line);
    319 }
    320 
    321 int out_config_node(struct formatter *f, const struct config_node *cn)
    322 {
    323 	return write_config_node(cn, _out_line, f);
    324 }
    325 
    326 static int _print_header(struct formatter *f,
    327 			 const char *desc)
    328 {
    329 	char *buf;
    330 	time_t t;
    331 
    332 	t = time(NULL);
    333 
    334 	outf(f, "# Generated by LVM2 version %s: %s", LVM_VERSION, ctime(&t));
    335 	outf(f, CONTENTS_FIELD " = \"" CONTENTS_VALUE "\"");
    336 	outf(f, FORMAT_VERSION_FIELD " = %d", FORMAT_VERSION_VALUE);
    337 	outnl(f);
    338 
    339 	if (!(buf = alloca(escaped_len(desc)))) {
    340 		log_error("temporary stack allocation for description"
    341 			  "string failed");
    342 		return 0;
    343 	}
    344 	outf(f, "description = \"%s\"", escape_double_quotes(buf, desc));
    345 	outnl(f);
    346 	outf(f, "creation_host = \"%s\"\t# %s %s %s %s %s", _utsname.nodename,
    347 	     _utsname.sysname, _utsname.nodename, _utsname.release,
    348 	     _utsname.version, _utsname.machine);
    349 	outf(f, "creation_time = %jd\t# %s", (intmax_t)t, ctime(&t));
    350 
    351 	return 1;
    352 }
    353 
    354 static int _print_flag_config(struct formatter *f, int status, int type)
    355 {
    356 	char buffer[4096];
    357 	if (!print_flags(status, type | STATUS_FLAG, buffer, sizeof(buffer)))
    358 		return_0;
    359 	outf(f, "status = %s", buffer);
    360 
    361 	if (!print_flags(status, type, buffer, sizeof(buffer)))
    362 		return_0;
    363 	outf(f, "flags = %s", buffer);
    364 
    365 	return 1;
    366 }
    367 
    368 static int _print_vg(struct formatter *f, struct volume_group *vg)
    369 {
    370 	char buffer[4096];
    371 
    372 	if (!id_write_format(&vg->id, buffer, sizeof(buffer)))
    373 		return_0;
    374 
    375 	outf(f, "id = \"%s\"", buffer);
    376 
    377 	outf(f, "seqno = %u", vg->seqno);
    378 
    379 	if (!_print_flag_config(f, vg->status, VG_FLAGS))
    380 		return_0;
    381 
    382 	if (!dm_list_empty(&vg->tags)) {
    383 		if (!print_tags(&vg->tags, buffer, sizeof(buffer)))
    384 			return_0;
    385 		outf(f, "tags = %s", buffer);
    386 	}
    387 
    388 	if (vg->system_id && *vg->system_id)
    389 		outf(f, "system_id = \"%s\"", vg->system_id);
    390 
    391 	if (!out_size(f, (uint64_t) vg->extent_size, "extent_size = %u",
    392 		      vg->extent_size))
    393 		return_0;
    394 	outf(f, "max_lv = %u", vg->max_lv);
    395 	outf(f, "max_pv = %u", vg->max_pv);
    396 
    397 	/* Default policy is NORMAL; INHERIT is meaningless */
    398 	if (vg->alloc != ALLOC_NORMAL && vg->alloc != ALLOC_INHERIT) {
    399 		outnl(f);
    400 		outf(f, "allocation_policy = \"%s\"",
    401 		     get_alloc_string(vg->alloc));
    402 	}
    403 
    404 	return 1;
    405 }
    406 
    407 /*
    408  * Get the pv%d name from the formatters hash
    409  * table.
    410  */
    411 static const char *_get_pv_name_from_uuid(struct formatter *f, char *uuid)
    412 {
    413 	return dm_hash_lookup(f->pv_names, uuid);
    414 }
    415 
    416 static const char *_get_pv_name(struct formatter *f, struct physical_volume *pv)
    417 {
    418 	char uuid[64] __attribute((aligned(8)));
    419 
    420 	if (!pv || !id_write_format(&pv->id, uuid, sizeof(uuid)))
    421 		return_NULL;
    422 
    423 	return _get_pv_name_from_uuid(f, uuid);
    424 }
    425 
    426 static int _print_pvs(struct formatter *f, struct volume_group *vg)
    427 {
    428 	struct pv_list *pvl;
    429 	struct physical_volume *pv;
    430 	char buffer[4096];
    431 	char *buf;
    432 	const char *name;
    433 
    434 	outf(f, "physical_volumes {");
    435 	_inc_indent(f);
    436 
    437 	dm_list_iterate_items(pvl, &vg->pvs) {
    438 		pv = pvl->pv;
    439 
    440 		if (!id_write_format(&pv->id, buffer, sizeof(buffer)))
    441 			return_0;
    442 
    443 		if (!(name = _get_pv_name_from_uuid(f, buffer)))
    444 			return_0;
    445 
    446 		outnl(f);
    447 		outf(f, "%s {", name);
    448 		_inc_indent(f);
    449 
    450 		outf(f, "id = \"%s\"", buffer);
    451 
    452 		if (!(buf = alloca(escaped_len(pv_dev_name(pv))))) {
    453 			log_error("temporary stack allocation for device name"
    454 				  "string failed");
    455 			return 0;
    456 		}
    457 
    458 		if (!out_hint(f, "device = \"%s\"",
    459 			      escape_double_quotes(buf, pv_dev_name(pv))))
    460 			return_0;
    461 		outnl(f);
    462 
    463 		if (!_print_flag_config(f, pv->status, PV_FLAGS))
    464 			return_0;
    465 
    466 		if (!dm_list_empty(&pv->tags)) {
    467 			if (!print_tags(&pv->tags, buffer, sizeof(buffer)))
    468 				return_0;
    469 			outf(f, "tags = %s", buffer);
    470 		}
    471 
    472 		if (!out_size(f, pv->size, "dev_size = %" PRIu64, pv->size))
    473 			return_0;
    474 
    475 		outf(f, "pe_start = %" PRIu64, pv->pe_start);
    476 		if (!out_size(f, vg->extent_size * (uint64_t) pv->pe_count,
    477 			      "pe_count = %u", pv->pe_count))
    478 			return_0;
    479 
    480 		_dec_indent(f);
    481 		outf(f, "}");
    482 	}
    483 
    484 	_dec_indent(f);
    485 	outf(f, "}");
    486 	return 1;
    487 }
    488 
    489 static int _print_segment(struct formatter *f, struct volume_group *vg,
    490 			  int count, struct lv_segment *seg)
    491 {
    492 	char buffer[4096];
    493 
    494 	outf(f, "segment%u {", count);
    495 	_inc_indent(f);
    496 
    497 	outf(f, "start_extent = %u", seg->le);
    498 	if (!out_size(f, (uint64_t) seg->len * vg->extent_size,
    499 		      "extent_count = %u", seg->len))
    500 		return_0;
    501 
    502 	outnl(f);
    503 	outf(f, "type = \"%s\"", seg->segtype->name);
    504 
    505 	if (!dm_list_empty(&seg->tags)) {
    506 		if (!print_tags(&seg->tags, buffer, sizeof(buffer)))
    507 			return_0;
    508 		outf(f, "tags = %s", buffer);
    509 	}
    510 
    511 	if (seg->segtype->ops->text_export &&
    512 	    !seg->segtype->ops->text_export(seg, f))
    513 		return_0;
    514 
    515 	_dec_indent(f);
    516 	outf(f, "}");
    517 
    518 	return 1;
    519 }
    520 
    521 int out_areas(struct formatter *f, const struct lv_segment *seg,
    522 	      const char *type)
    523 {
    524 	const char *name;
    525 	unsigned int s;
    526 
    527 	outnl(f);
    528 
    529 	outf(f, "%ss = [", type);
    530 	_inc_indent(f);
    531 
    532 	for (s = 0; s < seg->area_count; s++) {
    533 		switch (seg_type(seg, s)) {
    534 		case AREA_PV:
    535 			if (!(name = _get_pv_name(f, seg_pv(seg, s))))
    536 				return_0;
    537 
    538 			outf(f, "\"%s\", %u%s", name,
    539 			     seg_pe(seg, s),
    540 			     (s == seg->area_count - 1) ? "" : ",");
    541 			break;
    542 		case AREA_LV:
    543 			outf(f, "\"%s\", %u%s",
    544 			     seg_lv(seg, s)->name,
    545 			     seg_le(seg, s),
    546 			     (s == seg->area_count - 1) ? "" : ",");
    547 			break;
    548 		case AREA_UNASSIGNED:
    549 			return 0;
    550 		}
    551 	}
    552 
    553 	_dec_indent(f);
    554 	outf(f, "]");
    555 	return 1;
    556 }
    557 
    558 static int _print_lv(struct formatter *f, struct logical_volume *lv)
    559 {
    560 	struct lv_segment *seg;
    561 	char buffer[4096];
    562 	int seg_count;
    563 
    564 	outnl(f);
    565 	outf(f, "%s {", lv->name);
    566 	_inc_indent(f);
    567 
    568 	/* FIXME: Write full lvid */
    569 	if (!id_write_format(&lv->lvid.id[1], buffer, sizeof(buffer)))
    570 		return_0;
    571 
    572 	outf(f, "id = \"%s\"", buffer);
    573 
    574 	if (!_print_flag_config(f, lv->status, LV_FLAGS))
    575 		return_0;
    576 
    577 	if (!dm_list_empty(&lv->tags)) {
    578 		if (!print_tags(&lv->tags, buffer, sizeof(buffer)))
    579 			return_0;
    580 		outf(f, "tags = %s", buffer);
    581 	}
    582 
    583 	if (lv->alloc != ALLOC_INHERIT)
    584 		outf(f, "allocation_policy = \"%s\"",
    585 		     get_alloc_string(lv->alloc));
    586 
    587 	switch (lv->read_ahead) {
    588 	case DM_READ_AHEAD_NONE:
    589 		_out_comment(f, "# None", "read_ahead = -1");
    590 		break;
    591 	case DM_READ_AHEAD_AUTO:
    592 		/* No output - use default */
    593 		break;
    594 	default:
    595 		outf(f, "read_ahead = %u", lv->read_ahead);
    596 	}
    597 
    598 	if (lv->major >= 0)
    599 		outf(f, "major = %d", lv->major);
    600 	if (lv->minor >= 0)
    601 		outf(f, "minor = %d", lv->minor);
    602 	outf(f, "segment_count = %u", dm_list_size(&lv->segments));
    603 	outnl(f);
    604 
    605 	seg_count = 1;
    606 	dm_list_iterate_items(seg, &lv->segments) {
    607 		if (!_print_segment(f, lv->vg, seg_count++, seg))
    608 			return_0;
    609 	}
    610 
    611 	_dec_indent(f);
    612 	outf(f, "}");
    613 
    614 	return 1;
    615 }
    616 
    617 static int _print_lvs(struct formatter *f, struct volume_group *vg)
    618 {
    619 	struct lv_list *lvl;
    620 
    621 	/*
    622 	 * Don't bother with an lv section if there are no lvs.
    623 	 */
    624 	if (dm_list_empty(&vg->lvs))
    625 		return 1;
    626 
    627 	outf(f, "logical_volumes {");
    628 	_inc_indent(f);
    629 
    630 	/*
    631 	 * Write visible LVs first
    632 	 */
    633 	dm_list_iterate_items(lvl, &vg->lvs) {
    634 		if (!(lv_is_visible(lvl->lv)))
    635 			continue;
    636 		if (!_print_lv(f, lvl->lv))
    637 			return_0;
    638 	}
    639 
    640 	dm_list_iterate_items(lvl, &vg->lvs) {
    641 		if ((lv_is_visible(lvl->lv)))
    642 			continue;
    643 		if (!_print_lv(f, lvl->lv))
    644 			return_0;
    645 	}
    646 
    647 	_dec_indent(f);
    648 	outf(f, "}");
    649 
    650 	return 1;
    651 }
    652 
    653 /*
    654  * In the text format we refer to pv's as 'pv1',
    655  * 'pv2' etc.  This function builds a hash table
    656  * to enable a quick lookup from device -> name.
    657  */
    658 static int _build_pv_names(struct formatter *f, struct volume_group *vg)
    659 {
    660 	int count = 0;
    661 	struct pv_list *pvl;
    662 	struct physical_volume *pv;
    663 	char buffer[32], *uuid, *name;
    664 
    665 	if (!(f->mem = dm_pool_create("text pv_names", 512)))
    666 		return_0;
    667 
    668 	if (!(f->pv_names = dm_hash_create(128)))
    669 		return_0;
    670 
    671 	dm_list_iterate_items(pvl, &vg->pvs) {
    672 		pv = pvl->pv;
    673 
    674 		/* FIXME But skip if there's already an LV called pv%d ! */
    675 		if (dm_snprintf(buffer, sizeof(buffer), "pv%d", count++) < 0)
    676 			return_0;
    677 
    678 		if (!(name = dm_pool_strdup(f->mem, buffer)))
    679 			return_0;
    680 
    681 		if (!(uuid = dm_pool_zalloc(f->mem, 64)) ||
    682 		   !id_write_format(&pv->id, uuid, 64))
    683 			return_0;
    684 
    685 		if (!dm_hash_insert(f->pv_names, uuid, name))
    686 			return_0;
    687 	}
    688 
    689 	return 1;
    690 }
    691 
    692 static int _text_vg_export(struct formatter *f,
    693 			   struct volume_group *vg, const char *desc)
    694 {
    695 	int r = 0;
    696 
    697 	if (!_build_pv_names(f, vg))
    698 		goto_out;
    699 
    700 	if (f->header && !_print_header(f, desc))
    701 		goto_out;
    702 
    703 	if (!out_text(f, "%s {", vg->name))
    704 		goto_out;
    705 
    706 	_inc_indent(f);
    707 
    708 	if (!_print_vg(f, vg))
    709 		goto_out;
    710 
    711 	outnl(f);
    712 	if (!_print_pvs(f, vg))
    713 		goto_out;
    714 
    715 	outnl(f);
    716 	if (!_print_lvs(f, vg))
    717 		goto_out;
    718 
    719 	_dec_indent(f);
    720 	if (!out_text(f, "}"))
    721 		goto_out;
    722 
    723 	if (!f->header && !_print_header(f, desc))
    724 		goto_out;
    725 
    726 	r = 1;
    727 
    728       out:
    729 	if (f->mem)
    730 		dm_pool_destroy(f->mem);
    731 
    732 	if (f->pv_names)
    733 		dm_hash_destroy(f->pv_names);
    734 
    735 	return r;
    736 }
    737 
    738 int text_vg_export_file(struct volume_group *vg, const char *desc, FILE *fp)
    739 {
    740 	struct formatter *f;
    741 	int r;
    742 
    743 	_init();
    744 
    745 	if (!(f = dm_malloc(sizeof(*f))))
    746 		return_0;
    747 
    748 	memset(f, 0, sizeof(*f));
    749 	f->data.fp = fp;
    750 	f->indent = 0;
    751 	f->header = 1;
    752 	f->out_with_comment = &_out_with_comment_file;
    753 	f->nl = &_nl_file;
    754 
    755 	r = _text_vg_export(f, vg, desc);
    756 	if (r)
    757 		r = !ferror(f->data.fp);
    758 	dm_free(f);
    759 	return r;
    760 }
    761 
    762 /* Returns amount of buffer used incl. terminating NUL */
    763 int text_vg_export_raw(struct volume_group *vg, const char *desc, char **buf)
    764 {
    765 	struct formatter *f;
    766 	int r = 0;
    767 
    768 	_init();
    769 
    770 	if (!(f = dm_malloc(sizeof(*f))))
    771 		return_0;
    772 
    773 	memset(f, 0, sizeof(*f));
    774 
    775 	f->data.buf.size = 65536;	/* Initial metadata limit */
    776 	if (!(f->data.buf.start = dm_malloc(f->data.buf.size))) {
    777 		log_error("text_export buffer allocation failed");
    778 		goto out;
    779 	}
    780 
    781 	f->indent = 0;
    782 	f->header = 0;
    783 	f->out_with_comment = &_out_with_comment_raw;
    784 	f->nl = &_nl_raw;
    785 
    786 	if (!_text_vg_export(f, vg, desc)) {
    787 		dm_free(f->data.buf.start);
    788 		goto_out;
    789 	}
    790 
    791 	r = f->data.buf.used + 1;
    792 	*buf = f->data.buf.start;
    793 
    794       out:
    795 	dm_free(f);
    796 	return r;
    797 }
    798 
    799 int export_vg_to_buffer(struct volume_group *vg, char **buf)
    800 {
    801 	return text_vg_export_raw(vg, "", buf);
    802 }
    803 
    804 #undef outf
    805 #undef outnl
    806