/* * Copyright (C) 2008-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-exported.h" #include "lvm-string.h" #include "defaults.h" #include "segtype.h" #include "locking.h" #include "activate.h" #include "lvm_misc.h" #include "lvm2app.h" /* FIXME Improve all the log messages to include context. Which VG/LV as a minimum? */ struct lvm_lv_create_params { uint32_t magic; vg_t vg; struct lvcreate_params lvp; }; #define LV_CREATE_PARAMS_MAGIC 0xFEED0001 static int _lv_check_handle(const lv_t lv, const int vg_writeable) { if (!lv || !lv->vg || vg_read_error(lv->vg)) return -1; if (vg_writeable && !vg_check_write_mode(lv->vg)) return -1; return 0; } /* FIXME: have lib/report/report.c _disp function call lv_size()? */ uint64_t lvm_lv_get_size(const lv_t lv) { uint64_t rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = SECTOR_SIZE * lv_size(lv); restore_user_env(&e); return rc; } const char *lvm_lv_get_uuid(const lv_t lv) { const char *rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = lv_uuid_dup(lv->vg->vgmem, lv); restore_user_env(&e); return rc; } const char *lvm_lv_get_name(const lv_t lv) { const char *rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = dm_pool_strndup(lv->vg->vgmem, lv->name, NAME_LEN+1); restore_user_env(&e); return rc; } const char *lvm_lv_get_attr(const lv_t lv) { const char *rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = lv_attr_dup(lv->vg->vgmem, lv); restore_user_env(&e); return rc; } const char *lvm_lv_get_origin(const lv_t lv) { const char *rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = lv_origin_dup(lv->vg->vgmem, lv); restore_user_env(&e); return rc; } struct lvm_property_value lvm_lv_get_property(const lv_t lv, const char *name) { struct lvm_property_value rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = get_property(NULL, NULL, lv, NULL, NULL, NULL, NULL, name); restore_user_env(&e); return rc; } struct lvm_property_value lvm_lvseg_get_property(const lvseg_t lvseg, const char *name) { struct lvm_property_value rc; struct saved_env e = store_user_env(lvseg->lv->vg->cmd); rc = get_property(NULL, NULL, NULL, lvseg, NULL, NULL, NULL, name); restore_user_env(&e); return rc; } uint64_t lvm_lv_is_active(const lv_t lv) { uint64_t rc = 0; struct lvinfo info; struct saved_env e = store_user_env(lv->vg->cmd); if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists && info.live_table) rc = 1; restore_user_env(&e); return rc; } uint64_t lvm_lv_is_suspended(const lv_t lv) { uint64_t rc = 0; struct lvinfo info; struct saved_env e = store_user_env(lv->vg->cmd); if (lv_info(lv->vg->cmd, lv, 0, &info, 0, 0) && info.exists && info.suspended) rc = 1; restore_user_env(&e); return rc; } static int _lvm_lv_add_tag(lv_t lv, const char *tag) { if (_lv_check_handle(lv, 1)) return -1; if (!lv_change_tag(lv, tag, 1)) return -1; return 0; } int lvm_lv_add_tag(lv_t lv, const char *tag) { int rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = _lvm_lv_add_tag(lv, tag); restore_user_env(&e); return rc; } static int _lvm_lv_remove_tag(lv_t lv, const char *tag) { if (_lv_check_handle(lv, 1)) return -1; if (!lv_change_tag(lv, tag, 0)) return -1; return 0; } int lvm_lv_remove_tag(lv_t lv, const char *tag) { int rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = _lvm_lv_remove_tag(lv, tag); restore_user_env(&e); return rc; } struct dm_list *lvm_lv_get_tags(const lv_t lv) { struct dm_list *rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = tag_list_copy(lv->vg->vgmem, &lv->tags); restore_user_env(&e); return rc; } /* Set defaults for non-segment specific LV parameters */ static void _lv_set_default_params(struct lvcreate_params *lp, vg_t vg, const char *lvname, uint64_t extents) { lp->zero = 1; lp->wipe_signatures = 0; lp->major = -1; lp->minor = -1; lp->activate = CHANGE_AY; lp->lv_name = lvname; /* FIXME: check this for safety */ lp->pvh = &vg->pvs; lp->extents = extents; lp->permission = LVM_READ | LVM_WRITE; lp->read_ahead = DM_READ_AHEAD_NONE; lp->alloc = ALLOC_INHERIT; dm_list_init(&lp->tags); } static struct segment_type * _get_segtype(struct cmd_context *cmd) { struct segment_type *rc = get_segtype_from_string(cmd, SEG_TYPE_NAME_STRIPED); if (!rc) { log_error(INTERNAL_ERROR "Segtype striped not found."); } return rc; } /* Set default for linear segment specific LV parameters */ static int _lv_set_default_linear_params(struct cmd_context *cmd, struct lvcreate_params *lp) { if (!(lp->segtype = _get_segtype(cmd))) { return 0; } lp->stripes = 1; return 1; } /* * FIXME: This function should probably not commit to disk but require calling * lvm_vg_write. However, this appears to be non-trivial change until * lv_create_single is refactored by segtype. */ static lv_t _lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size) { struct lvcreate_params lp = { 0 }; uint64_t extents; struct logical_volume *lv; if (vg_read_error(vg)) return NULL; if (!vg_check_write_mode(vg)) return NULL; if (!(extents = extents_from_size(vg->cmd, size / SECTOR_SIZE, vg->extent_size))) { log_error("Unable to create LV without size."); return NULL; } _lv_set_default_params(&lp, vg, name, extents); if (!_lv_set_default_linear_params(vg->cmd, &lp)) return_NULL; if (!(lv = lv_create_single(vg, &lp))) return_NULL; return (lv_t) lv; } lv_t lvm_vg_create_lv_linear(vg_t vg, const char *name, uint64_t size) { lv_t rc; struct saved_env e = store_user_env(vg->cmd); rc = _lvm_vg_create_lv_linear(vg, name, size); restore_user_env(&e); return rc; } /* * FIXME: This function should probably not commit to disk but require calling * lvm_vg_write. */ static int _lvm_vg_remove_lv(lv_t lv) { if (!lv || !lv->vg || vg_read_error(lv->vg)) return -1; if (!vg_check_write_mode(lv->vg)) return -1; if (!lv_remove_single(lv->vg->cmd, lv, DONT_PROMPT, 0)) return -1; return 0; } int lvm_vg_remove_lv(lv_t lv) { int rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = _lvm_vg_remove_lv(lv); restore_user_env(&e); return rc; } static int _lvm_lv_activate(lv_t lv) { if (!lv || !lv->vg || vg_read_error(lv->vg) || !lv->vg->cmd) return -1; /* FIXME: handle pvmove stuff later */ if (lv_is_locked(lv)) { log_error("Unable to activate locked LV"); return -1; } /* FIXME: handle lvconvert stuff later */ if (lv_is_converting(lv)) { log_error("Unable to activate LV with in-progress lvconvert"); return -1; } if (lv_is_origin(lv) || lv_is_pvmove(lv) || seg_only_exclusive(first_seg(lv))) { log_verbose("Activating logical volume \"%s\" " "exclusively", lv->name); if (!activate_lv_excl(lv->vg->cmd, lv)) { /* FIXME Improve msg */ log_error("Activate exclusive failed."); return -1; } } else { log_verbose("Activating logical volume \"%s\"", lv->name); if (!activate_lv(lv->vg->cmd, lv)) { /* FIXME Improve msg */ log_error("Activate failed."); return -1; } } return 0; } int lvm_lv_activate(lv_t lv) { int rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = _lvm_lv_activate(lv); restore_user_env(&e); return rc; } static int _lvm_lv_deactivate(lv_t lv) { if (!lv || !lv->vg || vg_read_error(lv->vg) || !lv->vg->cmd) return -1; log_verbose("Deactivating logical volume \"%s\"", lv->name); if (!deactivate_lv(lv->vg->cmd, lv)) { log_error("Deactivate failed."); return -1; } return 0; } int lvm_lv_deactivate(lv_t lv) { int rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = _lvm_lv_deactivate(lv); restore_user_env(&e); return rc; } static struct dm_list *_lvm_lv_list_lvsegs(lv_t lv) { struct dm_list *list; lvseg_list_t *lvseg; struct lv_segment *lvl; if (dm_list_empty(&lv->segments)) return NULL; if (!(list = dm_pool_zalloc(lv->vg->vgmem, sizeof(*list)))) { log_errno(ENOMEM, "Memory allocation fail for dm_list."); return NULL; } dm_list_init(list); dm_list_iterate_items(lvl, &lv->segments) { if (!(lvseg = dm_pool_zalloc(lv->vg->vgmem, sizeof(*lvseg)))) { log_errno(ENOMEM, "Memory allocation fail for lvm_lvseg_list."); return NULL; } lvseg->lvseg = lvl; dm_list_add(list, &lvseg->list); } return list; } struct dm_list *lvm_lv_list_lvsegs(lv_t lv) { struct dm_list *rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = _lvm_lv_list_lvsegs(lv); restore_user_env(&e); return rc; } lv_t lvm_lv_from_name(vg_t vg, const char *name) { lv_t rc = NULL; struct lv_list *lvl; struct saved_env e = store_user_env(vg->cmd); dm_list_iterate_items(lvl, &vg->lvs) { if (!strcmp(name, lvl->lv->name)) { rc = lvl->lv; break; } } restore_user_env(&e); return rc; } static lv_t _lvm_lv_from_uuid(vg_t vg, const char *uuid) { struct lv_list *lvl; struct id id; if (strlen(uuid) < ID_LEN) { log_errno (EINVAL, "Invalid UUID string length"); return NULL; } if (!id_read_format(&id, uuid)) { log_errno(EINVAL, "Invalid UUID format."); return NULL; } dm_list_iterate_items(lvl, &vg->lvs) { if (id_equal(&vg->id, &lvl->lv->lvid.id[0]) && id_equal(&id, &lvl->lv->lvid.id[1])) return lvl->lv; } return NULL; } lv_t lvm_lv_from_uuid(vg_t vg, const char *uuid) { lv_t rc; struct saved_env e = store_user_env(vg->cmd); rc = _lvm_lv_from_uuid(vg, uuid); restore_user_env(&e); return rc; } int lvm_lv_rename(lv_t lv, const char *new_name) { int rc = 0; struct saved_env e = store_user_env(lv->vg->cmd); if (!lv_rename(lv->vg->cmd, lv, new_name)) { /* FIXME Improve msg */ log_error("LV rename failed."); rc = -1; } restore_user_env(&e); return rc; } int lvm_lv_resize(const lv_t lv, uint64_t new_size) { int rc = 0; struct lvresize_params lp = { .sign = SIGN_NONE, .percent = PERCENT_NONE, .resize = LV_ANY, .size = new_size >> SECTOR_SHIFT, .force = 1, /* Assume the user has a good backup? */ }; struct saved_env e = store_user_env(lv->vg->cmd); if (!lv_resize(lv, &lp, &lv->vg->pvs)) { /* FIXME Improve msg */ log_error("LV resize failed."); /* FIXME Define consistent symbolic return codes */ rc = -1; } restore_user_env(&e); return rc; } lv_t lvm_lv_snapshot(const lv_t lv, const char *snap_name, uint64_t max_snap_size) { lv_t rc = NULL; struct lvm_lv_create_params *lvcp = NULL; struct saved_env e = store_user_env(lv->vg->cmd); lvcp = lvm_lv_params_create_snapshot(lv, snap_name, max_snap_size); if (lvcp) { rc = lvm_lv_create(lvcp); } restore_user_env(&e); return rc; } /* Set defaults for thin pool specific LV parameters */ static int _lv_set_pool_params(struct lvcreate_params *lp, vg_t vg, const char *pool_name, uint64_t extents, uint64_t meta_size) { uint64_t pool_metadata_size; _lv_set_default_params(lp, vg, pool_name, extents); lp->create_pool = 1; lp->segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_THIN_POOL); lp->stripes = 1; if (!meta_size) { pool_metadata_size = extents * vg->extent_size / (lp->chunk_size * (SECTOR_SIZE / 64)); while ((pool_metadata_size > (DEFAULT_THIN_POOL_OPTIMAL_METADATA_SIZE * 2)) && lp->chunk_size < DM_THIN_MAX_DATA_BLOCK_SIZE) { lp->chunk_size <<= 1; pool_metadata_size >>= 1; } } else pool_metadata_size = meta_size; if (pool_metadata_size % vg->extent_size) pool_metadata_size += vg->extent_size - pool_metadata_size % vg->extent_size; if (!(lp->pool_metadata_extents = extents_from_size(vg->cmd, pool_metadata_size / SECTOR_SIZE, vg->extent_size))) return_0; return 1; } static lv_create_params_t _lvm_lv_params_create_thin_pool(vg_t vg, const char *pool_name, uint64_t size, uint32_t chunk_size, uint64_t meta_size, lvm_thin_discards_t discard) { uint64_t extents = 0; struct lvm_lv_create_params *lvcp = NULL; if (meta_size > (2 * DEFAULT_THIN_POOL_MAX_METADATA_SIZE)) { log_error("Invalid metadata size"); return NULL; } if (meta_size && meta_size < (2 * DEFAULT_THIN_POOL_MIN_METADATA_SIZE)) { log_error("Invalid metadata size"); return NULL; } if (vg_read_error(vg)) return NULL; if (!vg_check_write_mode(vg)) return NULL; if (pool_name == NULL || !strlen(pool_name)) { log_error("pool_name invalid"); return NULL; } if (!(extents = extents_from_size(vg->cmd, size / SECTOR_SIZE, vg->extent_size))) { log_error("Unable to create LV thin pool without size."); return NULL; } lvcp = dm_pool_zalloc(vg->vgmem, sizeof (struct lvm_lv_create_params)); if (lvcp) { lvcp->vg = vg; switch (discard) { case LVM_THIN_DISCARDS_IGNORE: lvcp->lvp.discards = THIN_DISCARDS_IGNORE; break; case LVM_THIN_DISCARDS_NO_PASSDOWN: lvcp->lvp.discards = THIN_DISCARDS_NO_PASSDOWN; break; case LVM_THIN_DISCARDS_PASSDOWN: lvcp->lvp.discards = THIN_DISCARDS_PASSDOWN; break; default: log_error("Invalid discard argument %d for thin pool creation.", discard); return NULL; } lvcp->lvp.zero_new_blocks = THIN_ZERO_YES; if (chunk_size) lvcp->lvp.chunk_size = chunk_size; else lvcp->lvp.chunk_size = DEFAULT_THIN_POOL_CHUNK_SIZE * 2; if (lvcp->lvp.chunk_size < DM_THIN_MIN_DATA_BLOCK_SIZE || lvcp->lvp.chunk_size > DM_THIN_MAX_DATA_BLOCK_SIZE) { log_error("Invalid chunk_size"); return NULL; } if (!_lv_set_pool_params(&lvcp->lvp, vg, pool_name, extents, meta_size)) return_NULL; lvcp->magic = LV_CREATE_PARAMS_MAGIC; } return lvcp; } lv_create_params_t lvm_lv_params_create_thin_pool(vg_t vg, const char *pool_name, uint64_t size, uint32_t chunk_size, uint64_t meta_size, lvm_thin_discards_t discard) { lv_create_params_t rc; struct saved_env e = store_user_env(vg->cmd); rc = _lvm_lv_params_create_thin_pool(vg, pool_name, size, chunk_size, meta_size, discard); restore_user_env(&e); return rc; } /* Set defaults for thin LV specific parameters */ static int _lv_set_thin_params(struct lvcreate_params *lp, vg_t vg, const char *pool_name, const char *lvname, uint32_t extents) { _lv_set_default_params(lp, vg, lvname, 0); lp->pool_name = pool_name; lp->segtype = get_segtype_from_string(vg->cmd, SEG_TYPE_NAME_THIN); lp->virtual_extents = extents; lp->stripes = 1; return 1; } static lv_create_params_t _lvm_lv_params_create_snapshot(const lv_t lv, const char *snap_name, uint64_t max_snap_size) { uint64_t size = 0; uint64_t extents = 0; struct lvm_lv_create_params *lvcp = NULL; if (vg_read_error(lv->vg)) { return NULL; } if (!vg_check_write_mode(lv->vg)) return NULL; if (snap_name == NULL || !strlen(snap_name)) { log_error("snap_name invalid"); return NULL; } if (max_snap_size) { size = max_snap_size >> SECTOR_SHIFT; if (!(extents = extents_from_size(lv->vg->cmd, size, lv->vg->extent_size))) return_NULL; } if (!size && !lv_is_thin_volume(lv) ) { log_error("Origin is not thin, specify size of snapshot"); return NULL; } lvcp = dm_pool_zalloc(lv->vg->vgmem, sizeof (struct lvm_lv_create_params)); if (lvcp) { lvcp->vg = lv->vg; _lv_set_default_params(&lvcp->lvp, lv->vg, snap_name, extents); if (size) { if (!(lvcp->lvp.segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_SNAPSHOT))) { log_error("Segtype snapshot not found."); return NULL; } lvcp->lvp.chunk_size = 8; lvcp->lvp.snapshot = 1; } else { if (!(lvcp->lvp.segtype = get_segtype_from_string(lv->vg->cmd, SEG_TYPE_NAME_THIN))) { log_error("Segtype thin not found."); return NULL; } lvcp->lvp.pool_name = first_seg(lv)->pool_lv->name; } lvcp->lvp.stripes = 1; lvcp->lvp.origin_name = lv->name; lvcp->magic = LV_CREATE_PARAMS_MAGIC; } return lvcp; } lv_create_params_t lvm_lv_params_create_snapshot(const lv_t lv, const char *snap_name, uint64_t max_snap_size) { lv_create_params_t rc; struct saved_env e = store_user_env(lv->vg->cmd); rc = _lvm_lv_params_create_snapshot(lv, snap_name, max_snap_size); restore_user_env(&e); return rc; } static lv_create_params_t _lvm_lv_params_create_thin(const vg_t vg, const char *pool_name, const char *lvname, uint64_t size) { struct lvm_lv_create_params *lvcp = NULL; uint32_t extents = 0; /* precondition checks */ if (vg_read_error(vg)) return NULL; if (!vg_check_write_mode(vg)) return NULL; if (pool_name == NULL || !strlen(pool_name)) { log_error("pool_name invalid"); return NULL; } if (lvname == NULL || !strlen(lvname)) { log_error("lvname invalid"); return NULL; } if (!(extents = extents_from_size(vg->cmd, size / SECTOR_SIZE, vg->extent_size))) { log_error("Unable to create thin LV without size."); return NULL; } lvcp = dm_pool_zalloc(vg->vgmem, sizeof (struct lvm_lv_create_params)); if (lvcp) { lvcp->vg = vg; if (!_lv_set_thin_params(&lvcp->lvp, vg, pool_name, lvname, extents)) return_NULL; lvcp->magic = LV_CREATE_PARAMS_MAGIC; } return lvcp; } lv_create_params_t lvm_lv_params_create_thin(const vg_t vg, const char *pool_name, const char *lvname, uint64_t size) { lv_create_params_t rc; struct saved_env e = store_user_env(vg->cmd); rc = _lvm_lv_params_create_thin(vg, pool_name, lvname, size); restore_user_env(&e); return rc; } struct lvm_property_value lvm_lv_params_get_property( const lv_create_params_t params, const char *name) { struct lvm_property_value rc = { .is_valid = 0 }; if (params && params->magic == LV_CREATE_PARAMS_MAGIC) { struct saved_env e = store_user_env(params->vg->cmd); rc = get_property(NULL, NULL, NULL, NULL, NULL, ¶ms->lvp, NULL, name); restore_user_env(&e); } else log_error("Invalid lv_create_params parameter"); return rc; } int lvm_lv_params_set_property(lv_create_params_t params, const char *name, struct lvm_property_value *prop) { int rc = -1; if (params && params->magic == LV_CREATE_PARAMS_MAGIC) { struct saved_env e = store_user_env(params->vg->cmd); rc = set_property(NULL, NULL, NULL, ¶ms->lvp, NULL, name, prop); restore_user_env(&e); } else log_error("Invalid lv_create_params parameter"); return rc; } static lv_t _lvm_lv_create(lv_create_params_t params) { struct lv_list *lvl = NULL; if (params && params->magic == LV_CREATE_PARAMS_MAGIC) { if (!params->lvp.segtype) { log_error("segtype parameter is NULL"); return_NULL; } if (!lv_create_single(params->vg, ¶ms->lvp)) return_NULL; /* * In some case we are making a thin pool so lv_name is not valid, but * pool is. */ if (!(lvl = find_lv_in_vg(params->vg, (params->lvp.lv_name) ? params->lvp.lv_name : params->lvp.pool_name))) return_NULL; return (lv_t) lvl->lv; } log_error("Invalid lv_create_params parameter"); return NULL; } lv_t lvm_lv_create(lv_create_params_t params) { lv_t rc; struct saved_env e = store_user_env(params->vg->cmd); rc = _lvm_lv_create(params); restore_user_env(&e); return rc; }