[DRBD-cvs] svn commit by lars - r2836 - trunk/user - checkin of rewrite of drbdmeta, though yet unfinished..

drbd-cvs at lists.linbit.com drbd-cvs at lists.linbit.com
Fri Apr 6 16:22:16 CEST 2007


Author: lars
Date: 2007-04-06 16:22:15 +0200 (Fri, 06 Apr 2007)
New Revision: 2836

Added:
   trunk/user/drbdmeta_unfinished_rewrite.c
Log:
checkin of rewrite of drbdmeta, though yet unfinished...

Added: trunk/user/drbdmeta_unfinished_rewrite.c
===================================================================
--- trunk/user/drbdmeta_unfinished_rewrite.c	2007-04-06 14:21:14 UTC (rev 2835)
+++ trunk/user/drbdmeta_unfinished_rewrite.c	2007-04-06 14:22:15 UTC (rev 2836)
@@ -0,0 +1,2382 @@
+/*
+   drbdmeta.c
+
+   This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
+
+   Copyright (C) 2004-2007, LINBIT Information Technologies GmbH
+   Copyright (C) 2004-2007, Philipp Reisner <philipp.reisner at linbit.com>
+   Copyright (C) 2004-2007, Lars Ellenberg  <lars.ellenberg at linbit.com>
+
+   drbd is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   drbd is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with drbd; see the file COPYING.  If not, write to
+   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+/* have the <sys/....h> first, otherwise you get e.g. "redefined" types from
+ * sys/types.h and other weird stuff */
+
+#define INITIALIZE_BITMAP 0
+
+#define _GNU_SOURCE
+#define _XOPEN_SOURCE 600
+#define _FILE_OFFSET_BITS 64
+#define __USE_LARGEFILE64
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <sys/time.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include <linux/drbd.h>		/* only use DRBD_MAGIC from here! */
+#include <linux/fs.h>           /* for BLKFLSBUF */
+
+#include "drbd_endian.h"
+#include "drbdtool_common.h"
+
+#include "drbdmeta_parser.h"
+extern FILE* yyin;
+YYSTYPE yylval;
+
+int     force = 0;
+
+struct option metaopt[] = {
+    { "force",  no_argument,    0, 'f' },
+    { NULL,     0,              0, 0 },
+};
+
+/* FIXME? should use sector_t and off_t, not long/u64 ... */
+
+/* Note RETURN VALUES:
+ * exit code convention: int vXY_something() and meta_blah return some negative
+ * error code, usually -1, when failed, 0 for success.
+ *
+ * FIXME some of the return -1; probably should better be exit(something);
+ * or some of the exit() should be rather some return?
+ *
+ * AND, the exit codes should follow some defined scheme.
+ */
+
+#if 0
+#define ASSERT(x) ((void)(0))
+#else
+#define ASSERT(x) do { if (!(x)) {			\
+	fprintf(stderr, "%s:%u:%s: ASSERT(%s) failed.\n",	\
+		__FILE__ , __LINE__ , __func__ , #x );		\
+	abort(); }						\
+	} while (0)
+#endif
+
+/*
+ * FIXME
+ *
+ * when configuring a drbd device:
+ *
+ * Require valid drbd meta data at the respective location.  A meta data
+ * block would only be created by the drbdmeta command.
+ *
+ * (How) do we want to implement this: A meta data block contains some
+ * reference to the physical device it belongs. Refuse to attach not
+ * corresponding meta data.
+ *
+ * THINK: put a checksum within the on-disk meta data block, too?
+ *
+ * When asked to create a new meta data block, the drbdmeta command
+ * warns loudly if either the data device or the meta data device seem
+ * to contain some data, and requires explicit confirmation anyways.
+ *
+ * See current implementation in check_for_existing_data below.
+ *
+ * XXX should also be done for meta-data != internal, i.e.  refuse to
+ * create meta data blocks on a device that seems to be in use for
+ * something else.
+ *
+ * Maybe with an external meta data device, we want to require a "meta
+ * data device super block", which could also serve as TOC to the meta
+ * data, once we have variable size meta data.  Other option could be a
+ * /var/lib/drbd/md-toc plain file, and some magic block on every device
+ * that serves as md storage.
+ *
+ * For certain content on the lower level device, we should refuse
+ * allways.  e.g. refuse to be created on top of a LVM2 physical volume,
+ * or on top of swap space. This would require people to do an dd
+ * if=/dev/zero of=device.  Protects them from shooting themselves,
+ * and blaming us...
+ */
+
+/* reiserfs sb offset is 64k plus */
+#define SO_MUCH (65*1024)
+
+/*
+ * I think this block of declarations and definitions should be
+ * in some common.h, too.
+ * {
+ */
+
+#ifndef ALIGN
+# define ALIGN(x,a) ( ((x) + (a)-1) &~ ((a)-1) )
+#endif
+
+#define MD_AL_OFFSET_07        8
+#define MD_AL_MAX_SECT_07     64
+#define MD_BM_OFFSET_07        (MD_AL_OFFSET_07 + MD_AL_MAX_SECT_07)
+#define MD_RESERVED_SECT_07    ( (u64)(128ULL << 11) )
+#define MD_BM_MAX_BYTE_07      ( (u64)(MD_RESERVED_SECT_07 - MD_BM_OFFSET_07)*512 )
+#define MD_BM_MAX_BYTE_FLEX    ( (u64)(1ULL << (32-3)) )
+
+#define DEFAULT_BM_BLOCK_SIZE  (1<<12)
+
+#define DRBD_MD_MAGIC_06   (DRBD_MAGIC+2)
+#define DRBD_MD_MAGIC_07   (DRBD_MAGIC+3)
+#define DRBD_MD_MAGIC_08   (DRBD_MAGIC+4)
+
+/*
+ * }
+ * end of should-be-shared
+ */
+
+/*
+ * global vaiables and data types
+ */
+
+const size_t buffer_size = 128*1024;
+size_t pagesize; /* = sysconf(_SC_PAGESIZE) */
+void *on_disk_buffer = NULL;
+int global_argc;
+char **global_argv;
+
+enum Known_Formats {
+	Drbd_06,
+	Drbd_07,
+	Drbd_08,
+	Drbd_Unknown,
+};
+
+/* let gcc help us get it right.
+ * some explicit endian types */
+typedef struct { u64 le; } le_u64;
+typedef struct { u64 be; } be_u64;
+typedef struct { u32 le; } le_u32;
+typedef struct { u32 be; } be_u32;
+typedef struct { s32 be; } be_s32;
+typedef struct { unsigned long le; } le_ulong;
+typedef struct { unsigned long be; } be_ulong;
+
+/* NOTE that this structure does not need to be packed,
+ * aligned, nor does it need to be in the same order as the on_disk variants.
+ */
+struct md_cpu {
+	/* present since drbd 0.6 */
+	u32 gc[GEN_CNT_SIZE];	/* generation counter */
+	u32 magic;
+	/* added in drbd 0.7;
+	 * 0.7 stores la_size on disk as kb, 0.8 in units of sectors.
+	 * we use sectors in our general working structure here */
+	u64 la_sect;		/* last agreed size. */
+	u32 md_size_sect;
+	s32 al_offset;		/* signed sector offset to this block */
+	u32 al_nr_extents;	/* important for restoring the AL */
+	s32 bm_offset;		/* signed sector offset to the bitmap, from here */
+	/* Since DRBD 0.8 we have uuid instead of gc */
+	u64 uuid[UUID_SIZE];
+	u32 flags;
+	u64 device_uuid;
+	u32 bm_bytes_per_bit;
+};
+
+/*
+ * drbdmeta specific types
+ */
+
+struct format_ops;
+
+struct format {
+	const struct format_ops *ops;
+	char *md_device_name;	/* well, in 06 it is file name */
+	char *drbd_dev_name;
+	int lock_fd;
+	int drbd_fd;		/* no longer used!   */
+	int ll_fd;		/* not yet used here */
+	int md_fd;
+
+	/* unused in 06 */
+	int md_index;
+	unsigned int bm_bytes;
+	unsigned int bits_set;	/* 32 bit should be enough. @4k ==> 16TB */
+	int bits_counted:1;
+
+	struct md_cpu md;
+
+	/* _byte_ offsets of our "super block" and other data, within fd */
+	u64 md_offset;
+	u64 al_offset;
+	u64 bm_offset;
+
+	/* convenience */
+	u64 bd_size; /* size of block device for internal meta data */
+};
+
+/* - parse is expected to exit() if it does not work out.
+ * - open is expected to read the respective on_disk members,
+ *   and copy the "superblock" meta data into the struct mem_cpu
+ * FIXME describe rest of them, and when they should exit,
+ * return error or success.
+ */
+struct format_ops {
+	const char *name;
+	char **args;
+	int (*parse) (struct format *, char **, int, int *);
+	int (*open) (struct format *);
+	int (*close) (struct format *);
+	int (*md_initialize) (struct format *);
+	int (*md_disk_to_cpu) (struct format *);
+	int (*md_cpu_to_disk) (struct format *);
+	void (*get_gi) (struct md_cpu *md);
+	void (*show_gi) (struct md_cpu *md);
+	void (*set_gi) (struct md_cpu *md, char **argv, int argc);
+	int (*outdate_gi) (struct md_cpu *md);
+};
+
+/*
+ * -- DRBD 0.6 --------------------------------------
+ */
+
+struct __attribute__ ((packed)) md_on_disk_06 {
+	be_u32 gc[GEN_CNT_SIZE];	/* generation counter */
+	be_u32 magic;
+};
+
+void md_disk_06_to_cpu(struct md_cpu *cpu, const struct md_on_disk_06 *disk)
+{
+	int i;
+
+	memset(cpu, 0, sizeof(*cpu));
+	for (i = 0; i < GEN_CNT_SIZE; i++)
+		cpu->gc[i] = be32_to_cpu(disk->gc[i].be);
+	cpu->magic = be32_to_cpu(disk->magic.be);
+}
+
+void md_cpu_to_disk_06(struct md_on_disk_06 *disk, struct md_cpu *cpu)
+{
+	int i;
+
+	for (i = 0; i < GEN_CNT_SIZE; i++)
+		disk->gc[i].be = cpu_to_be32(cpu->gc[i]);
+	disk->magic.be = cpu_to_be32(cpu->magic);
+}
+
+int v06_validate_md(struct format *cfg)
+{
+	if (cfg->md.magic != DRBD_MD_MAGIC_06) {
+		fprintf(stderr, "v06 Magic number not found\n");
+		return -1;
+	}
+	return 0;
+}
+
+/*
+ * -- DRBD 0.7 --------------------------------------
+ */
+
+struct __attribute__ ((packed)) md_on_disk_07 {
+	be_u64 la_kb;		/* last agreed size. */
+	be_u32 gc[GEN_CNT_SIZE];	/* generation counter */
+	be_u32 magic;
+	be_u32 md_size_sect;
+	be_s32 al_offset;	/* signed sector offset to this block */
+	be_u32 al_nr_extents;	/* important for restoring the AL */
+	be_s32 bm_offset;	/* signed sector offset to the bitmap, from here */
+	char reserved[8 * 512 - 48];
+};
+
+void md_disk_07_to_cpu(struct md_cpu *cpu, const struct md_on_disk_07 *disk)
+{
+	int i;
+
+	memset(cpu, 0, sizeof(*cpu));
+	cpu->la_sect = be64_to_cpu(disk->la_kb.be) << 1;
+	for (i = 0; i < GEN_CNT_SIZE; i++)
+		cpu->gc[i] = be32_to_cpu(disk->gc[i].be);
+	cpu->magic = be32_to_cpu(disk->magic.be);
+	cpu->md_size_sect = be32_to_cpu(disk->md_size_sect.be);
+	cpu->al_offset = be32_to_cpu(disk->al_offset.be);
+	cpu->al_nr_extents = be32_to_cpu(disk->al_nr_extents.be);
+	cpu->bm_offset = be32_to_cpu(disk->bm_offset.be);
+	cpu->bm_bytes_per_bit = 4096;
+}
+
+void md_cpu_to_disk_07(struct md_on_disk_07 *disk, const struct md_cpu const *cpu)
+{
+	int i;
+
+	disk->la_kb.be = cpu_to_be64(cpu->la_sect >> 1);
+	for (i = 0; i < GEN_CNT_SIZE; i++)
+		disk->gc[i].be = cpu_to_be32(cpu->gc[i]);
+	disk->magic.be = cpu_to_be32(cpu->magic);
+	disk->md_size_sect.be = cpu_to_be32(cpu->md_size_sect);
+	disk->al_offset.be = cpu_to_be32(cpu->al_offset);
+	disk->al_nr_extents.be = cpu_to_be32(cpu->al_nr_extents);
+	disk->bm_offset.be = cpu_to_be32(cpu->bm_offset);
+	memset(disk->reserved, 0, sizeof(disk->reserved));
+}
+
+int is_valid_md(int f,
+	const struct md_cpu const *md, const int md_index, const u64 ll_size)
+{
+	u64 md_size_sect;
+	char *v = (f == Drbd_07) ? "v07" : "v08";
+	const unsigned int magic = (f == Drbd_07) ? DRBD_MD_MAGIC_07 : DRBD_MD_MAGIC_08;
+	
+
+	ASSERT(f == Drbd_07 || f == Drbd_08);
+
+	if (md->magic != magic) {
+		fprintf(stderr, "%s Magic number not found\n", v);
+		return 0;
+	}
+
+	switch(md_index) {
+	default:
+	case DRBD_MD_INDEX_INTERNAL:
+	case DRBD_MD_INDEX_FLEX_EXT:
+		if (md->al_offset != MD_AL_OFFSET_07) {
+			fprintf(stderr, "%s Magic number (al_offset) not found\n", v);
+			return 0;
+		}
+		if (md->bm_offset != MD_BM_OFFSET_07) {
+			fprintf(stderr, "%s Magic number (bm_offset) not found\n", v);
+			return 0;
+		}
+		break;
+	case DRBD_MD_INDEX_FLEX_INT:
+		if (md->al_offset != -MD_AL_MAX_SECT_07) {
+			fprintf(stderr, "%s Magic number (al_offset) not found\n", v);
+			return 0;
+		}
+
+		/* we need (slightly less than) ~ this much bitmap sectors: */
+		md_size_sect = (ll_size + (1UL<<24)-1) >> 24; /* BM_EXT_SIZE_B */
+		md_size_sect = (md_size_sect + 7) & ~7ULL;    /* align on 4K blocks */
+		/* plus the "drbd meta data super block",
+		 * and the activity log; unit still sectors */
+		md_size_sect += MD_BM_OFFSET_07;
+
+		if (md->bm_offset != -(s64)md_size_sect + MD_AL_OFFSET_07) {
+			fprintf(stderr, "strange bm_offset %d (expected: "D64")\n",
+					md->bm_offset, -(s64)md_size_sect + MD_AL_OFFSET_07);
+			return 0;
+		};
+		if (md->md_size_sect != md_size_sect) {
+			fprintf(stderr, "strange md_size_sect %u (expected: "U64")\n",
+					md->md_size_sect, md_size_sect);
+			if (f == Drbd_08) return 0;
+			/* else not an error,
+			 * was inconsistently implemented in v07 */
+		}
+		break;
+	}
+
+	/* fixme consistency check, la_size < ll_device_size,
+	 * no overlap with internal meta data,
+	 * no overlap of flexible meta data offsets/sizes
+	 * ...
+	 */
+
+	return 1; /* VALID */
+}
+
+/*
+ * these stay the same for 0.8, too:
+ */
+
+struct __attribute__ ((packed)) al_sector_cpu {
+	u32 magic;
+	u32 tr_number;
+	struct __attribute__ ((packed)) {
+		u32 pos;
+		u32 extent;
+	} updates[62];
+	u32 xor_sum;
+};
+
+struct __attribute__ ((packed)) al_sector_on_disk {
+	be_u32 magic;
+	be_u32 tr_number;
+	struct __attribute__ ((packed)) {
+		be_u32 pos;
+		be_u32 extent;
+	} updates[62];
+	be_u32 xor_sum;
+};
+
+/*
+ * -- DRBD 0.8 --------------------------------------
+ */
+
+struct __attribute__ ((packed)) md_on_disk_08 {
+	be_u64 la_sect;		/* last agreed size. */
+	be_u64 uuid[UUID_SIZE];   // UUIDs.
+	be_u64 device_uuid;
+	be_u64 reserved_u64_1;
+	be_u32 flags;
+	be_u32 magic;
+	be_u32 md_size_sect;
+	be_s32 al_offset;	/* signed sector offset to this block */
+	be_u32 al_nr_extents;	/* important for restoring the AL */
+	be_s32 bm_offset;	/* signed sector offset to the bitmap, from here */
+	be_u32 bm_bytes_per_bit;
+	be_u32 reserved_u32[4];
+
+	char reserved[8 * 512 - (8*(UUID_SIZE+3)+4*11)];
+};
+
+void md_disk_08_to_cpu(struct md_cpu *cpu, const struct md_on_disk_08 *disk)
+{
+	int i;
+
+	memset(cpu, 0, sizeof(*cpu));
+	cpu->la_sect = be64_to_cpu(disk->la_sect.be);
+	for ( i=Current ; i<UUID_SIZE ; i++ )
+		cpu->uuid[i] = be64_to_cpu(disk->uuid[i].be);
+	cpu->device_uuid = be64_to_cpu(disk->device_uuid.be);
+	cpu->flags = be32_to_cpu(disk->flags.be);
+	cpu->magic = be32_to_cpu(disk->magic.be);
+	cpu->md_size_sect = be32_to_cpu(disk->md_size_sect.be);
+	cpu->al_offset = be32_to_cpu(disk->al_offset.be);
+	cpu->al_nr_extents = be32_to_cpu(disk->al_nr_extents.be);
+	cpu->bm_offset = be32_to_cpu(disk->bm_offset.be);
+	cpu->bm_bytes_per_bit = be32_to_cpu(disk->bm_bytes_per_bit.be);
+}
+
+void md_cpu_to_disk_08(struct md_on_disk_08 *disk, const struct md_cpu *cpu)
+{
+	int i;
+	disk->la_sect.be = cpu_to_be64(cpu->la_sect);
+	for ( i=Current ; i<UUID_SIZE ; i++ ) {
+		disk->uuid[i].be = cpu_to_be64(cpu->uuid[i]);
+	}
+	disk->device_uuid.be = cpu_to_be64(cpu->device_uuid);
+	disk->flags.be = cpu_to_be32(cpu->flags);
+	disk->magic.be = cpu_to_be32(cpu->magic);
+	disk->md_size_sect.be = cpu_to_be32(cpu->md_size_sect);
+	disk->al_offset.be = cpu_to_be32(cpu->al_offset);
+	disk->al_nr_extents.be = cpu_to_be32(cpu->al_nr_extents);
+	disk->bm_offset.be = cpu_to_be32(cpu->bm_offset);
+	disk->bm_bytes_per_bit.be = cpu_to_be32(cpu->bm_bytes_per_bit);
+	memset(disk->reserved, 0, sizeof(disk->reserved));
+}
+
+/* pre declarations */
+void m_get_gc(struct md_cpu *md);
+void m_show_gc(struct md_cpu *md);
+void m_set_gc(struct md_cpu *md, char **argv, int argc);
+int m_outdate_gc(struct md_cpu *md);
+void m_get_uuid(struct md_cpu *md);
+void m_show_uuid(struct md_cpu *md);
+void m_set_uuid(struct md_cpu *md, char **argv, int argc);
+int m_outdate_uuid(struct md_cpu *md);
+
+int generic_md_close(struct format *cfg);
+
+int v06_md_cpu_to_disk(struct format *cfg);
+int v06_md_disk_to_cpu(struct format *cfg);
+int v06_parse(struct format *cfg, char **argv, int argc, int *ai);
+int v06_md_open(struct format *cfg);
+int v06_md_initialize(struct format *cfg);
+
+int v07_md_cpu_to_disk(struct format *cfg);
+int v07_md_disk_to_cpu(struct format *cfg);
+int v07_parse(struct format *cfg, char **argv, int argc, int *ai);
+int v07_md_initialize(struct format *cfg);
+
+int v07_style_md_open(struct format *cfg); /* also v08 */
+
+int v08_md_cpu_to_disk(struct format *cfg);
+int v08_md_disk_to_cpu(struct format *cfg);
+int v08_md_initialize(struct format *cfg);
+
+struct format_ops f_ops[] = {
+	[Drbd_06] = {
+		     .name = "v06",
+		     .args = (char *[]){"minor", NULL},
+		     .parse = v06_parse,
+		     .open = v06_md_open,
+		     .close = generic_md_close,
+		     .md_initialize = v06_md_initialize,
+		     .md_disk_to_cpu = v06_md_disk_to_cpu,
+		     .md_cpu_to_disk = v06_md_cpu_to_disk,
+		     .get_gi = m_get_gc,
+		     .show_gi = m_show_gc,
+		     .set_gi = m_set_gc,
+		     .outdate_gi = m_outdate_gc,
+		     },
+	[Drbd_07] = {
+		     .name = "v07",
+		     .args = (char *[]){"device", "index", NULL},
+		     .parse = v07_parse,
+		     .open = v07_style_md_open,
+		     .close = generic_md_close,
+		     .md_initialize = v07_md_initialize,
+		     .md_disk_to_cpu = v07_md_disk_to_cpu,
+		     .md_cpu_to_disk = v07_md_cpu_to_disk,
+		     .get_gi = m_get_gc,
+		     .show_gi = m_show_gc,
+		     .set_gi = m_set_gc,
+		     .outdate_gi = m_outdate_gc,
+		     },
+	[Drbd_08] = {
+		     .name = "v08",
+		     .args = (char *[]){"device", "index", NULL},
+		     .parse = v07_parse,
+		     .open = v07_style_md_open,
+		     .close = generic_md_close,
+		     .md_initialize = v08_md_initialize,
+		     .md_disk_to_cpu = v08_md_disk_to_cpu,
+		     .md_cpu_to_disk = v08_md_cpu_to_disk,
+		     .get_gi = m_get_uuid,
+		     .show_gi = m_show_uuid,
+		     .set_gi = m_set_uuid,
+		     .outdate_gi = m_outdate_uuid,
+		     },
+};
+
+static inline enum Known_Formats format_version(struct format *cfg)
+{
+	return (cfg->ops - f_ops);
+}
+static inline int is_v06(struct format *cfg)
+{
+	return format_version(cfg) == Drbd_06;
+}
+static inline int is_v07(struct format *cfg)
+{
+	return format_version(cfg) == Drbd_07;
+}
+static inline int is_v08(struct format *cfg)
+{
+	return format_version(cfg) == Drbd_08;
+}
+
+/******************************************
+  Commands we know about:
+ ******************************************/
+
+struct meta_cmd {
+	const char *name;
+	const char *args;
+	int (*function) (struct format *, char **argv, int argc);
+	int show_in_usage;
+};
+
+/* pre declarations */
+int meta_get_gi(struct format *cfg, char **argv, int argc);
+int meta_show_gi(struct format *cfg, char **argv, int argc);
+int meta_dump_md(struct format *cfg, char **argv, int argc);
+int meta_restore_md(struct format *cfg, char **argv, int argc);
+int meta_create_md(struct format *cfg, char **argv, int argc);
+int meta_wipe_md(struct format *cfg, char **argv, int argc);
+int meta_outdate(struct format *cfg, char **argv, int argc);
+int meta_set_gi(struct format *cfg, char **argv, int argc);
+int meta_read_dev_uuid(struct format *cfg, char **argv, int argc);
+int meta_write_dev_uuid(struct format *cfg, char **argv, int argc);
+int meta_dstate(struct format *cfg, char **argv, int argc);
+
+struct meta_cmd cmds[] = {
+	{"get-gi", 0, meta_get_gi, 1},
+	{"show-gi", 0, meta_show_gi, 1},
+	{"dump-md", 0, meta_dump_md, 1},
+	{"restore-md", "file", meta_restore_md, 1},
+	{"create-md", 0, meta_create_md, 1},
+	{"wipe-md", 0, meta_wipe_md, 1},
+	{"outdate", 0, meta_outdate, 1},
+	{"dstate", 0, meta_dstate, 1},
+	{"read-dev-uuid", "VAL",  meta_read_dev_uuid,  0},
+	{"write-dev-uuid", "VAL", meta_write_dev_uuid, 0},
+	{"set-gi", ":::VAL:VAL:...", meta_set_gi, 0},
+};
+
+/*
+ * generic helpers
+ */
+
+#define PREAD(a,b,c,d) pread_or_die((a),(b),(c),(d), __func__ )
+#define PWRITE(a,b,c,d) pwrite_or_die((a),(b),(c),(d), __func__ )
+/* Do we want to exit() right here,
+ * or do we want to duplicate the error handling everywhere? */
+void pread_or_die(int fd, void *buf, size_t count, off_t offset, const char* tag)
+{
+	ssize_t c = pread(fd, buf, count, offset);
+	//printf("pread(%u,...,%lu,%llu)\n", fd, (unsigned long)count, (unsigned long long)offset);
+	if (c < 0) {
+		fprintf(stderr,"pread in %s failed: %s\n",
+			tag, strerror(errno));
+		exit(10);
+	} else if ((size_t)c != count) {
+		fprintf(stderr,"confused in %s: expected to read %d bytes,"
+			" actually read %d\n",
+			tag, (int)count, (int)c);
+		exit(10);
+	}
+}
+
+void pwrite_or_die(int fd, const void *buf, size_t count, off_t offset, const char* tag)
+{
+	ssize_t c = pwrite(fd, buf, count, offset);
+	//printf("pwrite(%u,...,%lu,%llu)\n", fd, (unsigned long)count, (unsigned long long)offset);
+	if (c < 0) {
+		fprintf(stderr,"pwrite in %s failed: %s\n",
+			tag, strerror(errno));
+		exit(10);
+	} else if ((size_t)c != count) {
+		/* FIXME we might just now have corrupted the on-disk data */
+		fprintf(stderr,"confused in %s: expected to write %d bytes,"
+			" actually wrote %d\n", tag, (int)count, (int)c);
+		exit(10);
+	}
+}
+
+int confirmed(const char *text)
+{
+	const char yes[] = "yes";
+	const ssize_t N = sizeof(yes);
+	char *answer = NULL;
+	size_t n = 0;
+	int ok;
+
+        printf("\n%s\n", text);
+
+        if (force) {
+            printf("*** confirmation forced via --force option ***\n");
+            ok = 1;
+        }
+        else {
+            printf("[need to type '%s' to confirm] ", yes);
+            ok = getline(&answer,&n,stdin) == N &&
+                strncmp(answer,yes,N-1) == 0;
+            if (answer) free(answer);
+            printf("\n");
+        }
+	return ok;
+}
+
+void m_get_gc(struct md_cpu *md)
+{
+	dt_print_gc(md->gc);
+}
+
+void m_show_gc(struct md_cpu *md)
+{
+	dt_pretty_print_gc(md->gc);
+}
+
+void m_get_uuid(struct md_cpu *md)
+{
+	dt_print_uuids(md->uuid,md->flags);
+}
+
+void m_show_uuid(struct md_cpu *md)
+{
+	dt_pretty_print_uuids(md->uuid,md->flags);
+}
+
+int m_strsep_u32(char **s, u32 *val)
+{
+	char *t, *e;
+	unsigned long v;
+
+	if ((t = strsep(s, ":"))) {
+		if (strlen(t)) {
+			e = t;
+			errno = 0;
+			v = strtoul(t, &e, 0);
+			if (*e != 0) {
+				fprintf(stderr, "'%s' is not a number.\n", *s);
+				exit(10);
+			}
+			if (errno) {
+				fprintf(stderr, "'%s': ", *s);
+				perror(0);
+				exit(10);
+			}
+			if (v > 0xFFffFFffUL) {
+				fprintf(stderr,
+					"'%s' is out of range (max 0xFFffFFff).\n",
+					*s);
+				exit(10);
+			}
+			*val = (u32)v;
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int m_strsep_u64(char **s, u64 *val)
+{
+	char *t, *e;
+	u64 v;
+
+	if ((t = strsep(s, ":"))) {
+		if (strlen(t)) {
+			e = t;
+			errno = 0;
+			v = strto_u64(t, &e, 16);
+			if (*e != 0) {
+				fprintf(stderr, "'%s' is not a number.\n", *s);
+				exit(10);
+			}
+			if (errno) {
+				fprintf(stderr, "'%s': ", *s);
+				perror(0);
+				exit(10);
+			}
+			*val = v;
+		}
+		return 1;
+	}
+	return 0;
+}
+
+int m_strsep_bit(char **s, u32 *val, int mask)
+{
+	u32 d;
+	int rv;
+
+	d = *val & mask ? 1 : 0;
+
+	rv = m_strsep_u32(s, &d);
+
+	if (d > 1) {
+		fprintf(stderr, "'%d' is not 0 or 1.\n", d);
+		exit(10);
+	}
+
+	if (d)
+		*val |= mask;
+	else
+		*val &= ~mask;
+
+	return rv;
+}
+
+void m_set_gc(struct md_cpu *md, char **argv, int argc __attribute((unused)))
+{
+	char **str;
+
+	str = &argv[0];
+
+	do {
+		if (!m_strsep_bit(str, &md->gc[Flags], MDF_Consistent)) break;
+		if (!m_strsep_u32(str, &md->gc[HumanCnt])) break;
+		if (!m_strsep_u32(str, &md->gc[TimeoutCnt])) break;
+		if (!m_strsep_u32(str, &md->gc[ConnectedCnt])) break;
+		if (!m_strsep_u32(str, &md->gc[ArbitraryCnt])) break;
+		if (!m_strsep_bit(str, &md->gc[Flags], MDF_PrimaryInd)) break;
+		if (!m_strsep_bit(str, &md->gc[Flags], MDF_ConnectedInd)) break;
+		if (!m_strsep_bit(str, &md->gc[Flags], MDF_FullSync)) break;
+	} while (0);
+}
+
+void m_set_uuid(struct md_cpu *md, char **argv, int argc __attribute((unused)))
+{
+	char **str;
+	int i;
+
+	str = &argv[0];
+
+	do {
+		for ( i=Current ; i<UUID_SIZE ; i++ ) {
+			if (!m_strsep_u64(str, &md->uuid[i])) return;
+		}
+		if (!m_strsep_bit(str, &md->flags, MDF_Consistent)) break;
+		if (!m_strsep_bit(str, &md->flags, MDF_WasUpToDate)) break;
+		if (!m_strsep_bit(str, &md->flags, MDF_PrimaryInd)) break;
+		if (!m_strsep_bit(str, &md->flags, MDF_ConnectedInd)) break;
+		if (!m_strsep_bit(str, &md->flags, MDF_FullSync)) break;
+		if (!m_strsep_bit(str, &md->flags, MDF_PeerOutDated)) break;
+	} while (0);
+}
+
+int m_outdate_gc(struct md_cpu *md __attribute((unused)))
+{
+	fprintf(stderr, "Can not outdate GC based meta data!\n");
+
+	return 5;
+}
+
+int m_outdate_uuid(struct md_cpu *md)
+{
+	if ( !(md->flags & MDF_Consistent) ) {
+		return 5;
+	}
+
+	md->flags &= ~MDF_WasUpToDate;
+
+	return 0;
+}
+
+
+
+/******************************************
+ begin of v06 {{{
+ ******************************************/
+
+int v06_md_disk_to_cpu(struct format *cfg)
+{
+	PREAD(cfg->md_fd, on_disk_buffer,
+		sizeof(struct md_on_disk_06), cfg->md_offset);
+	md_disk_06_to_cpu(&cfg->md, (struct md_on_disk_06*)on_disk_buffer);
+	return v06_validate_md(cfg);
+}
+
+int v06_md_cpu_to_disk(struct format *cfg)
+{
+	if (v06_validate_md(cfg))
+		return -1;
+	md_cpu_to_disk_06(on_disk_buffer, &cfg->md);
+	PWRITE(cfg->md_fd, on_disk_buffer,
+		sizeof(struct md_on_disk_06), cfg->md_offset);
+	return 0;
+}
+
+int v06_parse(struct format *cfg, char **argv, int argc, int *ai)
+{
+	unsigned long minor;
+	char *e;
+
+	if (argc < 1) {
+		fprintf(stderr, "Too few arguments for format\n");
+		exit(20);
+	}
+
+	e = argv[0];
+	minor = strtol(argv[0], &e, 0);
+	if (*e != 0 || minor > 255UL) {
+		fprintf(stderr, "'%s' is not a valid minor number.\n", argv[0]);
+		exit(20);
+	}
+	if (asprintf(&e, "/var/lib/drbd/drbd%lu", minor) <= 18) {
+		fprintf(stderr, "asprintf() failed.\n");
+		exit(20);
+	};
+	cfg->md_device_name = e;
+
+	*ai += 1;
+
+	return 0;
+}
+
+int v06_md_open(struct format *cfg)
+{
+	struct stat sb;
+
+	cfg->md_fd = open(cfg->md_device_name, O_RDWR);
+
+	if (cfg->md_fd == -1) {
+		PERROR("open(%s) failed", cfg->md_device_name);
+		return -1;
+	}
+
+	if (fstat(cfg->md_fd, &sb)) {
+		PERROR("fstat() failed");
+		return -1;
+	}
+
+	if (!S_ISREG(sb.st_mode)) {
+		fprintf(stderr, "'%s' is not a plain file!\n",
+			cfg->md_device_name);
+		return -1;
+	}
+
+	if (cfg->ops->md_disk_to_cpu(cfg)) {
+		return -1;
+	}
+
+	return 0;
+}
+
+int generic_md_close(struct format *cfg)
+{
+	if (close(cfg->md_fd)) {
+		PERROR("close() failed");
+		return -1;
+	}
+	return 0;
+}
+
+int v06_md_initialize(struct format *cfg)
+{
+	cfg->md.gc[Flags] = 0;
+	cfg->md.gc[HumanCnt] = 1;	/* THINK 0? 1? */
+	cfg->md.gc[TimeoutCnt] = 1;
+	cfg->md.gc[ConnectedCnt] = 1;
+	cfg->md.gc[ArbitraryCnt] = 1;
+	cfg->md.magic = DRBD_MD_MAGIC_06;
+	return 0;
+}
+
+/******************************************
+  }}} end of v06
+ ******************************************/
+
+int md_initialize_common(struct format *cfg)
+{
+	u64 md_size_sect;
+
+	/* no need to re-initialize the offset of md
+	 * FIXME we need to, if we convert, or resize, in case we allow/implement that...
+	 */
+
+	switch(cfg->md_index) {
+	default:
+		cfg->md.md_size_sect = MD_RESERVED_SECT_07;
+		cfg->md.al_offset = MD_AL_OFFSET_07;
+		cfg->md.bm_offset = MD_BM_OFFSET_07;
+		break;
+	case DRBD_MD_INDEX_FLEX_EXT:
+		/* just occupy the full device; unit: sectors */
+		cfg->md.md_size_sect = bdev_size(cfg->md_fd)>>9;
+		cfg->md.al_offset = MD_AL_OFFSET_07;
+		cfg->md.bm_offset = MD_BM_OFFSET_07;
+		break;
+	case DRBD_MD_INDEX_INTERNAL:
+		cfg->md.md_size_sect = MD_RESERVED_SECT_07;
+		cfg->md.al_offset = MD_AL_OFFSET_07;
+		cfg->md.bm_offset = MD_BM_OFFSET_07;
+		break;
+	case DRBD_MD_INDEX_FLEX_INT:
+		/* al size is still fixed */
+		cfg->md.al_offset = -MD_AL_MAX_SECT_07;
+
+		/* we need (slightly less than) ~ this much bitmap sectors: */
+		md_size_sect = (bdev_size(cfg->md_fd) + (1UL<<24)-1) >> 24; /* BM_EXT_SIZE_B */
+		md_size_sect = (md_size_sect + 7) & ~7ULL;             /* align on 4K blocks */
+
+		if (md_size_sect > (MD_BM_MAX_BYTE_FLEX>>9)) {
+			fprintf(stderr, "Device too large. We only support up to ~16TB.\n");
+			exit(10);
+		}
+		/* plus the "drbd meta data super block",
+		 * and the activity log; unit still sectors */
+		md_size_sect += MD_BM_OFFSET_07;
+		cfg->md.md_size_sect = md_size_sect;
+		cfg->md.bm_offset = -md_size_sect + MD_AL_OFFSET_07;
+		break;
+	}
+	cfg->md.al_nr_extents = 257;	/* arbitrary. */
+	cfg->md.bm_bytes_per_bit = DEFAULT_BM_BLOCK_SIZE;
+
+	cfg->al_offset = cfg->md_offset + cfg->md.al_offset * 512;
+	cfg->bm_offset = cfg->md_offset + cfg->md.bm_offset * 512;
+
+	//fprintf(stderr,"md_offset: "U64"\n", cfg->md_offset);
+	//fprintf(stderr,"al_offset: "U64" (%d)\n", cfg->al_offset, cfg->md.al_offset);
+	//fprintf(stderr,"bm_offset: "U64" (%d)\n", cfg->bm_offset, cfg->md.bm_offset);
+	//fprintf(stderr,"md_size_sect: %lu\n", (unsigned long)cfg->md.md_size_sect);
+	//fprintf(stderr,"bm_mmaped_length: %lu\n", (unsigned long)cfg->bm_mmaped_length);
+
+	/* do you want to initilize al to something more usefull? */
+	printf("initialising activity log\n");
+	if (MD_AL_MAX_SECT_07*512 > buffer_size) {
+		fprintf(stderr, "%s:%u: LOGIC BUG\n" , __FILE__ , __LINE__ );
+		exit(111);
+	}
+	memset(on_disk_buffer, 0x00, MD_AL_MAX_SECT_07*512);
+	pwrite_or_die(cfg->md_fd, on_disk_buffer, MD_AL_MAX_SECT_07*512, cfg->al_offset,
+		"md_initialize_common:AL");
+
+	/* THINK
+	 * do we really need to initialize the bitmap? */
+	if (INITIALIZE_BITMAP) {
+		/* need to sector-align this for O_DIRECT. to be
+ 		 * generic, maybe we even need to PAGE align it? */
+		const size_t bm_bytes = ALIGN(cfg->bm_bytes, 512);
+		size_t i = bm_bytes;
+		off_t bm_on_disk_off = cfg->bm_offset;
+		unsigned int percent_done = 0;
+		unsigned int percent_last_report = 0;
+		size_t chunk;
+		fprintf(stderr,"initialising bitmap (%u KB)\n",
+			(unsigned int)(bm_bytes>>10));
+
+		memset(on_disk_buffer, 0xff, buffer_size);
+		while (i) {
+			chunk = buffer_size < i ? buffer_size : i;
+			pwrite_or_die(cfg->md_fd, on_disk_buffer,
+				chunk, bm_on_disk_off,
+				"md_initialize_common:BM");
+			bm_on_disk_off += chunk;
+			i -= chunk;
+			percent_done = 100*(bm_bytes-i)/bm_bytes;
+			if (percent_done != percent_last_report) {
+				fprintf(stderr,"\r%u%%", percent_done);
+				percent_last_report = percent_done;
+			}
+		}
+		fprintf(stderr,"\r100%%\n");
+	} else {
+		fprintf(stderr,"NOT initialized bitmap\n");
+	}
+	return 0;
+}
+
+/******************************************
+ begin of v07 {{{
+ ******************************************/
+
+u64 v07_style_md_get_byte_offset(const int idx, const u64 bd_size)
+{
+	u64 offset;
+
+	switch(idx) {
+	default: /* external, some index */
+		offset = MD_RESERVED_SECT_07 * idx * 512;
+		break;
+	case DRBD_MD_INDEX_INTERNAL:
+		offset = (bd_size & ~4095LLU)
+		    - MD_RESERVED_SECT_07 * 512;
+		break;
+	case DRBD_MD_INDEX_FLEX_INT:
+		/* sizeof(struct md_on_disk_07) == 4k
+		 * position: last 4k aligned block of 4k size */
+		offset  = bd_size - 4096LLU;
+		offset &= ~4095LLU;
+		break;
+	case DRBD_MD_INDEX_FLEX_EXT:
+		offset = 0;
+		break;
+	}
+	return offset;
+}
+
+unsigned long bm_words(u64 sectors, int bytes_per_bit)
+{
+	unsigned long long bits;
+	unsigned long long words;
+
+	bits = ALIGN(sectors, 8) / (bytes_per_bit / 512);
+	words = ALIGN(bits, 64) >> LN2_BPL;
+
+	return words;
+}
+
+static void printf_bm_eol(unsigned int i)
+{
+	if ((i & 31) == 0)
+		printf("\n   # at %llukB\n   ", (256LLU * i));
+	else
+		printf("\n   ");
+}
+
+/* le_u64, because we want to be able to hexdump it reliably
+ * regardless of sizeof(long) */
+void printf_bm(struct format *cfg)
+{
+	off_t bm_on_disk_off = cfg->bm_offset;
+	le_u64 const *bm = on_disk_buffer;
+	le_u64 cw; /* current word for rll encoding */
+	const unsigned int n = cfg->bm_bytes/sizeof(*bm);
+	unsigned int count = 0;
+	unsigned int n_buffer = 0;
+	unsigned int r; /* real offset */
+	unsigned int i; /* in-buffer offset */
+	unsigned int j;
+
+	i=0; r=0;
+	cw.le = 0; /* silence compiler warning */
+	printf("bm {");
+	while (r < n) {
+		/* need to read on first iteration,
+		 * and on buffer wrap */
+		if (r*sizeof(*bm) % buffer_size == 0) {
+			size_t chunk = ALIGN( (n-r)*sizeof(*bm), 512 );
+			if (chunk > buffer_size) chunk = buffer_size;
+			ASSERT(chunk);
+			pread_or_die(cfg->md_fd, on_disk_buffer,
+				chunk, bm_on_disk_off, "printf_bm");
+			bm_on_disk_off += chunk;
+			i = 0;
+			n_buffer = chunk/sizeof(*bm);
+		}
+		ASSERT(i < n_buffer);
+		if (count == 0) cw = bm[i];
+		if ((i & 3) == 0) {
+			if (!count) printf_bm_eol(r);
+
+			for (j = i+1; j < n_buffer; j++) {
+				if(cw.le != bm[j].le) break;
+			}
+			j &= ~3; // round down to a multiple of 4
+			unsigned int tmp = (j-i);
+			if (tmp > 4) {
+				count += tmp;
+				r += tmp;
+				i = j;
+				if (j == n_buffer && r < n) continue;
+			}
+			if (count) {
+				printf(" %u times 0x"X64(016)";",
+				       count, le64_to_cpu(cw.le));
+				count = 0;
+				continue;
+			}
+		}
+		ASSERT(i < n_buffer);
+		printf(" 0x"X64(016)";", le64_to_cpu(bm[i].le));
+		r++; i++;
+	}
+	printf("\n}\n");
+}
+
+int v07_style_md_open(struct format *cfg)
+{
+	struct stat sb;
+	unsigned long words;
+
+	cfg->md_fd = open(cfg->md_device_name, O_RDWR | O_DIRECT);
+
+	if (cfg->md_fd == -1) {
+		PERROR("open(%s) failed", cfg->md_device_name);
+		exit(20);
+	}
+
+	if (fstat(cfg->md_fd, &sb)) {
+		PERROR("fstat(%s) failed", cfg->md_device_name);
+		exit(20);
+	}
+
+	if (!S_ISBLK(sb.st_mode)) {
+		fprintf(stderr, "'%s' is not a block device!\n",
+			cfg->md_device_name);
+		exit(20);
+	}
+
+	if (is_v08(cfg)) {
+		ASSERT(cfg->md_index != DRBD_MD_INDEX_INTERNAL);
+	}
+
+	cfg->bd_size = bdev_size(cfg->md_fd);
+	if ((cfg->bd_size >> 9) < MD_BM_OFFSET_07) {
+		fprintf(stderr, "%s is only %llu bytes. That's not enough.\n",
+			cfg->md_device_name, cfg->bd_size);
+		exit(10);
+	}
+	cfg->md_offset =
+		v07_style_md_get_byte_offset(cfg->md_index, cfg->bd_size);
+	if (cfg->md_offset > cfg->bd_size - 4096) {
+		fprintf(stderr,
+			"Device too small: expecting meta data block at\n"
+			"byte offset %lld, but %s is only %llu bytes.\n",
+			(signed long long)cfg->md_offset,
+			cfg->md_device_name,
+			cfg->bd_size);
+		exit(10);
+	}
+
+	if (cfg->ops->md_disk_to_cpu(cfg)) {
+		return -1;
+	}
+
+	cfg->al_offset = cfg->md_offset + cfg->md.al_offset * 512;
+	cfg->bm_offset = cfg->md_offset + cfg->md.bm_offset * 512;
+
+	// For the case that someone modified la_sect by hand..
+	if( (cfg->md_index == DRBD_MD_INDEX_INTERNAL ||
+	     cfg->md_index == DRBD_MD_INDEX_FLEX_INT ) &&
+	    (cfg->md.la_sect*512 > cfg->md_offset) ) {
+		printf("la-size-sect was too big, fixed.\n");
+		cfg->md.la_sect = cfg->md_offset/512;
+	}
+	if(cfg->md.bm_bytes_per_bit == 0 ) {
+		printf("bm-byte-per-bit was 0, fixed. (Set to 4096)\n");
+		cfg->md.bm_bytes_per_bit = 4096;
+	}
+	words = bm_words(cfg->md.la_sect, cfg->md.bm_bytes_per_bit);
+	cfg->bm_bytes = words * sizeof(long);
+
+	//fprintf(stderr,"al_offset: "U64" (%d)\n", cfg->al_offset, cfg->md.al_offset);
+	//fprintf(stderr,"bm_offset: "U64" (%d)\n", cfg->bm_offset, cfg->md.bm_offset);
+
+	cfg->bits_set = -1U;
+
+	/* FIXME paranoia verify that unused bits and words are unset... */
+	/* FIXME paranoia verify that unused bits and words are unset... */
+
+	return 0;
+}
+
+int v07_md_disk_to_cpu(struct format *cfg)
+{
+	PREAD(cfg->md_fd, on_disk_buffer,
+		sizeof(struct md_on_disk_07), cfg->md_offset);
+	md_disk_07_to_cpu(&cfg->md, (struct md_on_disk_07*)on_disk_buffer);
+	return !is_valid_md(Drbd_07,&cfg->md, cfg->md_index, cfg->bd_size);
+}
+
+int v07_md_cpu_to_disk(struct format *cfg)
+{
+	if (!is_valid_md(Drbd_07, &cfg->md, cfg->md_index, cfg->bd_size))
+		return -1;
+	md_cpu_to_disk_07(on_disk_buffer, &cfg->md);
+	PWRITE(cfg->md_fd, on_disk_buffer,
+		sizeof(struct md_on_disk_07), cfg->md_offset);
+	return 0;
+}
+
+int v07_parse(struct format *cfg, char **argv, int argc, int *ai)
+{
+	long index;
+	char *e;
+
+	if (argc < 2) {
+		fprintf(stderr, "Too few arguments for format\n");
+		return -1;
+	}
+
+	cfg->md_device_name = strdup(argv[0]);
+	if (!strcmp(argv[1],"internal")) {
+		index =
+		  is_v07(cfg) ? DRBD_MD_INDEX_INTERNAL
+			      : DRBD_MD_INDEX_FLEX_INT;
+	} else if (!strcmp(argv[1],"flex-external")) {
+		index = DRBD_MD_INDEX_FLEX_EXT;
+	} else if (!strcmp(argv[1],"flex-internal")) {
+		index = DRBD_MD_INDEX_FLEX_INT;
+	} else {
+		e = argv[1];
+		errno = 0;
+		index = strtol(argv[1], &e, 0);
+		if (*e != 0 || 0 > index || index > 255 || errno != 0) {
+			fprintf(stderr, "'%s' is not a valid index number.\n", argv[1]);
+			return -1;
+		}
+	}
+	cfg->md_index = index;
+
+	*ai += 2;
+
+	return 0;
+}
+
+int v07_md_initialize(struct format *cfg)
+{
+	memset(&cfg->md, 0, sizeof(cfg->md));
+
+	cfg->md.la_sect = 0;
+	cfg->md.gc[Flags] = 0;
+	cfg->md.gc[HumanCnt] = 1;	/* THINK 0? 1? */
+	cfg->md.gc[TimeoutCnt] = 1;
+	cfg->md.gc[ConnectedCnt] = 1;
+	cfg->md.gc[ArbitraryCnt] = 1;
+	cfg->md.magic = DRBD_MD_MAGIC_07;
+
+	return md_initialize_common(cfg);
+}
+
+/******************************************
+  }}} end of v07
+ ******************************************/
+/******************************************
+ begin of v08 {{{
+ ******************************************/
+
+int v08_md_disk_to_cpu(struct format *cfg)
+{
+	PREAD(cfg->md_fd, on_disk_buffer,
+		sizeof(struct md_on_disk_08), cfg->md_offset);
+	md_disk_08_to_cpu(&cfg->md, (struct md_on_disk_08*)on_disk_buffer);
+	return !is_valid_md(Drbd_08, &cfg->md, cfg->md_index, cfg->bd_size);
+}
+
+int v08_md_cpu_to_disk(struct format *cfg)
+{
+	if (!is_valid_md(Drbd_08, &cfg->md, cfg->md_index, cfg->bd_size))
+		return -1;
+	md_cpu_to_disk_08((struct md_on_disk_08 *)on_disk_buffer, &cfg->md);
+	PWRITE(cfg->md_fd, on_disk_buffer,
+		sizeof(struct md_on_disk_08), cfg->md_offset);
+	return 0;
+}
+
+int v08_md_initialize(struct format *cfg)
+{
+	size_t i;
+
+	memset(&cfg->md, 0, sizeof(cfg->md));
+
+	cfg->md.la_sect = 0;
+	cfg->md.uuid[Current] = UUID_JUST_CREATED;
+	cfg->md.uuid[Bitmap] = 0;
+	for ( i=History_start ; i<=History_end ; i++ ) {
+		cfg->md.uuid[i]=0;
+	}
+	cfg->md.flags = 0;
+	cfg->md.magic = DRBD_MD_MAGIC_08;
+
+	return md_initialize_common(cfg);
+}
+
+/******************************************
+  }}} end of v08
+ ******************************************/
+int meta_get_gi(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	cfg->ops->get_gi(&cfg->md);
+
+	return cfg->ops->close(cfg);
+}
+
+int meta_show_gi(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	char ppb[10];
+
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	cfg->ops->show_gi(&cfg->md);
+
+	if (cfg->md.la_sect) {
+		printf("last agreed size: %s\n",
+		       ppsize(ppb, cfg->md.la_sect >> 1));
+		printf("%u bits set in the bitmap [ %s out of sync ]\n",
+		       cfg->bits_set, ppsize(ppb, cfg->bits_set * 4));
+	} else {
+		printf("zero size device -- never seen peer yet?\n");
+	}
+
+	return cfg->ops->close(cfg);
+}
+
+int meta_dstate(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	if(cfg->md.flags & MDF_Consistent) {
+		if(cfg->md.flags & MDF_WasUpToDate) {
+			printf("Consistent/DUnknown\n");
+		} else {
+			printf("Outdated/DUnknown\n");
+		}
+	} else {
+		printf("Inconsistent/DUnknown\n");
+	}
+
+	return cfg->ops->close(cfg);
+}
+
+int meta_set_gi(struct format *cfg, char **argv, int argc)
+{
+	struct md_cpu tmp;
+	int err;
+
+	if (argc > 1) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+	if (argc < 1) {
+		fprintf(stderr, "Required Argument missing\n");
+		exit(10);
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	tmp = cfg->md;
+
+	cfg->ops->set_gi(&tmp,argv,argc);
+	printf("previously ");
+	cfg->ops->get_gi(&cfg->md);
+	printf("set GI to  ");
+	cfg->ops->get_gi(&tmp);
+
+	if (!confirmed("Write new GI to disk?")) {
+		printf("Operation cancelled.\n");
+		exit(0);
+	}
+
+	cfg->md = tmp;
+
+	err = cfg->ops->md_cpu_to_disk(cfg);
+	err = cfg->ops->close(cfg) || err;
+	if (err)
+		fprintf(stderr, "update failed\n");
+
+	return err;
+}
+
+void print_dump_header()
+{
+	char time_str[60];
+	struct utsname nodeinfo;
+	time_t t = time(NULL);
+	int i;
+
+	strftime(time_str, sizeof(time_str), "%F %T %z [%s]", localtime(&t));
+	uname(&nodeinfo);
+	printf("# DRBD meta data dump\n# %s\n# %s>",
+		time_str, nodeinfo.nodename);
+
+	for (i=0; i < global_argc; i++)
+		printf(" %s",global_argv[i]);
+	printf("\n#\n\n");
+}
+
+int meta_dump_md(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	int i;
+
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	print_dump_header();
+	printf("version \"%s\";\n\n", cfg->ops->name);
+	if (format_version(cfg) < Drbd_08) {
+		printf("gc {\n   ");
+		for (i = 0; i < GEN_CNT_SIZE; i++) {
+			printf(" %d;", cfg->md.gc[i]);
+		}
+		printf("\n}\n");
+	} else { // >= 08
+		printf("uuid {\n   ");
+		for ( i=Current ; i<UUID_SIZE ; i++ ) {
+			printf(" 0x"X64(016)";", cfg->md.uuid[i]);
+		}
+		printf("\n");
+		printf("    flags 0x"X32(08)";\n",cfg->md.flags);
+		printf("}\n");
+	}
+
+	if (format_version(cfg) >= Drbd_07) {
+		printf("la-size-sect "U64";\n", cfg->md.la_sect);
+		if (format_version(cfg) >= Drbd_08) {
+			printf("bm-byte-per-bit "U32";\n",
+			       cfg->md.bm_bytes_per_bit);
+			printf("device-uuid 0x"X64(016)";\n",
+			       cfg->md.device_uuid);
+		}
+		printf("# bm-bytes %u;\n", cfg->bm_bytes);
+		printf("# bits-set %u;\n", cfg->bits_set);
+		printf_bm(cfg);
+	}
+
+	/* MAYBE dump activity log?
+	 * but that probably does not make any sense,
+	 * beyond debugging. */
+
+	return cfg->ops->close(cfg);
+}
+
+void md_parse_error(const char *etext)
+{
+	fprintf(stderr,"Parse error '%s' expected.",etext);
+	exit(10);
+}
+
+#define EXP(TOKEN) if(yylex() != TOKEN) md_parse_error( #TOKEN );
+
+int meta_restore_md(struct format *cfg, char **argv, int argc)
+{
+	int i,times;
+	int err;
+	off_t bm_on_disk_off;
+	le_u64 *bm, value;
+
+	if (argc > 0) {
+		yyin = fopen(argv[0],"r");
+		if(yyin == NULL) {
+			fprintf(stderr, "open of '%s' failed.\n",argv[0]);
+			exit(20);
+		}
+	}
+
+	if (!cfg->ops->open(cfg)) {
+		if (!confirmed("Valid meta-data in place, overwrite?"))
+			return -1;
+	}
+
+	EXP(TK_VERSION); EXP(TK_STRING);
+	if(strcmp(yylval.txt,cfg->ops->name)) {
+		fprintf(stderr,"dump is '%s' you requested '%s'.\n",
+			yylval.txt,cfg->ops->name);
+		exit(10);
+	}
+	EXP(';');
+	if (format_version(cfg) < Drbd_08) {
+		EXP(TK_GC); EXP('{');
+		for (i = 0; i < GEN_CNT_SIZE; i++) {
+			EXP(TK_NUM); EXP(';');
+			cfg->md.gc[i] = yylval.u64;
+		}
+		EXP('}');
+	} else { // >= 08
+		EXP(TK_UUID); EXP('{');
+		for ( i=Current ; i<UUID_SIZE ; i++ ) {
+			EXP(TK_U64); EXP(';');
+			cfg->md.uuid[i] = yylval.u64;
+		}
+		EXP(TK_FLAGS); EXP(TK_U32); EXP(';');
+		cfg->md.flags = (u32)yylval.u64;
+		EXP('}');
+	}
+	EXP(TK_LA_SIZE); EXP(TK_NUM); EXP(';');
+	cfg->md.la_sect = yylval.u64;
+	if (format_version(cfg) >= Drbd_08) {
+		EXP(TK_BM_BYTE_PER_BIT); EXP(TK_NUM); EXP(';');
+		cfg->md.bm_bytes_per_bit = yylval.u64;
+		EXP(TK_DEVICE_UUID); EXP(TK_U64); EXP(';');
+		cfg->md.device_uuid = yylval.u64;
+	} else {
+		cfg->md.bm_bytes_per_bit = 4096;
+	}
+	EXP(TK_BM); EXP('{');
+	bm = (le_u64 *)on_disk_buffer;
+	i = 0;
+	bm_on_disk_off = cfg->bm_offset;
+	while(1) {
+		switch(yylex()) {
+		case TK_U64:
+			bm[i].le = cpu_to_le64(yylval.u64);
+			if (++i == buffer_size/sizeof(*bm)) {
+				pwrite_or_die(cfg->md_fd, on_disk_buffer,
+					buffer_size, bm_on_disk_off,
+					"meta_restore_md:TK_U64");
+				bm_on_disk_off += buffer_size;
+				i = 0;
+			}
+			EXP(';');
+			break;
+		case TK_NUM:
+			EXP(TK_TIMES);
+			times = yylval.u64;
+			EXP(TK_U64);
+			value.le = cpu_to_le64(yylval.u64);
+			EXP(';');
+			while(times--) {
+				bm[i] = value;
+				if (++i == buffer_size/sizeof(*bm)) {
+					pwrite_or_die(cfg->md_fd, on_disk_buffer,
+						buffer_size, bm_on_disk_off,
+						"meta_restore_md:TK_NUM");
+					bm_on_disk_off += buffer_size;
+					i = 0;
+				}
+			}
+			break;
+		case '}':
+			goto break_loop;
+		default:
+			md_parse_error("TK_U64, TK_NUM or }");
+			goto break_loop;
+		}
+	}
+	break_loop:
+	if (i) {
+		size_t s = i * sizeof(*bm);
+		memset(bm+i, 0x00, buffer_size - s);
+		/* need to sector-align this for O_DIRECT. to be
+ 		 * generic, maybe we even need to PAGE align it? */
+		s = ALIGN(s, 512);
+		pwrite_or_die(cfg->md_fd, on_disk_buffer,
+			s, bm_on_disk_off, "meta_restore_md");
+	}
+
+	err = cfg->ops->md_cpu_to_disk(cfg);
+	err = cfg->ops->close(cfg) || err;
+	if (err) {
+		fprintf(stderr, "Writing failed\n");
+		return -1;
+	}
+
+	printf("Successfully restored meta data\n");
+
+	return 0;
+}
+
+#undef EXP
+
+void md_convert_07_to_08(struct format *cfg)
+{
+	int i,j=1;
+	/*
+	 * FIXME
+	 * what about the Bitmap, and the Activity Log?
+	 * how to bring them over for internal meta data?
+	 *
+	 * maybe just refuse to convert anything that is not
+	 * "clean"? how to detect that?
+	 *
+	 * FIXME: if I am a crashed Primary, or Inconsistent,
+	 * or Want-Full-Sync or the like,
+	 * refuse, and indicate how to solve this */
+
+	printf("Converting meta data...\n");
+
+	//if (!cfg->bits_counted) count_bits(cfg);
+	/* FIXME:
+	 * if this is "internal" meta data, and I have bits set,
+	 * either move the bitmap into the newly expected place,
+	 * or refuse, and indicate how to solve this */
+
+	/* KB <-> sectors is done in the md disk<->cpu functions.
+	 * We only need to adjust the magic here. */
+	cfg->md.magic = DRBD_MD_MAGIC_08;
+
+	// The MDF Flags are (nearly) the same in 07 and 08
+	cfg->md.flags = cfg->md.gc[Flags];
+
+	cfg->md.uuid[Current] =
+		(u64)(cfg->md.gc[HumanCnt] & 0xffff) << 48 |
+		(u64)(cfg->md.gc[TimeoutCnt] & 0xffff) << 32 |
+		(u64)((cfg->md.gc[ConnectedCnt]+cfg->md.gc[ArbitraryCnt])
+		       & 0xffff) << 16 |
+		(u64)0xbabe;
+	cfg->md.uuid[Bitmap] = (u64)0;
+	if (cfg->bits_set) i = Bitmap;
+	else i = History_start;
+	for ( ; i<=History_end ; i++ ) {
+		cfg->md.uuid[i] = cfg->md.uuid[Current] - j*0x10000;
+		j++;
+	}
+	if (!is_valid_md(Drbd_08, &cfg->md, cfg->md_index, cfg->bd_size)) {
+		fprintf(stderr, "Conversion failed.\nThis is a bug :(\n");
+		exit(111);
+	}	
+}
+
+void md_convert_08_to_07(struct format *cfg)
+{
+	/*
+	 * FIXME
+	 * what about the Bitmap, and the Activity Log?
+	 * how to bring them over for internal meta data?
+	 *
+	 * maybe just refuse to convert anything that is not
+	 * "clean"? how to detect that?
+	 *
+	 * FIXME: if I am a crashed Primary, or Inconsistent,
+	 * or Want-Full-Sync or the like,
+	 * refuse, and indicate how to solve this */
+
+	printf("Converting meta data...\n");
+	//if (!cfg->bits_counted) count_bits(cfg);
+	/* FIXME:
+	 * if this is "internal" meta data, and I have bits set,
+	 * either move the bitmap into the newly expected place,
+	 * or refuse, and indicate how to solve this */
+
+	/* KB <-> sectors is done in the md disk<->cpu functions.
+	 * We only need to adjust the magic here. */
+	cfg->md.magic = DRBD_MD_MAGIC_07;
+
+	/* FIXME somehow generate GCs in a sane way */
+	/* FIXME convert the flags? */
+	printf("Conversion v08 -> v07 is BROKEN!\n"
+		"Be prepared to manually intervene!\n");
+	/* FIXME put some more helpful text here, indicating what exactly is to
+	 * be done to make this work as expected. */
+
+	if (!is_valid_md(Drbd_07, &cfg->md, cfg->md_index, cfg->bd_size)) {
+		fprintf(stderr, "Conversion failed.\nThis is a bug :(\n");
+		exit(111);
+	}	
+}
+
+/* if on the physical device we find some data we can interpret,
+ * print some informational message about what we found,
+ * and what we think how much room it needs.
+ *
+ * look into /usr/share/misc/magic for inspiration
+ * also consider e.g. xfsprogs/libdisk/fstype.c,
+ * and of course the linux kernel headers...
+ */
+struct fstype_s {
+	const char * type;
+	unsigned long long bnum, bsize;
+};
+
+int may_be_extX(const char *data, struct fstype_s *f)
+{
+	unsigned int size;
+	if (le16_to_cpu(*(u16*)(data+0x438)) == 0xEF53) {
+		if ( (le32_to_cpu(*(data+0x45c)) & 4) == 4 )
+			f->type = "ext3 filesystem";
+		else
+			f->type = "ext2 filesystem";
+		f->bnum  = le32_to_cpu(*(u32*)(data+0x404));
+		size     = le32_to_cpu(*(u32*)(data+0x418));
+		f->bsize = size == 0 ? 1024 :
+			size == 1 ? 2048 :
+			size == 2 ? 4096 :
+			4096; /* DEFAULT */
+		return 1;
+	}
+	return 0;
+}
+
+int may_be_xfs(const char *data, struct fstype_s *f)
+{
+	if (be32_to_cpu(*(u32*)(data+0)) == 0x58465342) {
+		f->type = "xfs filesystem";
+		f->bsize = be32_to_cpu(*(u32*)(data+4));
+		f->bnum  = be64_to_cpu(*(u64*)(data+8));
+		return 1;
+	}
+	return 0;
+}
+
+int may_be_reiserfs(const char *data, struct fstype_s *f)
+{
+	if (strncmp("ReIsErFs",data+0x10034,8) == 0 ||
+	    strncmp("ReIsEr2Fs",data+0x10034,9) == 0) {
+		f->type = "reiser filesystem";
+		f->bnum  = le32_to_cpu(*(u32*)(data+0x10000));
+		f->bsize = le16_to_cpu(*(u16*)(data+0x1002c));
+		return 1;
+	}
+	return 0;
+}
+
+int may_be_jfs(const char *data, struct fstype_s *f)
+{
+	if (strncmp("JFS1",data+0x8000,4) == 0) {
+		f->type = "JFS filesystem";
+		f->bnum = le64_to_cpu(*(u64*)(data+0x8008));
+		f->bsize = le32_to_cpu(*(u32*)(data+0x8018));
+		return 1;
+	}
+	return 0;
+}
+
+/* really large block size,
+ * will always refuse */
+#define REFUSE_BSIZE 0xFFFFffffFFFF0000LLU
+#define REFUSE_IT    f->bnum = 1; f->bsize = REFUSE_BSIZE;
+int may_be_swap(const char *data, struct fstype_s *f)
+{
+	int looks_like_swap =
+		strncmp(data+(1<<12)-10, "SWAP-SPACE", 10) == 0 ||
+		strncmp(data+(1<<12)-10, "SWAPSPACE2", 10) == 0 ||
+		strncmp(data+(1<<13)-10, "SWAP-SPACE", 10) == 0 ||
+		strncmp(data+(1<<13)-10, "SWAPSPACE2", 10) == 0;
+	if (looks_like_swap) {
+		f->type = "swap space signature";
+		REFUSE_IT
+		return 1;
+	}
+	return 0;
+}
+
+int may_be_LVM(const char *data, struct fstype_s *f)
+{
+	if (strncmp("LVM2",data+0x218,4) == 0) {
+		f->type = "LVM2 physical volume signature";
+		REFUSE_IT
+		return 1;
+	}
+	return 0;
+}
+
+void check_for_existing_data(struct format *cfg)
+{
+	struct fstype_s f;
+	size_t i;
+
+	PREAD(cfg->md_fd, on_disk_buffer, SO_MUCH, 0);
+
+	for (i = 0; i < SO_MUCH/sizeof(long); i++) {
+		if (((long*)(on_disk_buffer))[i] != 0LU) break;
+	}
+	/* all zeros? no message */
+	if (i == SO_MUCH/sizeof(long)) return;
+
+	f.type = "some data";
+	f.bnum = 0;
+	f.bsize = 0;
+
+/* FIXME add more detection magic
+ */
+
+	(void)(
+	may_be_swap     (on_disk_buffer,&f) ||
+	may_be_LVM      (on_disk_buffer,&f) ||
+
+	may_be_extX     (on_disk_buffer,&f) ||
+	may_be_xfs      (on_disk_buffer,&f) ||
+	may_be_jfs      (on_disk_buffer,&f) ||
+	may_be_reiserfs (on_disk_buffer,&f)
+	);
+
+	/* FIXME
+	 * some of the messages below only make sense for internal meta data.
+	 * for external meta data, we now only checked the meta-disk.
+	 * we should still check the actual lower level storage area for
+	 * existing data, too, and give apropriate warnings when it would
+	 * appear to be truncated by too small external meta data */
+
+	printf("\nFound %s ", f.type);
+	if (f.bnum) {
+		/* FIXME overflow check missing!
+		 * relevant for ln2(bsize) + ln2(bnum) >= 64, thus only for
+		 * device sizes of more than several exa byte.
+		 * seems irrelevant to me for now.
+		 */
+		u64 fs_kB = ((f.bsize * f.bnum) + (1<<10)-1) >> 10;
+		u64 max_usable_kB;
+
+		if (f.bsize == REFUSE_BSIZE) {
+			printf(
+"\nDevice size would be truncated, which\n"
+"would corrupt data and result in\n"
+"'access beyond end of device' errors.\n"
+"If you want me to do this, you need to zero out the first part\n"
+"of the device (destroy the content).\n"
+"You should be very sure that you mean it.\n"
+"Operation refused.\n\n");
+			exit(40); /* FIXME sane exit code! */
+		}
+
+#define min(x,y) ((x) < (y) ? (x) : (y))
+		max_usable_kB =
+			min( cfg->md_offset,
+			min( cfg->al_offset,
+			     cfg->bm_offset )) >> 10;
+#undef min
+
+		printf("md_offset %llu\n", cfg->md_offset);
+		printf("al_offset %llu\n", cfg->al_offset);
+		printf("bm_offset %llu\n", cfg->bm_offset);
+
+		/* looks like file system data */
+		printf("which uses "U64" kB\n", fs_kB);
+		printf("current configuration leaves usable "U64" kB\n", max_usable_kB);
+		if (fs_kB > max_usable_kB) {
+			printf(
+"\nDevice size would be truncated, which\n"
+"would corrupt data and result in\n"
+"'access beyond end of device' errors.\n"
+"You need to either\n"
+"   * use external meta data (recommended)\n"
+"   * shrink that filesystem first\n"
+"   * zero out the device (destroy the filesystem)\n"
+"Operation refused.\n\n");
+			exit(40); /* FIXME sane exit code! */
+		}
+	}
+}
+
+void check_internal_md_flavours(struct format * cfg) {
+	struct md_cpu md_07;
+	struct md_cpu md_07p;
+	struct md_cpu md_08;
+	off_t fixed_offset, flex_offset;
+	int have_fixed_v07 = 0;
+	int have_flex_v07  = 0;
+	int have_flex_v08  = 0;
+
+	ASSERT( cfg->md_index == DRBD_MD_INDEX_INTERNAL ||
+		cfg->md_index == DRBD_MD_INDEX_FLEX_INT );
+
+	fixed_offset = v07_style_md_get_byte_offset(
+		DRBD_MD_INDEX_INTERNAL, cfg->bd_size);
+	flex_offset = v07_style_md_get_byte_offset(
+		DRBD_MD_INDEX_FLEX_INT, cfg->bd_size);
+
+	printf("%lld\n%lld\n%lld\n", cfg->bd_size, fixed_offset, flex_offset);
+	if (fixed_offset < (off_t)cfg->bd_size - 4096) {
+		/* ... v07 fixed-size internal meta data? */
+		PREAD(cfg->md_fd, on_disk_buffer, 4096, fixed_offset);
+	
+		md_disk_07_to_cpu(&md_07,
+			(struct md_on_disk_07*)on_disk_buffer);
+		have_fixed_v07 = is_valid_md(Drbd_07,
+			&md_07, DRBD_MD_INDEX_INTERNAL, cfg->bd_size);
+	}
+
+	PREAD(cfg->md_fd, on_disk_buffer, 4096, flex_offset);
+	
+	/* ... v07 (plus) flex-internal meta data? */
+	md_disk_07_to_cpu(&md_07p, (struct md_on_disk_07*)on_disk_buffer);
+	have_flex_v07 = is_valid_md(Drbd_07,
+		&md_07p, DRBD_MD_INDEX_FLEX_INT, cfg->bd_size);
+
+	/* ... v08 flex-internal meta data?
+	 * (same offset, same on disk data) */
+	md_disk_08_to_cpu(&md_08, (struct md_on_disk_08*)on_disk_buffer);
+	have_flex_v08 = is_valid_md(Drbd_08,
+		&md_08, DRBD_MD_INDEX_FLEX_INT, cfg->bd_size);
+
+	if (!(have_fixed_v07 || have_flex_v07 || have_flex_v08))
+		return;
+
+	ASSERT(have_flex_v07 == 0 || have_flex_v08 == 0); /* :-) */
+
+	fprintf(stderr, "You want me to create a %s%s style %s internal meta data block.\n",
+		cfg->ops->name,
+		(is_v07(cfg) && cfg->md_index == DRBD_MD_INDEX_FLEX_INT) ? "(plus)" : "",
+		cfg->md_index == DRBD_MD_INDEX_FLEX_INT ? "flexible-size" : "fixed-size");
+
+	if (have_fixed_v07) {
+		fprintf(stderr, "There apears to be a v07 fixed-size internal meta data block\n"
+				"already in place on %s at byte offset %llu\n",
+				cfg->md_device_name, fixed_offset);
+	}
+	if (have_flex_v07) {
+		fprintf(stderr, "There apears to be a v07(plus) flexible-size internal meta data block\n"
+				"already in place on %s at byte offset %llu",
+		cfg->md_device_name, flex_offset);
+	}
+	if (have_flex_v08) {
+		fprintf(stderr, "There apears to be a v08 flexible-size internal meta data block\n"
+				"already in place on %s at byte offset %llu",
+		cfg->md_device_name, flex_offset);
+	}
+
+	if (have_fixed_v07 && have_flex_v07) {
+		fprintf(stderr, "Don't know what to do now. If you want this to work,\n"
+				"Please wipe out at least one of these.\n");
+		exit(10);
+	}
+
+	if (is_v08(cfg)) {
+		if (have_flex_v08) {
+			if (!confirmed("Do you really want to overwrite the existing v08 meta-data?")) {
+				printf("Operation cancelled.\n");
+				exit(1); // 1 to avoid online resource counting
+			}
+			/* no need to wipe flex offset,
+			 * will be overwritten with new data */
+			have_flex_v08 = 0;
+		}
+		if ( (have_fixed_v07||have_flex_v07) ) {
+			if (confirmed("Convert the existing v07 meta-data to v08?")) {
+				cfg->md = have_fixed_v07 ? md_07 : md_07p;
+				md_convert_07_to_08(cfg);
+				/* goto wipe; */
+			} else if (!confirmed("So you want me to wipe out the v07 meta-data?")) {
+				printf("Operation cancelled.\n");
+				exit(1); // 1 to avoid online resource counting
+			}
+		}
+	} else { /* is_v07(cfg) */
+		if (have_fixed_v07 || have_flex_v07) {
+			if (!confirmed("Do you really want to overwrite the existing v07 meta-data?")) {
+				printf("Operation cancelled.\n");
+				exit(1); // 1 to avoid online resource counting
+			}
+			/* no need to wipe the requested flavour,
+			 * will be overwritten with new data */
+			if (cfg->md_index == DRBD_MD_INDEX_INTERNAL)
+				have_fixed_v07 = 0;
+			else
+				have_flex_v07 = 0;
+		}
+		if (have_flex_v08) {
+			if (confirmed("Valid v08 meta-data found, convert back to v07?")) {
+				cfg->md = md_08;
+				md_convert_08_to_07(cfg);
+				if (cfg->md_index == DRBD_MD_INDEX_FLEX_INT)
+					have_flex_v08 = 0;
+				/* goto wipe; */
+			}
+		}
+	}
+
+ /* wipe: */
+	memset(on_disk_buffer, 0x00, 4096);
+	if (have_fixed_v07) {
+		pwrite_or_die(cfg->md_fd, on_disk_buffer, 4096, fixed_offset,
+			"wipe fixed-size v07 internal md");
+	}
+	if (have_flex_v08 || have_flex_v07)
+		pwrite_or_die(cfg->md_fd, on_disk_buffer, 4096, flex_offset,
+			"wipe flexible-size internal md");
+}
+
+int meta_create_md(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	int err = 0;
+
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	if (cfg->ops->open(cfg)) /* reset cfg->md if not valid */
+		memset(&cfg->md, 0, sizeof(cfg->md));
+
+	/* the offset of v07 fixed-size internal meta data is different from
+	 * the offset of the flexible-size v07 ("plus") and v08 (default)
+	 * internal meta data.
+	 * to avoid the situation where we would have "valid" meta data blocks
+	 * of different versions at different offsets, we also need to check
+	 * the other format, and the other offset.
+	 * 
+	 * on a request to create v07 fixed-size internal meta data, we also
+	 * check flex-internal v08 [and v07 (plus)] at the other offset.
+	 *
+	 * on a request to create v08 flex-internal meta data (or v07 plus, for
+	 * that matter), we also check the same offset for the respective other
+	 * flex-internal format version, as well as the v07 fixed-size internal
+	 * meta data offset for its flavour of meta data.
+	 */
+	if (cfg->md_index == DRBD_MD_INDEX_INTERNAL ||
+	    cfg->md_index == DRBD_MD_INDEX_FLEX_INT)
+		check_internal_md_flavours(cfg);
+
+	printf("Writing meta data...\n");
+	if (!cfg->md.magic) /* initialize unless converted */
+		err = cfg->ops->md_initialize(cfg);
+	err = err || cfg->ops->md_cpu_to_disk(cfg); // <- short circuit
+	err = cfg->ops->close(cfg)          || err; // <- close always
+	if (err)
+		fprintf(stderr, "operation failed\n");
+	else
+		printf("New drbd meta data block sucessfully created.\n");
+
+	return err;
+}
+
+int meta_wipe_md(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	int virgin, err;
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	virgin = cfg->ops->open(cfg);
+	if (virgin) {
+		fprintf(stderr,"There apears to be no drbd meta data to wipe out?\n");
+		return 0;
+	}
+
+	printf("Wiping meta data...\n");
+	memset(on_disk_buffer, 0, 4096);
+	PWRITE(cfg->md_fd, on_disk_buffer, 4096, cfg->md_offset);
+
+	err = cfg->ops->close(cfg);
+	if (err)
+		fprintf(stderr, "operation failed\n");
+	else
+		printf("DRBD meta data block successfully wiped out.\n");
+
+	return err;
+}
+
+int meta_outdate(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	int err;
+
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	if (cfg->ops->outdate_gi(&cfg->md)) {
+		fprintf(stderr, "Device is inconsistent.\n");
+		exit(5);
+	}
+
+	err = cfg->ops->md_cpu_to_disk(cfg);
+	err = cfg->ops->close(cfg)          || err; // <- close always
+	if (err)
+		fprintf(stderr, "update failed\n");
+
+	return err;
+}
+
+int meta_read_dev_uuid(struct format *cfg, char **argv __attribute((unused)), int argc)
+{
+	if (argc > 0) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	printf(X64(016)"\n",cfg->md.device_uuid);
+
+	return cfg->ops->close(cfg);
+}
+
+int meta_write_dev_uuid(struct format *cfg, char **argv, int argc)
+{
+	int err;
+
+	if (argc > 1) {
+		fprintf(stderr, "Ignoring additional arguments\n");
+	}
+	if (argc < 1) {
+		fprintf(stderr, "Required Argument missing\n");
+		exit(10);
+	}
+
+	if (cfg->ops->open(cfg))
+		return -1;
+
+	cfg->md.device_uuid = strto_u64(argv[0],NULL,16);
+
+	err = cfg->ops->md_cpu_to_disk(cfg);
+	err = cfg->ops->close(cfg) || err;
+	if (err)
+		fprintf(stderr, "update failed\n");
+
+	return err;
+}
+
+char *progname = NULL;
+void print_usage_and_exit()
+{
+	char **args;
+	size_t i;
+
+	printf
+	    ("\nUSAGE: %s [--force] DEVICE FORMAT [FORMAT ARGS...] COMMAND [CMD ARGS...]\n",
+	     progname);
+
+	printf("\nFORMATS:\n");
+	for (i = Drbd_06; i < Drbd_Unknown; i++) {
+		printf("  %s", f_ops[i].name);
+		if ((args = f_ops[i].args)) {
+			while (*args) {
+				printf(" %s", *args++);
+			}
+		}
+		printf("\n");
+	}
+
+	printf("\nCOMMANDS:\n");
+	for (i = 0; i < ARRY_SIZE(cmds); i++) {
+		if (!cmds[i].show_in_usage)
+			continue;
+		printf("  %s %s\n", cmds[i].name,
+		       cmds[i].args ? cmds[i].args : "");
+	}
+
+	exit(20);
+}
+
+int parse_format(struct format *cfg, char **argv, int argc, int *ai)
+{
+	enum Known_Formats f;
+
+	if (argc < 1) {
+		fprintf(stderr, "Format identifier missing\n");
+		return -1;
+	}
+
+	for (f = Drbd_06; f < Drbd_Unknown; f++) {
+		if (!strcmp(f_ops[f].name, argv[0]))
+			break;
+	}
+	if (f == Drbd_Unknown) {
+		fprintf(stderr, "Unknown format '%s'.\n", argv[0]);
+		return -1;
+	}
+
+	(*ai)++;
+
+	cfg->ops = f_ops + f;
+	return cfg->ops->parse(cfg, argv + 1, argc - 1, ai);
+}
+
+int is_attached(int minor)
+{
+	FILE *pr;
+	char token[40];
+	int rv=-1;
+	long m,cm=-1;
+	char *p;
+
+	pr = fopen("/proc/drbd","r");
+	if(!pr) return 0;
+
+	while(fget_token(token,40,pr) != EOF) {
+		m=strtol(token,&p,10);
+		if(*p==':' && p-token == (long)strlen(token)-1 ) cm=m;
+		if( cm == minor && rv == -1 ) rv=1;
+		if( cm == minor ) {
+			if(!strcmp(token,"cs:Unconfigured")) rv = 0;
+			if(!strncmp(token,"ds:Diskless",11)) rv = 0;
+		}
+	}
+	fclose(pr);
+
+	if(rv == -1) rv = 0; // minor not found -> not attached.
+	return rv;
+}
+
+/* CALL ONLY ONCE as long as on_disk_buffer is global! */
+struct format *new_cfg()
+{
+	int err;
+	struct format *cfg;
+
+	errno = 0;
+	pagesize = sysconf(_SC_PAGESIZE);
+	if (errno) {
+		perror("could not determine pagesize");
+		exit(20);
+	}
+	cfg = calloc(1, sizeof(struct format));
+	if (!cfg) {
+		fprintf(stderr, "could not calloc() cfg\n");
+		exit(20);
+	}
+	err = posix_memalign(&on_disk_buffer,pagesize,
+		(buffer_size+pagesize-1)/pagesize*pagesize);
+	if (err) {
+		fprintf(stderr, "could not posix_memalign() on_disk_buffer\n");
+		exit(20);
+	}
+	return cfg;
+}
+
+int main(int argc, char **argv)
+{
+	struct meta_cmd *command = NULL;
+	struct format *cfg;
+	size_t i;
+	int ai;
+
+
+#if 1
+	if (sizeof(struct md_on_disk_07) != 4096) {
+		fprintf(stderr, "Where did you get this broken build!?\n"
+			        "sizeof(md_on_disk_07) == %lu, should be 4096\n",
+				(unsigned long)sizeof(struct md_on_disk_07));
+		exit(111);
+	}
+	if (sizeof(struct md_on_disk_08) != 4096) {
+		fprintf(stderr, "Where did you get this broken build!?\n"
+			        "sizeof(md_on_disk_08) == %lu, should be 4096\n",
+				(unsigned long)sizeof(struct md_on_disk_08));
+		exit(111);
+	}
+#if 0
+	printf("v07: al_offset: %u\n", (int)&(((struct md_on_disk_07*)0)->al_offset));
+	printf("v07: bm_offset: %u\n", (int)&(((struct md_on_disk_07*)0)->bm_offset));
+	printf("v08: al_offset: %u\n", (int)&(((struct md_on_disk_08*)0)->al_offset));
+	printf("v08: bm_offset: %u\n", (int)&(((struct md_on_disk_08*)0)->bm_offset));
+	exit(0);
+#endif
+#endif
+
+	if ((progname = strrchr(argv[0], '/'))) {
+		argv[0] = ++progname;
+	} else {
+		progname = argv[0];
+	}
+
+	if (argc < 4)
+		print_usage_and_exit();
+
+	/* so dump_md can write a nice header */
+	global_argc = argc;
+	global_argv = argv;
+
+        /* Check for options (e.g. --force) */
+        while (1) {
+            int c = getopt_long(argc,argv,make_optstring(metaopt,0),metaopt,0);
+
+            if (c == -1)
+                break;
+
+            switch (c) {
+            case 'f':
+                force = 1;
+                break;
+            default:
+                print_usage_and_exit();
+                break;
+            }
+        }
+
+        // Next argument to process is specified by optind...
+        ai = optind;
+
+	cfg = new_cfg();
+	cfg->drbd_dev_name = argv[ai++];
+
+	if (parse_format(cfg, argv + ai, argc - ai, &ai)) {
+		/* parse has already printed some error message */
+		exit(20);
+	}
+
+	if (ai >= argc) {
+		fprintf(stderr, "command missing\n");
+		exit(20);
+	}
+
+	for (i = 0; i < ARRY_SIZE(cmds); i++) {
+		if (!strcmp(cmds[i].name, argv[ai])) {
+			command = cmds + i;
+			break;
+		}
+	}
+	if (command == NULL) {
+		fprintf(stderr, "Unknown command '%s'.\n", argv[ai]);
+		exit(20);
+	}
+	ai++;
+
+	/* does exit() unless we aquired the lock.
+	 * unlock happens implicitly when the process dies,
+	 * but may be requested implicitly
+	 */
+	cfg->lock_fd = dt_lock_drbd(cfg->drbd_dev_name);
+
+	/* unconditionally check whether this is in use */
+	if (is_attached(dt_minor_of_dev(cfg->drbd_dev_name))) {
+		if (!(force && (command->function == meta_dump_md))) {
+			fprintf(stderr, "Device '%s' is configured!\n",
+				cfg->drbd_dev_name);
+			exit(20);
+		}
+	}
+
+	return command->function(cfg, argv + ai, argc - ai);
+	/* and if we want an explicit free,
+	 * this would be the place for it.
+	 * free(cfg->md_device_name), free(cfg) ...
+	 */
+}



More information about the drbd-cvs mailing list