1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-10-28 20:25:52 +03:00
lvm2/lib/format_text/format-text.c
Alasdair Kergon 8a2fc58645 Check for libsepol.
Add some cflow & scope support.
Separate out DEFS from CFLAGS.
Remove inlines and use unique function names.
2006-04-19 15:33:07 +00:00

1806 lines
44 KiB
C

/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License v.2.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "lib.h"
#include "format-text.h"
#include "import-export.h"
#include "device.h"
#include "lvm-file.h"
#include "config.h"
#include "display.h"
#include "toolcontext.h"
#include "lvm-string.h"
#include "uuid.h"
#include "layout.h"
#include "crc.h"
#include "xlate.h"
#include "label.h"
#include "memlock.h"
#include "lvmcache.h"
#include <unistd.h>
#include <sys/file.h>
#include <limits.h>
#include <dirent.h>
#include <ctype.h>
#define FMT_TEXT_NAME "lvm2"
#define FMT_TEXT_ALIAS "text"
static struct format_instance *_text_create_text_instance(const struct format_type
*fmt, const char *vgname,
const char *vgid,
void *context);
struct text_fid_context {
char *raw_metadata_buf;
uint32_t raw_metadata_buf_size;
};
struct dir_list {
struct list list;
char dir[0];
};
struct raw_list {
struct list list;
struct device_area dev_area;
};
struct text_context {
char *path_live; /* Path to file holding live metadata */
char *path_edit; /* Path to file holding edited metadata */
char *desc; /* Description placed inside file */
};
/*
* NOTE: Currently there can be only one vg per text file.
*/
static int _text_vg_setup(struct format_instance *fid, struct volume_group *vg)
{
if (vg->extent_size & (vg->extent_size - 1)) {
log_error("Extent size must be power of 2");
return 0;
}
return 1;
}
static int _text_lv_setup(struct format_instance *fid, struct logical_volume *lv)
{
/******** FIXME Any LV size restriction?
uint64_t max_size = UINT_MAX;
if (lv->size > max_size) {
char *dummy = display_size(max_size, SIZE_SHORT);
log_error("logical volumes cannot be larger than %s", dummy);
dm_free(dummy);
return 0;
}
*/
if (!*lv->lvid.s && !lvid_create(&lv->lvid, &lv->vg->id)) {
log_error("Random lvid creation failed for %s/%s.",
lv->vg->name, lv->name);
return 0;
}
return 1;
}
static void _xlate_mdah(struct mda_header *mdah)
{
struct raw_locn *rl;
mdah->version = xlate32(mdah->version);
mdah->start = xlate64(mdah->start);
mdah->size = xlate64(mdah->size);
rl = &mdah->raw_locns[0];
while (rl->offset) {
rl->checksum = xlate32(rl->checksum);
rl->offset = xlate64(rl->offset);
rl->size = xlate64(rl->size);
rl++;
}
}
static struct mda_header *_raw_read_mda_header(const struct format_type *fmt,
struct device_area *dev_area)
{
struct mda_header *mdah;
if (!(mdah = dm_pool_alloc(fmt->cmd->mem, MDA_HEADER_SIZE))) {
log_error("struct mda_header allocation failed");
return NULL;
}
if (!dev_read(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, mdah)) {
stack;
dm_pool_free(fmt->cmd->mem, mdah);
return NULL;
}
if (mdah->checksum_xl != xlate32(calc_crc(INITIAL_CRC, mdah->magic,
MDA_HEADER_SIZE -
sizeof(mdah->checksum_xl)))) {
log_error("Incorrect metadata area header checksum");
return NULL;
}
_xlate_mdah(mdah);
if (strncmp(mdah->magic, FMTT_MAGIC, sizeof(mdah->magic))) {
log_error("Wrong magic number in metadata area header");
return NULL;
}
if (mdah->version != FMTT_VERSION) {
log_error("Incompatible metadata area header version: %d",
mdah->version);
return NULL;
}
if (mdah->start != dev_area->start) {
log_error("Incorrect start sector in metadata area header: %"
PRIu64, mdah->start);
return NULL;
}
return mdah;
}
static int _raw_write_mda_header(const struct format_type *fmt,
struct device *dev,
uint64_t start_byte, struct mda_header *mdah)
{
strncpy(mdah->magic, FMTT_MAGIC, sizeof(mdah->magic));
mdah->version = FMTT_VERSION;
mdah->start = start_byte;
_xlate_mdah(mdah);
mdah->checksum_xl = xlate32(calc_crc(INITIAL_CRC, mdah->magic,
MDA_HEADER_SIZE -
sizeof(mdah->checksum_xl)));
if (!dev_write(dev, start_byte, MDA_HEADER_SIZE, mdah)) {
stack;
dm_pool_free(fmt->cmd->mem, mdah);
return 0;
}
return 1;
}
static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area,
struct mda_header *mdah,
const char *vgname,
int *precommitted)
{
size_t len;
char vgnamebuf[NAME_LEN + 2];
struct raw_locn *rlocn, *rlocn_precommitted;
struct lvmcache_info *info;
rlocn = mdah->raw_locns; /* Slot 0 */
rlocn_precommitted = rlocn + 1; /* Slot 1 */
/* Should we use precommitted metadata? */
if (*precommitted && rlocn_precommitted->size &&
(rlocn_precommitted->offset != rlocn->offset)) {
rlocn = rlocn_precommitted;
} else
*precommitted = 0;
/* FIXME Loop through rlocns two-at-a-time. List null-terminated. */
/* FIXME Ignore if checksum incorrect!!! */
if (!dev_read(dev_area->dev, dev_area->start + rlocn->offset,
sizeof(vgnamebuf), vgnamebuf)) {
stack;
goto error;
}
if (!strncmp(vgnamebuf, vgname, len = strlen(vgname)) &&
(isspace(vgnamebuf[len]) || vgnamebuf[len] == '{')) {
return rlocn;
}
error:
if ((info = info_from_pvid(dev_area->dev->pvid)))
lvmcache_update_vgname_and_id(info, ORPHAN, ORPHAN, 0, NULL);
return NULL;
}
/*
* Determine offset for uncommitted metadata
*/
static uint64_t _next_rlocn_offset(struct raw_locn *rlocn,
struct mda_header *mdah)
{
if (!rlocn)
/* Find an empty slot */
/* FIXME Assume only one VG per mdah for now */
return MDA_HEADER_SIZE;
/* Start of free space - round up to next sector; circular */
return ((rlocn->offset + rlocn->size +
(SECTOR_SIZE - rlocn->size % SECTOR_SIZE) -
MDA_HEADER_SIZE) % (mdah->size - MDA_HEADER_SIZE))
+ MDA_HEADER_SIZE;
}
static int _raw_holds_vgname(struct format_instance *fid,
struct device_area *dev_area, const char *vgname)
{
int r = 0;
int noprecommit = 0;
struct mda_header *mdah;
if (!dev_open(dev_area->dev)) {
stack;
return 0;
}
if (!(mdah = _raw_read_mda_header(fid->fmt, dev_area))) {
stack;
return 0;
}
if (_find_vg_rlocn(dev_area, mdah, vgname, &noprecommit))
r = 1;
if (!dev_close(dev_area->dev))
stack;
return r;
}
static struct volume_group *_vg_read_raw_area(struct format_instance *fid,
const char *vgname,
struct device_area *area,
int precommitted)
{
struct volume_group *vg = NULL;
struct raw_locn *rlocn;
struct mda_header *mdah;
time_t when;
char *desc;
uint32_t wrap = 0;
if (!dev_open(area->dev)) {
stack;
return NULL;
}
if (!(mdah = _raw_read_mda_header(fid->fmt, area))) {
stack;
goto out;
}
if (!(rlocn = _find_vg_rlocn(area, mdah, vgname, &precommitted))) {
log_debug("VG %s not found on %s", vgname, dev_name(area->dev));
goto out;
}
if (rlocn->offset + rlocn->size > mdah->size)
wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
if (wrap > rlocn->offset) {
log_error("VG %s metadata too large for circular buffer",
vg->name);
goto out;
}
/* FIXME 64-bit */
if (!(vg = text_vg_import_fd(fid, NULL, area->dev,
(off_t) (area->start + rlocn->offset),
(uint32_t) (rlocn->size - wrap),
(off_t) (area->start + MDA_HEADER_SIZE),
wrap, calc_crc, rlocn->checksum, &when,
&desc))) {
stack;
goto out;
}
log_debug("Read %s %smetadata (%u) from %s at %" PRIu64 " size %"
PRIu64, vg->name, precommitted ? "pre-commit " : "",
vg->seqno, dev_name(area->dev),
area->start + rlocn->offset, rlocn->size);
if (precommitted)
vg->status |= PRECOMMITTED;
out:
if (!dev_close(area->dev))
stack;
return vg;
}
static struct volume_group *_vg_read_raw(struct format_instance *fid,
const char *vgname,
struct metadata_area *mda)
{
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
return _vg_read_raw_area(fid, vgname, &mdac->area, 0);
}
static struct volume_group *_vg_read_precommit_raw(struct format_instance *fid,
const char *vgname,
struct metadata_area *mda)
{
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
return _vg_read_raw_area(fid, vgname, &mdac->area, 1);
}
static int _vg_write_raw(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
struct raw_locn *rlocn;
struct mda_header *mdah;
struct pv_list *pvl;
int r = 0;
uint32_t new_wrap = 0, old_wrap = 0;
int found = 0;
int noprecommit = 0;
/* Ignore any mda on a PV outside the VG. vgsplit relies on this */
list_iterate_items(pvl, &vg->pvs) {
if (pvl->pv->dev == mdac->area.dev) {
found = 1;
break;
}
}
if (!found)
return 1;
if (!dev_open(mdac->area.dev)) {
stack;
return 0;
}
if (!(mdah = _raw_read_mda_header(fid->fmt, &mdac->area))) {
stack;
goto out;
}
rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, &noprecommit);
mdac->rlocn.offset = _next_rlocn_offset(rlocn, mdah);
if (!fidtc->raw_metadata_buf &&
!(fidtc->raw_metadata_buf_size =
text_vg_export_raw(vg, "", &fidtc->raw_metadata_buf))) {
log_error("VG %s metadata writing failed", vg->name);
goto out;
}
mdac->rlocn.size = fidtc->raw_metadata_buf_size;
if (mdac->rlocn.offset + mdac->rlocn.size > mdah->size)
new_wrap = (mdac->rlocn.offset + mdac->rlocn.size) - mdah->size;
if (rlocn && (rlocn->offset + rlocn->size > mdah->size))
old_wrap = (rlocn->offset + rlocn->size) - mdah->size;
if ((new_wrap && old_wrap) ||
(rlocn && ((new_wrap > rlocn->offset) ||
(old_wrap && (mdac->rlocn.offset + mdac->rlocn.size >
rlocn->offset)))) ||
(mdac->rlocn.size >= mdah->size)) {
log_error("VG %s metadata too large for circular buffer",
vg->name);
goto out;
}
log_debug("Writing %s metadata to %s at %" PRIu64 " len %" PRIu64,
vg->name, dev_name(mdac->area.dev), mdac->area.start +
mdac->rlocn.offset, mdac->rlocn.size - new_wrap);
/* Write text out, circularly */
if (!dev_write(mdac->area.dev, mdac->area.start + mdac->rlocn.offset,
(size_t) (mdac->rlocn.size - new_wrap),
fidtc->raw_metadata_buf)) {
stack;
goto out;
}
if (new_wrap) {
log_debug("Writing metadata to %s at %" PRIu64 " len %" PRIu32,
dev_name(mdac->area.dev), mdac->area.start +
MDA_HEADER_SIZE, new_wrap);
if (!dev_write(mdac->area.dev,
mdac->area.start + MDA_HEADER_SIZE,
(size_t) new_wrap,
fidtc->raw_metadata_buf +
mdac->rlocn.size - new_wrap)) {
stack;
goto out;
}
}
mdac->rlocn.checksum = calc_crc(INITIAL_CRC, fidtc->raw_metadata_buf,
(uint32_t) (mdac->rlocn.size -
new_wrap));
if (new_wrap)
mdac->rlocn.checksum = calc_crc(mdac->rlocn.checksum,
fidtc->raw_metadata_buf +
mdac->rlocn.size -
new_wrap, new_wrap);
r = 1;
out:
if (!r && !dev_close(mdac->area.dev))
stack;
return r;
}
static int _vg_commit_raw_rlocn(struct format_instance *fid,
struct volume_group *vg,
struct metadata_area *mda,
int precommit)
{
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
struct text_fid_context *fidtc = (struct text_fid_context *) fid->private;
struct mda_header *mdah;
struct raw_locn *rlocn;
struct pv_list *pvl;
int r = 0;
int found = 0;
int noprecommit = 0;
/* Ignore any mda on a PV outside the VG. vgsplit relies on this */
list_iterate_items(pvl, &vg->pvs) {
if (pvl->pv->dev == mdac->area.dev) {
found = 1;
break;
}
}
if (!found)
return 1;
if (!(mdah = _raw_read_mda_header(fid->fmt, &mdac->area))) {
stack;
goto out;
}
if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, &noprecommit))) {
mdah->raw_locns[0].offset = 0;
mdah->raw_locns[0].size = 0;
mdah->raw_locns[0].checksum = 0;
mdah->raw_locns[1].offset = 0;
mdah->raw_locns[1].size = 0;
mdah->raw_locns[1].checksum = 0;
mdah->raw_locns[2].offset = 0;
mdah->raw_locns[2].size = 0;
mdah->raw_locns[2].checksum = 0;
rlocn = &mdah->raw_locns[0];
}
if (precommit)
rlocn++;
else {
/* If not precommitting, wipe the precommitted rlocn */
mdah->raw_locns[1].offset = 0;
mdah->raw_locns[1].size = 0;
mdah->raw_locns[1].checksum = 0;
}
/* Is there new metadata to commit? */
if (mdac->rlocn.size) {
rlocn->offset = mdac->rlocn.offset;
rlocn->size = mdac->rlocn.size;
rlocn->checksum = mdac->rlocn.checksum;
log_debug("%sCommitting %s metadata (%u) to %s header at %"
PRIu64, precommit ? "Pre-" : "", vg->name, vg->seqno,
dev_name(mdac->area.dev), mdac->area.start);
} else
log_debug("Wiping pre-committed %s metadata from %s "
"header at %" PRIu64, vg->name,
dev_name(mdac->area.dev), mdac->area.start);
if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start,
mdah)) {
log_error("Failed to write metadata area header");
goto out;
}
r = 1;
out:
if (!precommit) {
if (!dev_close(mdac->area.dev))
stack;
if (fidtc->raw_metadata_buf) {
dm_free(fidtc->raw_metadata_buf);
fidtc->raw_metadata_buf = NULL;
}
}
return r;
}
static int _vg_commit_raw(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
return _vg_commit_raw_rlocn(fid, vg, mda, 0);
}
static int _vg_precommit_raw(struct format_instance *fid,
struct volume_group *vg,
struct metadata_area *mda)
{
return _vg_commit_raw_rlocn(fid, vg, mda, 1);
}
/* Close metadata area devices */
static int _vg_revert_raw(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
struct pv_list *pvl;
int found = 0;
/* Ignore any mda on a PV outside the VG. vgsplit relies on this */
list_iterate_items(pvl, &vg->pvs) {
if (pvl->pv->dev == mdac->area.dev) {
found = 1;
break;
}
}
if (!found)
return 1;
/* Wipe pre-committed metadata */
mdac->rlocn.size = 0;
return _vg_commit_raw_rlocn(fid, vg, mda, 0);
}
static int _vg_remove_raw(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
struct mda_header *mdah;
struct raw_locn *rlocn;
int r = 0;
int noprecommit = 0;
if (!dev_open(mdac->area.dev)) {
stack;
return 0;
}
if (!(mdah = _raw_read_mda_header(fid->fmt, &mdac->area))) {
stack;
goto out;
}
if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, &noprecommit))) {
rlocn = &mdah->raw_locns[0];
mdah->raw_locns[1].offset = 0;
}
rlocn->offset = 0;
rlocn->size = 0;
rlocn->checksum = 0;
if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start,
mdah)) {
log_error("Failed to write metadata area header");
goto out;
}
r = 1;
out:
if (!dev_close(mdac->area.dev))
stack;
return r;
}
static struct volume_group *_vg_read_file_name(struct format_instance *fid,
const char *vgname,
const char *read_path)
{
struct volume_group *vg;
time_t when;
char *desc;
if (!(vg = text_vg_import_file(fid, read_path, &when, &desc))) {
stack;
return NULL;
}
/*
* Currently you can only have a single volume group per
* text file (this restriction may remain). We need to
* check that it contains the correct volume group.
*/
if (vgname && strcmp(vgname, vg->name)) {
dm_pool_free(fid->fmt->cmd->mem, vg);
log_err("'%s' does not contain volume group '%s'.",
read_path, vgname);
return NULL;
} else
log_debug("Read volume group %s from %s", vg->name, read_path);
return vg;
}
static struct volume_group *_vg_read_file(struct format_instance *fid,
const char *vgname,
struct metadata_area *mda)
{
struct text_context *tc = (struct text_context *) mda->metadata_locn;
return _vg_read_file_name(fid, vgname, tc->path_live);
}
static struct volume_group *_vg_read_precommit_file(struct format_instance *fid,
const char *vgname,
struct metadata_area *mda)
{
struct text_context *tc = (struct text_context *) mda->metadata_locn;
struct volume_group *vg;
if ((vg = _vg_read_file_name(fid, vgname, tc->path_edit)))
vg->status |= PRECOMMITTED;
else
vg = _vg_read_file_name(fid, vgname, tc->path_live);
return vg;
}
static int _vg_write_file(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
struct text_context *tc = (struct text_context *) mda->metadata_locn;
FILE *fp;
int fd;
char *slash;
char temp_file[PATH_MAX], temp_dir[PATH_MAX];
slash = rindex(tc->path_edit, '/');
if (slash == 0)
strcpy(temp_dir, ".");
else if (slash - tc->path_edit < PATH_MAX) {
strncpy(temp_dir, tc->path_edit,
(size_t) (slash - tc->path_edit));
temp_dir[slash - tc->path_edit] = '\0';
} else {
log_error("Text format failed to determine directory.");
return 0;
}
if (!create_temp_name(temp_dir, temp_file, sizeof(temp_file), &fd)) {
log_err("Couldn't create temporary text file name.");
return 0;
}
if (!(fp = fdopen(fd, "w"))) {
log_sys_error("fdopen", temp_file);
close(fd);
return 0;
}
log_debug("Writing %s metadata to %s", vg->name, temp_file);
if (!text_vg_export_file(vg, tc->desc, fp)) {
log_error("Failed to write metadata to %s.", temp_file);
fclose(fp);
return 0;
}
if (fsync(fd) && (errno != EROFS) && (errno != EINVAL)) {
log_sys_error("fsync", tc->path_edit);
fclose(fp);
return 0;
}
if (fclose(fp)) {
log_sys_error("fclose", tc->path_edit);
return 0;
}
if (rename(temp_file, tc->path_edit)) {
log_debug("Renaming %s to %s", temp_file, tc->path_edit);
log_error("%s: rename to %s failed: %s", temp_file,
tc->path_edit, strerror(errno));
return 0;
}
return 1;
}
static int _vg_commit_file_backup(struct format_instance *fid,
struct volume_group *vg,
struct metadata_area *mda)
{
struct text_context *tc = (struct text_context *) mda->metadata_locn;
if (test_mode()) {
log_verbose("Test mode: Skipping committing %s metadata (%u)",
vg->name, vg->seqno);
if (unlink(tc->path_edit)) {
log_debug("Unlinking %s", tc->path_edit);
log_sys_error("unlink", tc->path_edit);
return 0;
}
} else {
log_debug("Committing %s metadata (%u)", vg->name, vg->seqno);
log_debug("Renaming %s to %s", tc->path_edit, tc->path_live);
if (rename(tc->path_edit, tc->path_live)) {
log_error("%s: rename to %s failed: %s", tc->path_edit,
tc->path_live, strerror(errno));
return 0;
}
}
sync_dir(tc->path_edit);
return 1;
}
static int _vg_commit_file(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
struct text_context *tc = (struct text_context *) mda->metadata_locn;
char *slash;
char newname[PATH_MAX];
size_t len;
if (!_vg_commit_file_backup(fid, vg, mda))
return 0;
/* vgrename? */
if ((slash = rindex(tc->path_live, '/')))
slash = slash + 1;
else
slash = tc->path_live;
if (strcmp(slash, vg->name)) {
len = slash - tc->path_live;
strncpy(newname, tc->path_live, len);
strcpy(newname + len, vg->name);
log_debug("Renaming %s to %s", tc->path_live, newname);
if (test_mode())
log_verbose("Test mode: Skipping rename");
else {
if (rename(tc->path_live, newname)) {
log_error("%s: rename to %s failed: %s",
tc->path_live, newname,
strerror(errno));
sync_dir(newname);
return 0;
}
}
}
return 1;
}
static int _vg_remove_file(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
struct text_context *tc = (struct text_context *) mda->metadata_locn;
if (path_exists(tc->path_edit) && unlink(tc->path_edit)) {
log_sys_error("unlink", tc->path_edit);
return 0;
}
if (path_exists(tc->path_live) && unlink(tc->path_live)) {
log_sys_error("unlink", tc->path_live);
return 0;
}
sync_dir(tc->path_live);
return 1;
}
static int _scan_file(const struct format_type *fmt)
{
struct dirent *dirent;
struct dir_list *dl;
struct list *dir_list;
char *tmp;
DIR *d;
struct volume_group *vg;
struct format_instance *fid;
char path[PATH_MAX];
char *vgname;
dir_list = &((struct mda_lists *) fmt->private)->dirs;
list_iterate_items(dl, dir_list) {
if (!(d = opendir(dl->dir))) {
log_sys_error("opendir", dl->dir);
continue;
}
while ((dirent = readdir(d)))
if (strcmp(dirent->d_name, ".") &&
strcmp(dirent->d_name, "..") &&
(!(tmp = strstr(dirent->d_name, ".tmp")) ||
tmp != dirent->d_name + strlen(dirent->d_name)
- 4)) {
vgname = dirent->d_name;
if (lvm_snprintf(path, PATH_MAX, "%s/%s",
dl->dir, vgname) < 0) {
log_error("Name too long %s/%s",
dl->dir, vgname);
break;
}
/* FIXME stat file to see if it's changed */
fid = _text_create_text_instance(fmt, NULL, NULL,
NULL);
if ((vg = _vg_read_file_name(fid, vgname,
path)))
/* FIXME Store creation host in vg */
lvmcache_update_vg(vg);
}
if (closedir(d))
log_sys_error("closedir", dl->dir);
}
return 1;
}
const char *vgname_from_mda(const struct format_type *fmt,
struct device_area *dev_area, struct id *vgid,
uint32_t *vgstatus, char **creation_host)
{
struct raw_locn *rlocn;
struct mda_header *mdah;
uint32_t wrap = 0;
const char *vgname = NULL;
unsigned int len = 0;
char buf[NAME_LEN + 1];
if (!dev_open(dev_area->dev)) {
stack;
return NULL;
}
if (!(mdah = _raw_read_mda_header(fmt, dev_area)))
goto_out;
/* FIXME Cope with returning a list */
rlocn = mdah->raw_locns;
/* Do quick check for a vgname */
if (!dev_read(dev_area->dev, dev_area->start + rlocn->offset,
NAME_LEN, buf))
goto_out;
while (buf[len] && !isspace(buf[len]) && buf[len] != '{' &&
len < (NAME_LEN - 1))
len++;
buf[len] = '\0';
/* Ignore this entry if the characters aren't permissible */
if (!validate_name(buf))
goto_out;
/* We found a VG - now check the metadata */
if (rlocn->offset + rlocn->size > mdah->size)
wrap = (uint32_t) ((rlocn->offset + rlocn->size) - mdah->size);
if (wrap > rlocn->offset) {
log_error("%s: metadata too large for circular buffer",
dev_name(dev_area->dev));
goto out;
}
/* FIXME 64-bit */
if (!(vgname = text_vgname_import(fmt, dev_area->dev,
(off_t) (dev_area->start +
rlocn->offset),
(uint32_t) (rlocn->size - wrap),
(off_t) (dev_area->start +
MDA_HEADER_SIZE),
wrap, calc_crc, rlocn->checksum,
vgid, vgstatus, creation_host)))
goto_out;
/* Ignore this entry if the characters aren't permissible */
if (!validate_name(vgname)) {
stack;
vgname = NULL;
goto out;
}
log_debug("%s: Found metadata at %" PRIu64 " size %" PRIu64
" for %s (%s)",
dev_name(dev_area->dev), dev_area->start + rlocn->offset,
rlocn->size, vgname, vgid->uuid);
out:
if (!dev_close(dev_area->dev))
stack;
return vgname;
}
static int _scan_raw(const struct format_type *fmt)
{
struct raw_list *rl;
struct list *raw_list;
const char *vgname;
struct volume_group *vg;
struct format_instance fid;
struct id vgid;
uint32_t vgstatus;
raw_list = &((struct mda_lists *) fmt->private)->raws;
fid.fmt = fmt;
list_init(&fid.metadata_areas);
list_iterate_items(rl, raw_list) {
/* FIXME We're reading mdah twice here... */
if ((vgname = vgname_from_mda(fmt, &rl->dev_area, &vgid, &vgstatus,
NULL))) {
if ((vg = _vg_read_raw_area(&fid, vgname,
&rl->dev_area, 0)))
lvmcache_update_vg(vg);
}
}
return 1;
}
static int _text_scan(const struct format_type *fmt)
{
return (_scan_file(fmt) & _scan_raw(fmt));
}
/* For orphan, creates new mdas according to policy.
Always have an mda between end-of-label and PE_ALIGN boundary */
static int _mda_setup(const struct format_type *fmt,
uint64_t pe_start, uint64_t pe_end,
int pvmetadatacopies,
uint64_t pvmetadatasize, struct list *mdas,
struct physical_volume *pv, struct volume_group *vg)
{
uint64_t mda_adjustment, disk_size, alignment;
uint64_t start1, mda_size1; /* First area - start of disk */
uint64_t start2, mda_size2; /* Second area - end of disk */
uint64_t wipe_size = 8 << SECTOR_SHIFT;
size_t pagesize = getpagesize();
if (!pvmetadatacopies) {
/* Space available for PEs */
pv->size -= PE_ALIGN;
return 1;
}
alignment = PE_ALIGN << SECTOR_SHIFT;
disk_size = pv->size << SECTOR_SHIFT;
pe_start <<= SECTOR_SHIFT;
pe_end <<= SECTOR_SHIFT;
if (pe_end > disk_size) {
log_error("Physical extents end beyond end of device %s!",
dev_name(pv->dev));
return 0;
}
/* Requested metadatasize */
mda_size1 = pvmetadatasize << SECTOR_SHIFT;
/* Space available for PEs (before any mdas created) */
pv->size -= LABEL_SCAN_SECTORS;
/* Place mda straight after label area at start of disk */
start1 = LABEL_SCAN_SIZE;
/* Unless the space available is tiny, round to PAGE_SIZE boundary */
if ((!pe_start && !pe_end) ||
((pe_start > start1) && (pe_start - start1 >= MDA_SIZE_MIN))) {
mda_adjustment = start1 % pagesize;
if (mda_adjustment) {
start1 += (pagesize - mda_adjustment);
pv->size -= ((pagesize - mda_adjustment) >>
SECTOR_SHIFT);
}
}
/* Ensure it's not going to be bigger than the disk! */
if (start1 + mda_size1 > disk_size) {
log_print("Warning: metadata area fills disk leaving no "
"space for data on %s.", dev_name(pv->dev));
/* Leave some free space for rounding */
/* Avoid empty data area as could cause tools problems */
mda_size1 = disk_size - start1 - alignment * 2;
/* Only have 1 mda in this case */
pvmetadatacopies = 1;
}
/* Round up to PE_ALIGN boundary */
mda_adjustment = (mda_size1 + start1) % alignment;
if (mda_adjustment)
mda_size1 += (alignment - mda_adjustment);
/* If we already have PEs, avoid overlap */
if (pe_start || pe_end) {
if (pe_start <= start1)
mda_size1 = 0;
else if (start1 + mda_size1 > pe_start)
mda_size1 = pe_start - start1;
}
/* FIXME If creating new mdas, wipe them! */
if (mda_size1) {
if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start1,
mda_size1)) return 0;
if (!dev_zero((struct device *) pv->dev, start1,
(size_t) (mda_size1 >
wipe_size ? wipe_size : mda_size1))) {
log_error("Failed to wipe new metadata area");
return 0;
}
pv->size -= mda_size1 >> SECTOR_SHIFT;
if (pvmetadatacopies == 1)
return 1;
} else
start1 = 0;
/* A second copy at end of disk */
mda_size2 = pvmetadatasize << SECTOR_SHIFT;
/* Ensure it's not going to be bigger than the disk! */
if (mda_size2 > disk_size)
mda_size2 = disk_size - start1 - mda_size1;
mda_adjustment = (disk_size - mda_size2) % alignment;
if (mda_adjustment)
mda_size2 += mda_adjustment;
start2 = disk_size - mda_size2;
/* If we already have PEs, avoid overlap */
if (pe_start || pe_end) {
if (start2 < pe_end) {
mda_size2 -= (pe_end - start2);
start2 = pe_end;
}
}
/* If we already have a first mda, avoid overlap */
if (mda_size1) {
if (start2 < start1 + mda_size1) {
mda_size2 -= (start1 + mda_size1 - start2);
start2 = start1 + mda_size1;
}
/* No room for any PEs here now! */
}
if (mda_size2) {
if (!add_mda(fmt, fmt->cmd->mem, mdas, pv->dev, start2,
mda_size2)) return 0;
if (!dev_zero(pv->dev, start2,
(size_t) (mda_size1 >
wipe_size ? wipe_size : mda_size1))) {
log_error("Failed to wipe new metadata area");
return 0;
}
pv->size -= mda_size2 >> SECTOR_SHIFT;
} else
return 0;
return 1;
}
/* Only for orphans */
/* Set label_sector to -1 if rewriting existing label into same sector */
static int _text_pv_write(const struct format_type *fmt, struct physical_volume *pv,
struct list *mdas, int64_t label_sector)
{
struct label *label;
struct lvmcache_info *info;
struct mda_context *mdac;
struct metadata_area *mda;
char buf[MDA_HEADER_SIZE];
struct mda_header *mdah = (struct mda_header *) buf;
uint64_t adjustment;
/* FIXME Test mode don't update cache? */
if (!(info = lvmcache_add(fmt->labeller, (char *) &pv->id, pv->dev,
ORPHAN, NULL, 0))) {
stack;
return 0;
}
label = info->label;
if (label_sector != -1)
label->sector = label_sector;
info->device_size = pv->size << SECTOR_SHIFT;
info->fmt = fmt;
/* If mdas supplied, use them regardless of existing ones, */
/* otherwise retain existing ones */
if (mdas) {
if (info->mdas.n)
del_mdas(&info->mdas);
else
list_init(&info->mdas);
list_iterate_items(mda, mdas) {
mdac = mda->metadata_locn;
log_debug("Creating metadata area on %s at sector %"
PRIu64 " size %" PRIu64 " sectors",
dev_name(mdac->area.dev),
mdac->area.start >> SECTOR_SHIFT,
mdac->area.size >> SECTOR_SHIFT);
add_mda(fmt, NULL, &info->mdas, mdac->area.dev,
mdac->area.start, mdac->area.size);
}
/* FIXME Temporary until mda creation supported by tools */
} else if (!info->mdas.n) {
list_init(&info->mdas);
}
if (info->das.n)
del_das(&info->das);
else
list_init(&info->das);
/* Set pe_start to first aligned sector after any metadata
* areas that begin before pe_start */
pv->pe_start = PE_ALIGN;
list_iterate_items(mda, &info->mdas) {
mdac = (struct mda_context *) mda->metadata_locn;
if (pv->dev == mdac->area.dev &&
(mdac->area.start < (pv->pe_start << SECTOR_SHIFT)) &&
(mdac->area.start + mdac->area.size >
(pv->pe_start << SECTOR_SHIFT))) {
pv->pe_start = (mdac->area.start + mdac->area.size)
>> SECTOR_SHIFT;
adjustment = pv->pe_start % PE_ALIGN;
if (adjustment)
pv->pe_start += (PE_ALIGN - adjustment);
}
}
if (!add_da
(fmt, NULL, &info->das, pv->pe_start << SECTOR_SHIFT, UINT64_C(0))) {
stack;
return 0;
}
if (!dev_open(pv->dev)) {
stack;
return 0;
}
list_iterate_items(mda, &info->mdas) {
mdac = mda->metadata_locn;
memset(&buf, 0, sizeof(buf));
mdah->size = mdac->area.size;
if (!_raw_write_mda_header(fmt, mdac->area.dev,
mdac->area.start, mdah)) {
stack;
if (!dev_close(pv->dev))
stack;
return 0;
}
}
label_write(pv->dev, label);
if (!dev_close(pv->dev)) {
stack;
return 0;
}
return 1;
}
static int _add_raw(struct list *raw_list, struct device_area *dev_area)
{
struct raw_list *rl;
/* Already present? */
list_iterate_items(rl, raw_list) {
/* FIXME Check size/overlap consistency too */
if (rl->dev_area.dev == dev_area->dev &&
rl->dev_area.start == dev_area->start)
return 1;
}
if (!(rl = dm_malloc(sizeof(struct raw_list)))) {
log_error("_add_raw allocation failed");
return 0;
}
memcpy(&rl->dev_area, dev_area, sizeof(*dev_area));
list_add(raw_list, &rl->list);
return 1;
}
static int _text_pv_read(const struct format_type *fmt, const char *pv_name,
struct physical_volume *pv, struct list *mdas)
{
struct label *label;
struct device *dev;
struct lvmcache_info *info;
struct metadata_area *mda, *mda_new;
struct mda_context *mdac, *mdac_new;
struct data_area_list *da;
if (!(dev = dev_cache_get(pv_name, fmt->cmd->filter))) {
stack;
return 0;
}
/* FIXME Optimise out repeated reading when cache lock held */
if (!(label_read(dev, &label))) {
stack;
return 0;
}
info = (struct lvmcache_info *) label->info;
/* Have we already cached vgname? */
if (info->vginfo && info->vginfo->vgname && *info->vginfo->vgname &&
get_pv_from_vg_by_id(info->fmt, info->vginfo->vgname,
info->vginfo->vgid, info->dev->pvid, pv)) {
return 1;
}
/* Perform full scan (just the first time) and try again */
if (!memlock() && !full_scan_done()) {
lvmcache_label_scan(fmt->cmd, 2);
if (info->vginfo && info->vginfo->vgname &&
*info->vginfo->vgname &&
get_pv_from_vg_by_id(info->fmt, info->vginfo->vgname,
info->vginfo->vgid,
info->dev->pvid, pv)) {
return 1;
}
}
/* Orphan */
pv->dev = info->dev;
pv->fmt = info->fmt;
pv->size = info->device_size >> SECTOR_SHIFT;
pv->vg_name = ORPHAN;
memcpy(&pv->id, &info->dev->pvid, sizeof(pv->id));
/* Currently only support exactly one data area */
if (list_size(&info->das) != 1) {
log_error("Must be exactly one data area (found %d) on PV %s",
list_size(&info->das), dev_name(dev));
return 0;
}
list_iterate_items(da, &info->das)
pv->pe_start = da->disk_locn.offset >> SECTOR_SHIFT;
if (!mdas)
return 1;
/* Add copy of mdas to supplied list */
list_iterate_items(mda, &info->mdas) {
mdac = (struct mda_context *) mda->metadata_locn;
if (!(mda_new = dm_pool_alloc(fmt->cmd->mem, sizeof(*mda_new)))) {
log_error("metadata_area allocation failed");
return 0;
}
if (!(mdac_new = dm_pool_alloc(fmt->cmd->mem, sizeof(*mdac_new)))) {
log_error("metadata_area allocation failed");
return 0;
}
memcpy(mda_new, mda, sizeof(*mda));
memcpy(mdac_new, mdac, sizeof(*mdac));
mda_new->metadata_locn = mdac_new;
list_add(mdas, &mda_new->list);
}
return 1;
}
static void _text_destroy_instance(struct format_instance *fid)
{
return;
}
static void _free_dirs(struct list *dir_list)
{
struct list *dl, *tmp;
list_iterate_safe(dl, tmp, dir_list) {
list_del(dl);
dm_free(dl);
}
}
static void _free_raws(struct list *raw_list)
{
struct list *rl, *tmp;
list_iterate_safe(rl, tmp, raw_list) {
list_del(rl);
dm_free(rl);
}
}
static void _text_destroy(const struct format_type *fmt)
{
if (fmt->private) {
_free_dirs(&((struct mda_lists *) fmt->private)->dirs);
_free_raws(&((struct mda_lists *) fmt->private)->raws);
dm_free(fmt->private);
}
dm_free((void *) fmt);
}
static struct metadata_area_ops _metadata_text_file_ops = {
vg_read:_vg_read_file,
vg_read_precommit:_vg_read_precommit_file,
vg_write:_vg_write_file,
vg_remove:_vg_remove_file,
vg_commit:_vg_commit_file
};
static struct metadata_area_ops _metadata_text_file_backup_ops = {
vg_read:_vg_read_file,
vg_write:_vg_write_file,
vg_remove:_vg_remove_file,
vg_commit:_vg_commit_file_backup
};
static struct metadata_area_ops _metadata_text_raw_ops = {
vg_read:_vg_read_raw,
vg_read_precommit:_vg_read_precommit_raw,
vg_write:_vg_write_raw,
vg_remove:_vg_remove_raw,
vg_precommit:_vg_precommit_raw,
vg_commit:_vg_commit_raw,
vg_revert:_vg_revert_raw
};
/* pvmetadatasize in sectors */
static int _text_pv_setup(const struct format_type *fmt,
uint64_t pe_start, uint32_t extent_count,
uint32_t extent_size,
int pvmetadatacopies,
uint64_t pvmetadatasize, struct list *mdas,
struct physical_volume *pv, struct volume_group *vg)
{
struct metadata_area *mda, *mda_new, *mda2;
struct mda_context *mdac, *mdac_new, *mdac2;
struct list *pvmdas;
struct lvmcache_info *info;
int found;
uint64_t pe_end = 0;
/* FIXME if vg, adjust start/end of pe area to avoid mdas! */
/* FIXME Cope with pvchange */
/* FIXME Merge code with _text_create_text_instance */
/* If new vg, add any further mdas on this PV to the fid's mda list */
if (vg) {
/* Iterate through all mdas on this PV */
if ((info = info_from_pvid(pv->dev->pvid))) {
pvmdas = &info->mdas;
list_iterate_items(mda, pvmdas) {
mdac =
(struct mda_context *) mda->metadata_locn;
/* FIXME Check it isn't already in use */
/* Ensure it isn't already on list */
found = 0;
list_iterate_items(mda2, mdas) {
if (mda2->ops !=
&_metadata_text_raw_ops) continue;
mdac2 =
(struct mda_context *)
mda2->metadata_locn;
if (!memcmp
(&mdac2->area, &mdac->area,
sizeof(mdac->area))) {
found = 1;
break;
}
}
if (found)
continue;
if (!(mda_new = dm_pool_alloc(fmt->cmd->mem,
sizeof(*mda_new)))) {
stack;
return 0;
}
if (!(mdac_new = dm_pool_alloc(fmt->cmd->mem,
sizeof(*mdac_new)))) {
stack;
return 0;
}
/* FIXME multiple dev_areas inside area */
memcpy(mda_new, mda, sizeof(*mda));
memcpy(mdac_new, mdac, sizeof(*mdac));
mda_new->metadata_locn = mdac_new;
list_add(mdas, &mda_new->list);
}
}
/* Unlike LVM1, we don't store this outside a VG */
/* FIXME Default from config file? vgextend cmdline flag? */
pv->status |= ALLOCATABLE_PV;
} else {
if (extent_count)
pe_end = pe_start + extent_count * extent_size - 1;
if (!_mda_setup(fmt, pe_start, pe_end, pvmetadatacopies,
pvmetadatasize, mdas, pv, vg)) {
stack;
return 0;
}
}
return 1;
}
/* NULL vgname means use only the supplied context e.g. an archive file */
static struct format_instance *_text_create_text_instance(const struct format_type
*fmt, const char *vgname,
const char *vgid,
void *context)
{
struct format_instance *fid;
struct text_fid_context *fidtc;
struct metadata_area *mda, *mda_new;
struct mda_context *mdac, *mdac_new;
struct dir_list *dl;
struct raw_list *rl;
struct list *dir_list, *raw_list, *mdas;
char path[PATH_MAX];
struct lvmcache_vginfo *vginfo;
struct lvmcache_info *info;
if (!(fid = dm_pool_alloc(fmt->cmd->mem, sizeof(*fid)))) {
log_error("Couldn't allocate format instance object.");
return NULL;
}
if (!(fidtc = (struct text_fid_context *)
dm_pool_zalloc(fmt->cmd->mem,sizeof(*fidtc)))) {
log_error("Couldn't allocate text_fid_context.");
return NULL;
}
fidtc->raw_metadata_buf = NULL;
fid->private = (void *) fidtc;
fid->fmt = fmt;
list_init(&fid->metadata_areas);
if (!vgname) {
if (!(mda = dm_pool_alloc(fmt->cmd->mem, sizeof(*mda)))) {
stack;
return NULL;
}
mda->ops = &_metadata_text_file_backup_ops;
mda->metadata_locn = context;
list_add(&fid->metadata_areas, &mda->list);
} else {
dir_list = &((struct mda_lists *) fmt->private)->dirs;
list_iterate_items(dl, dir_list) {
if (lvm_snprintf(path, PATH_MAX, "%s/%s",
dl->dir, vgname) < 0) {
log_error("Name too long %s/%s", dl->dir,
vgname);
return NULL;
}
context = create_text_context(fmt->cmd, path, NULL);
if (!(mda = dm_pool_alloc(fmt->cmd->mem, sizeof(*mda)))) {
stack;
return NULL;
}
mda->ops = &_metadata_text_file_ops;
mda->metadata_locn = context;
list_add(&fid->metadata_areas, &mda->list);
}
raw_list = &((struct mda_lists *) fmt->private)->raws;
list_iterate_items(rl, raw_list) {
/* FIXME Cache this; rescan below if some missing */
if (!_raw_holds_vgname(fid, &rl->dev_area, vgname))
continue;
if (!(mda = dm_pool_alloc(fmt->cmd->mem, sizeof(*mda)))) {
stack;
return NULL;
}
if (!(mdac = dm_pool_alloc(fmt->cmd->mem, sizeof(*mdac)))) {
stack;
return NULL;
}
mda->metadata_locn = mdac;
/* FIXME Allow multiple dev_areas inside area */
memcpy(&mdac->area, &rl->dev_area, sizeof(mdac->area));
mda->ops = &_metadata_text_raw_ops;
/* FIXME MISTAKE? mda->metadata_locn = context; */
list_add(&fid->metadata_areas, &mda->list);
}
/* Scan PVs in VG for any further MDAs */
lvmcache_label_scan(fmt->cmd, 0);
if (!(vginfo = vginfo_from_vgname(vgname, vgid))) {
stack;
goto out;
}
list_iterate_items(info, &vginfo->infos) {
mdas = &info->mdas;
list_iterate_items(mda, mdas) {
mdac =
(struct mda_context *) mda->metadata_locn;
/* FIXME Check it holds this VG */
if (!(mda_new = dm_pool_alloc(fmt->cmd->mem,
sizeof(*mda_new)))) {
stack;
return NULL;
}
if (!(mdac_new = dm_pool_alloc(fmt->cmd->mem,
sizeof(*mdac_new)))) {
stack;
return NULL;
}
/* FIXME multiple dev_areas inside area */
memcpy(mda_new, mda, sizeof(*mda));
memcpy(mdac_new, mdac, sizeof(*mdac));
mda_new->metadata_locn = mdac_new;
list_add(&fid->metadata_areas, &mda_new->list);
}
}
/* FIXME Check raw metadata area count - rescan if required */
}
out:
return fid;
}
void *create_text_context(struct cmd_context *cmd, const char *path,
const char *desc)
{
struct text_context *tc;
char *tmp;
if ((tmp = strstr(path, ".tmp")) && (tmp == path + strlen(path) - 4)) {
log_error("%s: Volume group filename may not end in .tmp",
path);
return NULL;
}
if (!(tc = dm_pool_alloc(cmd->mem, sizeof(*tc)))) {
stack;
return NULL;
}
if (!(tc->path_live = dm_pool_strdup(cmd->mem, path))) {
stack;
goto no_mem;
}
if (!(tc->path_edit = dm_pool_alloc(cmd->mem, strlen(path) + 5))) {
stack;
goto no_mem;
}
sprintf(tc->path_edit, "%s.tmp", path);
if (!desc)
desc = "";
if (!(tc->desc = dm_pool_strdup(cmd->mem, desc))) {
stack;
goto no_mem;
}
return (void *) tc;
no_mem:
dm_pool_free(cmd->mem, tc);
log_err("Couldn't allocate text format context object.");
return NULL;
}
static struct format_handler _text_handler = {
scan:_text_scan,
pv_read:_text_pv_read,
pv_setup:_text_pv_setup,
pv_write:_text_pv_write,
vg_setup:_text_vg_setup,
lv_setup:_text_lv_setup,
create_instance:_text_create_text_instance,
destroy_instance:_text_destroy_instance,
destroy:_text_destroy
};
static int _add_dir(const char *dir, struct list *dir_list)
{
struct dir_list *dl;
if (create_dir(dir)) {
if (!(dl = dm_malloc(sizeof(struct list) + strlen(dir) + 1))) {
log_error("_add_dir allocation failed");
return 0;
}
log_very_verbose("Adding text format metadata dir: %s", dir);
strcpy(dl->dir, dir);
list_add(dir_list, &dl->list);
return 1;
}
return 0;
}
static int _get_config_disk_area(struct cmd_context *cmd,
struct config_node *cn, struct list *raw_list)
{
struct device_area dev_area;
char *id_str;
struct id id;
if (!(cn = cn->child)) {
log_error("Empty metadata disk_area section of config file");
return 0;
}
if (!get_config_uint64(cn, "start_sector", &dev_area.start)) {
log_error("Missing start_sector in metadata disk_area section "
"of config file");
return 0;
}
dev_area.start <<= SECTOR_SHIFT;
if (!get_config_uint64(cn, "size", &dev_area.size)) {
log_error("Missing size in metadata disk_area section "
"of config file");
return 0;
}
dev_area.size <<= SECTOR_SHIFT;
if (!get_config_str(cn, "id", &id_str)) {
log_error("Missing uuid in metadata disk_area section "
"of config file");
return 0;
}
if (!id_read_format(&id, id_str)) {
log_error("Invalid uuid in metadata disk_area section "
"of config file: %s", id_str);
return 0;
}
if (!(dev_area.dev = device_from_pvid(cmd, &id))) {
char buffer[64];
if (!id_write_format(&id, buffer, sizeof(buffer)))
log_err("Couldn't find device.");
else
log_err("Couldn't find device with uuid '%s'.", buffer);
return 0;
}
return _add_raw(raw_list, &dev_area);
}
struct format_type *create_text_format(struct cmd_context *cmd)
{
struct format_type *fmt;
struct config_node *cn;
struct config_value *cv;
struct mda_lists *mda_lists;
if (!(fmt = dm_malloc(sizeof(*fmt)))) {
stack;
return NULL;
}
fmt->cmd = cmd;
fmt->ops = &_text_handler;
fmt->name = FMT_TEXT_NAME;
fmt->alias = FMT_TEXT_ALIAS;
fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_PRECOMMIT |
FMT_UNLIMITED_VOLS | FMT_RESIZE_PV;
if (!(mda_lists = dm_malloc(sizeof(struct mda_lists)))) {
log_error("Failed to allocate dir_list");
return NULL;
}
list_init(&mda_lists->dirs);
list_init(&mda_lists->raws);
mda_lists->file_ops = &_metadata_text_file_ops;
mda_lists->raw_ops = &_metadata_text_raw_ops;
fmt->private = (void *) mda_lists;
if (!(fmt->labeller = text_labeller_create(fmt))) {
log_error("Couldn't create text label handler.");
return NULL;
}
if (!(label_register_handler(FMT_TEXT_NAME, fmt->labeller))) {
log_error("Couldn't register text label handler.");
return NULL;
}
if ((cn = find_config_node(cmd->cft->root, "metadata/dirs"))) {
for (cv = cn->v; cv; cv = cv->next) {
if (cv->type != CFG_STRING) {
log_error("Invalid string in config file: "
"metadata/dirs");
goto err;
}
if (!_add_dir(cv->v.str, &mda_lists->dirs)) {
log_error("Failed to add %s to internal device "
"cache", cv->v.str);
goto err;
}
}
}
if ((cn = find_config_node(cmd->cft->root, "metadata/disk_areas"))) {
for (cn = cn->child; cn; cn = cn->sib) {
if (!_get_config_disk_area(cmd, cn, &mda_lists->raws))
goto err;
}
}
log_very_verbose("Initialised format: %s", fmt->name);
return fmt;
err:
_free_dirs(&mda_lists->dirs);
dm_free(fmt);
return NULL;
}