/* $NetBSD: label.c,v 1.1.1.2 2009/12/02 00:26:32 haad Exp $ */ /* * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved. * Copyright (C) 2004-2007 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "lib.h" #include "label.h" #include "crc.h" #include "xlate.h" #include "lvmcache.h" #include "metadata.h" #include #include #include /* FIXME Allow for larger labels? Restricted to single sector currently */ /* * 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; } static void _free_li(struct labeller_i *li) { dm_free(li); } int label_init(void) { dm_list_init(&_labellers); return 1; } void label_exit(void) { struct dm_list *c, *n; struct labeller_i *li; for (c = _labellers.n; c && c != &_labellers; c = n) { n = c->n; li = dm_list_item(c, struct labeller_i); li->l->ops->destroy(li->l); _free_li(li); } dm_list_init(&_labellers); } int label_register_handler(const char *name, struct labeller *handler) { struct labeller_i *li; if (!(li = _alloc_li(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 struct labeller *_find_labeller(struct device *dev, char *buf, uint64_t *label_sector, uint64_t scan_sector) { struct labeller_i *li; struct labeller *r = NULL; struct label_header *lh; struct lvmcache_info *info; uint64_t sector; int found = 0; char readbuf[LABEL_SCAN_SIZE] __attribute((aligned(8))); if (!dev_read(dev, scan_sector << SECTOR_SHIFT, LABEL_SCAN_SIZE, readbuf)) { log_debug("%s: Failed to read label area", dev_name(dev)); goto out; } /* 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 (found) { 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_info("%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, &lh->offset_xl, LABEL_SIZE - ((uintptr_t) &lh->offset_xl - (uintptr_t) lh)) != xlate32(lh->crc_xl)) { log_info("Label checksum incorrect on %s - " "ignoring", dev_name(dev)); continue; } if (found) 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", dev_name(dev), li->name); if (found) { log_error("Ignoring additional label " "on %s at sector %" PRIu64, dev_name(dev), sector + scan_sector); continue; } r = li->l; memcpy(buf, lh, LABEL_SIZE); if (label_sector) *label_sector = sector + scan_sector; found = 1; break; } } } out: if (!found) { if ((info = info_from_pvid(dev->pvid, 0))) lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name, info->fmt->orphan_vg_name, 0, NULL); log_very_verbose("%s: No label detected", dev_name(dev)); } return r; } /* FIXME Also wipe associated metadata area headers? */ int label_remove(struct device *dev) { char buf[LABEL_SIZE] __attribute((aligned(8))); char readbuf[LABEL_SCAN_SIZE] __attribute((aligned(8))); int r = 1; uint64_t sector; int wipe; struct labeller_i *li; struct label_header *lh; memset(buf, 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 (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) { log_debug("%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_info("%s: Wiping label at sector %" PRIu64, dev_name(dev), sector); if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE, buf)) { 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; } int label_read(struct device *dev, struct label **result, uint64_t scan_sector) { char buf[LABEL_SIZE] __attribute((aligned(8))); struct labeller *l; uint64_t sector; struct lvmcache_info *info; int r = 0; if ((info = info_from_pvid(dev->pvid, 1))) { log_debug("Using cached label for %s", dev_name(dev)); *result = info->label; return 1; } if (!dev_open(dev)) { stack; if ((info = info_from_pvid(dev->pvid, 0))) lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name, info->fmt->orphan_vg_name, 0, NULL); return r; } if (!(l = _find_labeller(dev, buf, §or, scan_sector))) goto out; if ((r = (l->ops->read)(l, dev, buf, result)) && result && *result) (*result)->sector = sector; out: if (!dev_close(dev)) stack; return r; } /* 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, &lh->offset_xl, LABEL_SIZE - ((uintptr_t) &lh->offset_xl - (uintptr_t) lh))); if (!dev_open(dev)) return_0; log_info("%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, buf)) { log_debug("Failed to write label to %s", dev_name(dev)); r = 0; } if (!dev_close(dev)) stack; return r; } /* Unused */ int label_verify(struct device *dev) { struct labeller *l; char buf[LABEL_SIZE] __attribute((aligned(8))); uint64_t sector; struct lvmcache_info *info; int r = 0; if (!dev_open(dev)) { if ((info = info_from_pvid(dev->pvid, 0))) lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name, info->fmt->orphan_vg_name, 0, NULL); return_0; } if (!(l = _find_labeller(dev, buf, §or, UINT64_C(0)))) goto out; r = l->ops->verify ? l->ops->verify(l, buf, sector) : 1; out: 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_malloc(sizeof(*label)))) { log_error("label allocaction failed"); return NULL; } memset(label, 0, sizeof(*label)); label->labeller = labeller; labeller->ops->initialise_label(labeller, label); return label; }