1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-22 17:35:59 +03:00
lvm2/lib/label/label.c
2018-02-08 20:19:21 +00:00

450 lines
11 KiB
C

/*
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2018 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 "crc.h"
#include "xlate.h"
#include "lvmcache.h"
#include "lvmetad.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* FIXME Allow for larger labels? Restricted to single sector currently */
static struct dm_pool *_labeller_mem;
/*
* Internal labeller struct.
*/
struct labeller_i {
struct dm_list list;
struct labeller *l;
char name[0];
};
static struct dm_list _labellers;
static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
{
struct labeller_i *li;
size_t len;
len = sizeof(*li) + strlen(name) + 1;
if (!(li = dm_malloc(len))) {
log_error("Couldn't allocate memory for labeller list object.");
return NULL;
}
li->l = l;
strcpy(li->name, name);
return li;
}
int label_init(void)
{
if (!(_labeller_mem = dm_pool_create("label scan", 128))) {
log_error("Labeller pool creation failed.");
return 0;
}
dm_list_init(&_labellers);
return 1;
}
void label_exit(void)
{
struct labeller_i *li, *tli;
dm_list_iterate_items_safe(li, tli, &_labellers) {
dm_list_del(&li->list);
li->l->ops->destroy(li->l);
dm_free(li);
}
dm_list_init(&_labellers);
dm_pool_destroy(_labeller_mem);
}
int label_register_handler(struct labeller *handler)
{
struct labeller_i *li;
if (!(li = _alloc_li(handler->fmt->name, handler)))
return_0;
dm_list_add(&_labellers, &li->list);
return 1;
}
struct labeller *label_get_handler(const char *name)
{
struct labeller_i *li;
dm_list_iterate_items(li, &_labellers)
if (!strcmp(li->name, name))
return li->l;
return NULL;
}
static void _update_lvmcache_orphan(struct lvmcache_info *info)
{
struct lvmcache_vgsummary vgsummary_orphan = {
.vgname = lvmcache_fmt(info)->orphan_vg_name,
};
memcpy(&vgsummary_orphan.vgid, lvmcache_fmt(info)->orphan_vg_name, strlen(lvmcache_fmt(info)->orphan_vg_name));
if (!lvmcache_update_vgname_and_id(info, &vgsummary_orphan))
stack;
}
struct find_labeller_params {
struct device *dev;
uint64_t scan_sector; /* Sector to be scanned */
uint64_t label_sector; /* Sector where label found */
lvm_callback_fn_t process_label_data_fn;
void *process_label_data_context;
struct label **result;
int ret;
};
static void _set_label_read_result(int failed, unsigned ioflags, void *context, const void *data)
{
struct find_labeller_params *flp = context;
struct label **result = flp->result;
struct label *label = (struct label *) data;
if (failed) {
flp->ret = 0;
goto_out;
}
/* Fix up device and label sector which the low-level code doesn't set */
if (label) {
label->dev = flp->dev;
label->sector = flp->label_sector;
}
if (result)
*result = (struct label *) label;
out:
if (!dev_close(flp->dev))
stack;
if (flp->process_label_data_fn) {
log_debug_io("Completed label reading for %s", dev_name(flp->dev));
flp->process_label_data_fn(!flp->ret, ioflags, flp->process_label_data_context, NULL);
}
}
static void _find_labeller(int failed, unsigned ioflags, void *context, const void *data)
{
struct find_labeller_params *flp = context;
const char *readbuf = data;
struct device *dev = flp->dev;
uint64_t scan_sector = flp->scan_sector;
char labelbuf[LABEL_SIZE] __attribute__((aligned(8)));
struct labeller_i *li;
struct labeller *l = NULL; /* Set when a labeller claims the label */
const struct label_header *lh;
struct lvmcache_info *info;
uint64_t sector;
if (failed) {
log_debug_devs("%s: Failed to read label area", dev_name(dev));
_set_label_read_result(1, ioflags, flp, NULL);
return;
}
/* Scan a few sectors for a valid label */
for (sector = 0; sector < LABEL_SCAN_SECTORS;
sector += LABEL_SIZE >> SECTOR_SHIFT) {
lh = (struct label_header *) (readbuf + (sector << SECTOR_SHIFT));
if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
if (l) {
log_error("Ignoring additional label on %s at "
"sector %" PRIu64, dev_name(dev),
sector + scan_sector);
}
if (xlate64(lh->sector_xl) != sector + scan_sector) {
log_very_verbose("%s: Label for sector %" PRIu64
" found at sector %" PRIu64
" - ignoring", dev_name(dev),
(uint64_t)xlate64(lh->sector_xl),
sector + scan_sector);
continue;
}
if (calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
((uint8_t *) &lh->offset_xl - (uint8_t *) lh)) !=
xlate32(lh->crc_xl)) {
log_very_verbose("Label checksum incorrect on %s - "
"ignoring", dev_name(dev));
continue;
}
if (l)
continue;
}
dm_list_iterate_items(li, &_labellers) {
if (li->l->ops->can_handle(li->l, (char *) lh,
sector + scan_sector)) {
log_very_verbose("%s: %s label detected at "
"sector %" PRIu64,
dev_name(dev), li->name,
sector + scan_sector);
if (l) {
log_error("Ignoring additional label "
"on %s at sector %" PRIu64,
dev_name(dev),
sector + scan_sector);
continue;
}
memcpy(labelbuf, lh, LABEL_SIZE);
flp->label_sector = sector + scan_sector;
l = li->l;
break;
}
}
}
if (!l) {
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
_update_lvmcache_orphan(info);
log_very_verbose("%s: No label detected", dev_name(dev));
flp->ret = 0;
_set_label_read_result(1, ioflags, flp, NULL);
} else
(void) (l->ops->read)(l, dev, labelbuf, ioflags, &_set_label_read_result, flp);
}
/* FIXME Also wipe associated metadata area headers? */
int label_remove(struct device *dev)
{
char labelbuf[LABEL_SIZE] __attribute__((aligned(8)));
int r = 1;
uint64_t sector;
int wipe;
struct labeller_i *li;
struct label_header *lh;
struct lvmcache_info *info;
const char *readbuf = NULL;
memset(labelbuf, 0, LABEL_SIZE);
log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev));
if (!dev_open(dev))
return_0;
/*
* We flush the device just in case someone is stupid
* enough to be trying to import an open pv into lvm.
*/
dev_flush(dev);
if (!(readbuf = dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, DEV_IO_LABEL))) {
log_debug_devs("%s: Failed to read label area", dev_name(dev));
goto out;
}
/* Scan first few sectors for anything looking like a label */
for (sector = 0; sector < LABEL_SCAN_SECTORS;
sector += LABEL_SIZE >> SECTOR_SHIFT) {
lh = (struct label_header *) (readbuf + (sector << SECTOR_SHIFT));
wipe = 0;
if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
if (xlate64(lh->sector_xl) == sector)
wipe = 1;
} else {
dm_list_iterate_items(li, &_labellers) {
if (li->l->ops->can_handle(li->l, (char *) lh,
sector)) {
wipe = 1;
break;
}
}
}
if (wipe) {
log_very_verbose("%s: Wiping label at sector %" PRIu64,
dev_name(dev), sector);
if (dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE, DEV_IO_LABEL, labelbuf)) {
/* Also remove the PV record from cache. */
info = lvmcache_info_from_pvid(dev->pvid, dev, 0);
if (info)
lvmcache_del(info);
} else {
log_error("Failed to remove label from %s at "
"sector %" PRIu64, dev_name(dev),
sector);
r = 0;
}
}
}
out:
if (!dev_close(dev))
stack;
return r;
}
static int _label_read(struct device *dev, uint64_t scan_sector, struct label **result,
unsigned ioflags, lvm_callback_fn_t process_label_data_fn, void *process_label_data_context)
{
struct lvmcache_info *info;
struct find_labeller_params *flp;
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 1))) {
log_debug_devs("Reading label from lvmcache for %s", dev_name(dev));
if (result)
*result = lvmcache_get_label(info);
if (process_label_data_fn) {
log_debug_io("Completed label reading for %s", dev_name(dev));
process_label_data_fn(0, ioflags, process_label_data_context, NULL);
}
return 1;
}
if (!(flp = dm_pool_zalloc(_labeller_mem, sizeof *flp))) {
log_error("find_labeller_params allocation failed.");
return 0;
}
flp->dev = dev;
flp->scan_sector = scan_sector;
flp->result = result;
flp->process_label_data_fn = process_label_data_fn;
flp->process_label_data_context = process_label_data_context;
flp->ret = 1;
/* Ensure result is always wiped as a precaution */
if (result)
*result = NULL;
log_debug_devs("Reading label from device %s", dev_name(dev));
if (!dev_open_readonly(dev)) {
stack;
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
_update_lvmcache_orphan(info);
return 0;
}
dev_read_callback(dev, scan_sector << SECTOR_SHIFT, LABEL_SCAN_SIZE, DEV_IO_LABEL, ioflags, _find_labeller, flp);
if (process_label_data_fn)
return 1;
else
return flp->ret;
}
/* result may be NULL if caller doesn't need it */
int label_read(struct device *dev, struct label **result, uint64_t scan_sector)
{
return _label_read(dev, scan_sector, result, 0, NULL, NULL);
}
int label_read_callback(struct device *dev, uint64_t scan_sector, unsigned ioflags,
lvm_callback_fn_t process_label_data_fn, void *process_label_data_context)
{
return _label_read(dev, scan_sector, NULL, ioflags, process_label_data_fn, process_label_data_context);
}
/* Caller may need to use label_get_handler to create label struct! */
int label_write(struct device *dev, struct label *label)
{
char buf[LABEL_SIZE] __attribute__((aligned(8)));
struct label_header *lh = (struct label_header *) buf;
int r = 1;
if (!label->labeller->ops->write) {
log_error("Label handler does not support label writes");
return 0;
}
if ((LABEL_SIZE + (label->sector << SECTOR_SHIFT)) > LABEL_SCAN_SIZE) {
log_error("Label sector %" PRIu64 " beyond range (%ld)",
label->sector, LABEL_SCAN_SECTORS);
return 0;
}
memset(buf, 0, LABEL_SIZE);
strncpy((char *)lh->id, LABEL_ID, sizeof(lh->id));
lh->sector_xl = xlate64(label->sector);
lh->offset_xl = xlate32(sizeof(*lh));
if (!(label->labeller->ops->write)(label, buf))
return_0;
lh->crc_xl = xlate32(calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
((uint8_t *) &lh->offset_xl - (uint8_t *) lh)));
if (!dev_open(dev))
return_0;
log_very_verbose("%s: Writing label to sector %" PRIu64 " with stored offset %"
PRIu32 ".", dev_name(dev), label->sector,
xlate32(lh->offset_xl));
if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, DEV_IO_LABEL, buf)) {
log_debug_devs("Failed to write label to %s", dev_name(dev));
r = 0;
}
if (!dev_close(dev))
stack;
return r;
}
void label_destroy(struct label *label)
{
label->labeller->ops->destroy_label(label->labeller, label);
dm_free(label);
}
struct label *label_create(struct labeller *labeller)
{
struct label *label;
if (!(label = dm_zalloc(sizeof(*label)))) {
log_error("label allocaction failed");
return NULL;
}
label->labeller = labeller;
labeller->ops->initialise_label(labeller, label);
return label;
}