Home | History | Annotate | Line # | Download | only in label
      1 /*	$NetBSD: label.c,v 1.1.1.2 2009/12/02 00:26:32 haad Exp $	*/
      2 
      3 /*
      4  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
      5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
      6  *
      7  * This file is part of LVM2.
      8  *
      9  * This copyrighted material is made available to anyone wishing to use,
     10  * modify, copy, or redistribute it subject to the terms and conditions
     11  * of the GNU Lesser General Public License v.2.1.
     12  *
     13  * You should have received a copy of the GNU Lesser General Public License
     14  * along with this program; if not, write to the Free Software Foundation,
     15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     16  */
     17 
     18 #include "lib.h"
     19 #include "label.h"
     20 #include "crc.h"
     21 #include "xlate.h"
     22 #include "lvmcache.h"
     23 #include "metadata.h"
     24 
     25 #include <sys/stat.h>
     26 #include <fcntl.h>
     27 #include <unistd.h>
     28 
     29 /* FIXME Allow for larger labels?  Restricted to single sector currently */
     30 
     31 /*
     32  * Internal labeller struct.
     33  */
     34 struct labeller_i {
     35 	struct dm_list list;
     36 
     37 	struct labeller *l;
     38 	char name[0];
     39 };
     40 
     41 static struct dm_list _labellers;
     42 
     43 static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
     44 {
     45 	struct labeller_i *li;
     46 	size_t len;
     47 
     48 	len = sizeof(*li) + strlen(name) + 1;
     49 
     50 	if (!(li = dm_malloc(len))) {
     51 		log_error("Couldn't allocate memory for labeller list object.");
     52 		return NULL;
     53 	}
     54 
     55 	li->l = l;
     56 	strcpy(li->name, name);
     57 
     58 	return li;
     59 }
     60 
     61 static void _free_li(struct labeller_i *li)
     62 {
     63 	dm_free(li);
     64 }
     65 
     66 int label_init(void)
     67 {
     68 	dm_list_init(&_labellers);
     69 	return 1;
     70 }
     71 
     72 void label_exit(void)
     73 {
     74 	struct dm_list *c, *n;
     75 	struct labeller_i *li;
     76 
     77 	for (c = _labellers.n; c && c != &_labellers; c = n) {
     78 		n = c->n;
     79 		li = dm_list_item(c, struct labeller_i);
     80 		li->l->ops->destroy(li->l);
     81 		_free_li(li);
     82 	}
     83 
     84 	dm_list_init(&_labellers);
     85 }
     86 
     87 int label_register_handler(const char *name, struct labeller *handler)
     88 {
     89 	struct labeller_i *li;
     90 
     91 	if (!(li = _alloc_li(name, handler)))
     92 		return_0;
     93 
     94 	dm_list_add(&_labellers, &li->list);
     95 	return 1;
     96 }
     97 
     98 struct labeller *label_get_handler(const char *name)
     99 {
    100 	struct labeller_i *li;
    101 
    102 	dm_list_iterate_items(li, &_labellers)
    103 		if (!strcmp(li->name, name))
    104 			return li->l;
    105 
    106 	return NULL;
    107 }
    108 
    109 static struct labeller *_find_labeller(struct device *dev, char *buf,
    110 				       uint64_t *label_sector,
    111 				       uint64_t scan_sector)
    112 {
    113 	struct labeller_i *li;
    114 	struct labeller *r = NULL;
    115 	struct label_header *lh;
    116 	struct lvmcache_info *info;
    117 	uint64_t sector;
    118 	int found = 0;
    119 	char readbuf[LABEL_SCAN_SIZE] __attribute((aligned(8)));
    120 
    121 	if (!dev_read(dev, scan_sector << SECTOR_SHIFT,
    122 		      LABEL_SCAN_SIZE, readbuf)) {
    123 		log_debug("%s: Failed to read label area", dev_name(dev));
    124 		goto out;
    125 	}
    126 
    127 	/* Scan a few sectors for a valid label */
    128 	for (sector = 0; sector < LABEL_SCAN_SECTORS;
    129 	     sector += LABEL_SIZE >> SECTOR_SHIFT) {
    130 		lh = (struct label_header *) (readbuf +
    131 					      (sector << SECTOR_SHIFT));
    132 
    133 		if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
    134 			if (found) {
    135 				log_error("Ignoring additional label on %s at "
    136 					  "sector %" PRIu64, dev_name(dev),
    137 					  sector + scan_sector);
    138 			}
    139 			if (xlate64(lh->sector_xl) != sector + scan_sector) {
    140 				log_info("%s: Label for sector %" PRIu64
    141 					 " found at sector %" PRIu64
    142 					 " - ignoring", dev_name(dev),
    143 					 (uint64_t)xlate64(lh->sector_xl),
    144 					 sector + scan_sector);
    145 				continue;
    146 			}
    147 			if (calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE -
    148 				     ((uintptr_t) &lh->offset_xl - (uintptr_t) lh)) !=
    149 			    xlate32(lh->crc_xl)) {
    150 				log_info("Label checksum incorrect on %s - "
    151 					 "ignoring", dev_name(dev));
    152 				continue;
    153 			}
    154 			if (found)
    155 				continue;
    156 		}
    157 
    158 		dm_list_iterate_items(li, &_labellers) {
    159 			if (li->l->ops->can_handle(li->l, (char *) lh,
    160 						   sector + scan_sector)) {
    161 				log_very_verbose("%s: %s label detected",
    162 						 dev_name(dev), li->name);
    163 				if (found) {
    164 					log_error("Ignoring additional label "
    165 						  "on %s at sector %" PRIu64,
    166 						  dev_name(dev),
    167 						  sector + scan_sector);
    168 					continue;
    169 				}
    170 				r = li->l;
    171 				memcpy(buf, lh, LABEL_SIZE);
    172 				if (label_sector)
    173 					*label_sector = sector + scan_sector;
    174 				found = 1;
    175 				break;
    176 			}
    177 		}
    178 	}
    179 
    180       out:
    181 	if (!found) {
    182 		if ((info = info_from_pvid(dev->pvid, 0)))
    183 			lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
    184 						      info->fmt->orphan_vg_name,
    185 						      0, NULL);
    186 		log_very_verbose("%s: No label detected", dev_name(dev));
    187 	}
    188 
    189 	return r;
    190 }
    191 
    192 /* FIXME Also wipe associated metadata area headers? */
    193 int label_remove(struct device *dev)
    194 {
    195 	char buf[LABEL_SIZE] __attribute((aligned(8)));
    196 	char readbuf[LABEL_SCAN_SIZE] __attribute((aligned(8)));
    197 	int r = 1;
    198 	uint64_t sector;
    199 	int wipe;
    200 	struct labeller_i *li;
    201 	struct label_header *lh;
    202 
    203 	memset(buf, 0, LABEL_SIZE);
    204 
    205 	log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev));
    206 
    207 	if (!dev_open(dev))
    208 		return_0;
    209 
    210 	/*
    211 	 * We flush the device just in case someone is stupid
    212 	 * enough to be trying to import an open pv into lvm.
    213 	 */
    214 	dev_flush(dev);
    215 
    216 	if (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) {
    217 		log_debug("%s: Failed to read label area", dev_name(dev));
    218 		goto out;
    219 	}
    220 
    221 	/* Scan first few sectors for anything looking like a label */
    222 	for (sector = 0; sector < LABEL_SCAN_SECTORS;
    223 	     sector += LABEL_SIZE >> SECTOR_SHIFT) {
    224 		lh = (struct label_header *) (readbuf +
    225 					      (sector << SECTOR_SHIFT));
    226 
    227 		wipe = 0;
    228 
    229 		if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
    230 			if (xlate64(lh->sector_xl) == sector)
    231 				wipe = 1;
    232 		} else {
    233 			dm_list_iterate_items(li, &_labellers) {
    234 				if (li->l->ops->can_handle(li->l, (char *) lh,
    235 							   sector)) {
    236 					wipe = 1;
    237 					break;
    238 				}
    239 			}
    240 		}
    241 
    242 		if (wipe) {
    243 			log_info("%s: Wiping label at sector %" PRIu64,
    244 				 dev_name(dev), sector);
    245 			if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE,
    246 				       buf)) {
    247 				log_error("Failed to remove label from %s at "
    248 					  "sector %" PRIu64, dev_name(dev),
    249 					  sector);
    250 				r = 0;
    251 			}
    252 		}
    253 	}
    254 
    255       out:
    256 	if (!dev_close(dev))
    257 		stack;
    258 
    259 	return r;
    260 }
    261 
    262 int label_read(struct device *dev, struct label **result,
    263 		uint64_t scan_sector)
    264 {
    265 	char buf[LABEL_SIZE] __attribute((aligned(8)));
    266 	struct labeller *l;
    267 	uint64_t sector;
    268 	struct lvmcache_info *info;
    269 	int r = 0;
    270 
    271 	if ((info = info_from_pvid(dev->pvid, 1))) {
    272 		log_debug("Using cached label for %s", dev_name(dev));
    273 		*result = info->label;
    274 		return 1;
    275 	}
    276 
    277 	if (!dev_open(dev)) {
    278 		stack;
    279 
    280 		if ((info = info_from_pvid(dev->pvid, 0)))
    281 			lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
    282 						      info->fmt->orphan_vg_name,
    283 						      0, NULL);
    284 
    285 		return r;
    286 	}
    287 
    288 	if (!(l = _find_labeller(dev, buf, &sector, scan_sector)))
    289 		goto out;
    290 
    291 	if ((r = (l->ops->read)(l, dev, buf, result)) && result && *result)
    292 		(*result)->sector = sector;
    293 
    294       out:
    295 	if (!dev_close(dev))
    296 		stack;
    297 
    298 	return r;
    299 }
    300 
    301 /* Caller may need to use label_get_handler to create label struct! */
    302 int label_write(struct device *dev, struct label *label)
    303 {
    304 	char buf[LABEL_SIZE] __attribute((aligned(8)));
    305 	struct label_header *lh = (struct label_header *) buf;
    306 	int r = 1;
    307 
    308 	if (!label->labeller->ops->write) {
    309 		log_error("Label handler does not support label writes");
    310 		return 0;
    311 	}
    312 
    313 	if ((LABEL_SIZE + (label->sector << SECTOR_SHIFT)) > LABEL_SCAN_SIZE) {
    314 		log_error("Label sector %" PRIu64 " beyond range (%ld)",
    315 			  label->sector, LABEL_SCAN_SECTORS);
    316 		return 0;
    317 	}
    318 
    319 	memset(buf, 0, LABEL_SIZE);
    320 
    321 	strncpy((char *)lh->id, LABEL_ID, sizeof(lh->id));
    322 	lh->sector_xl = xlate64(label->sector);
    323 	lh->offset_xl = xlate32(sizeof(*lh));
    324 
    325 	if (!(label->labeller->ops->write)(label, buf))
    326 		return_0;
    327 
    328 	lh->crc_xl = xlate32(calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE -
    329 				      ((uintptr_t) &lh->offset_xl - (uintptr_t) lh)));
    330 
    331 	if (!dev_open(dev))
    332 		return_0;
    333 
    334 	log_info("%s: Writing label to sector %" PRIu64 " with stored offset %"
    335 		 PRIu32 ".", dev_name(dev), label->sector,
    336 		 xlate32(lh->offset_xl));
    337 	if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) {
    338 		log_debug("Failed to write label to %s", dev_name(dev));
    339 		r = 0;
    340 	}
    341 
    342 	if (!dev_close(dev))
    343 		stack;
    344 
    345 	return r;
    346 }
    347 
    348 /* Unused */
    349 int label_verify(struct device *dev)
    350 {
    351 	struct labeller *l;
    352 	char buf[LABEL_SIZE] __attribute((aligned(8)));
    353 	uint64_t sector;
    354 	struct lvmcache_info *info;
    355 	int r = 0;
    356 
    357 	if (!dev_open(dev)) {
    358 		if ((info = info_from_pvid(dev->pvid, 0)))
    359 			lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
    360 						      info->fmt->orphan_vg_name,
    361 						      0, NULL);
    362 
    363 		return_0;
    364 	}
    365 
    366 	if (!(l = _find_labeller(dev, buf, &sector, UINT64_C(0))))
    367 		goto out;
    368 
    369 	r = l->ops->verify ? l->ops->verify(l, buf, sector) : 1;
    370 
    371       out:
    372 	if (!dev_close(dev))
    373 		stack;
    374 
    375 	return r;
    376 }
    377 
    378 void label_destroy(struct label *label)
    379 {
    380 	label->labeller->ops->destroy_label(label->labeller, label);
    381 	dm_free(label);
    382 }
    383 
    384 struct label *label_create(struct labeller *labeller)
    385 {
    386 	struct label *label;
    387 
    388 	if (!(label = dm_malloc(sizeof(*label)))) {
    389 		log_error("label allocaction failed");
    390 		return NULL;
    391 	}
    392 	memset(label, 0, sizeof(*label));
    393 
    394 	label->labeller = labeller;
    395 
    396 	labeller->ops->initialise_label(labeller, label);
    397 
    398 	return label;
    399 }
    400