summaryrefslogtreecommitdiff
path: root/lib/metadata/lv.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/metadata/lv.c')
-rw-r--r--lib/metadata/lv.c332
1 files changed, 304 insertions, 28 deletions
diff --git a/lib/metadata/lv.c b/lib/metadata/lv.c
index 4e7d9dc..4032f34 100644
--- a/lib/metadata/lv.c
+++ b/lib/metadata/lv.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
- * Copyright (C) 2004-2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
*
* This file is part of LVM2.
*
@@ -21,18 +21,106 @@
#include "segtype.h"
#include "str_list.h"
+#include <time.h>
+#include <sys/utsname.h>
+
+static struct utsname _utsname;
+static int _utsinit = 0;
+
+static char *_format_pvsegs(struct dm_pool *mem, const struct lv_segment *seg,
+ int range_format)
+{
+ unsigned int s;
+ const char *name = NULL;
+ uint32_t extent = 0;
+ char extent_str[32];
+
+ if (!dm_pool_begin_object(mem, 256)) {
+ log_error("dm_pool_begin_object failed");
+ return NULL;
+ }
+
+ for (s = 0; s < seg->area_count; s++) {
+ switch (seg_type(seg, s)) {
+ case AREA_LV:
+ name = seg_lv(seg, s)->name;
+ extent = seg_le(seg, s);
+ break;
+ case AREA_PV:
+ name = dev_name(seg_dev(seg, s));
+ extent = seg_pe(seg, s);
+ break;
+ case AREA_UNASSIGNED:
+ name = "unassigned";
+ extent = 0;
+ break;
+ default:
+ log_error(INTERNAL_ERROR "Unknown area segtype.");
+ return NULL;
+ }
+
+ if (!dm_pool_grow_object(mem, name, strlen(name))) {
+ log_error("dm_pool_grow_object failed");
+ return NULL;
+ }
+
+ if (dm_snprintf(extent_str, sizeof(extent_str),
+ "%s%" PRIu32 "%s",
+ range_format ? ":" : "(", extent,
+ range_format ? "-" : ")") < 0) {
+ log_error("Extent number dm_snprintf failed");
+ return NULL;
+ }
+ if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
+ log_error("dm_pool_grow_object failed");
+ return NULL;
+ }
+
+ if (range_format) {
+ if (dm_snprintf(extent_str, sizeof(extent_str),
+ "%" PRIu32, extent + seg->area_len - 1) < 0) {
+ log_error("Extent number dm_snprintf failed");
+ return NULL;
+ }
+ if (!dm_pool_grow_object(mem, extent_str, strlen(extent_str))) {
+ log_error("dm_pool_grow_object failed");
+ return NULL;
+ }
+ }
+
+ if ((s != seg->area_count - 1) &&
+ !dm_pool_grow_object(mem, range_format ? " " : ",", 1)) {
+ log_error("dm_pool_grow_object failed");
+ return NULL;
+ }
+ }
+
+ if (!dm_pool_grow_object(mem, "\0", 1)) {
+ log_error("dm_pool_grow_object failed");
+ return NULL;
+ }
+
+ return dm_pool_end_object(mem);
+}
+
+char *lvseg_devices(struct dm_pool *mem, const struct lv_segment *seg)
+{
+ return _format_pvsegs(mem, seg, 0);
+}
+
+char *lvseg_seg_pe_ranges(struct dm_pool *mem, const struct lv_segment *seg)
+{
+ return _format_pvsegs(mem, seg, 1);
+}
+
char *lvseg_tags_dup(const struct lv_segment *seg)
{
return tags_format_and_copy(seg->lv->vg->vgmem, &seg->tags);
}
-char *lvseg_segtype_dup(const struct lv_segment *seg)
+char *lvseg_segtype_dup(struct dm_pool *mem, const struct lv_segment *seg)
{
- if (seg->area_count == 1) {
- return (char *)"linear";
- }
-
- return dm_pool_strdup(seg->lv->vg->vgmem, seg->segtype->ops->name(seg));
+ return dm_pool_strdup(mem, seg->segtype->ops->name(seg));
}
uint64_t lvseg_chunksize(const struct lv_segment *seg)
@@ -41,8 +129,11 @@ uint64_t lvseg_chunksize(const struct lv_segment *seg)
if (lv_is_cow(seg->lv))
size = (uint64_t) find_cow(seg->lv)->chunk_size;
+ else if (lv_is_thin_pool(seg->lv))
+ size = (uint64_t) seg->chunk_size;
else
size = UINT64_C(0);
+
return size;
}
@@ -69,6 +160,10 @@ char *lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
if (lv_is_cow(lv))
return lv_name_dup(mem, origin_from_cow(lv));
+
+ if (lv_is_thin_volume(lv) && first_seg(lv)->origin)
+ return lv_name_dup(mem, first_seg(lv)->origin);
+
return NULL;
}
@@ -95,14 +190,36 @@ char *lv_mirror_log_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
struct lv_segment *seg;
- dm_list_iterate_items(seg, &lv->segments) {
- if (!seg_is_mirrored(seg) || !seg->log_lv)
- continue;
- return dm_pool_strdup(mem, seg->log_lv->name);
- }
+ dm_list_iterate_items(seg, &lv->segments)
+ if (seg_is_mirrored(seg) && seg->log_lv)
+ return dm_pool_strdup(mem, seg->log_lv->name);
+
+ return NULL;
+}
+
+char *lv_pool_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ struct lv_segment *seg;
+
+ dm_list_iterate_items(seg, &lv->segments)
+ if (seg_is_thin_volume(seg) && seg->pool_lv)
+ return dm_pool_strdup(mem, seg->pool_lv->name);
+
return NULL;
}
+char *lv_data_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ return lv_is_thin_pool(lv) ?
+ dm_pool_strdup(mem, seg_lv(first_seg(lv), 0)->name) : NULL;
+}
+
+char *lv_metadata_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ return lv_is_thin_pool(lv) ?
+ dm_pool_strdup(mem, first_seg(lv)->metadata_lv->name) : NULL;
+}
+
int lv_kernel_minor(const struct logical_volume *lv)
{
struct lvinfo info;
@@ -155,11 +272,19 @@ uint64_t lv_origin_size(const struct logical_volume *lv)
return 0;
}
+uint64_t lv_metadata_size(const struct logical_volume *lv)
+{
+ return lv_is_thin_pool(lv) ? first_seg(lv)->metadata_lv->size : 0;
+}
+
char *lv_path_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
char *repstr;
size_t len;
+ if (!*lv->vg->name)
+ return dm_pool_strdup(mem, "");
+
len = strlen(lv->vg->cmd->dev_dir) + strlen(lv->vg->name) +
strlen(lv->name) + 2;
@@ -173,6 +298,7 @@ char *lv_path_dup(struct dm_pool *mem, const struct logical_volume *lv)
log_error("lvpath snprintf failed");
return 0;
}
+
return repstr;
}
@@ -206,13 +332,56 @@ static int _lv_mimage_in_sync(const struct logical_volume *lv)
return (percent == PERCENT_100) ? 1 : 0;
}
+static int _lv_raid_image_in_sync(const struct logical_volume *lv)
+{
+ percent_t percent;
+ struct lv_segment *raid_seg;
+
+ if (!(lv->status & RAID_IMAGE)) {
+ log_error(INTERNAL_ERROR "%s is not a RAID image", lv->name);
+ return 0;
+ }
+
+ raid_seg = get_only_segment_using_this_lv(first_seg(lv)->lv);
+ if (!raid_seg) {
+ log_error("Failed to find RAID segment for %s", lv->name);
+ return 0;
+ }
+
+ if (!seg_is_raid(raid_seg)) {
+ log_error("%s on %s is not a RAID segment",
+ raid_seg->lv->name, lv->name);
+ return 0;
+ }
+
+ if (!lv_raid_percent(raid_seg->lv, &percent))
+ return_0;
+
+ if (percent == PERCENT_100)
+ return 1;
+
+ /*
+ * FIXME: Get individual RAID image status.
+ * The status health characters reported from a RAID target
+ * indicate whether the whole array or just individual devices
+ * are in-sync. If the corresponding character for this image
+ * was 'A', we could report a more accurate status. This is
+ * especially so in the case of failures or rebuildings.
+ *
+ * We need to test the health characters anyway to report
+ * the correct 4th attr character. Just need to figure out
+ * where to put this functionality.
+ */
+ return 0;
+}
char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
percent_t snap_percent;
struct lvinfo info;
+ struct lv_segment *seg;
char *repstr;
- if (!(repstr = dm_pool_zalloc(mem, 7))) {
+ if (!(repstr = dm_pool_zalloc(mem, 10))) {
log_error("dm_pool_alloc failed");
return 0;
}
@@ -225,16 +394,27 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
repstr[0] = 'p';
else if (lv->status & CONVERTING)
repstr[0] = 'c';
+ /* Origin takes precedence over mirror and thin volume */
+ else if (lv_is_origin(lv))
+ repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
+ else if (lv->status & RAID)
+ repstr[0] = (lv->status & LV_NOTSYNCED) ? 'R' : 'r';
+ else if (lv->status & MIRRORED)
+ repstr[0] = (lv->status & LV_NOTSYNCED) ? 'M' : 'm';
+ else if (lv_is_thin_volume(lv))
+ repstr[0] = 'V';
else if (lv->status & VIRTUAL)
repstr[0] = 'v';
- /* Origin takes precedence over Mirror */
- else if (lv_is_origin(lv)) {
- repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
- }
- else if (lv->status & MIRRORED) {
- repstr[0] = (lv->status & MIRROR_NOTSYNCED) ? 'M' : 'm';
- }else if (lv->status & MIRROR_IMAGE)
+ else if (lv_is_thin_pool(lv))
+ repstr[0] = 't';
+ else if (lv_is_thin_pool_data(lv))
+ repstr[0] = 'T';
+ else if (lv_is_thin_pool_metadata(lv) || (lv->status & RAID_META))
+ repstr[0] = 'e';
+ else if (lv->status & MIRROR_IMAGE)
repstr[0] = (_lv_mimage_in_sync(lv)) ? 'i' : 'I';
+ else if (lv->status & RAID_IMAGE)
+ repstr[0] = (_lv_raid_image_in_sync(lv)) ? 'i' : 'I';
else if (lv->status & MIRROR_LOG)
repstr[0] = 'l';
else if (lv_is_cow(lv)) {
@@ -269,21 +449,117 @@ char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
repstr[4] = 'd'; /* Inactive without table */
/* Snapshot dropped? */
- if (info.live_table && lv_is_cow(lv) &&
- (!lv_snapshot_percent(lv, &snap_percent) ||
- snap_percent == PERCENT_INVALID)) {
- repstr[0] = toupper(repstr[0]);
- if (info.suspended)
- repstr[4] = 'S'; /* Susp Inv snapshot */
- else
- repstr[4] = 'I'; /* Invalid snapshot */
+ if (info.live_table && lv_is_cow(lv)) {
+ if (!lv_snapshot_percent(lv, &snap_percent) ||
+ snap_percent == PERCENT_INVALID) {
+ if (info.suspended)
+ repstr[4] = 'S'; /* Susp Inv snapshot */
+ else
+ repstr[4] = 'I'; /* Invalid snapshot */
+ }
+ else if (snap_percent == PERCENT_MERGE_FAILED) {
+ if (info.suspended)
+ repstr[4] = 'M'; /* Susp snapshot merge failed */
+ else
+ repstr[4] = 'm'; /* snapshot merge failed */
+ }
}
+ /*
+ * 'R' indicates read-only activation of a device that
+ * does not have metadata flagging it as read-only.
+ */
+ if (repstr[1] != 'r' && info.read_only)
+ repstr[1] = 'R';
+
repstr[5] = (info.open_count) ? 'o' : '-';
} else {
repstr[4] = '-';
repstr[5] = '-';
}
+
+ if (lv_is_thin_type(lv))
+ repstr[6] = 't';
+ else if (lv_is_raid_type(lv))
+ repstr[6] = 'r';
+ else if (lv_is_mirror_type(lv))
+ repstr[6] = 'm';
+ else if (lv_is_cow(lv) || lv_is_origin(lv))
+ repstr[6] = 's';
+ else if (lv_has_unknown_segments(lv))
+ repstr[6] = 'u';
+ else if (lv_is_virtual(lv))
+ repstr[6] = 'v';
+ else
+ repstr[6] = '-';
+
+ if (((lv_is_thin_volume(lv) && (seg = first_seg(lv)) && seg->pool_lv && (seg = first_seg(seg->pool_lv))) ||
+ (lv_is_thin_pool(lv) && (seg = first_seg(lv)))) &&
+ seg->zero_new_blocks)
+ repstr[7] = 'z';
+ else
+ repstr[7] = '-';
+
+ if (lv->status & PARTIAL_LV)
+ repstr[8] = 'p';
+ else
+ repstr[8] = '-';
+
out:
return repstr;
}
+
+int lv_set_creation(struct logical_volume *lv,
+ const char *hostname, uint64_t timestamp)
+{
+ const char *hn;
+
+ if (!hostname) {
+ if (!_utsinit) {
+ if (uname(&_utsname)) {
+ log_error("uname failed: %s", strerror(errno));
+ memset(&_utsname, 0, sizeof(_utsname));
+ }
+
+ _utsinit = 1;
+ }
+
+ hostname = _utsname.nodename;
+ }
+
+ if (!(hn = dm_hash_lookup(lv->vg->hostnames, hostname))) {
+ if (!(hn = dm_pool_strdup(lv->vg->vgmem, hostname))) {
+ log_error("Failed to duplicate hostname");
+ return 0;
+ }
+
+ if (!dm_hash_insert(lv->vg->hostnames, hostname, (void*)hn))
+ return_0;
+ }
+
+ lv->hostname = hn;
+ lv->timestamp = timestamp ? : (uint64_t) time(NULL);
+
+ return 1;
+}
+
+char *lv_time_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ char buffer[50];
+ struct tm *local_tm;
+ time_t ts = (time_t)lv->timestamp;
+
+ if (!ts ||
+ !(local_tm = localtime(&ts)) ||
+ /* FIXME: make this lvm.conf configurable */
+ !strftime(buffer, sizeof(buffer),
+ "%Y-%m-%d %T %z", local_tm))
+ buffer[0] = 0;
+
+ return dm_pool_strdup(mem, buffer);
+}
+
+char *lv_host_dup(struct dm_pool *mem, const struct logical_volume *lv)
+{
+ return dm_pool_strdup(mem, lv->hostname ? : "");
+}