mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
455 lines
7.9 KiB
C
455 lines
7.9 KiB
C
/*
|
|
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
|
*
|
|
* This file is released under the LGPL.
|
|
*/
|
|
|
|
#include "import-export.h"
|
|
#include "metadata.h"
|
|
#include "log.h"
|
|
#include "hash.h"
|
|
#include "pool.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
/*
|
|
* The first half of this file deals with
|
|
* exporting the vg, ie. writing it to a file.
|
|
*/
|
|
struct formatter {
|
|
struct pool *mem; /* pv names allocated from here */
|
|
struct hash_table *pv_names; /* dev_name -> pv_name (eg, pv1) */
|
|
|
|
FILE *fp; /* where we're writing to */
|
|
int indent; /* current level of indentation */
|
|
};
|
|
|
|
#define MAX_INDENT 5
|
|
static void _inc_indent(struct formatter *f)
|
|
{
|
|
if (++f->indent > MAX_INDENT)
|
|
f->indent = MAX_INDENT;
|
|
}
|
|
|
|
static void _dec_indent(struct formatter *f)
|
|
{
|
|
if (--f->indent) {
|
|
log_debug("Indenting seems to have messed up\n");
|
|
f->indent = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Newline function for prettier layout.
|
|
*/
|
|
static void _nl(struct formatter *f)
|
|
{
|
|
fprintf(f->fp, "\n");
|
|
}
|
|
|
|
#define COMMENT_TAB 6
|
|
static void _out_with_comment(struct formatter *f, const char *comment,
|
|
const char *fmt, va_list ap)
|
|
{
|
|
int i;
|
|
char white_space[MAX_INDENT + 1];
|
|
|
|
for (i = 0; i < f->indent; i++)
|
|
white_space[i] = '\t';
|
|
white_space[i] = '\0';
|
|
fprintf(f->fp, white_space);
|
|
|
|
i = vfprintf(f->fp, fmt, ap);
|
|
|
|
if (comment) {
|
|
/*
|
|
* line comments up if possible.
|
|
*/
|
|
i += 8 * f->indent;
|
|
i /= 8;
|
|
i++;
|
|
|
|
do
|
|
fputc('\t', f->fp);
|
|
|
|
while (++i < COMMENT_TAB);
|
|
|
|
fprintf(f->fp, comment);
|
|
}
|
|
fputc('\n', f->fp);
|
|
}
|
|
|
|
/*
|
|
* Formats a string, converting a size specified
|
|
* in 512-byte sectors to a more human readable
|
|
* form (eg, megabytes). We may want to lift this
|
|
* for other code to use.
|
|
*/
|
|
static int _sectors_to_units(uint64_t sectors, char *buffer, size_t s)
|
|
{
|
|
static char *_units = [
|
|
"Kilobytes",
|
|
"Megabytes",
|
|
"Gigabytes",
|
|
"Terrabytes",
|
|
NULL
|
|
];
|
|
|
|
int i;
|
|
double d = (double) sectors;
|
|
|
|
/* to convert to K */
|
|
d /= 2.0;
|
|
|
|
for (i = 0; (d > 1024.0) && _units[i]; i++)
|
|
d /= 1024.0;
|
|
|
|
/* FIXME: arrange so this doesn't print a
|
|
* decimal point unless we have a
|
|
* fractional part. */
|
|
return snprintf(buffer, s, "# %g %s", d, _units[i]) > 0;
|
|
}
|
|
|
|
/*
|
|
* Appends a comment giving a size in more easily
|
|
* readable form (eg, 4M instead of 8096).
|
|
*/
|
|
static void _out_size(struct formatter *f, uint64_t size,
|
|
const char *fmt, ...)
|
|
{
|
|
char buffer[64];
|
|
va_list ap;
|
|
|
|
_sectors_to_units(size, buffer, sizeof(buffer));
|
|
|
|
va_start(ap, fmt);
|
|
_out_with_comment(f, buffer, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* Appends a comment indicating that the line is
|
|
* only a hint.
|
|
*/
|
|
static void _out_hint(struct formatter *f, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
_out_with_comment(f, "# Hint only", fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* The normal output function.
|
|
*/
|
|
static void _out(struct formatter *f, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
_out_with_comment(f, NULL, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
static int _print_header(struct formatter *f, struct volume_group *vg)
|
|
{
|
|
_out(f,
|
|
"# This file was originally generated by the LVM2 library\n"
|
|
"# It is inadvisable for people to edit this by hand unless\n"
|
|
"# they *really* know what they're doing.\n"
|
|
"# Generated: %s\n", ctime(time(NULL)));
|
|
return 1;
|
|
}
|
|
|
|
static int _print_vg(struct formatter *f, struct volume_group *vg)
|
|
{
|
|
char buffer[256];
|
|
|
|
if (!id_write_format(&vg.uuid, buffer, sizeof(buffer))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
_out(f, "id = \"%s\"", buffer);
|
|
_nl(f);
|
|
|
|
if (!_print_flags(vg->status, _vg_flags, buffer, size)) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
_out(f, "status = %s");
|
|
_nl(f);
|
|
|
|
_out_size(f, vg->extent_size, "extent_size = %u", vg->extent_size);
|
|
_nl(f);
|
|
|
|
_out(f, "max_lv = %u", vg->max_lv);
|
|
_out(f, "max_pv = %u", vg->max_pv);
|
|
_nl(f);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Get the pv%d name from the formatters hash
|
|
* table.
|
|
*/
|
|
static inline const char *
|
|
_get_pv_name(struct formatter *f, struct physical_volume *pv)
|
|
{
|
|
return (const char *) hash_lookup(f->pv_names, dev_name(pv));
|
|
}
|
|
|
|
static int _print_pvs(struct formatter *f, struct volume_group *vg)
|
|
{
|
|
struct list pvh;
|
|
struct physical_volume *pv;
|
|
char buffer[256];
|
|
const char *name;
|
|
|
|
_out(f, "physical_volumes {");
|
|
_nl(f);
|
|
_inc_indent(f);
|
|
|
|
list_iterate (pvh, &vg->pvs) {
|
|
|
|
if (!(name = _get_pv_name(f, pv))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
_out(f, "%s {", name);
|
|
_inc_indent(f);
|
|
|
|
pv = &list_item(pvh, struct pv_list)->pv;
|
|
|
|
if (!id_write_format(&pv->uuid, buffer, sizeof(buffer))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
_out(f, "tid = \"%s\"", buffer);
|
|
_out_hint(f, "device = %s", dev_name(pv->dev));
|
|
_nl(f);
|
|
|
|
if (!print_flags(pv->status, _pv_flags,
|
|
buffer, sizeof(buffer))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
_out(f, "status = %s", buffer);
|
|
_out(f, "pe_start = %u", pv->pe_start);
|
|
_out_size(f, vg->extent_size * (uint64_t) pv->pe_count,
|
|
"pe_count = %u", pv->pe_count);
|
|
|
|
_dec_indent(f);
|
|
_out(f, "}");
|
|
_nl(f);
|
|
}
|
|
|
|
_dec_indent(f);
|
|
_out(f, "}");
|
|
return 1;
|
|
}
|
|
|
|
static int _print_segment(struct formatter *f, struct volume_group *vg,
|
|
int count, struct stripe_segment *seg, int last)
|
|
{
|
|
int s;
|
|
const char *name;
|
|
|
|
_out(f, "segment%u {", count);
|
|
_inc_indent(f);
|
|
|
|
_out(f, "start_entent = %u", seg->le);
|
|
_out_size(f, seg->len * vg->extent_size, "extent_count = %u", seg->len);
|
|
_out(f, "stripes = %u", seg->stripes);
|
|
|
|
if (seg->stripes > 1)
|
|
_out_size(f, "stripe_size = %u", seg->stripe_size);
|
|
|
|
_nl(f);
|
|
_out(f, "areas = [");
|
|
_inc_indent(f);
|
|
|
|
for (s = 0; s < seg->stripes; s++) {
|
|
if (!(name = _get_pv_name(f, seg->areas[s].pv))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
_out(f, "\"%s\", %u%s", name, seg->area[s].pe,
|
|
last ? "" : ",");
|
|
}
|
|
|
|
_dec_indent(f);
|
|
_out(f, "]");
|
|
|
|
_dec_indent(f);
|
|
_out(f, "}");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int _print_lvs(struct formatter *f, struct volume_group *vg)
|
|
{
|
|
struct list *lvh, *segh;
|
|
struct logical_volume *lv;
|
|
struct stripe_segment *seg;
|
|
char buffer[256];
|
|
int seg_count = 1;
|
|
|
|
_out(f, "logical_volumes {");
|
|
_nl(f);
|
|
_inc_indent(f);
|
|
|
|
list_iterate (lvh, &vg->lvs) {
|
|
lv = &list_item(lvh, struct lv_list)->lv;
|
|
|
|
_out(f, "%s {", lv->name);
|
|
_inc_indent(f);
|
|
|
|
if (!_print_flags(lv->status, _lv_flags,
|
|
buffer, sizeof(buffer))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
_out(f, "read_ahead = %u", lv->read_ahead);
|
|
_nl(f);
|
|
|
|
_out(f, "segment_count = %u", _count_segments(lv));
|
|
_nl(f);
|
|
|
|
list_iterate (segh, &lv->segments) {
|
|
seg = list_item(segh, struct stripe_segment);
|
|
|
|
if (!_print_segment(f, seg_count++, seg)) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
_out(f, "}");
|
|
_dec_indent(f);
|
|
}
|
|
|
|
_out(f, "}");
|
|
_nl(f);
|
|
_dec_indent(f);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* In the text format we refer to pv's as 'pv1',
|
|
* 'pv2' etc. This function builds a hash table
|
|
* to enable a quick lookup from device -> name.
|
|
*/
|
|
static int _build_pv_names(struct formatter *f,
|
|
struct volume_group *vg)
|
|
{
|
|
int count = 0;
|
|
struct list *pvh;
|
|
struct physical_volume *pv;
|
|
char buffer[32], *name;
|
|
|
|
if (!(f->mem = pool_create(512))) {
|
|
stack;
|
|
goto bad;
|
|
}
|
|
|
|
if (!(f->pv_names = hash_create(128))) {
|
|
stack;
|
|
goto bad;
|
|
}
|
|
|
|
list_iterate (pvh, &vg->pvs) {
|
|
pv = &list_item(pvh, struct pv_list)->pv;
|
|
|
|
if (snprintf(buffer, sizeof(buffer), "pv%d", count++) < 0) {
|
|
stack;
|
|
goto bad;
|
|
}
|
|
|
|
if (!(name = pool_strdup(f->mem, buffer))) {
|
|
stack;
|
|
goto bad;
|
|
}
|
|
|
|
if (!hash_insert(f->pv_names, dev_name(pv->dev), buffer)) {
|
|
stack;
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
|
|
bad:
|
|
if (f->mem)
|
|
pool_destroy(f->mem);
|
|
|
|
if (f->pv_names)
|
|
hash_destroy(f->pv_names);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int text_vg_export(FILE *fp, struct volume_group *vg)
|
|
{
|
|
int r = 0;
|
|
struct formatter *f;
|
|
|
|
if (!f = dbg_malloc(sizeof(*f))) {
|
|
stack;
|
|
return 0;
|
|
}
|
|
|
|
memset(f, 0, sizeof(*f));
|
|
f->fp = fp;
|
|
f->indent = 0;
|
|
|
|
if (!_build_pv_names(f, vg)) {
|
|
stack;
|
|
goto out;
|
|
}
|
|
|
|
#define fail do {stack; goto out;} while(0)
|
|
|
|
if (!_print_header(f, vg))
|
|
fail;
|
|
|
|
_out(f, "%s {", vg->name);
|
|
_inc_indent(f):
|
|
|
|
if (!_print_vg(f, vg))
|
|
fail;
|
|
|
|
if (!_print_pvs(f, vg))
|
|
fail;
|
|
|
|
if (!_print_lvs(f, vg))
|
|
fail;
|
|
|
|
#undef fail
|
|
|
|
_dec_indent(f);
|
|
_out(f, "}");
|
|
|
|
r = 1;
|
|
|
|
out:
|
|
if (f->mem)
|
|
pool_destroy(f->mem);
|
|
|
|
if (f->pv_names)
|
|
hash_destroy(f->pv_names);
|
|
|
|
dbg_free(f):
|
|
return r;
|
|
}
|