--- /dev/null
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+#include <lafs/lafs.h>
+#include "internal.h"
+
+/* read the device blocks and state blocks from this device.
+ * If valid, return a 'lafs_dev' suitably filled in.
+ * The most recent state block is found and index is recorded in lafs_dev
+ *
+ * device block should be near the start and near the end.
+ * Should probably determine block size, but for not just iterate
+ * over sectors.
+ */
+
+static int valid_devblock(struct lafs_dev *db, unsigned long long addr,
+ unsigned long long device_bytes)
+{
+ /* check that this devblock is valid, given that
+ * it was found at sector 'addr'
+ */
+ u32 crc, crc2;
+ u64 byteaddr;
+ unsigned long long segsize;
+ int i;
+
+ if (strncmp(db->idtag, "LaFS-DeviceBlock", 16) != 0)
+ return 0;
+ if (strncmp(db->version, "AlphaDevel ", 16) != 0)
+ return 0;
+ /* uuid can be anything */
+ crc = db->checksum;
+ db->checksum = 0;
+ crc2 = crc32(0, (uint32_t*)db, LAFS_DEVBLK_SIZE);
+ db->checksum = crc;
+ if (crc2 != crc)
+ return 0;
+
+ byteaddr = addr << 9; /* convert to byte */
+ if (__le64_to_cpu(db->devaddr[0]) != byteaddr &&
+ __le64_to_cpu(db->devaddr[1]) != byteaddr)
+ return 0;
+
+ if (db->statebits < 10 || db->statebits > 16)
+ return 0;
+ if (db->blockbits < 9 || db->blockbits > 20)
+ return 0;
+ if (__le16_to_cpu(db->width) < 1 || __le16_to_cpu(db->width) >= 512)
+ return 0;
+ if (__le32_to_cpu(db->stride) < 1)
+ return 0;
+ /* devaddr[0] must be early, [1] must be late */
+ if (__le64_to_cpu(db->devaddr[0]) >=
+ __le64_to_cpu(db->segment_offset))
+ return 0;
+
+ if (__le64_to_cpu(db->devaddr[1]) <
+ __le64_to_cpu(db->segment_offset) +
+ ((((unsigned long long)__le32_to_cpu(db->segment_count)
+ * __le32_to_cpu(db->segment_size)))
+ << db->blockbits))
+ return 0;
+
+ /* 2 is an absolute minimum segment size, a few hundred is more
+ * likely. We'll put a lower limit of 8, and an upper of 800000
+ */
+ if (__le32_to_cpu(db->segment_size) < 8 ||
+ __le32_to_cpu(db->segment_size) > 800000)
+ return 0;
+
+ if (__le32_to_cpu(db->segment_offset) >
+ (__le32_to_cpu(db->segment_size)<<db->blockbits) * 10)
+ return 0;
+
+ /* The 4 state blocks live before the first or after the last segment.
+ * The distance from start of first to end of last is either:
+ * - segment_count * segment_size if width*stride <= segment_size
+ * - (width-1) * stride + segment_size / width * segment_count
+ * if width * stride > segment_size
+ */
+ segsize = __le32_to_cpu(db->segment_size);
+ segsize *= __le32_to_cpu(db->segment_count);
+ if (__le16_to_cpu(db->width) * __le32_to_cpu(db->stride)
+ > __le32_to_cpu(db->segment_size)) {
+ int stride = __le32_to_cpu(db->stride);
+ int width = __le16_to_cpu(db->width);
+
+ segsize /= width;
+ segsize += (width - 1) * stride;
+ }
+ segsize <<= db->blockbits;
+ for (i = 0; i < 4; i++) {
+ unsigned long long addr = __le64_to_cpu(db->stateaddr[i]);
+ int offset = __le32_to_cpu(db->segment_offset);
+ if (addr + (1<<db->statebits) > offset &&
+ addr < offset + segsize)
+ return 0;
+ if (addr + (1<<db->statebits) > device_bytes)
+ return 0;
+ }
+
+ /* Check all segments fit within device */
+ if (__le32_to_cpu(db->segment_offset) + segsize > device_bytes)
+ return 0;
+
+ if (__le32_to_cpu(db->level) > 10)
+ return 0;
+
+ /* I guess it look sane enough... */
+ return 1;
+}
+
+static int compare_dev(struct lafs_dev *curr, struct lafs_dev *new)
+{
+ if (memcmp(curr->uuid, new->uuid, 16) != 0)
+ /* there are different */
+ return -1;
+ if (memcmp(curr->version, new->version, 16) != 0)
+ return -1;
+
+ if (__le32_to_cpu(curr->seq) >= __le32_to_cpu(new->seq))
+ return 0; /*current is best */
+ return 1;
+}
+
+static int check_state(struct lafs_device *dev, int which)
+{
+ /* Load this state block, perform basic check and update
+ * dev->state_seq if seq number is new
+ */
+ struct lafs_state *sb = malloc(dev->statesize);
+ u32 crc, crc2;
+ int rv = 0;
+
+ lseek64(dev->fd, dev->stateaddr[which], 0);
+ if (read(dev->fd, sb, dev->statesize) != dev->statesize)
+ goto out;
+ if (memcmp(sb->idtag, "LaFS-State-Block", 16) != 0)
+ goto out;
+ if (memcmp(sb->uuid, dev->uuid, 16) != 0)
+ goto out;
+ crc = sb->checksum;
+ sb->checksum = 0;
+ crc2 = crc32(0, (uint32_t*)sb, dev->statesize);
+ if (crc != crc2)
+ goto out;
+
+ if (__le32_to_cpu(sb->seq) > dev->state_seq) {
+ dev->recent_state = which;
+ dev->state_seq = __le32_to_cpu(sb->seq);
+ dev->devices = __le32_to_cpu(sb->devices);
+ }
+ rv = 1;
+
+out:
+ free(sb);
+ return rv;
+}
+
+static int destroy(struct lafs_device *dev)
+{
+ if (dev->fd >= 0)
+ close(dev->fd);
+ return 0;
+}
+
+
+struct lafs_device *lafs_load(int fd, long long device_bytes, char **err)
+{
+ char buf[LAFS_DEVBLK_SIZE];
+ struct lafs_dev *d = (void*)buf;
+ struct lafs_dev best;
+ int have_best = 0;
+ unsigned long long addr, best_addr;
+ struct lafs_device *dev;
+ int i;
+ int found;
+
+ err = NULL;
+
+ for (addr = 0; addr < device_bytes; addr += 512) {
+ if (addr == 32*512 && device_bytes > 64*512)
+ addr = device_bytes - 32*512;
+
+ lseek64(fd, addr, 0);
+ if (read(fd, buf, LAFS_DEVBLK_SIZE) != LAFS_DEVBLK_SIZE)
+ continue;
+ if (!valid_devblock(d, addr, device_bytes))
+ continue;
+ if (!have_best) {
+ best = *d;
+ best_addr = addr;
+ have_best = 1;
+ continue;
+ }
+ switch(compare_dev(&best, d)) {
+ case 0: /* best is still best */
+ continue;
+ case 1: /* d is better */
+ best = *d;
+ best_addr = addr;
+ continue;
+ default: /* incompatible */
+ *err = "inconsistent device information blocks found";
+ return NULL;
+ }
+ }
+ if (!have_best) {
+ *err = "no valid device block found";
+ return NULL;
+ }
+
+ /* talloc device, fill in details, record where loaded from */
+ dev = talloc(NULL, struct lafs_device);
+ talloc_set_destructor(dev, destroy);
+ memset(dev, 0, sizeof(*dev));
+ dev->fd = fd;
+ dev->seq = __le32_to_cpu(best.seq);
+ dev->start = __le64_to_cpu(best.start);
+ dev->size = __le64_to_cpu(best.size);
+ for (i=0 ; i<2; i++) {
+ dev->devaddr[i] = __le64_to_cpu(best.devaddr[i]);
+ if (dev->devaddr[i] == best_addr)
+ dev->recent_super = i;
+ }
+ for (i=0; i<4; i++)
+ dev->stateaddr[i] = __le64_to_cpu(best.stateaddr[i]);
+
+ lafs_decode_timeval(&dev->ctime, best.ctime);
+ dev->width = __le16_to_cpu(best.width);
+ dev->stride = __le32_to_cpu(best.stride);
+ dev->segment_size = __le32_to_cpu(best.segment_size);
+ dev->segment_offset = __le32_to_cpu(best.segment_offset);
+ dev->segment_count = __le32_to_cpu(best.segment_count);
+ dev->usage_inum = __le32_to_cpu(best.usage_inum);
+
+ memcpy(dev->version, best.version, 16);
+ memcpy(dev->uuid, best.uuid, 16);
+ dev->blocksize = 1 << best.blockbits;
+ dev->statesize = 1 << best.statebits;
+
+ found = 0;
+ for (i=0; i<4; i++)
+ found += check_state(dev, i);
+
+ if (!found)
+ *err = "No valid state block found on device";
+
+ return dev;
+}
+
+
(unsigned long long) st->lafs->seq);
}
+/****** LOAD_DEV ******/
+static char help_load_dev[] = "Allow access to LaFS filesystem stored on given device";
+static struct args args_load_dev[] = {
+ { "DEVNAME", external, -1, {NULL}, "Device to load filesystem info from"},
+ { "-file", external, 0, {NULL}, "Regular file to load filesystem info from"},
+ TERMINAL_ARG
+};
+static void c_load_dev(struct state *st, void **args)
+{
+ char *devname = args[1];
+ int fd;
+ long long device_bytes = 0;
+ char *err;
+ struct lafs_device *dev;
+
+ if (!devname) {
+ printf("load_dev: No device of file name given to load\n");
+ return;
+ }
+
+ fd = open_device(devname, &device_bytes, args[2] != NULL, &err);
+
+ if (fd < 0) {
+ printf("load_dev: %s\n", err);
+ free(err);
+ return;
+ }
+
+ dev = lafs_load(fd, device_bytes, &err);
+
+ if (err) {
+ printf("load_dev: Cannot load %s: %s\n", devname, err);
+ if (dev)
+ talloc_free(dev);
+ close(fd);
+ return;
+ }
+
+ if (lafs_include_dev(st->lafs, dev, &err) != 0) {
+ printf("load_dev: Cannot include %s: %s\n", devname, err);
+ talloc_free(dev);
+ return;
+ }
+ if (st->verbose) {
+ printf("loaded device %s - have %d of %d\n", devname,
+ st->lafs->loaded_devs, st->lafs->devices);
+ }
+}
+
/****** STORE ******/
static char help_store[] = "Create a file in the LaFS from an external file";
static struct args args_store[] = {
CMD(add_device),
CMD(exit),
CMD(help),
+ CMD(load_dev),
CMD(newfs),
CMD(quit),
CMD(reset),