diff --git a/lib/format1/disk-rep.c b/lib/format1/disk-rep.c new file mode 100644 index 000000000..3fb329831 --- /dev/null +++ b/lib/format1/disk-rep.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + * + */ + +#include "disk-rep.h" +#include "pool.h" + +#define xx16(v) disk->v = xlate16(disk->v) +#define xx32(v) disk->v = xlate32(disk->v) +#define xx64(v) disk->v = xlate64(disk->v) + +/* + * Functions to perform the endian conversion + * between disk and core. The same code works + * both ways of course. + */ +static void _xlate_pv(struct pv_disk *disk) +{ + xx16(version); + + xx32(pv_on_disk.base); xx32(pv_on_disk.size); + xx32(vg_on_disk.base); xx32(vg_on_disk.size); + xx32(pv_uuid_on_disk.base); xx32(pv_uuid_on_disk.size); + xx32(lv_on_disk.base); xx32(lv_on_disk.size); + xx32(pe_on_disk.base); xx32(pe_on_disk.size); + + xx32(pv_major); + xx32(pv_number); + xx32(pv_status); + xx32(pv_allocatable); + xx32(pv_size); + xx32(lv_cur); + xx32(pe_size); + xx32(pe_total); + xx32(pe_allocated); + xx32(pe_start); + + /* FIXME: put v1, v2 munging in here. */ +} + +static void _xlate_lv(struct lv_disk *disk) +{ + xx32(lv_access); + xx32(lv_status); + xx32(lv_open); + xx32(lv_dev); + xx32(lv_number); + xx32(lv_mirror_copies); + xx32(lv_recovery); + xx32(lv_schedule); + xx32(lv_size); + xx32(lv_snapshot_minor); + xx16(lv_chunk_size); + xx16(dummy); + xx32(lv_allocated_le); + xx32(lv_stripes); + xx32(lv_stripesize); + xx32(lv_badblock); + xx32(lv_allocation); + xx32(lv_io_timeout); + xx32(lv_read_ahead); +} + +static void _xlate_vg(struct vg_disk *disk) +{ + xx32(vg_number); + xx32(vg_access); + xx32(vg_status); + xx32(lv_max); + xx32(lv_cur); + xx32(lv_open); + xx32(pv_max); + xx32(pv_cur); + xx32(pv_act); + xx32(dummy); + xx32(vgda); + xx32(pe_size); + xx32(pe_total); + xx32(pe_allocated); + xx32(pvg_total); +} + +static int _read_pv(struct device *dev, struct pv_disk *disk) +{ + if (dev_read(dev, 0, sizeof(*disk), disk) != sizeof(&disk)) { + log_error("failed to read pv from disk (%s)", dev->name); + return 0; + } + + _xlate_pv(disk); + memset(disk->pv_name, 0, sizeof (disk->pv_name)); + strncpy(disk->pv_name, dev->name, sizeof(disk->pv_name) - 1); + + disk->pv_dev = dev->dev; + return 1; +} + +static int _read_lv(struct device *dev, ulong pos, struct lv_disk *disk) +{ + if (dev_read(dev, pos, sizeof(*disk), disk) != sizeof(*disk)) { + log_error("failed to read lv from disk (%s)", dev->name); + return 0; + } + + _xlate_lv(disk); + return 1; +} + +static int _read_vg(struct device *dev, ulong pos, struct vg_disk *disk) +{ + if (dev_read(dev, pos, sizeof(*disk), disk) != sizeof(*disk)) { + log_error("failed to read vg from disk (%s)", dev->name); + return 0; + } + + _xlate_vg(disk); + return 1; +} + +static int _read_uuids(struct device *dev, struct disk_list *data) +{ + int num_read = 0; + struct uuid_list *ul; + char buffer[NAME_LEN]; + ulong pos = data->pv.pv_uuidlist_on_disk.base; + ulong end = pos + data->pv.pv_uuidlist_on_disk.size; + + while(pos < end && num_read < ABS_MAX_PV) { + if (dev_read(dev, pos, sizeof(buffer), buffer) != + sizeof(buffer)) { + log_err("failed to read a pv_uuid from %s\n", + dev->name); + return 0; + } + + if (!(ul = pool_alloc(data->mem, sizeof(*ul)))) { + stack; + return 0; + } + + memcpy(ul->uuid, buffer, NAME_LEN); + ul->uuid[NAME_LEN] = '\0'; + + list_add(&ui->list, &data->uuids); + + pos += NAME_LEN; + num_read++; + } + return 1; +} + +static int _read_lvs(struct device *dev, struct disk_list *data) +{ + int i; + unsigned long pos; + + for(i = 0; i < data->vg.lv_cur; i++) { + pos = data->pv.lv_on_disk.base + (i * sizeof(struct lv_list)); + struct lv_list *ll = pool_alloc(sizeof(*ll)); + + if (!ll) { + stack; + return 0; + } + + if (!_read_lv(dev, pos, &ll->lv)) { + stack; + return 0; + } + + list_add(&ll->list, &data->lvs); + } + + return 1; +} + +static int _read_extents(struct device *dev, struct disk_list *data) +{ + size_t len = sizeof(struct pe_disk) * data->pv->pe_total; + struct pe_disk *extents = pool_alloc(data->mem, len); + unsigned long pos = data->pv.pe_on_disk.base; + + if (!extents) { + stack; + return 0; + } + + if (dev_read(dev, pos, len, extents) != len) { + log_error("failed to read extents from disk (%s)", dev->name); + return 0; + } + + for (i = 0; i < data->pv->pe_total; i++) { + extents[i].lv_num = xlate16(extents[i].lv_num); + extents[i].le_num = xlate16(extents[i].le_num); + } + + data->extents = extents; + return 1; +} + +static int _read_all_pv_data(struct device *dev, struct disk_list *data) +{ + if (!_read_pv(dev, &data->pv)) { + stack; + return 0; + } + + if (strcmp(data->pv.id, "HM")) { + log_info("%s does not have a valid PV identifier.\n", + dev->name); + return 0; + } + + if (!_read_vg(dev, data->pv.pv_vg_on_disk.base, &data->vg)) { + log_err("failed to read vg data from pv (%s)\n", dev->name); + return 0; + } + + if (!_read_uuids(dev, data)) { + log_err("failed to read pv uuid list from %s\n", dev->name); + return 0; + } + + if (!_read_lvs(dev, data)) { + log_err("failed to read lv's from %s\n", dev->name); + return 0; + } + + if (!_read_extents(dev, data)) { + log_err("failed to read extents from %s\n", dev->name); + return 0; + } + + return 1; +} + +/* + * Build a list of pv_d's structures, allocated + * from mem. We keep track of the first object + * allocated form the pool so we can free off all + * the memory if something goes wrong. + */ +int read_pvs_in_vg(struct v1 *v, const char *vg_name, + struct pool *mem, struct list_head *head) +{ + struct dev_cache_iter *iter = dev_iter_create(v->filter); + struct device *dev; + struct disk_list *data = NULL; + + for (dev = dev_iter_get(iter); dev; dev = dev_iter_get(iter)) { + struct disk_list *data = pool_alloc(mem, sizeof(*data)); + + if (!data) { + stack; + goto bad; + } + + if (!first) + first = data; + + if (_read_all_pv_data(dev, pvd) && + !strcmp(pvd->pv.vg_name, vg_name)) + list_add(&pvd->list, head); + else + pool_free(mem, pvd); + } + return 1; + + bad: + if (first) + pool_free(mem, first); + return 0; +} diff --git a/lib/format1/disk-rep.h b/lib/format1/disk-rep.h new file mode 100644 index 000000000..7190e80b9 --- /dev/null +++ b/lib/format1/disk-rep.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + * + */ + +#ifndef DISK_REP_FORMAT1_H +#define DISK_REP_FORMAT1_H + +struct data_area { + uint32_t base; + uint32_t size; +}; + +struct pv_disk { + uint8_t id[2]; + uint16_t version; /* lvm version */ + struct data_area pv_on_disk; + struct data_area vg_on_disk; + struct data_area pv_uuidlist_on_disk; + struct data_area lv_on_disk; + struct data_area pe_on_disk; + uint8_t pv_uuid[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint8_t system_id[NAME_LEN]; /* for vgexport/vgimport */ + uint32_t pv_major; + uint32_t pv_number; + uint32_t pv_status; + uint32_t pv_allocatable; + uint32_t pv_size; + uint32_t lv_cur; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + + /* only present on version == 2 pv's */ + uint32_t pe_start; +}; + +struct lv_disk { + uint8_t lv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint32_t lv_access; + uint32_t lv_status; + uint32_t lv_open; + uint32_t lv_dev; + uint32_t lv_number; + uint32_t lv_mirror_copies; /* for future use */ + uint32_t lv_recovery; /* " */ + uint32_t lv_schedule; /* " */ + uint32_t lv_size; + uint32_t lv_snapshot_minor; /* minor number of original */ + uint16_t lv_chunk_size; /* chunk size of snapshot */ + uint16_t dummy; + uint32_t lv_allocated_le; + uint32_t lv_stripes; + uint32_t lv_stripesize; + uint32_t lv_badblock; /* for future use */ + uint32_t lv_allocation; + uint32_t lv_io_timeout; /* for future use */ + uint32_t lv_read_ahead; +}; + +struct vg_disk { + uint8_t vg_uuid[UUID_LEN]; /* volume group UUID */ + uint8_t vg_name_dummy[NAME_LEN-UUID_LEN]; /* rest of v1 VG name */ + uint32_t vg_number; /* volume group number */ + uint32_t vg_access; /* read/write */ + uint32_t vg_status; /* active or not */ + uint32_t lv_max; /* maximum logical volumes */ + uint32_t lv_cur; /* current logical volumes */ + uint32_t lv_open; /* open logical volumes */ + uint32_t pv_max; /* maximum physical volumes */ + uint32_t pv_cur; /* current physical volumes FU */ + uint32_t pv_act; /* active physical volumes */ + uint32_t dummy; + uint32_t vgda; /* volume group descriptor arrays FU */ + uint32_t pe_size; /* physical extent size in sectors */ + uint32_t pe_total; /* total of physical extents */ + uint32_t pe_allocated; /* allocated physical extents */ + uint32_t pvg_total; /* physical volume groups FU */ +}; + +struct pe_disk { + uint16_t lv_num; + uint16_t le_num; +}; + + +struct uuid_list { + struct list_head list; + char uuid[NAME_LEN + 1]; +}; + +struct lv_list { + struct list_head list; + struct lv_disk lv; + +struct disk_list { + struct list_head list; + struct pv_disk pv; + struct vg_disk vg; + struct list_head uuids; + struct list_head lvs; + struct pe_disk *extents; +}; + +int read_pvs_in_vg(const char *vg_name, struct dev_filter *filter, + struct pool *mem, struct list_head *results); + +#endif diff --git a/lib/format1/format1.c b/lib/format1/format1.c new file mode 100644 index 000000000..c28906caa --- /dev/null +++ b/lib/format1/format1.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + * + */ + +#include "disk-rep.h" +#include "dbg_malloc.h" +#include "pool.h" +#include "hash.h" +#include "list.h" + + +struct v1 { + struct pool *mem; + struct dev_filter *filter; +}; + +static int _import_vg(struct volume_group *vg, struct list_head *pvs) +{ + struct list_head *tmp; + struct disk_list *dl; + struct vg_disk *first = NULL; + + /* check all the vg's are the same */ + list_for_each(tmp, pvs) { + dl = list_entry(tmp, struct disk_list, list); + + if (!first) { + first = &dl->vg; + + memcpy(vg->id, &first->vg_uuid, ID_LEN); + vg->name = NULL; + vg->status = first->vg_status; + vg->access = first->vg_access; + vg->extent_size = first->pe_size; + vg->extent_count = first->pe_total; + vg->free_count = first->pe_total - first->pe_allocated; + vg->max_lv = first->lv_max; + vg->max_pv = first->pv_max; + + } else if (memcmp(first, &dl->vg, sizeof(*first))) { + log_err("vg data differs on pvs\n"); + return 0; + } + } + + return 1; +} + +static int _import_pvs(struct pool *mem, struct volume_group *vg, + struct list_head *pvs) +{ + struct list_head *tmp; + struct disk_list *dl; + struct pv_list *pvl; + struct physical_volume *pv; + + vg->pv_count = 0; + list_for_each(tmp, pvs) { + dl = list_entry(tmp, struct disk_list, list); + pvl = pool_alloc(mem, sizeof(*pvl)); + + if (!pvl) { + stack; + return 0; + } + + pv = &pvl->pv; + memcpy(&pv->id, &dl->pv.pv_uuid, ID_LEN); + pv->dev = ??; + pv->vg_name = pool_strdup(dl->pv.vg_name); + + if (!pv->vg_name) { + stack; + return 0; + } + + pv->exported = ??; + pv->status = dl->pv.pv_status; + pv->size = dl->pv.pv_size; + pv->pe_size = dl->pv.pv_size; + pe_start = dl->pv.pe_start; + pe_count = dl->pv.pe_count; + pe_allocated = dl->pv.pe_allocated; + + list_add(&pvl->list, vg->pvs); + vg->pv_count++; + } + + return 1; +} + +static struct logical_volume *_find_lv(struct volume_group *vg, + const char *name) +{ + struct list_head *tmp; + struct logical_volume *lv; + + list_for_each(tmp, &vg->lvs) { + lv = list_entry(tmp, struct logical_volume, list); + if (!strcmp(name, lv->name)) + return lv; + } + return NULL; +} + +static struct logical_volume *_add_lv(struct volume_group *vg, + struct lv_disk *lvd) +{ + struct logical_volume *lv = pool_alloc(mem, sizeof(*lv)); + + if (!lv) { + stack; + return 0; + } + + memset(lv->id, 0, sizeof(lv->id)); + if (!(lv->name = pool_dupstr(lvd->lv_name))) { + stack; + return 0; + } + + lv->access = lvd->lv_access; + lv->status = lvd->lv_status; + lv->open = lvd->lv_open; + lv->size = lvd->lv_size; + lv->le_count = lvd->lv_allocated_lv; + lv->map = pool_alloc(mem, sizeof(struct pe_specifier) * lv->le_count); + + if (!lv->map) { + stack; + return 0; + } + + return 1; +} + +static int _import_lvs(struct pool *mem, struct volume_group *vg, + struct list_head *pvs) +{ + struct list_head *tmp, tmp2; + struct disk_list *dl; + struct lv_disk *lvd; + struct logical_volume *lv; + int i; + + list_for_each(tmp, pvs) { + dl = list_entry(tmp, struct disk_list, list); + list_for_each(tmp2, &dl->lvs) { + lvd = list_entry(tmp2, struct lv_disk, list); + if (!_find_lv(vg, lvd->lvname) && !_add_lv(vg, lvd)) { + stack; + return 0; + } + } + } + + return 1; +} + +static int _import_extents(struct pool *mem, struct volume_group *vg, + struct list_head *pvs) +{ + struct list_head *tmp; + struct disk_list *dl; + struct logical_volume *lv; + struct physical_volume *pv; + struct pe_disk *e; + int i, le; + + list_for_each(tmp, pvs) { + dl = list_entry(tmp, struct disk_list, list); + pv = _find_pv(vg, dl->pv.pv_name); + e = dl->extents; + + for (i = 0; i < dl->pv.pe_total; i++) { + if (e[i].lv_num) { + if (!(lv = _find_lv_num(vg, e[i].lv_num))) { + stack; + return 0; + } + + le = e[i].le_num; + lv->map[le].pv = pv; + lv->map[le].pe = i; + } + } + } + + return 1; +} + +static struct volume_group _build_vg(struct pool *mem, struct list_head *pvs) +{ + struct volume_group *vg = pool_alloc(mem, sizeof(*vg)); + + if (!vg) { + stack; + return 0; + } + + memset(vg, 0, sizeof(*vg)); + + if (!_import_vg(vg, pvs)) + goto bad; + + if (!_import_pvs(mem, vg, pvs)) + goto bad; + + if (!_import_lvs(mem, vg, pvs)) + goto bad; + + return vg; + + bad: + stack; + pool_free(mem, vg); + return NULL; +} + +static struct volume_group _vg_read(struct io_space *is, const char *vg_name) +{ + struct pool *mem = pool_create(1024 * 10); + struct list_head pvs; + struct volume_group *vg; + + if (!mem) { + stack; + return NULL; + } + + if (!read_pvs_in_vg(vg_name, is->filter, mem, &pvs)) { + stack; + return NULL; + } + + if (!(vg = _build_vg(is->mem, &pvs))) { + stack; + } + + pool_destroy(mem); + return vg; +} + + +struct io_space *create_lvm1_format(struct device_manager *mgr) +{ + +} + +