| /* |
| * Copyright (C) 1997-2004 Sistina Software, Inc. All rights reserved. |
| * Copyright (C) 2004-2006 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 "label.h" |
| #include "metadata.h" |
| #include "lvmcache.h" |
| #include "xlate.h" |
| #include "disk_rep.h" |
| #include "toolcontext.h" |
| |
| #include <assert.h> |
| |
| /* FIXME: memcpy might not be portable */ |
| #define CPIN_8(x, y, z) {memcpy((x), (y), (z));} |
| #define CPOUT_8(x, y, z) {memcpy((y), (x), (z));} |
| #define CPIN_16(x, y) {(x) = xlate16_be((y));} |
| #define CPOUT_16(x, y) {(y) = xlate16_be((x));} |
| #define CPIN_32(x, y) {(x) = xlate32_be((y));} |
| #define CPOUT_32(x, y) {(y) = xlate32_be((x));} |
| #define CPIN_64(x, y) {(x) = xlate64_be((y));} |
| #define CPOUT_64(x, y) {(y) = xlate64_be((x));} |
| |
| static int __read_pool_disk(const struct format_type *fmt, struct device *dev, |
| struct dm_pool *mem __attribute__((unused)), struct pool_list *pl, |
| const char *vg_name __attribute__((unused))) |
| { |
| char buf[512] __attribute__((aligned(8))); |
| |
| /* FIXME: Need to check the cache here first */ |
| if (!dev_read(dev, UINT64_C(0), 512, buf)) { |
| log_very_verbose("Failed to read PV data from %s", |
| dev_name(dev)); |
| return 0; |
| } |
| |
| if (!read_pool_label(pl, fmt->labeller, dev, buf, NULL)) |
| return_0; |
| |
| return 1; |
| } |
| |
| static void _add_pl_to_list(struct cmd_context *cmd, struct dm_list *head, struct pool_list *data) |
| { |
| struct pool_list *pl; |
| |
| dm_list_iterate_items(pl, head) { |
| if (id_equal(&data->pv_uuid, &pl->pv_uuid)) { |
| char uuid[ID_LEN + 7] __attribute__((aligned(8))); |
| |
| if (!id_write_format(&pl->pv_uuid, uuid, ID_LEN + 7)) |
| stack; |
| |
| if (!dev_subsystem_part_major(cmd->dev_types, data->dev)) { |
| log_very_verbose("Ignoring duplicate PV %s on " |
| "%s", uuid, |
| dev_name(data->dev)); |
| return; |
| } |
| log_very_verbose("Duplicate PV %s - using %s %s", |
| uuid, dev_subsystem_name(cmd->dev_types, data->dev), |
| dev_name(data->dev)); |
| dm_list_del(&pl->list); |
| break; |
| } |
| } |
| dm_list_add(head, &data->list); |
| } |
| |
| int read_pool_label(struct pool_list *pl, struct labeller *l, |
| struct device *dev, char *buf, struct label **label) |
| { |
| struct lvmcache_info *info; |
| struct id pvid; |
| struct id vgid; |
| char uuid[ID_LEN + 7] __attribute__((aligned(8))); |
| struct pool_disk *pd = &pl->pd; |
| |
| pool_label_in(pd, buf); |
| |
| get_pool_pv_uuid(&pvid, pd); |
| if (!id_write_format(&pvid, uuid, ID_LEN + 7)) |
| stack; |
| log_debug_metadata("Calculated uuid %s for %s", uuid, dev_name(dev)); |
| |
| get_pool_vg_uuid(&vgid, pd); |
| if (!id_write_format(&vgid, uuid, ID_LEN + 7)) |
| stack; |
| log_debug_metadata("Calculated uuid %s for %s", uuid, pd->pl_pool_name); |
| |
| if (!(info = lvmcache_add(l, (char *) &pvid, dev, pd->pl_pool_name, |
| (char *) &vgid, 0))) |
| return_0; |
| if (label) |
| *label = lvmcache_get_label(info); |
| |
| lvmcache_set_device_size(info, ((uint64_t)xlate32_be(pd->pl_blocks)) << SECTOR_SHIFT); |
| lvmcache_set_ext_version(info, 0); |
| lvmcache_set_ext_flags(info, 0); |
| lvmcache_del_mdas(info); |
| lvmcache_del_bas(info); |
| lvmcache_make_valid(info); |
| |
| pl->dev = dev; |
| pl->pv = NULL; |
| memcpy(&pl->pv_uuid, &pvid, sizeof(pvid)); |
| |
| return 1; |
| } |
| |
| /** |
| * pool_label_out - copies a pool_label_t into a char buffer |
| * @pl: ptr to a pool_label_t struct |
| * @buf: ptr to raw space where label info will be copied |
| * |
| * This function is important because it takes care of all of |
| * the endian issues when copying to disk. This way, when |
| * machines of different architectures are used, they will |
| * be able to interpret ondisk labels correctly. Always use |
| * this function before writing to disk. |
| */ |
| void pool_label_out(struct pool_disk *pl, void *buf) |
| { |
| struct pool_disk *bufpl = (struct pool_disk *) buf; |
| |
| CPOUT_64(pl->pl_magic, bufpl->pl_magic); |
| CPOUT_64(pl->pl_pool_id, bufpl->pl_pool_id); |
| CPOUT_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE); |
| CPOUT_32(pl->pl_version, bufpl->pl_version); |
| CPOUT_32(pl->pl_subpools, bufpl->pl_subpools); |
| CPOUT_32(pl->pl_sp_id, bufpl->pl_sp_id); |
| CPOUT_32(pl->pl_sp_devs, bufpl->pl_sp_devs); |
| CPOUT_32(pl->pl_sp_devid, bufpl->pl_sp_devid); |
| CPOUT_32(pl->pl_sp_type, bufpl->pl_sp_type); |
| CPOUT_64(pl->pl_blocks, bufpl->pl_blocks); |
| CPOUT_32(pl->pl_striping, bufpl->pl_striping); |
| CPOUT_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs); |
| CPOUT_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid); |
| CPOUT_32(pl->pl_sp_weight, bufpl->pl_sp_weight); |
| CPOUT_32(pl->pl_minor, bufpl->pl_minor); |
| CPOUT_32(pl->pl_padding, bufpl->pl_padding); |
| CPOUT_8(pl->pl_reserve, bufpl->pl_reserve, 184); |
| } |
| |
| /** |
| * pool_label_in - copies a char buffer into a pool_label_t |
| * @pl: ptr to a pool_label_t struct |
| * @buf: ptr to raw space where label info is copied from |
| * |
| * This function is important because it takes care of all of |
| * the endian issues when information from disk is about to be |
| * used. This way, when machines of different architectures |
| * are used, they will be able to interpret ondisk labels |
| * correctly. Always use this function before using labels that |
| * were read from disk. |
| */ |
| void pool_label_in(struct pool_disk *pl, void *buf) |
| { |
| struct pool_disk *bufpl = (struct pool_disk *) buf; |
| |
| CPIN_64(pl->pl_magic, bufpl->pl_magic); |
| CPIN_64(pl->pl_pool_id, bufpl->pl_pool_id); |
| CPIN_8(pl->pl_pool_name, bufpl->pl_pool_name, POOL_NAME_SIZE); |
| CPIN_32(pl->pl_version, bufpl->pl_version); |
| CPIN_32(pl->pl_subpools, bufpl->pl_subpools); |
| CPIN_32(pl->pl_sp_id, bufpl->pl_sp_id); |
| CPIN_32(pl->pl_sp_devs, bufpl->pl_sp_devs); |
| CPIN_32(pl->pl_sp_devid, bufpl->pl_sp_devid); |
| CPIN_32(pl->pl_sp_type, bufpl->pl_sp_type); |
| CPIN_64(pl->pl_blocks, bufpl->pl_blocks); |
| CPIN_32(pl->pl_striping, bufpl->pl_striping); |
| CPIN_32(pl->pl_sp_dmepdevs, bufpl->pl_sp_dmepdevs); |
| CPIN_32(pl->pl_sp_dmepid, bufpl->pl_sp_dmepid); |
| CPIN_32(pl->pl_sp_weight, bufpl->pl_sp_weight); |
| CPIN_32(pl->pl_minor, bufpl->pl_minor); |
| CPIN_32(pl->pl_padding, bufpl->pl_padding); |
| CPIN_8(pl->pl_reserve, bufpl->pl_reserve, 184); |
| } |
| |
| static char _calc_char(unsigned int id) |
| { |
| /* |
| * [0-9A-Za-z!#] - 64 printable chars (6-bits) |
| */ |
| |
| if (id < 10) |
| return id + 48; |
| if (id < 36) |
| return (id - 10) + 65; |
| if (id < 62) |
| return (id - 36) + 97; |
| if (id == 62) |
| return '!'; |
| if (id == 63) |
| return '#'; |
| |
| return '%'; |
| } |
| |
| void get_pool_uuid(char *uuid, uint64_t poolid, uint32_t spid, uint32_t devid) |
| { |
| int i; |
| unsigned shifter = 0x003F; |
| |
| assert(ID_LEN == 32); |
| memset(uuid, 0, ID_LEN); |
| strcat(uuid, "POOL0000000000"); |
| |
| /* We grab the entire 64 bits (+2 that get shifted in) */ |
| for (i = 13; i < 24; i++) { |
| uuid[i] = _calc_char(((unsigned) poolid) & shifter); |
| poolid = poolid >> 6; |
| } |
| |
| /* We grab the entire 32 bits (+4 that get shifted in) */ |
| for (i = 24; i < 30; i++) { |
| uuid[i] = _calc_char((unsigned) (spid & shifter)); |
| spid = spid >> 6; |
| } |
| |
| /* |
| * Since we can only have 128 devices, we only worry about the |
| * last 12 bits |
| */ |
| for (i = 30; i < 32; i++) { |
| uuid[i] = _calc_char((unsigned) (devid & shifter)); |
| devid = devid >> 6; |
| } |
| |
| } |
| |
| struct _read_pool_pv_baton { |
| const struct format_type *fmt; |
| struct dm_pool *mem, *tmpmem; |
| struct pool_list *pl; |
| struct dm_list *head; |
| const char *vgname; |
| uint32_t *sp_devs; |
| uint32_t sp_count; |
| int failed; |
| int empty; |
| }; |
| |
| static int _read_pool_pv(struct lvmcache_info *info, void *baton) |
| { |
| struct _read_pool_pv_baton *b = baton; |
| |
| b->empty = 0; |
| |
| if (lvmcache_device(info) && |
| !(b->pl = read_pool_disk(b->fmt, lvmcache_device(info), b->mem, b->vgname))) |
| return 0; |
| |
| /* |
| * We need to keep track of the total expected number |
| * of devices per subpool |
| */ |
| if (!b->sp_count) { |
| /* FIXME pl left uninitialised if !info->dev */ |
| if (!b->pl) { |
| log_error(INTERNAL_ERROR "device is missing"); |
| dm_pool_destroy(b->tmpmem); |
| b->failed = 1; |
| return 0; |
| } |
| b->sp_count = b->pl->pd.pl_subpools; |
| if (!(b->sp_devs = |
| dm_pool_zalloc(b->tmpmem, |
| sizeof(uint32_t) * b->sp_count))) { |
| log_error("Unable to allocate %d 32-bit uints", |
| b->sp_count); |
| dm_pool_destroy(b->tmpmem); |
| b->failed = 1; |
| return 0; |
| } |
| } |
| |
| /* |
| * watch out for a pool label with a different subpool |
| * count than the original - give up if it does |
| */ |
| if (b->sp_count != b->pl->pd.pl_subpools) |
| return 0; |
| |
| _add_pl_to_list(lvmcache_fmt(info)->cmd, b->head, b->pl); |
| |
| if (b->sp_count > b->pl->pd.pl_sp_id && b->sp_devs[b->pl->pd.pl_sp_id] == 0) |
| b->sp_devs[b->pl->pd.pl_sp_id] = b->pl->pd.pl_sp_devs; |
| |
| return 1; |
| } |
| |
| static int _read_vg_pds(struct _read_pool_pv_baton *b, |
| struct lvmcache_vginfo *vginfo, |
| uint32_t *devcount) |
| { |
| uint32_t i; |
| |
| b->sp_count = 0; |
| b->sp_devs = NULL; |
| b->failed = 0; |
| b->pl = NULL; |
| |
| /* FIXME: maybe should return a different error in memory |
| * allocation failure */ |
| if (!(b->tmpmem = dm_pool_create("pool read_vg", 512))) |
| return_0; |
| |
| lvmcache_foreach_pv(vginfo, _read_pool_pv, b); |
| |
| *devcount = 0; |
| for (i = 0; i < b->sp_count; i++) |
| *devcount += b->sp_devs[i]; |
| |
| dm_pool_destroy(b->tmpmem); |
| |
| if (b->pl && *b->pl->pd.pl_pool_name) |
| return 1; |
| |
| return 0; |
| |
| } |
| |
| int read_pool_pds(const struct format_type *fmt, const char *vg_name, |
| struct dm_pool *mem, struct dm_list *pdhead) |
| { |
| struct lvmcache_vginfo *vginfo; |
| uint32_t totaldevs; |
| int full_scan = -1; |
| |
| struct _read_pool_pv_baton baton; |
| |
| baton.vgname = vg_name; |
| baton.mem = mem; |
| baton.fmt = fmt; |
| baton.head = pdhead; |
| baton.empty = 1; |
| |
| do { |
| /* |
| * If the cache scanning doesn't work, this will never work |
| */ |
| if (vg_name && (vginfo = lvmcache_vginfo_from_vgname(vg_name, NULL)) && |
| _read_vg_pds(&baton, vginfo, &totaldevs) && !baton.empty) |
| { |
| /* |
| * If we found all the devices we were expecting, return |
| * success |
| */ |
| if (dm_list_size(pdhead) == totaldevs) |
| return 1; |
| |
| /* |
| * accept partial pool if we've done a full rescan of |
| * the cache |
| */ |
| if (full_scan > 0) |
| return 1; |
| } |
| |
| /* Failed */ |
| dm_list_init(pdhead); |
| |
| full_scan++; |
| if (full_scan > 1) { |
| log_debug_metadata("No devices for vg %s found in cache", |
| vg_name); |
| return 0; |
| } |
| if (full_scan > 0) |
| lvmcache_force_next_label_scan(); |
| lvmcache_label_scan(fmt->cmd); |
| |
| } while (1); |
| |
| } |
| |
| struct pool_list *read_pool_disk(const struct format_type *fmt, |
| struct device *dev, struct dm_pool *mem, |
| const char *vg_name) |
| { |
| struct pool_list *pl; |
| |
| if (!dev_open_readonly(dev)) |
| return_NULL; |
| |
| if (!(pl = dm_pool_zalloc(mem, sizeof(*pl)))) { |
| log_error("Unable to allocate pool list structure"); |
| return 0; |
| } |
| |
| if (!__read_pool_disk(fmt, dev, mem, pl, vg_name)) |
| return_NULL; |
| |
| if (!dev_close(dev)) |
| stack; |
| |
| return pl; |
| |
| } |