blob: 9ee67b4d2da4ade1778f6585dab67ffbe1b30d76 [file] [log] [blame]
/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2013 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 Lesser General Public License v.2.1.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "lib.h"
#include "metadata.h"
#include "display.h"
#include "activate.h"
#include "toolcontext.h"
#include "segtype.h"
#include "str_list.h"
#include "lvmlockd.h"
#include <time.h>
#include <sys/utsname.h>
static struct utsname _utsname;
static int _utsinit = 0;
int lv_is_historical(const struct logical_volume *lv)
{
return lv->this_glv && lv->this_glv->is_historical;
}
static struct dm_list *_format_pvsegs(struct dm_pool *mem, const struct lv_segment *seg,
int range_format, int metadata_areas_only,
int mark_hidden)
{
unsigned int s;
const char *name = NULL;
uint32_t extent = 0;
uint32_t seg_len = 0;
char extent_str[32];
struct logical_volume *lv;
int visible = 1;
char *list_item;
size_t list_item_len;
struct dm_list *result = NULL;
if (!(result = str_list_create(mem))) {
log_error("_format_pvsegs: str_list_create failed");
goto bad;
}
if (metadata_areas_only && (!seg_is_raid_with_meta(seg) || !seg->meta_areas || lv_is_raid_metadata(seg->lv) || lv_is_raid_image(seg->lv)))
goto out;
for (s = 0; s < seg->area_count; s++) {
switch (metadata_areas_only ? seg_metatype(seg, s) : seg_type(seg, s)) {
case AREA_LV:
lv = metadata_areas_only ? seg_metalv(seg, s) : seg_lv(seg, s);
seg_len = metadata_areas_only ? seg_metalv(seg, s)->le_count : seg_lv(seg, s)->le_count;
visible = lv_is_visible(lv);
name = lv->name;
extent = metadata_areas_only ? seg_le(seg, s) : 0;
break;
case AREA_PV:
/* Raid metadata never uses PVs directly */
if (metadata_areas_only)
continue;
name = dev_name(seg_dev(seg, s));
extent = seg_pe(seg, s);
seg_len = seg->area_len;
break;
case AREA_UNASSIGNED:
name = "unassigned";
extent = 0;
seg_len = 0;
break;
default:
log_error(INTERNAL_ERROR "Unknown area segtype.");
goto bad;
}
list_item_len = strlen(name);
if (!visible && mark_hidden)
/* +2 for [ ] */
list_item_len += 2;
if (range_format) {
if (dm_snprintf(extent_str, sizeof(extent_str),
":%" PRIu32 "-%" PRIu32,
extent, extent + seg_len - 1) < 0) {
log_error("_format_pvseggs: extent range dm_snprintf failed");
goto bad;
}
} else {
if (dm_snprintf(extent_str, sizeof(extent_str),
"(%" PRIu32 ")", extent) < 0) {
log_error("_format_pvsegs: extent number dm_snprintf failed");
goto bad;
}
}
list_item_len += strlen(extent_str);
/* trialing 0 */
list_item_len += 1;
if (!(list_item = dm_pool_zalloc(mem, list_item_len))) {
log_error("_format_pvsegs: list item dm_pool_zalloc failed");
goto bad;
}
if (dm_snprintf(list_item, list_item_len,
"%s%s%s%s",
(!visible && mark_hidden) ? "[" : "",
name,
(!visible && mark_hidden) ? "]" : "",
extent_str) < 0) {
log_error("_format_pvsegs: list item dmsnprintf failed");
goto bad;
}
if (!str_list_add_no_dup_check(mem, result, list_item)) {
log_error("_format_pvsegs: failed to add item to list");
goto bad;
}
}
out:
return result;
bad:
dm_pool_free(mem, result);
return NULL;
}
struct dm_list *lvseg_devices(struct dm_pool *mem, const struct lv_segment *seg)
{
return _format_pvsegs(mem, seg, 0, 0, 0);
}
char *lvseg_devices_str(struct dm_pool *mem, const struct lv_segment *seg)
{
struct dm_list *list;
if (!(list = lvseg_devices(mem, seg)))
return_NULL;
return str_list_to_str(mem, list, ",");
}
struct dm_list *lvseg_metadata_devices(struct dm_pool *mem, const struct lv_segment *seg)
{
return _format_pvsegs(mem, seg, 0, 1, 0);
}
char *lvseg_metadata_devices_str(struct dm_pool *mem, const struct lv_segment *seg)
{
struct dm_list *list;
if (!(list = lvseg_devices(mem, seg)))
return_NULL;
return str_list_to_str(mem, list, ",");
}
struct dm_list *lvseg_seg_pe_ranges(struct dm_pool *mem, const struct lv_segment *seg)
{
return _format_pvsegs(mem, seg, 1, 0, 0);
}
char *lvseg_seg_pe_ranges_str(struct dm_pool *mem, const struct lv_segment *seg)
{
struct dm_list *list;
if (!(list = lvseg_seg_pe_ranges(mem, seg)))
return_NULL;
return str_list_to_str(mem, list, " ");
}
struct dm_list *lvseg_seg_le_ranges(struct dm_pool *mem, const struct lv_segment *seg)
{
return _format_pvsegs(mem, seg, 1, 0, seg->lv->vg->cmd->report_mark_hidden_devices);
}
char *lvseg_seg_le_ranges_str(struct dm_pool *mem, const struct lv_segment *seg)
{
struct dm_list *list;
if (!(list = lvseg_seg_pe_ranges(mem, seg)))
return_NULL;
return str_list_to_str(mem, list, seg->lv->vg->cmd->report_list_item_separator);
}
struct dm_list *lvseg_seg_metadata_le_ranges(struct dm_pool *mem, const struct lv_segment *seg)
{
return _format_pvsegs(mem, seg, 1, 1, seg->lv->vg->cmd->report_mark_hidden_devices);
}
char *lvseg_seg_metadata_le_ranges_str(struct dm_pool *mem, const struct lv_segment *seg)
{
struct dm_list *list;
if (!(list = lvseg_seg_metadata_le_ranges(mem, seg)))
return_NULL;
return str_list_to_str(mem, list, seg->lv->vg->cmd->report_list_item_separator);
}
char *lvseg_tags_dup(const struct lv_segment *seg)
{
return tags_format_and_copy(seg->lv->vg->vgmem, &seg->tags);
}
char *lvseg_segtype_dup(struct dm_pool *mem, const struct lv_segment *seg)
{
return dm_pool_strdup(mem, lvseg_name(seg));
}
char *lvseg_discards_dup(struct dm_pool *mem, const struct lv_segment *seg)
{
return dm_pool_strdup(mem, get_pool_discards_name(seg->discards));
}
char *lvseg_kernel_discards_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_with_info_and_seg_status *lvdm)
{
const char *s = "";
char *ret;
if (lvdm->seg_status.type == SEG_STATUS_THIN_POOL)
s = get_pool_discards_name(lvdm->seg_status.thin_pool->discards);
if (!(ret = dm_pool_strdup(mem, s))) {
log_error("lvseg_kernel_discards_dup_with_info_and_seg_status: dm_pool_strdup failed");
return NULL;
}
return ret;
}
char *lvseg_kernel_discards_dup(struct dm_pool *mem, const struct lv_segment *seg)
{
char *ret = NULL;
struct lv_with_info_and_seg_status status = {
.seg_status = {
.type = SEG_STATUS_NONE,
.seg = seg
},
};
if (!lv_is_thin_pool(seg->lv))
return NULL;
if (!(status.seg_status.mem = dm_pool_create("reporter_pool", 1024)))
return_NULL;
if (!(status.info_ok = lv_info_with_seg_status(seg->lv->vg->cmd, seg->lv, seg, 1, &status, 0, 0)))
goto_bad;
ret = lvseg_kernel_discards_dup_with_info_and_seg_status(mem, &status);
bad:
dm_pool_destroy(status.seg_status.mem);
return ret;
}
char *lvseg_cachemode_dup(struct dm_pool *mem, const struct lv_segment *seg)
{
const char *name = get_cache_mode_name(seg);
if (!name)
return_NULL;
return dm_pool_strdup(mem, name);
}
#ifdef DMEVENTD
# include "libdevmapper-event.h"
#endif
char *lvseg_monitor_dup(struct dm_pool *mem, const struct lv_segment *seg)
{
const char *s = "";
#ifdef DMEVENTD
struct lvinfo info;
int pending = 0, monitored;
struct lv_segment *segm = (struct lv_segment *) seg;
if (lv_is_cow(seg->lv) && !lv_is_merging_cow(seg->lv))
segm = first_seg(seg->lv->snapshot->lv);
// log_debug("Query LV:%s mon:%s segm:%s tgtm:%p segmon:%d statusm:%d", seg->lv->name, segm->lv->name, segm->segtype->name, segm->segtype->ops->target_monitored, seg_monitored(segm), (int)(segm->status & PVMOVE));
if ((dmeventd_monitor_mode() != 1) ||
!segm->segtype->ops ||
!segm->segtype->ops->target_monitored)
/* Nothing to do, monitoring not supported */;
else if (lv_is_cow_covering_origin(seg->lv))
/* Nothing to do, snapshot already covers origin */;
else if (!seg_monitored(segm) || (segm->status & PVMOVE))
s = "not monitored";
else if (lv_info(seg->lv->vg->cmd, seg->lv, 1, &info, 0, 0) && info.exists) {
monitored = segm->segtype->ops->target_monitored(segm, &pending);
if (pending)
s = "pending";
else
s = (monitored) ? "monitored" : "not monitored";
} // else log_debug("Not active");
#endif
return dm_pool_strdup(mem, s);
}
uint64_t lvseg_chunksize(const struct lv_segment *seg)
{
uint64_t size;
if (lv_is_cow(seg->lv))
size = (uint64_t) find_snapshot(seg->lv)->chunk_size;
else if (seg_is_pool(seg))
size = (uint64_t) seg->chunk_size;
else if (seg_is_cache(seg))
return lvseg_chunksize(first_seg(seg->pool_lv));
else
size = UINT64_C(0);
return size;
}
const char *lvseg_name(const struct lv_segment *seg)
{
/* Support even segtypes without 'ops' */
if (seg->segtype->ops &&
seg->segtype->ops->name)
return seg->segtype->ops->name(seg);
return seg->segtype->name;
}
uint64_t lvseg_start(const struct lv_segment *seg)
{
return (uint64_t) seg->le * seg->lv->vg->extent_size;
}
uint64_t lvseg_size(const struct lv_segment *seg)
{
return (uint64_t) seg->len * seg->lv->vg->extent_size;
}
dm_percent_t lvseg_percent_with_info_and_seg_status(const struct lv_with_info_and_seg_status *lvdm,
percent_get_t type)
{
dm_percent_t p;
uint64_t csize;
const struct lv_segment *seg;
const struct lv_seg_status *s = &lvdm->seg_status;
/*
* TODO:
* Later move to segment methods, instead of using single place.
* Also handle logic for mirror segments and it total_* summing
* Esentially rework _target_percent API for segtype.
*/
switch (s->type) {
case SEG_STATUS_CACHE:
if (s->cache->fail || s->cache->error)
p = DM_PERCENT_INVALID;
else {
switch (type) {
case PERCENT_GET_DIRTY:
p = dm_make_percent(s->cache->dirty_blocks,
s->cache->used_blocks);
break;
case PERCENT_GET_METADATA:
p = dm_make_percent(s->cache->metadata_used_blocks,
s->cache->metadata_total_blocks);
break;
default:
p = dm_make_percent(s->cache->used_blocks,
s->cache->total_blocks);
}
}
break;
case SEG_STATUS_SNAPSHOT:
if (s->snapshot->merge_failed)
p = DM_PERCENT_INVALID;
else if (s->snapshot->invalid)
p = DM_PERCENT_100; /* Shown as 100% full */
else if (s->snapshot->has_metadata_sectors &&
(s->snapshot->used_sectors == s->snapshot->metadata_sectors))
p = DM_PERCENT_0;
else
p = dm_make_percent(s->snapshot->used_sectors,
s->snapshot->total_sectors);
break;
case SEG_STATUS_THIN_POOL:
if (s->thin_pool->fail || s->thin_pool->error)
p = DM_PERCENT_INVALID;
else if (type == PERCENT_GET_METADATA)
p = dm_make_percent(s->thin_pool->used_metadata_blocks,
s->thin_pool->total_metadata_blocks);
else
p = dm_make_percent(s->thin_pool->used_data_blocks,
s->thin_pool->total_data_blocks);
break;
case SEG_STATUS_THIN:
if (s->thin->fail || (type != PERCENT_GET_DATA))
/* TODO: expose highest mapped sector */
p = DM_PERCENT_INVALID;
else {
seg = first_seg(lvdm->lv);
/* Pool allocates whole chunk so round-up to nearest one */
csize = first_seg(seg->pool_lv)->chunk_size;
csize = ((seg->lv->size + csize - 1) / csize) * csize;
if (s->thin->mapped_sectors <= csize)
p = dm_make_percent(s->thin->mapped_sectors, csize);
else {
log_warn("WARNING: Thin volume %s maps %s while the size is only %s.",
display_lvname(seg->lv),
display_size(lvdm->lv->vg->cmd, s->thin->mapped_sectors),
display_size(lvdm->lv->vg->cmd, csize));
/* Don't show nonsense numbers like i.e. 1000% full */
p = DM_PERCENT_100;
}
}
break;
default:
p = DM_PERCENT_INVALID;
}
return p;
}
uint32_t lv_kernel_read_ahead(const struct logical_volume *lv)
{
struct lvinfo info;
if (!lv_info(lv->vg->cmd, lv, 0, &info, 0, 1) || !info.exists)
return UINT32_MAX;
return info.read_ahead;
}
struct pv_and_int {
struct physical_volume *pv;
int *i;
};
static int _lv_is_on_pv(struct logical_volume *lv, void *data)
{
int *is_on_pv = ((struct pv_and_int *)data)->i;
struct physical_volume *pv = ((struct pv_and_int *)data)->pv;
uint32_t s;
struct physical_volume *pv2;
struct lv_segment *seg;
if (!lv || !(first_seg(lv)))
return_0;
/*
* If the LV has already been found to be on the PV, then
* we don't need to continue checking - just return.
*/
if (*is_on_pv)
return 1;
dm_list_iterate_items(seg, &lv->segments) {
for (s = 0; s < seg->area_count; s++) {
if (seg_type(seg, s) != AREA_PV)
continue;
pv2 = seg_pv(seg, s);
if (id_equal(&pv->id, &pv2->id)) {
*is_on_pv = 1;
return 1;
}
if (pv->dev && pv2->dev &&
(pv->dev->dev == pv2->dev->dev)) {
*is_on_pv = 1;
return 1;
}
}
}
return 1;
}
/*
* lv_is_on_pv
* @lv:
* @pv:
*
* If any of the component devices of the LV are on the given PV, 1
* is returned; otherwise 0. For example if one of the images of a RAID
* (or its metadata device) is on the PV, 1 would be returned for the
* top-level LV.
* If you wish to check the images themselves, you should pass them.
*
* Returns: 1 if LV (or part of LV) is on PV, 0 otherwise
*/
int lv_is_on_pv(struct logical_volume *lv, struct physical_volume *pv)
{
int is_on_pv = 0;
struct pv_and_int context = { pv, &is_on_pv };
if (!_lv_is_on_pv(lv, &context) ||
!for_each_sub_lv(lv, _lv_is_on_pv, &context))
/* Failure only happens if bad arguments are passed */
log_error(INTERNAL_ERROR "for_each_sub_lv failure.");
log_debug_metadata("%s is %son %s", lv->name,
is_on_pv ? "" : "not ", pv_dev_name(pv));
return is_on_pv;
}
/*
* lv_is_on_pvs
* @lv
* @pvs
*
* Returns 1 if the LV (or part of the LV) is on any of the pvs
* in the list, 0 otherwise.
*/
int lv_is_on_pvs(struct logical_volume *lv, struct dm_list *pvs)
{
struct pv_list *pvl;
dm_list_iterate_items(pvl, pvs)
if (lv_is_on_pv(lv, pvl->pv))
return 1;
return 0;
}
struct logical_volume *lv_origin_lv(const struct logical_volume *lv)
{
struct logical_volume *origin = NULL;
if (lv_is_cow(lv))
origin = origin_from_cow(lv);
else if (lv_is_cache(lv) && first_seg(lv)->origin)
origin = first_seg(lv)->origin;
else if (lv_is_thin_volume(lv) && first_seg(lv)->origin)
origin = first_seg(lv)->origin;
else if (lv_is_thin_volume(lv) && first_seg(lv)->external_lv)
origin = first_seg(lv)->external_lv;
return origin;
}
static char *_do_lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv,
int uuid)
{
struct logical_volume *origin_lv = lv_origin_lv(lv);
if (!origin_lv)
return NULL;
if (uuid)
return lv_uuid_dup(mem, origin_lv);
else
return lv_name_dup(mem, origin_lv);
}
char *lv_origin_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_origin_dup(mem, lv, 0);
}
char *lv_origin_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_origin_dup(mem, lv, 1);
}
char *lv_name_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return dm_pool_strdup(mem, lv->name);
}
char *lv_fullname_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
char lvfullname[NAME_LEN * 2 + 2];
if (dm_snprintf(lvfullname, sizeof(lvfullname), "%s/%s", lv->vg->name, lv->name) < 0) {
log_error("lvfullname snprintf failed");
return NULL;
}
return dm_pool_strdup(mem, lvfullname);
}
struct logical_volume *lv_parent(const struct logical_volume *lv)
{
struct logical_volume *parent_lv = NULL;
struct lv_segment *seg;
if (lv_is_visible(lv))
;
else if ((lv_is_mirror_image(lv) || lv_is_mirror_log(lv)) ||
(lv_is_raid_image(lv) || lv_is_raid_metadata(lv)) ||
(lv_is_cache_pool_data(lv) || lv_is_cache_pool_metadata(lv)) ||
(lv_is_thin_pool_data(lv) || lv_is_thin_pool_metadata(lv))) {
if (!(seg = get_only_segment_using_this_lv(lv)))
stack;
else
parent_lv = seg->lv;
}
return parent_lv;
}
char *lv_parent_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
struct logical_volume *parent_lv = lv_parent(lv);
return dm_pool_strdup(mem, parent_lv ? parent_lv->name : "");
}
char *lv_modules_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
struct dm_list *modules;
if (!(modules = str_list_create(mem))) {
log_error("modules str_list allocation failed");
return NULL;
}
if (!list_lv_modules(mem, lv, modules))
return_NULL;
return tags_format_and_copy(mem, modules);
}
struct logical_volume *lv_mirror_log_lv(const struct logical_volume *lv)
{
struct lv_segment *seg;
dm_list_iterate_items(seg, &lv->segments) {
if (seg_is_mirrored(seg) && seg->log_lv)
return seg->log_lv;
}
return NULL;
}
static char *_do_lv_mirror_log_dup(struct dm_pool *mem, const struct logical_volume *lv,
int uuid)
{
struct logical_volume *mirror_log_lv = lv_mirror_log_lv(lv);
if (!mirror_log_lv)
return NULL;
if (uuid)
return lv_uuid_dup(mem, mirror_log_lv);
else
return lv_name_dup(mem, mirror_log_lv);
}
char *lv_mirror_log_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_mirror_log_dup(mem, lv, 0);
}
char *lv_mirror_log_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_mirror_log_dup(mem, lv, 1);
}
struct logical_volume *lv_pool_lv(const struct logical_volume *lv)
{
struct lv_segment *seg = (lv_is_thin_volume(lv) || lv_is_cache(lv)) ?
first_seg(lv) : NULL;
struct logical_volume *pool_lv = seg ? seg->pool_lv : NULL;
return pool_lv;
}
static char *_do_lv_pool_lv_dup(struct dm_pool *mem, const struct logical_volume *lv,
int uuid)
{
struct logical_volume *pool_lv = lv_pool_lv(lv);
if (!pool_lv)
return NULL;
if (uuid)
return lv_uuid_dup(mem, pool_lv);
else
return lv_name_dup(mem, pool_lv);
}
char *lv_pool_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_pool_lv_dup(mem, lv, 0);
}
char *lv_pool_lv_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_pool_lv_dup(mem, lv, 1);
}
struct logical_volume *lv_data_lv(const struct logical_volume *lv)
{
struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ?
first_seg(lv) : NULL;
struct logical_volume *data_lv = seg ? seg_lv(seg, 0) : NULL;
return data_lv;
}
static char *_do_lv_data_lv_dup(struct dm_pool *mem, const struct logical_volume *lv,
int uuid)
{
struct logical_volume *data_lv = lv_data_lv(lv);
if (!data_lv)
return NULL;
if (uuid)
return lv_uuid_dup(mem, data_lv);
else
return lv_name_dup(mem, data_lv);
}
char *lv_data_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_data_lv_dup(mem, lv, 0);
}
char *lv_data_lv_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_data_lv_dup(mem, lv, 1);
}
struct logical_volume *lv_metadata_lv(const struct logical_volume *lv)
{
struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ?
first_seg(lv) : NULL;
struct logical_volume *metadata_lv = seg ? seg->metadata_lv : NULL;
return metadata_lv;
}
static char *_do_lv_metadata_lv_dup(struct dm_pool *mem, const struct logical_volume *lv,
int uuid)
{
struct logical_volume *metadata_lv = lv_metadata_lv(lv);
if (!metadata_lv)
return NULL;
if (uuid)
return lv_uuid_dup(mem, metadata_lv);
else
return lv_name_dup(mem, metadata_lv);
}
char *lv_metadata_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_metadata_lv_dup(mem, lv, 0);
}
char *lv_metadata_lv_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_metadata_lv_dup(mem, lv, 1);
}
const char *lv_layer(const struct logical_volume *lv)
{
if (lv_is_thin_pool(lv))
return "tpool";
else if (lv_is_origin(lv) || lv_is_external_origin(lv))
return "real";
return NULL;
}
int lv_kernel_minor(const struct logical_volume *lv)
{
struct lvinfo info;
if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists)
return info.minor;
return -1;
}
int lv_kernel_major(const struct logical_volume *lv)
{
struct lvinfo info;
if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists)
return info.major;
return -1;
}
struct logical_volume *lv_convert_lv(const struct logical_volume *lv)
{
struct lv_segment *seg;
if (lv_is_converting(lv) || lv_is_mirrored(lv)) {
seg = first_seg(lv);
/* Temporary mirror is always area_num == 0 */
if (seg_type(seg, 0) == AREA_LV &&
is_temporary_mirror_layer(seg_lv(seg, 0)))
return seg_lv(seg, 0);
}
return NULL;
}
static char *_do_lv_convert_lv_dup(struct dm_pool *mem, const struct logical_volume *lv,
int uuid)
{
struct logical_volume *convert_lv = lv_convert_lv(lv);
if (!convert_lv)
return NULL;
if (uuid)
return lv_uuid_dup(mem, convert_lv);
else
return lv_name_dup(mem, convert_lv);
}
char *lv_convert_lv_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_convert_lv_dup(mem, lv, 0);
}
char *lv_convert_lv_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_convert_lv_dup(mem, lv, 1);
}
static char *_do_lv_move_pv_dup(struct dm_pool *mem, const struct logical_volume *lv,
int uuid)
{
struct logical_volume *mimage0_lv;
struct lv_segment *seg;
struct pv_segment *pvseg;
dm_list_iterate_items(seg, &lv->segments) {
if (seg->status & PVMOVE) {
if (seg_type(seg, 0) == AREA_LV) { /* atomic pvmove */
mimage0_lv = seg_lv(seg, 0);
if (!lv_is_mirror_image(mimage0_lv)) {
log_error(INTERNAL_ERROR
"Bad pvmove structure");
return NULL;
}
pvseg = seg_pvseg(first_seg(mimage0_lv), 0);
} else /* Segment pvmove */
pvseg = seg_pvseg(seg, 0);
if (uuid)
return pv_uuid_dup(mem, pvseg->pv);
else
return pv_name_dup(mem, pvseg->pv);
}
}
return NULL;
}
char *lv_move_pv_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_move_pv_dup(mem, lv, 0);
}
char *lv_move_pv_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return _do_lv_move_pv_dup(mem, lv, 1);
}
uint64_t lv_origin_size(const struct logical_volume *lv)
{
struct lv_segment *seg;
if (lv_is_cow(lv))
return (uint64_t) find_snapshot(lv)->len * lv->vg->extent_size;
if (lv_is_thin_volume(lv) && (seg = first_seg(lv)) &&
seg->external_lv)
return seg->external_lv->size;
if (lv_is_origin(lv))
return lv->size;
return 0;
}
uint64_t lv_metadata_size(const struct logical_volume *lv)
{
struct lv_segment *seg = (lv_is_thin_pool(lv) || lv_is_cache_pool(lv)) ?
first_seg(lv) : NULL;
return seg ? seg->metadata_lv->size : 0;
}
char *lv_path_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
char *repstr;
size_t len;
/* Only for visible devices that get a link from /dev/vg */
if (!*lv->vg->name || !lv_is_visible(lv) || lv_is_thin_pool(lv))
return dm_pool_strdup(mem, "");
len = strlen(lv->vg->cmd->dev_dir) + strlen(lv->vg->name) +
strlen(lv->name) + 2;
if (!(repstr = dm_pool_zalloc(mem, len))) {
log_error("dm_pool_alloc failed");
return NULL;
}
if (dm_snprintf(repstr, len, "%s%s/%s",
lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0) {
log_error("lvpath snprintf failed");
return NULL;
}
return repstr;
}
char *lv_dmpath_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
char *name;
char *repstr;
size_t len;
if (!*lv->vg->name)
return dm_pool_strdup(mem, "");
if (!(name = dm_build_dm_name(mem, lv->vg->name, lv->name, NULL))) {
log_error("dm_build_dm_name failed");
return NULL;
}
len = strlen(dm_dir()) + strlen(name) + 2;
if (!(repstr = dm_pool_zalloc(mem, len))) {
log_error("dm_pool_alloc failed");
return NULL;
}
if (dm_snprintf(repstr, len, "%s/%s", dm_dir(), name) < 0) {
log_error("lv_dmpath snprintf failed");
return NULL;
}
return repstr;
}
char *lv_uuid_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return id_format_and_copy(mem ? mem : lv->vg->vgmem, &lv->lvid.id[1]);
}
char *lv_tags_dup(const struct logical_volume *lv)
{
return tags_format_and_copy(lv->vg->vgmem, &lv->tags);
}
uint64_t lv_size(const struct logical_volume *lv)
{
return lv->size;
}
int lv_mirror_image_in_sync(const struct logical_volume *lv)
{
dm_percent_t percent;
struct lv_segment *seg = first_seg(lv);
struct lv_segment *mirror_seg;
if (!(lv->status & MIRROR_IMAGE) || !seg ||
!(mirror_seg = find_mirror_seg(seg))) {
log_error(INTERNAL_ERROR "Cannot find mirror segment.");
return 0;
}
if (!lv_mirror_percent(lv->vg->cmd, mirror_seg->lv, 0, &percent,
NULL))
return_0;
return (percent == DM_PERCENT_100) ? 1 : 0;
}
int lv_raid_image_in_sync(const struct logical_volume *lv)
{
unsigned s;
dm_percent_t percent;
char *raid_health;
struct lv_segment *seg, *raid_seg = NULL;
/*
* If the LV is not active locally,
* it doesn't make sense to check status
*/
if (!lv_is_active_locally(lv))
return 0; /* Assume not in-sync */
if (!lv_is_raid_image(lv)) {
log_error(INTERNAL_ERROR "%s is not a RAID image", lv->name);
return 0;
}
if ((seg = first_seg(lv)))
raid_seg = get_only_segment_using_this_lv(seg->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 == DM_PERCENT_100)
return 1;
/* Find out which sub-LV this is. */
for (s = 0; s < raid_seg->area_count; s++)
if (seg_lv(raid_seg, s) == lv)
break;
if (s == raid_seg->area_count) {
log_error(INTERNAL_ERROR
"sub-LV %s was not found in raid segment",
lv->name);
return 0;
}
if (!lv_raid_dev_health(raid_seg->lv, &raid_health))
return_0;
if (raid_health[s] == 'A')
return 1;
return 0;
}
/*
* _lv_raid_healthy
* @lv: A RAID_IMAGE, RAID_META, or RAID logical volume.
*
* Returns: 1 if healthy, 0 if device is not health
*/
int lv_raid_healthy(const struct logical_volume *lv)
{
unsigned s;
char *raid_health;
struct lv_segment *seg, *raid_seg = NULL;
/*
* If the LV is not active locally,
* it doesn't make sense to check status
*/
if (!lv_is_active_locally(lv))
return 1; /* assume healthy */
if (!lv_is_raid_type(lv)) {
log_error(INTERNAL_ERROR "%s is not of RAID type", lv->name);
return 0;
}
if (lv_is_raid(lv))
raid_seg = first_seg(lv);
else if ((seg = first_seg(lv)))
raid_seg = get_only_segment_using_this_lv(seg->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_dev_health(raid_seg->lv, &raid_health))
return_0;
if (lv_is_raid(lv)) {
if (strchr(raid_health, 'D'))
return 0;
else
return 1;
}
/* Find out which sub-LV this is. */
for (s = 0; s < raid_seg->area_count; s++)
if ((lv_is_raid_image(lv) && (seg_lv(raid_seg, s) == lv)) ||
(lv_is_raid_metadata(lv) && (seg_metalv(raid_seg, s) == lv)))
break;
if (s == raid_seg->area_count) {
log_error(INTERNAL_ERROR
"sub-LV %s was not found in raid segment",
lv->name);
return 0;
}
if (raid_health[s] == 'D')
return 0;
return 1;
}
char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_with_info_and_seg_status *lvdm)
{
const struct logical_volume *lv = lvdm->lv;
struct lv_segment *seg;
char *repstr;
if (!(repstr = dm_pool_zalloc(mem, 11))) {
log_error("dm_pool_alloc failed");
return 0;
}
/* Blank if this is a "free space" LV. */
if (!*lv->name && !lv_is_historical(lv))
goto out;
if (lv_is_pvmove(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) || lv_is_external_origin(lv))
repstr[0] = (lv_is_merging_origin(lv)) ? 'O' : 'o';
else if (lv_is_pool_metadata(lv) ||
lv_is_pool_metadata_spare(lv) ||
lv_is_raid_metadata(lv))
repstr[0] = 'e';
else if (lv_is_cache_type(lv))
repstr[0] = 'C';
else if (lv_is_raid(lv))
repstr[0] = (lv_is_not_synced(lv)) ? 'R' : 'r';
else if (lv_is_mirror(lv))
repstr[0] = (lv_is_not_synced(lv)) ? 'M' : 'm';
else if (lv_is_thin_volume(lv))
repstr[0] = lv_is_merging_origin(lv) ?
'O' : (lv_is_merging_thin_snapshot(lv) ? 'S' : 'V');
else if (lv_is_virtual(lv))
repstr[0] = 'v';
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_mirror_image(lv))
repstr[0] = (lv_mirror_image_in_sync(lv)) ? 'i' : 'I';
else if (lv_is_raid_image(lv))
/*
* Visible RAID_IMAGES are sub-LVs that have been exposed for
* top-level use by being split from the RAID array with
* '--splitmirrors 1 --trackchanges'. They always report 'I'.
*/
repstr[0] = (!lv_is_visible(lv) && lv_raid_image_in_sync(lv)) ?
'i' : 'I';
else if (lv_is_mirror_log(lv))
repstr[0] = 'l';
else if (lv_is_cow(lv))
repstr[0] = (lv_is_merging_cow(lv)) ? 'S' : 's';
else if (lv_is_cache_origin(lv))
repstr[0] = 'o';
else
repstr[0] = '-';
if (lv_is_pvmove(lv))
repstr[1] = '-';
else if (lv->status & LVM_WRITE)
repstr[1] = 'w';
else if (lv->status & LVM_READ)
repstr[1] = 'r';
else
repstr[1] = '-';
repstr[2] = alloc_policy_char(lv->alloc);
if (lv_is_locked(lv))
repstr[2] = toupper(repstr[2]);
repstr[3] = (lv->status & FIXED_MINOR) ? 'm' : '-';
if (lv_is_historical(lv)) {
repstr[4] = 'h';
repstr[5] = '-';
} else if (!activation() || !lvdm->info_ok) {
repstr[4] = 'X'; /* Unknown */
repstr[5] = 'X'; /* Unknown */
} else if (lvdm->info.exists) {
if (lvdm->info.suspended)
repstr[4] = 's'; /* Suspended */
else if (lvdm->info.live_table)
repstr[4] = 'a'; /* Active */
else if (lvdm->info.inactive_table)
repstr[4] = 'i'; /* Inactive with table */
else
repstr[4] = 'd'; /* Inactive without table */
/* Snapshot dropped? */
if (lvdm->info.live_table &&
(lvdm->seg_status.type == SEG_STATUS_SNAPSHOT)) {
if (lvdm->seg_status.snapshot->invalid) {
if (lvdm->info.suspended)
repstr[4] = 'S'; /* Susp Inv snapshot */
else
repstr[4] = 'I'; /* Invalid snapshot */
} else if (lvdm->seg_status.snapshot->merge_failed) {
if (lvdm->info.suspended)
repstr[4] = 'M'; /* Susp snapshot merge failed */
else
repstr[4] = 'm'; /* Snapshot merge failed */
}
}
/* 'c' when cache/thin-pool is active with needs_check flag
* 'C' for suspend */
if ((lv_is_thin_pool(lv) &&
lvdm->seg_status.thin_pool->needs_check) ||
(lv_is_cache(lv) &&
lvdm->seg_status.cache->needs_check))
repstr[4] = lvdm->info.suspended ? 'C' : 'c';
/*
* 'R' indicates read-only activation of a device that
* does not have metadata flagging it as read-only.
*/
if (repstr[1] != 'r' && lvdm->info.read_only)
repstr[1] = 'R';
repstr[5] = (lvdm->info.open_count) ? 'o' : '-';
} else {
repstr[4] = '-';
repstr[5] = '-';
}
if (lv_is_thin_pool(lv) || lv_is_thin_volume(lv))
repstr[6] = 't';
else if (lv_is_cache_pool(lv) || lv_is_cache(lv) || lv_is_cache_origin(lv))
repstr[6] = 'C';
else if (lv_is_raid_type(lv))
repstr[6] = 'r';
else if (lv_is_mirror_type(lv) || lv_is_pvmove(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] = '-';
repstr[8] = '-';
if (lv_is_partial(lv))
repstr[8] = 'p';
else if (lv_is_raid_type(lv)) {
uint64_t n;
if (!activation())
repstr[8] = 'X'; /* Unknown */
else if (!lv_raid_healthy(lv))
repstr[8] = 'r'; /* RAID needs 'r'efresh */
else if (lv_is_raid(lv)) {
if (lv_raid_mismatch_count(lv, &n) && n)
repstr[8] = 'm'; /* RAID has 'm'ismatches */
} else if (lv->status & LV_WRITEMOSTLY)
repstr[8] = 'w'; /* sub-LV has 'w'ritemostly */
} else if (lv_is_cache(lv) &&
(lvdm->seg_status.type != SEG_STATUS_NONE)) {
if (lvdm->seg_status.type == SEG_STATUS_UNKNOWN)
repstr[8] = 'X'; /* Unknown */
else if (lvdm->seg_status.cache->fail)
repstr[8] = 'F';
else if (lvdm->seg_status.cache->read_only)
repstr[8] = 'M';
} else if (lv_is_thin_pool(lv) &&
(lvdm->seg_status.type != SEG_STATUS_NONE)) {
if (lvdm->seg_status.type == SEG_STATUS_UNKNOWN)
repstr[8] = 'X'; /* Unknown */
else if (lvdm->seg_status.thin_pool->fail)
repstr[8] = 'F';
else if (lvdm->seg_status.thin_pool->out_of_data_space)
repstr[8] = 'D';
else if (lvdm->seg_status.thin_pool->read_only)
repstr[8] = 'M';
} else if (lv_is_thin_volume(lv) &&
(lvdm->seg_status.type != SEG_STATUS_NONE)) {
if (lvdm->seg_status.type == SEG_STATUS_UNKNOWN)
repstr[8] = 'X'; /* Unknown */
else if (lvdm->seg_status.thin->fail)
repstr[8] = 'F';
}
if (lv->status & LV_ACTIVATION_SKIP)
repstr[9] = 'k';
else
repstr[9] = '-';
out:
return repstr;
}
/* backward compatible internal API for lvm2api, TODO improve it */
char *lv_attr_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
char *ret = NULL;
struct lv_with_info_and_seg_status status = {
.seg_status.type = SEG_STATUS_NONE,
.lv = lv
};
if (!(status.seg_status.mem = dm_pool_create("reporter_pool", 1024)))
return_0;
if (!(status.info_ok = lv_info_with_seg_status(lv->vg->cmd, lv, first_seg(lv), 1, &status, 1, 1)))
goto_bad;
ret = lv_attr_dup_with_info_and_seg_status(mem, &status);
bad:
dm_pool_destroy(status.seg_status.mem);
return ret;
}
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;
}
static char *_time_dup(struct cmd_context *cmd, struct dm_pool *mem,
time_t ts, int iso_mode)
{
char buffer[4096];
struct tm *local_tm;
const char *format = iso_mode ? DEFAULT_TIME_FORMAT
: cmd->time_format;
if (!ts ||
!(local_tm = localtime(&ts)) ||
!strftime(buffer, sizeof(buffer), format, local_tm))
buffer[0] = 0;
return dm_pool_strdup(mem, buffer);
}
char *lv_creation_time_dup(struct dm_pool *mem, const struct logical_volume *lv, int iso_mode)
{
time_t ts = lv_is_historical(lv) ? (time_t) lv->this_glv->historical->timestamp
: (time_t) lv->timestamp;
return _time_dup(lv->vg->cmd, mem, ts, iso_mode);
}
char *lv_removal_time_dup(struct dm_pool *mem, const struct logical_volume *lv, int iso_mode)
{
time_t ts = lv_is_historical(lv) ? (time_t)lv->this_glv->historical->timestamp_removed
: (time_t)0;
return _time_dup(lv->vg->cmd, mem, ts, iso_mode);
}
char *lv_host_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
return dm_pool_strdup(mem, lv->hostname ? : "");
}
static int _lv_is_exclusive(struct logical_volume *lv)
{
struct lv_segment *seg;
/* Some seg types require exclusive activation */
/* FIXME Scan recursively */
dm_list_iterate_items(seg, &lv->segments)
if (seg_only_exclusive(seg))
return 1;
/* Origin has no seg type require exlusiveness */
return lv_is_origin(lv);
}
int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv,
enum activation_change activate, int needs_exclusive)
{
const char *ay_with_mode = NULL;
if (activate == CHANGE_ASY)
ay_with_mode = "sh";
if (activate == CHANGE_AEY)
ay_with_mode = "ex";
if (is_change_activating(activate) &&
!lockd_lv(cmd, lv, ay_with_mode, LDLV_PERSISTENT)) {
log_error("Failed to lock logical volume %s.", display_lvname(lv));
return 0;
}
switch (activate) {
case CHANGE_AN:
deactivate:
log_verbose("Deactivating logical volume %s.", display_lvname(lv));
if (!deactivate_lv(cmd, lv))
return_0;
break;
case CHANGE_ALN:
if (vg_is_clustered(lv->vg) && (needs_exclusive || _lv_is_exclusive(lv))) {
if (!lv_is_active_locally(lv)) {
log_error("Cannot deactivate remotely exclusive device %s locally.",
display_lvname(lv));
return 0;
}
/* Unlock whole exclusive activation */
goto deactivate;
}
log_verbose("Deactivating logical volume %s locally.",
display_lvname(lv));
if (!deactivate_lv_local(cmd, lv))
return_0;
break;
case CHANGE_ALY:
case CHANGE_AAY:
if (needs_exclusive || _lv_is_exclusive(lv)) {
log_verbose("Activating logical volume %s exclusively locally.",
display_lvname(lv));
if (!activate_lv_excl_local(cmd, lv))
return_0;
} else {
log_verbose("Activating logical volume %s locally.",
display_lvname(lv));
if (!activate_lv_local(cmd, lv))
return_0;
}
break;
case CHANGE_AEY:
exclusive:
log_verbose("Activating logical volume %s exclusively.",
display_lvname(lv));
if (!activate_lv_excl(cmd, lv))
return_0;
break;
case CHANGE_ASY:
case CHANGE_AY:
default:
if (needs_exclusive || _lv_is_exclusive(lv))
goto exclusive;
log_verbose("Activating logical volume %s.", display_lvname(lv));
if (!activate_lv(cmd, lv))
return_0;
}
if (!is_change_activating(activate) &&
!lockd_lv(cmd, lv, "un", LDLV_PERSISTENT))
log_error("Failed to unlock logical volume %s.", display_lvname(lv));
return 1;
}
char *lv_active_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
const char *s;
if (!activation()) {
s = "unknown";
goto out;
}
if (vg_is_clustered(lv->vg)) {
//const struct logical_volume *lvo = lv;
lv = lv_lock_holder(lv);
//log_debug("Holder for %s => %s.", lvo->name, lv->name);
}
if (!lv_is_active(lv))
s = ""; /* not active */
else if (!vg_is_clustered(lv->vg))
s = "active";
else if (lv_is_active_exclusive(lv))
/* exclusive cluster activation */
s = lv_is_active_exclusive_locally(lv) ?
"local exclusive" : "remote exclusive";
else /* locally active */
s = lv_is_active_but_not_locally(lv) ?
"remotely" : "locally";
out:
return dm_pool_strdup(mem, s);
}
char *lv_profile_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
const char *profile_name = lv->profile ? lv->profile->name : "";
return dm_pool_strdup(mem, profile_name);
}
char *lv_lock_args_dup(struct dm_pool *mem, const struct logical_volume *lv)
{
const char *lock_args = lv->lock_args ? lv->lock_args : "";
return dm_pool_strdup(mem, lock_args);
}
/* For given LV find recursively the LV which holds lock for it */
const struct logical_volume *lv_lock_holder(const struct logical_volume *lv)
{
const struct seg_list *sl;
if (lv_is_cow(lv))
return lv_lock_holder(origin_from_cow(lv));
if (lv_is_thin_pool(lv))
/* Find any active LV from the pool */
dm_list_iterate_items(sl, &lv->segs_using_this_lv)
if (lv_is_active(sl->seg->lv)) {
log_debug_activation("Thin volume %s is active.",
display_lvname(lv));
return sl->seg->lv;
}
/* RAID changes visibility of splitted LVs but references them still as leg/meta */
if ((lv_is_raid_image(lv) || lv_is_raid_metadata(lv)) && lv_is_visible(lv))
return lv;
/* For other types, by default look for the first user */
dm_list_iterate_items(sl, &lv->segs_using_this_lv) {
/* FIXME: complete this exception list */
if (lv_is_thin_volume(lv) &&
lv_is_thin_volume(sl->seg->lv) &&
first_seg(lv)->pool_lv == sl->seg->pool_lv)
continue; /* Skip thin snaphost */
if (lv_is_external_origin(lv) &&
lv_is_thin_volume(sl->seg->lv))
continue; /* Skip external origin */
if (lv_is_pending_delete(sl->seg->lv))
continue; /* Skip deleted LVs */
return lv_lock_holder(sl->seg->lv);
}
return lv;
}
struct profile *lv_config_profile(const struct logical_volume *lv)
{
return lv->profile ? : lv->vg->profile;
}
int lv_has_constant_stripes(struct logical_volume *lv)
{
uint32_t previous_area_count = 0;
struct lv_segment *seg;
dm_list_iterate_items(seg, &lv->segments) {
if (!seg_is_striped(seg))
return 0;
if (previous_area_count && previous_area_count != seg->area_count)
return 0;
previous_area_count = seg->area_count;
}
return 1;
}