/* * 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 "dbg_malloc.h" #include "lvm-string.h" #include #include #include /* * 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 */ int error; }; /* * Formatting functions. */ static void _out_size(struct formatter *f, uint64_t size, const char *fmt, ...) __attribute__ (( format (printf, 3, 4) )); static void _out_hint(struct formatter *f, const char *fmt, ...) __attribute__ (( format (printf, 2, 3) )); static void _out(struct formatter *f, const char *fmt, ...) __attribute__ (( format (printf, 2, 3) )); #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_error("Internal error tracking indentation"); 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]; if (ferror(f->fp)) return; 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; return lvm_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, const char *desc) { time_t t; t = time(NULL); _out(f, "# This file was originally generated by the LVM2 library\n" "# Generated: %s\n", ctime(&t)); _out(f, "description = \"%s\"", desc); _out(f, "creation_time = %lu\n", t); return 1; } static int _print_vg(struct formatter *f, struct volume_group *vg) { char buffer[256]; if (!id_write_format(&vg->id, buffer, sizeof(buffer))) { stack; return 0; } _out(f, "id = \"%s\"", buffer); if (!print_flags(vg->status, VG_FLAGS, buffer, sizeof(buffer))) { stack; return 0; } _out(f, "status = %s", buffer); if (vg->system_id && *vg->system_id) _out(f, "system_id = \"%s\"", vg->system_id); _out_size(f, vg->extent_size, "extent_size = %u", vg->extent_size); _out(f, "max_lv = %u", vg->max_lv); _out(f, "max_pv = %u", vg->max_pv); 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 (pv) ? (const char *) hash_lookup(f->pv_names, dev_name(pv->dev)) : "Missing"; } 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 {"); _inc_indent(f); list_iterate (pvh, &vg->pvs) { pv = list_item(pvh, struct pv_list)->pv; if (!(name = _get_pv_name(f, pv))) { stack; return 0; } _nl(f); _out(f, "%s {", name); _inc_indent(f); if (!id_write_format(&pv->id, buffer, sizeof(buffer))) { stack; return 0; } _out(f, "id = \"%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 = %llu", 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, "}"); } _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 s; const char *name; _out(f, "segment%u {", count); _inc_indent(f); _out(f, "start_extent = %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, seg->stripe_size, "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->area[s].pv))) { stack; return 0; } _out(f, "\"%s\", %u%s", name, seg->area[s].pe, (s == seg->stripes - 1) ? "" : ","); } _dec_indent(f); _out(f, "]"); _dec_indent(f); _out(f, "}"); return 1; } static int _count_segments(struct logical_volume *lv) { int r = 0; struct list *segh; list_iterate (segh, &lv->segments) r++; return r; } 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; /* * Don't bother with an lv section if there are no lvs. */ if (list_empty(&vg->lvs)) return 1; _out(f, "logical_volumes {"); _inc_indent(f); list_iterate (lvh, &vg->lvs) { lv = list_item(lvh, struct lv_list)->lv; _nl(f); _out(f, "%s {", lv->name); _inc_indent(f); if (!print_flags(lv->status, LV_FLAGS, buffer, sizeof(buffer))) { stack; return 0; } _out(f, "status = %s", buffer); _out(f, "read_ahead = %u", lv->read_ahead); if (lv->minor >= 0) _out(f, "minor = %d", lv->minor); _out(f, "segment_count = %u", _count_segments(lv)); _nl(f); seg_count = 1; list_iterate (segh, &lv->segments) { seg = list_item(segh, struct stripe_segment); if (!_print_segment(f, vg, seg_count++, seg)) { stack; return 0; } } _dec_indent(f); _out(f, "}"); } _dec_indent(f); _out(f, "}"); return 1; } static int _print_snapshot(struct formatter *f, struct snapshot *s, unsigned int count) { _nl(f); _out(f, "snapshot%u {", count); _inc_indent(f); _out(f, "chunk_size = %u", s->chunk_size); _out(f, "origin = \"%s\"", s->origin->name); _out(f, "cow_store = \"%s\"", s->cow->name); _dec_indent(f); _out(f, "}"); return 1; } static int _print_snapshots(struct formatter *f, struct volume_group *vg) { struct list *sh; struct snapshot *s; unsigned int count = 0; /* * Don't bother with a snapshot section if there are no * snapshots. */ if (list_empty(&vg->snapshots)) return 1; _out(f, "snapshots {"); _inc_indent(f); list_iterate (sh, &vg->snapshots) { s = list_item(sh, struct snapshot_list)->snapshot; if (!_print_snapshot(f, s, count++)) { stack; return 0; } } _dec_indent(f); _out(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 (lvm_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), name)) { 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, const char *desc) { 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, desc)) fail; _out(f, "%s {", vg->name); _inc_indent(f); if (!_print_vg(f, vg)) fail; _nl(f); if (!_print_pvs(f, vg)) fail; _nl(f); if (!_print_lvs(f, vg)) fail; _nl(f); if (!_print_snapshots(f, vg)) fail; #undef fail _dec_indent(f); _out(f, "}"); r = !ferror(f->fp); out: if (f->mem) pool_destroy(f->mem); if (f->pv_names) hash_destroy(f->pv_names); dbg_free(f); return r; }