static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area,
struct mda_header *mdah,
- const char *vgname)
+ const char *vgname,
+ int precommit)
{
size_t len;
char vgnamebuf[NAME_LEN + 2];
struct raw_locn *rlocn;
struct lvmcache_info *info;
- rlocn = mdah->raw_locns;
+ rlocn = mdah->raw_locns; /* Slot 0 */
+
+ if (precommit)
+ rlocn++; /* Slot 1 */
+ /* FIXME Loop through rlocns two-at-a-time. List null-terminated. */
/* FIXME Ignore if checksum incorrect!!! */
- while (rlocn->offset) {
- 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;
- }
- rlocn++;
+ 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:
return 0;
}
- if (_find_vg_rlocn(dev_area, mdah, vgname))
+ if (_find_vg_rlocn(dev_area, mdah, vgname, 0))
r = 1;
if (!dev_close(dev_area->dev))
static struct volume_group *_vg_read_raw_area(struct format_instance *fid,
const char *vgname,
- struct device_area *area)
+ struct device_area *area,
+ int precommit)
{
struct volume_group *vg = NULL;
struct raw_locn *rlocn;
goto out;
}
- if (!(rlocn = _find_vg_rlocn(area, mdah, vgname))) {
+ if (!(rlocn = _find_vg_rlocn(area, mdah, vgname, precommit))) {
log_debug("VG %s not found on %s", vgname, dev_name(area->dev));
goto out;
}
stack;
goto out;
}
- log_debug("Read %s metadata (%u) from %s at %" PRIu64 " size %" PRIu64,
- vg->name, vg->seqno, dev_name(area->dev),
+ log_debug("Read %s %smetadata (%u) from %s at %" PRIu64 " size %"
+ PRIu64, vg->name, precommit ? "pre-commit " : "",
+ vg->seqno, dev_name(area->dev),
area->start + rlocn->offset, rlocn->size);
out:
{
struct mda_context *mdac = (struct mda_context *) mda->metadata_locn;
- return _vg_read_raw_area(fid, vgname, &mdac->area);
+ 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,
goto out;
}
- rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name);
+ rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, 0);
mdac->rlocn.offset = _next_rlocn_offset(rlocn, mdah);
if (!(mdac->rlocn.size = text_vg_export_raw(vg, "", buf, sizeof(buf)))) {
return r;
}
-static int _vg_commit_raw(struct format_instance *fid, struct volume_group *vg,
- struct metadata_area *mda)
+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 mda_header *mdah;
goto out;
}
- if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name))) {
- rlocn = &mdah->raw_locns[0];
+ if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, 0))) {
+ mdah->raw_locns[0].offset = 0;
mdah->raw_locns[1].offset = 0;
+ mdah->raw_locns[2].offset = 0;
+ rlocn = &mdah->raw_locns[0];
}
+ if (precommit)
+ rlocn++;
+
rlocn->offset = mdac->rlocn.offset;
rlocn->size = mdac->rlocn.size;
rlocn->checksum = mdac->rlocn.checksum;
- log_debug("Committing %s metadata (%u) to %s header at %" PRIu64,
- vg->name, vg->seqno, dev_name(mdac->area.dev),
- mdac->area.start);
+ 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);
if (!_raw_write_mda_header(fid->fmt, mdac->area.dev, mdac->area.start,
mdah)) {
log_error("Failed to write metadata area header");
r = 1;
out:
- if (!dev_close(mdac->area.dev))
+ if (!precommit && !dev_close(mdac->area.dev))
stack;
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)
goto out;
}
- if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name))) {
+ if (!(rlocn = _find_vg_rlocn(&mdac->area, mdah, vg->name, 0))) {
rlocn = &mdah->raw_locns[0];
mdah->raw_locns[1].offset = 0;
}
static struct volume_group *_vg_read_file_name(struct format_instance *fid,
const char *vgname,
- const char *path_live)
+ const char *read_path)
{
struct volume_group *vg;
time_t when;
char *desc;
- if (!(vg = text_vg_import_file(fid, path_live, &when, &desc))) {
+ if (!(vg = text_vg_import_file(fid, read_path, &when, &desc))) {
stack;
return NULL;
}
if (vgname && strcmp(vgname, vg->name)) {
pool_free(fid->fmt->cmd->mem, vg);
log_err("'%s' does not contain volume group '%s'.",
- path_live, vgname);
+ read_path, vgname);
return NULL;
} else
- log_debug("Read volume group %s from %s", vg->name, path_live);
+ log_debug("Read volume group %s from %s", vg->name, read_path);
return vg;
}
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;
+
+ return _vg_read_file_name(fid, vgname, tc->path_edit);
+}
+
static int _vg_write_file(struct format_instance *fid, struct volume_group *vg,
struct metadata_area *mda)
{
if (vgname_from_mda(fmt, &rl->dev_area, vgnamebuf,
sizeof(vgnamebuf))) {
if ((vg = _vg_read_raw_area(&fid, vgnamebuf,
- &rl->dev_area)))
+ &rl->dev_area, 0)))
lvmcache_update_vg(vg);
}
}
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_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
};
fmt->ops = &_text_handler;
fmt->name = FMT_TEXT_NAME;
fmt->alias = FMT_TEXT_ALIAS;
- fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_UNLIMITED_VOLS;
+ fmt->features = FMT_SEGMENTS | FMT_MDAS | FMT_TAGS | FMT_PRECOMMIT |
+ FMT_UNLIMITED_VOLS;
if (!(mda_lists = dbg_malloc(sizeof(struct mda_lists)))) {
log_error("Failed to allocate dir_list");
/* Revert */
list_uniterate(mdah2, &vg->fid->metadata_areas, mdah) {
mda = list_item(mdah2, struct metadata_area);
+
if (mda->ops->vg_revert &&
!mda->ops->vg_revert(vg->fid, vg, mda)) {
stack;
}
}
+ /* Now pre-commit each copy of the new metadata */
+ list_iterate(mdah, &vg->fid->metadata_areas) {
+ mda = list_item(mdah, struct metadata_area);
+ if (mda->ops->vg_precommit &&
+ !mda->ops->vg_precommit(vg->fid, vg, mda)) {
+ stack;
+ /* Revert */
+ list_iterate(mdah2, &vg->fid->metadata_areas) {
+ mda = list_item(mdah2, struct metadata_area);
+ if (mda->ops->vg_revert &&
+ !mda->ops->vg_revert(vg->fid, vg, mda)) {
+ stack;
+ }
+ }
+ return 0;
+ }
+ }
+
return 1;
}
* and take appropriate action if it isn't (e.g. abort; get write lock
* and call vg_read again).
*/
-struct volume_group *vg_read(struct cmd_context *cmd, const char *vgname,
- int *consistent)
+static struct volume_group *_vg_read(struct cmd_context *cmd,
+ const char *vgname,
+ int *consistent, int precommitted)
{
struct format_instance *fid;
const struct format_type *fmt;
int inconsistent = 0;
if (!*vgname) {
+ if (precommitted) {
+ log_error("Internal error: vg_read requires vgname "
+ "with pre-commit.");
+ return NULL;
+ }
*consistent = 1;
return _vg_read_orphans(cmd);
}
}
}
+ if (precommitted && !(fmt->features & FMT_PRECOMMIT)) {
+ log_error("Internal error: %s doesn't support "
+ "pre-commit", fmt->name);
+ return NULL;
+ }
+
/* create format instance with appropriate metadata area */
if (!(fid = fmt->ops->create_instance(fmt, vgname, NULL))) {
log_error("Failed to create format instance");
/* Ensure contents of all metadata areas match - else do recovery */
list_iterate(mdah, &fid->metadata_areas) {
mda = list_item(mdah, struct metadata_area);
- if (!(vg = mda->ops->vg_read(fid, vgname, mda))) {
+ if ((precommitted &&
+ !(vg = mda->ops->vg_read_precommit(fid, vgname, mda))) ||
+ (!precommitted &&
+ !(vg = mda->ops->vg_read(fid, vgname, mda)))) {
inconsistent = 1;
continue;
}
return NULL;
}
+ if (precommitted && !(fmt->features & FMT_PRECOMMIT)) {
+ log_error("Internal error: %s doesn't support "
+ "pre-commit", fmt->name);
+ return NULL;
+ }
+
/* create format instance with appropriate metadata area */
if (!(fid = fmt->ops->create_instance(fmt, vgname, NULL))) {
log_error("Failed to create format instance");
/* Ensure contents of all metadata areas match - else recover */
list_iterate(mdah, &fid->metadata_areas) {
mda = list_item(mdah, struct metadata_area);
- if (!(vg = mda->ops->vg_read(fid, vgname, mda))) {
+ if ((precommitted &&
+ !(vg = mda->ops->vg_read_precommit(fid, vgname,
+ mda))) ||
+ (!precommitted &&
+ !(vg = mda->ops->vg_read(fid, vgname, mda)))) {
inconsistent = 1;
continue;
}
lvmcache_update_vg(correct_vg);
if (inconsistent) {
+ if (precommitted) {
+ log_error("Inconsistent pre-commit metadata copies "
+ "for volume group %s", vgname);
+ return NULL;
+ }
+
if (!*consistent)
return correct_vg;
return correct_vg;
}
+struct volume_group *vg_read(struct cmd_context *cmd, const char *vgname,
+ int *consistent)
+{
+ return _vg_read(cmd, vgname, consistent, 0);
+}
+
+struct volume_group *vg_read_precommitted(struct cmd_context *cmd,
+ const char *vgname,
+ int *consistent)
+{
+ return _vg_read(cmd, vgname, consistent, 1);
+}
+
/* This is only called by lv_from_lvid, which is only called from
* activate.c so we know the appropriate VG lock is already held and
* the vg_read is therefore safe.