diff --git a/lib/label/label.c b/lib/label/label.c new file mode 100644 index 000000000..c921fd210 --- /dev/null +++ b/lib/label/label.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2001 Sistina Software + * + * This file is released under the LGPL. + */ + +#include +#include +#include +#include + +#include "device.h" +#include "dev-cache.h" +#include "log.h" +#include "filter.h" +#include "label.h" +#include "pool.h" +#include "xlate.h" + +/* Label Magic is "LnXl" - error: imagination failure */ +#define LABEL_MAGIC 0x6c586e4c + +/* Our memory pool */ +static void *label_pool = NULL; + +/* This is just the "struct label" with the data pointer removed */ +struct label_ondisk +{ + uint32_t magic; + uint32_t format_type; + uint32_t checksum; + uint16_t datalen; + uint16_t pad; +}; + +struct filter_private +{ + void *mem; + uint32_t format_type; +}; + + +/* Calculate checksum */ +static uint32_t calc_checksum(struct label *label) +{ + uint32_t csum = 0; + int i; + + csum += label->magic; + csum += label->format_type; + csum += label->datalen; + + for (i=0; idatalen; i++) + { + csum += label->data[i]; + } + + return csum; +} + +int label_read(struct device *dev, struct label *label) +{ + uint64_t size; + uint32_t sectsize; + char *block; + struct label_ondisk *ondisk; + int status; + int iter; + + if (!dev_get_size(dev, &size)) + return 0; + + if (!dev_get_sectsize(dev, §size)) + return 0; + + if (dev_open(dev, O_RDWR)) + return 0; + + if (label_pool == NULL) + label_pool = pool_create(512); + + block = pool_alloc(label_pool, sectsize); + if (!block) + { + stack; + return 1; + } + ondisk = (struct label_ondisk *)block; + + status = dev_read(dev, sectsize*2, sectsize, block); + + /* If the first label is bad then use the second */ + for (iter = 0; iter <= 1; iter++) + { + if (iter == 0) + status = dev_read(dev, sectsize*2, sectsize, block); + else + status = dev_read(dev, size*512 - sectsize, sizeof(struct label_ondisk) + label->datalen, block); + + if (!status) + { + struct label incore; + + /* Copy and convert endianness */ + incore.magic = xlate32(ondisk->magic); + incore.checksum = xlate32(ondisk->checksum); + incore.datalen = xlate16(ondisk->datalen); + incore.data = block + sizeof(struct label_ondisk); + + if (incore.magic != LABEL_MAGIC) + continue; + + /* Check Checksum */ + if (incore.checksum != calc_checksum(&incore)) + { + log_error("Checksum %d does not match\n", iter); + continue; + } + *label = incore; + label->data = pool_alloc(label_pool, incore.datalen); + if (!label->data) + { + stack; + return 1; + } + label->pool = label_pool; + if (label->data) memcpy(label->data, incore.data, incore.datalen); + pool_free(label_pool, block); + dev_close(dev); + return 1; + } + } + + pool_free(label_pool, block); + dev_close(dev); + + return 0; +} + +int label_write(struct device *dev, struct label *label) +{ + uint64_t size; + uint32_t sectsize; + char *block; + struct label_ondisk *ondisk; + int status1, status2; + + if (!dev_get_size(dev, &size)) + return 0; + + if (!dev_get_sectsize(dev, §size)) + return 0; + + /* Can the metata fit in the remaining space ? */ + if (label->datalen > sectsize - sizeof(struct label_ondisk)) + return 0; + + if (label_pool == NULL) + label_pool = pool_create(512); + + block = pool_alloc(label_pool, sizeof(struct label_ondisk) + label->datalen); + if (!block) + { + stack; + return 1; + } + + ondisk = (struct label_ondisk *)block; + + /* Make into ondisk format */ + ondisk->magic = xlate32(LABEL_MAGIC); + ondisk->format_type = xlate32(label->format_type); + ondisk->datalen = xlate16(label->datalen); + ondisk->checksum = xlate32(calc_checksum(label)); + memcpy(block+sizeof(struct label_ondisk), label->data, label->datalen); + + /* Write metadata to disk */ + if (!dev_open(dev, O_RDWR)) + { + pool_free(label_pool, block); + return 0; + } + + status1 = dev_write(dev, sectsize*2, sizeof(struct label_ondisk) + label->datalen, block); + + /* Write another at the end of the device */ + status2 = dev_write(dev, size*512 - sectsize, sizeof(struct label_ondisk) + label->datalen, block); + + pool_free(label_pool, block); + dev_close(dev); + + return ((status1 != 0) && (status2 != 0)); +} + + + +/* Return 1 for Yes, 0 for No */ +int is_labelled(struct device *dev) +{ + struct label l; + int status; + + status = label_read(dev, &l); + pool_free(l.pool, l.data); + + return 1-status; +} + +/* Check the device is labelled and has the right format_type */ +static int _accept_format(struct dev_filter *f, struct device *dev) +{ + struct label l; + int status; + + status = label_read(dev, &l); + pool_free(l.pool, l.data); + + if (status && l.format_type == (uint32_t)f->private) + return 1; + + else + return 0; +} + +/* We just want to know if it's labelled or not */ +static int _accept_label(struct dev_filter *f, struct device *dev) +{ + return is_labelled(dev); +} + +static void _destroy(struct dev_filter *f) +{ + struct filter_private *fp = (struct filter_private *) f->private; + pool_destroy(fp->mem); +} + +/* A filter to find devices with a particular label type on them */ +struct dev_filter *label_format_filter_create(uint32_t format_type) +{ + struct pool *mem = pool_create(10 * 1024); + struct filter_private *fp; + struct dev_filter *f; + + if (!mem) { + stack; + return NULL; + } + + if (!(f = pool_zalloc(mem, sizeof(*f)))) { + stack; + goto bad; + } + + if (!(fp = pool_zalloc(mem, sizeof(*fp)))) { + stack; + goto bad; + } + + fp->mem = mem; + fp->format_type = format_type; + f->passes_filter = _accept_format; + f->destroy = _destroy; + f->private = fp; + + return f; + + bad: + pool_destroy(mem); + return NULL; +} + +/* A filter to find devices with any label on them */ +struct dev_filter *label_filter_create() +{ + struct pool *mem = pool_create(10 * 1024); + struct filter_private *fp; + struct dev_filter *f; + + if (!mem) { + stack; + return NULL; + } + + if (!(f = pool_zalloc(mem, sizeof(*f)))) { + stack; + goto bad; + } + + if (!(fp = pool_zalloc(mem, sizeof(*fp)))) { + stack; + goto bad; + } + + fp->mem = mem; + f->passes_filter = _accept_label; + f->destroy = _destroy; + f->private = fp; + + return f; + + bad: + pool_destroy(mem); + return NULL; +} diff --git a/lib/label/label.h b/lib/label/label.h new file mode 100644 index 000000000..c58f07c35 --- /dev/null +++ b/lib/label/label.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +struct label +{ + uint32_t magic; + uint32_t format_type; + uint32_t checksum; + uint16_t datalen; + char *data; + void *pool; /* Pool that data is allocated from */ +}; + + + +extern int label_write(struct device *dev, struct label *label); +extern int label_read(struct device *dev, struct label *label); +extern int is_labelled(struct device *dev); + +extern struct dev_filter *label_filter_create(); +extern struct dev_filter *label_format_filter_create(uint32_t format_type);