| /* |
| * 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 "import-export.h" |
| #include "lvm-string.h" |
| |
| /* |
| * Bitsets held in the 'status' flags get |
| * converted into arrays of strings. |
| */ |
| struct flag { |
| const uint64_t mask; |
| const char *description; |
| int kind; |
| }; |
| |
| static const struct flag _vg_flags[] = { |
| {EXPORTED_VG, "EXPORTED", STATUS_FLAG}, |
| {RESIZEABLE_VG, "RESIZEABLE", STATUS_FLAG}, |
| {PVMOVE, "PVMOVE", STATUS_FLAG}, |
| {LVM_READ, "READ", STATUS_FLAG}, |
| {LVM_WRITE, "WRITE", STATUS_FLAG}, |
| {LVM_WRITE_LOCKED, "WRITE_LOCKED", COMPATIBLE_FLAG}, |
| {CLUSTERED, "CLUSTERED", STATUS_FLAG}, |
| {SHARED, "SHARED", STATUS_FLAG}, |
| {PARTIAL_VG, NULL, 0}, |
| {PRECOMMITTED, NULL, 0}, |
| {ARCHIVED_VG, NULL, 0}, |
| {0, NULL, 0} |
| }; |
| |
| static const struct flag _pv_flags[] = { |
| {ALLOCATABLE_PV, "ALLOCATABLE", STATUS_FLAG}, |
| {EXPORTED_VG, "EXPORTED", STATUS_FLAG}, |
| {MISSING_PV, "MISSING", COMPATIBLE_FLAG}, |
| {UNLABELLED_PV, NULL, 0}, |
| {0, NULL, 0} |
| }; |
| |
| static const struct flag _lv_flags[] = { |
| {LVM_READ, "READ", STATUS_FLAG}, |
| {LVM_WRITE, "WRITE", STATUS_FLAG}, |
| {LVM_WRITE_LOCKED, "WRITE_LOCKED", COMPATIBLE_FLAG}, |
| {FIXED_MINOR, "FIXED_MINOR", STATUS_FLAG}, |
| {VISIBLE_LV, "VISIBLE", STATUS_FLAG}, |
| {PVMOVE, "PVMOVE", STATUS_FLAG}, |
| {LOCKED, "LOCKED", STATUS_FLAG}, |
| {LV_NOTSYNCED, "NOTSYNCED", STATUS_FLAG}, |
| {LV_REBUILD, "REBUILD", STATUS_FLAG}, |
| {LV_WRITEMOSTLY, "WRITEMOSTLY", STATUS_FLAG}, |
| {LV_ACTIVATION_SKIP, "ACTIVATION_SKIP", COMPATIBLE_FLAG}, |
| {LV_ERROR_WHEN_FULL, "ERROR_WHEN_FULL", COMPATIBLE_FLAG}, |
| {LV_NOSCAN, NULL, 0}, |
| {LV_TEMPORARY, NULL, 0}, |
| {POOL_METADATA_SPARE, NULL, 0}, |
| {LOCKD_SANLOCK_LV, NULL, 0}, |
| {RAID, NULL, 0}, |
| {RAID_META, NULL, 0}, |
| {RAID_IMAGE, NULL, 0}, |
| {MIRROR, NULL, 0}, |
| {MIRROR_IMAGE, NULL, 0}, |
| {MIRROR_LOG, NULL, 0}, |
| {MIRRORED, NULL, 0}, |
| {VIRTUAL, NULL, 0}, |
| {SNAPSHOT, NULL, 0}, |
| {MERGING, NULL, 0}, |
| {CONVERTING, NULL, 0}, |
| {PARTIAL_LV, NULL, 0}, |
| {POSTORDER_FLAG, NULL, 0}, |
| {VIRTUAL_ORIGIN, NULL, 0}, |
| {REPLICATOR, NULL, 0}, |
| {REPLICATOR_LOG, NULL, 0}, |
| {THIN_VOLUME, NULL, 0}, |
| {THIN_POOL, NULL, 0}, |
| {THIN_POOL_DATA, NULL, 0}, |
| {THIN_POOL_METADATA, NULL, 0}, |
| {CACHE, NULL, 0}, |
| {CACHE_POOL, NULL, 0}, |
| {CACHE_POOL_DATA, NULL, 0}, |
| {CACHE_POOL_METADATA, NULL, 0}, |
| {LV_PENDING_DELETE, NULL, 0}, /* FIXME Display like COMPATIBLE_FLAG */ |
| {LV_REMOVED, NULL, 0}, |
| {0, NULL, 0} |
| }; |
| |
| static const struct flag *_get_flags(int type) |
| { |
| switch (type & ~STATUS_FLAG) { |
| case VG_FLAGS: |
| return _vg_flags; |
| |
| case PV_FLAGS: |
| return _pv_flags; |
| |
| case LV_FLAGS: |
| return _lv_flags; |
| } |
| |
| log_error("Unknown flag set requested."); |
| return NULL; |
| } |
| |
| /* |
| * Converts a bitset to an array of string values, |
| * using one of the tables defined at the top of |
| * the file. |
| */ |
| int print_flags(uint64_t status, int type, char *buffer, size_t size) |
| { |
| int f, first = 1; |
| const struct flag *flags; |
| |
| if (!(flags = _get_flags(type))) |
| return_0; |
| |
| if (!emit_to_buffer(&buffer, &size, "[")) |
| return 0; |
| |
| for (f = 0; flags[f].mask; f++) { |
| if (status & flags[f].mask) { |
| status &= ~flags[f].mask; |
| |
| if ((type & STATUS_FLAG) != flags[f].kind) |
| continue; |
| |
| /* Internal-only flag? */ |
| if (!flags[f].description) |
| continue; |
| |
| if (!first) { |
| if (!emit_to_buffer(&buffer, &size, ", ")) |
| return 0; |
| } else |
| first = 0; |
| |
| if (!emit_to_buffer(&buffer, &size, "\"%s\"", |
| flags[f].description)) |
| return 0; |
| } |
| } |
| |
| if (!emit_to_buffer(&buffer, &size, "]")) |
| return 0; |
| |
| if (status) |
| log_warn(INTERNAL_ERROR "Metadata inconsistency: " |
| "Not all flags successfully exported."); |
| |
| return 1; |
| } |
| |
| int read_flags(uint64_t *status, int type, const struct dm_config_value *cv) |
| { |
| int f; |
| uint64_t s = UINT64_C(0); |
| const struct flag *flags; |
| |
| if (!(flags = _get_flags(type))) |
| return_0; |
| |
| if (cv->type == DM_CFG_EMPTY_ARRAY) |
| goto out; |
| |
| while (cv) { |
| if (cv->type != DM_CFG_STRING) { |
| log_error("Status value is not a string."); |
| return 0; |
| } |
| |
| for (f = 0; flags[f].description; f++) |
| if (!strcmp(flags[f].description, cv->v.str)) { |
| s |= flags[f].mask; |
| break; |
| } |
| |
| if (type == VG_FLAGS && !strcmp(cv->v.str, "PARTIAL")) { |
| /* |
| * Exception: We no longer write this flag out, but it |
| * might be encountered in old backup files, so restore |
| * it in that case. It is never part of live metadata |
| * though, so only vgcfgrestore needs to be concerned |
| * by this case. |
| */ |
| s |= PARTIAL_VG; |
| } else if (!flags[f].description && (type & STATUS_FLAG)) { |
| log_error("Unknown status flag '%s'.", cv->v.str); |
| return 0; |
| } |
| |
| cv = cv->next; |
| } |
| |
| out: |
| *status |= s; |
| return 1; |
| } |