Index: uspace/srv/bd/hr/hr.c
===================================================================
--- uspace/srv/bd/hr/hr.c	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/hr.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -79,5 +79,5 @@
 	size_t i, size;
 	hr_config_t *cfg;
-	hr_volume_t *new_volume;
+	hr_volume_t *vol;
 	ipc_call_t call;
 
@@ -127,5 +127,6 @@
 	}
 
-	rc = hr_create_vol_struct(&new_volume, cfg->level, cfg->devname);
+	rc = hr_create_vol_struct(&vol, cfg->level, cfg->devname,
+	    HR_METADATA_NATIVE);
 	if (rc != EOK) {
 		free(cfg);
@@ -134,34 +135,34 @@
 	}
 
-	rc = hr_init_extents_from_cfg(new_volume, cfg);
-	if (rc != EOK)
-		goto error;
-
-	new_volume->hr_ops.init(new_volume);
-	if (rc != EOK)
-		goto error;
-
-	rc = hr_metadata_init(new_volume, new_volume->in_mem_md);
-	if (rc != EOK)
-		goto error;
-
-	rc = new_volume->hr_ops.create(new_volume);
-	if (rc != EOK)
-		goto error;
-
-	rc = hr_metadata_save(new_volume, WITH_STATE_CALLBACK);
-	if (rc != EOK)
-		goto error;
-
-	rc = hr_register_volume(new_volume);
+	rc = hr_init_extents_from_cfg(vol, cfg);
+	if (rc != EOK)
+		goto error;
+
+	vol->hr_ops.init(vol);
+	if (rc != EOK)
+		goto error;
+
+	rc = vol->meta_ops->init_vol2meta(vol, vol->in_mem_md);
+	if (rc != EOK)
+		goto error;
+
+	rc = vol->hr_ops.create(vol);
+	if (rc != EOK)
+		goto error;
+
+	rc = vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
+	if (rc != EOK)
+		goto error;
+
+	rc = hr_register_volume(vol);
 	if (rc != EOK)
 		goto error;
 
 	fibril_rwlock_write_lock(&hr_volumes_lock);
-	list_append(&new_volume->lvolumes, &hr_volumes);
+	list_append(&vol->lvolumes, &hr_volumes);
 	fibril_rwlock_write_unlock(&hr_volumes_lock);
 
-	HR_DEBUG("created volume \"%s\" (%" PRIun ")\n", new_volume->devname,
-	    new_volume->svc_id);
+	HR_DEBUG("created volume \"%s\" (%" PRIun ")\n", vol->devname,
+	    vol->svc_id);
 
 	free(cfg);
@@ -170,5 +171,5 @@
 error:
 	free(cfg);
-	hr_destroy_vol_struct(new_volume);
+	hr_destroy_vol_struct(vol);
 	async_answer_0(icall, rc);
 }
Index: uspace/srv/bd/hr/meson.build
===================================================================
--- uspace/srv/bd/hr/meson.build	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/meson.build	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -32,4 +32,5 @@
             'hr.c',
             'io.c',
+            'metadata/native.c',
             'raid0.c',
             'raid1.c',
Index: uspace/srv/bd/hr/metadata/native.c
===================================================================
--- uspace/srv/bd/hr/metadata/native.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
+++ uspace/srv/bd/hr/metadata/native.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2025 Miroslav Cimerman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup hr
+ * @{
+ */
+/**
+ * @file
+ */
+
+#include <adt/list.h>
+#include <block.h>
+#include <byteorder.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <io/log.h>
+#include <loc.h>
+#include <mem.h>
+#include <uuid.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <str.h>
+#include <types/uuid.h>
+
+#include "../util.h"
+#include "../var.h"
+
+#include "native.h"
+
+static void		*meta_native_alloc_struct(void);
+static errno_t		 meta_native_init_vol2meta(const hr_volume_t *, void *);
+static errno_t		 meta_native_init_meta2vol(const list_t *,
+    hr_volume_t *);
+static void		 meta_native_encode(const void *, void *);
+static void		 meta_native_decode(const void *, void *);
+static errno_t		 meta_native_get_block(service_id_t, void **);
+static errno_t		 meta_native_write_block(service_id_t, const void *);
+static bool		 meta_native_has_valid_magic(const void *);
+static bool		 meta_native_compare_uuids(const void *, const void *);
+static void		 meta_native_inc_counter(void *);
+static errno_t		 meta_native_save(hr_volume_t *, bool);
+static const char	*meta_native_get_devname(const void *);
+static hr_level_t	 meta_native_get_level(const void *);
+static uint64_t		 meta_native_get_data_offset(void);
+static size_t		 meta_native_get_size(void);
+static uint8_t		 meta_native_get_flags(void);
+static metadata_type_t	 meta_native_get_type(void);
+static void		 meta_native_dump(const void *);
+
+hr_superblock_ops_t metadata_native_ops = {
+	.alloc_struct		= meta_native_alloc_struct,
+	.init_vol2meta		= meta_native_init_vol2meta,
+	.init_meta2vol		= meta_native_init_meta2vol,
+	.encode			= meta_native_encode,
+	.decode			= meta_native_decode,
+	.get_block		= meta_native_get_block,
+	.write_block		= meta_native_write_block,
+	.has_valid_magic	= meta_native_has_valid_magic,
+	.compare_uuids		= meta_native_compare_uuids,
+	.inc_counter		= meta_native_inc_counter,
+	.save			= meta_native_save,
+	.get_devname		= meta_native_get_devname,
+	.get_level		= meta_native_get_level,
+	.get_data_offset	= meta_native_get_data_offset,
+	.get_size		= meta_native_get_size,
+	.get_flags		= meta_native_get_flags,
+	.get_type		= meta_native_get_type,
+	.dump			= meta_native_dump
+};
+
+static void *meta_native_alloc_struct(void)
+{
+	return calloc(1, sizeof(hr_metadata_t));
+}
+
+static errno_t meta_native_init_vol2meta(const hr_volume_t *vol, void *mdp)
+{
+	HR_DEBUG("%s()", __func__);
+
+	hr_metadata_t *md = (hr_metadata_t *)mdp;
+
+	str_cpy(md->magic, HR_NATIVE_MAGIC_SIZE, HR_NATIVE_MAGIC_STR);
+
+	md->version = HR_NATIVE_METADATA_VERSION;
+
+	md->counter = 0;
+
+	uuid_t uuid;
+	/* rndgen */
+	fibril_usleep(1000);
+	errno_t rc = uuid_generate(&uuid);
+	if (rc != EOK)
+		return rc;
+
+	/* XXX: for now we just copy byte by byte as "encoding" */
+	memcpy(md->uuid, &uuid, HR_NATIVE_UUID_LEN);
+	/* uuid_encode(&uuid, metadata->uuid); */
+
+	md->nblocks = vol->nblocks;
+	md->data_blkno = vol->data_blkno;
+	md->truncated_blkno = vol->truncated_blkno;
+	md->data_offset = vol->data_offset;
+	md->extent_no = vol->extent_no;
+	md->level = vol->level;
+	md->layout = vol->layout;
+	md->strip_size = vol->strip_size;
+	md->bsize = vol->bsize;
+	memcpy(md->devname, vol->devname, HR_DEVNAME_LEN);
+
+	return EOK;
+}
+
+static errno_t meta_native_init_meta2vol(const list_t *list, hr_volume_t *vol)
+{
+	HR_DEBUG("%s()", __func__);
+
+	errno_t rc = EOK;
+
+	hr_metadata_t *main_meta = NULL;
+	size_t max_counter_val = 0;
+
+	list_foreach(*list, link, struct dev_list_member, iter) {
+		hr_metadata_t *iter_meta = (hr_metadata_t *)iter->md;
+		if (iter_meta->counter >= max_counter_val) {
+			max_counter_val = iter_meta->counter;
+			main_meta = iter_meta;
+		}
+	}
+
+	assert(main_meta != NULL);
+
+	vol->nblocks = main_meta->nblocks;
+	vol->data_blkno = main_meta->data_blkno;
+	vol->truncated_blkno = main_meta->truncated_blkno;
+	vol->data_offset = main_meta->data_offset;
+	vol->extent_no = main_meta->extent_no;
+	/* vol->level = main_meta->level; */
+	vol->layout = main_meta->layout;
+	vol->strip_size = main_meta->strip_size;
+	vol->bsize = main_meta->bsize;
+	/* memcpy(vol->devname, main_meta->devname, HR_DEVNAME_LEN); */
+	memcpy(vol->in_mem_md, main_meta, sizeof(hr_metadata_t));
+
+	list_foreach(*list, link, struct dev_list_member, iter) {
+		hr_metadata_t *iter_meta = (hr_metadata_t *)iter->md;
+
+		vol->extents[iter_meta->index].svc_id = iter->svc_id;
+
+		uint64_t blkno;
+		rc = block_get_nblocks(iter->svc_id, &blkno);
+		if (rc != EOK)
+			goto error;
+
+		vol->extents[iter_meta->index].blkno = blkno;
+
+		if (iter_meta->counter == max_counter_val)
+			vol->extents[iter_meta->index].status = HR_EXT_ONLINE;
+		else
+			vol->extents[iter_meta->index].status = HR_EXT_INVALID;
+	}
+
+	for (size_t i = 0; i < vol->extent_no; i++) {
+		if (vol->extents[i].status == HR_EXT_NONE)
+			vol->extents[i].status = HR_EXT_MISSING;
+	}
+
+error:
+	return rc;
+}
+
+static void meta_native_encode(const void *mdp, void *block)
+{
+	HR_DEBUG("%s()", __func__);
+
+	const hr_metadata_t *metadata = (hr_metadata_t *)mdp;
+
+	/*
+	 * Use scratch metadata for easier encoding without the need
+	 * for manualy specifying offsets.
+	 */
+	hr_metadata_t scratch_md;
+
+	memcpy(scratch_md.magic, metadata->magic, HR_NATIVE_MAGIC_SIZE);
+	memcpy(scratch_md.uuid, metadata->uuid, HR_NATIVE_UUID_LEN);
+	/* uuid_decode((uint8_t *)scratch_md.uuid, (uuid_t *)metadata->uuid); */
+
+	scratch_md.nblocks = host2uint64_t_le(metadata->nblocks);
+	scratch_md.data_blkno = host2uint64_t_le(metadata->data_blkno);
+	scratch_md.truncated_blkno = host2uint64_t_le(
+	    metadata->truncated_blkno);
+	scratch_md.data_offset = host2uint64_t_le(metadata->data_offset);
+	scratch_md.counter = host2uint64_t_le(metadata->counter);
+	scratch_md.version = host2uint32_t_le(metadata->version);
+	scratch_md.extent_no = host2uint32_t_le(metadata->extent_no);
+	scratch_md.index = host2uint32_t_le(metadata->index);
+	scratch_md.level = host2uint32_t_le(metadata->level);
+	scratch_md.layout = host2uint32_t_le(metadata->layout);
+	scratch_md.strip_size = host2uint32_t_le(metadata->strip_size);
+	scratch_md.bsize = host2uint32_t_le(metadata->bsize);
+	memcpy(scratch_md.devname, metadata->devname, HR_DEVNAME_LEN);
+
+	memcpy(block, &scratch_md, sizeof(hr_metadata_t));
+}
+
+static void meta_native_decode(const void *block, void *mdp)
+{
+	HR_DEBUG("%s()", __func__);
+
+	hr_metadata_t *metadata = (hr_metadata_t *)mdp;
+
+	/*
+	 * Use scratch metadata for easier decoding without the need
+	 * for manualy specifying offsets.
+	 */
+	hr_metadata_t scratch_md;
+	memcpy(&scratch_md, block, sizeof(hr_metadata_t));
+
+	memcpy(metadata->magic, scratch_md.magic, HR_NATIVE_MAGIC_SIZE);
+	memcpy(metadata->uuid, scratch_md.uuid, HR_NATIVE_UUID_LEN);
+	/* uuid_decode((uint8_t *)scratch_md.uuid, (uuid_t *)metadata->uuid); */
+
+	metadata->nblocks = uint64_t_le2host(scratch_md.nblocks);
+	metadata->data_blkno = uint64_t_le2host(scratch_md.data_blkno);
+	metadata->truncated_blkno = uint64_t_le2host(
+	    scratch_md.truncated_blkno);
+	metadata->data_offset = uint64_t_le2host(scratch_md.data_offset);
+	metadata->counter = uint64_t_le2host(scratch_md.counter);
+	metadata->version = uint32_t_le2host(scratch_md.version);
+	metadata->extent_no = uint32_t_le2host(scratch_md.extent_no);
+	metadata->index = uint32_t_le2host(scratch_md.index);
+	metadata->level = uint32_t_le2host(scratch_md.level);
+	metadata->layout = uint32_t_le2host(scratch_md.layout);
+	metadata->strip_size = uint32_t_le2host(scratch_md.strip_size);
+	metadata->bsize = uint32_t_le2host(scratch_md.bsize);
+	memcpy(metadata->devname, scratch_md.devname, HR_DEVNAME_LEN);
+}
+
+static errno_t meta_native_get_block(service_id_t dev, void **rblock)
+{
+	HR_DEBUG("%s()", __func__);
+
+	errno_t rc;
+	uint64_t blkno;
+	size_t bsize;
+	void *block;
+
+	if (rblock == NULL)
+		return EINVAL;
+
+	rc = block_get_bsize(dev, &bsize);
+	if (rc != EOK)
+		return rc;
+
+	if (bsize < sizeof(hr_metadata_t))
+		return EINVAL;
+
+	rc = block_get_nblocks(dev, &blkno);
+	if (rc != EOK)
+		return rc;
+
+	if (blkno < HR_NATIVE_META_SIZE)
+		return EINVAL;
+
+	block = malloc(bsize);
+	if (block == NULL)
+		return ENOMEM;
+
+	rc = block_read_direct(dev, blkno - 1, HR_NATIVE_META_SIZE, block);
+	/*
+	 * XXX: here maybe call vol status event or the state callback...
+	 *
+	 * but need to pass vol pointer
+	 */
+	if (rc != EOK) {
+		free(block);
+		return rc;
+	}
+
+	*rblock = block;
+	return EOK;
+}
+
+static errno_t meta_native_write_block(service_id_t dev, const void *block)
+{
+	HR_DEBUG("%s()", __func__);
+
+	errno_t rc;
+	uint64_t blkno;
+	size_t bsize;
+
+	rc = block_get_bsize(dev, &bsize);
+	if (rc != EOK)
+		return rc;
+
+	if (bsize < sizeof(hr_metadata_t))
+		return EINVAL;
+
+	rc = block_get_nblocks(dev, &blkno);
+	if (rc != EOK)
+		return rc;
+
+	if (blkno < HR_NATIVE_META_SIZE)
+		return EINVAL;
+
+	rc = block_write_direct(dev, blkno - 1, HR_NATIVE_META_SIZE, block);
+
+	return rc;
+}
+
+static bool meta_native_has_valid_magic(const void *mdp)
+{
+	HR_DEBUG("%s()", __func__);
+
+	const hr_metadata_t *md = (hr_metadata_t *)mdp;
+
+	if (str_lcmp(md->magic, HR_NATIVE_MAGIC_STR, HR_NATIVE_MAGIC_SIZE) != 0)
+		return false;
+
+	return true;
+}
+
+static bool meta_native_compare_uuids(const void *m1p, const void *m2p)
+{
+	const hr_metadata_t *m1 = m1p;
+	const hr_metadata_t *m2 = m2p;
+	if (memcmp(m1->uuid, m2->uuid, HR_NATIVE_UUID_LEN) == 0)
+		return true;
+
+	return false;
+}
+
+static void meta_native_inc_counter(void *m)
+{
+	hr_metadata_t *meta = (hr_metadata_t *)m;
+
+	meta->counter++;
+}
+
+/*
+ * XXX: finish this fcn documentation
+ *
+ * Returns ENOMEM else EOK
+ */
+static errno_t meta_native_save(hr_volume_t *vol, bool with_state_callback)
+{
+	HR_DEBUG("%s()", __func__);
+
+	errno_t rc = EOK;
+
+	void *md_block = calloc(1, vol->bsize);
+	if (md_block == NULL)
+		return ENOMEM;
+
+	hr_metadata_t *md = (hr_metadata_t *)vol->in_mem_md;
+
+	fibril_rwlock_read_lock(&vol->extents_lock);
+
+	fibril_mutex_lock(&vol->md_lock);
+
+	for (size_t i = 0; i < vol->extent_no; i++) {
+		hr_extent_t *ext = &vol->extents[i];
+
+		fibril_rwlock_read_lock(&vol->states_lock);
+
+		/* TODO: special case for REBUILD */
+		if (ext->status != HR_EXT_ONLINE)
+			continue;
+
+		fibril_rwlock_read_unlock(&vol->states_lock);
+
+		md->index = i;
+		meta_native_encode(md, md_block);
+		rc = meta_native_write_block(ext->svc_id, md_block);
+		if (with_state_callback && rc != EOK)
+			vol->state_callback(vol, i, rc);
+	}
+
+	fibril_mutex_unlock(&vol->md_lock);
+
+	fibril_rwlock_read_unlock(&vol->extents_lock);
+
+	if (with_state_callback)
+		vol->hr_ops.status_event(vol);
+
+	free(md_block);
+	return EOK;
+}
+
+
+static const char *meta_native_get_devname(const void *m)
+{
+	hr_metadata_t *meta = (hr_metadata_t *)m;
+
+	return meta->devname;
+}
+
+static hr_level_t meta_native_get_level(const void *m)
+{
+	hr_metadata_t *meta = (hr_metadata_t *)m;
+
+	return meta->level;
+}
+
+static uint64_t meta_native_get_data_offset(void)
+{
+	return HR_NATIVE_DATA_OFF;
+}
+
+static size_t meta_native_get_size(void)
+{
+	return HR_NATIVE_META_SIZE;
+}
+
+static uint8_t meta_native_get_flags(void)
+{
+	uint8_t flags = 0;
+
+	flags |= HR_METADATA_HOTSPARE_SUPPORT;
+
+	return flags;
+}
+
+static metadata_type_t meta_native_get_type(void)
+{
+	return HR_METADATA_NATIVE;
+}
+
+static void meta_native_dump(const void *mdp)
+{
+	HR_DEBUG("%s()", __func__);
+
+	const hr_metadata_t *metadata = (hr_metadata_t *)mdp;
+
+	printf("\tmagic: %s\n", metadata->magic);
+	printf("\tUUID: ");
+	for (size_t i = 0; i < HR_NATIVE_UUID_LEN; ++i) {
+		printf("%.2X", metadata->uuid[i]);
+		if (i + 1 < HR_NATIVE_UUID_LEN)
+			printf(" ");
+	}
+	printf("\n");
+	printf("\tnblocks: %" PRIu64 "\n", metadata->nblocks);
+	printf("\tdata_blkno: %" PRIu64 "\n", metadata->data_blkno);
+	printf("\ttruncated_blkno: %" PRIu64 "\n", metadata->truncated_blkno);
+	printf("\tdata_offset: %" PRIu64 "\n", metadata->data_offset);
+	printf("\tcounter: %" PRIu64 "\n", metadata->counter);
+	printf("\tversion: %" PRIu32 "\n", metadata->version);
+	printf("\textent_no: %" PRIu32 "\n", metadata->extent_no);
+	printf("\tindex: %" PRIu32 "\n", metadata->index);
+	printf("\tlevel: %" PRIu32 "\n", metadata->level);
+	printf("\tlayout: %" PRIu32 "\n", metadata->layout);
+	printf("\tstrip_size: %" PRIu32 "\n", metadata->strip_size);
+	printf("\tbsize: %" PRIu32 "\n", metadata->bsize);
+	printf("\tdevname: %s\n", metadata->devname);
+}
+
+/** @}
+ */
Index: uspace/srv/bd/hr/metadata/native.h
===================================================================
--- uspace/srv/bd/hr/metadata/native.h	(revision 506034057bb4777c8c01885722953431aa519ae8)
+++ uspace/srv/bd/hr/metadata/native.h	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2025 Miroslav Cimerman
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup hr
+ * @{
+ */
+/**
+ * @file
+ */
+
+#ifndef _HR_METADATA_NATIVE_H
+#define _HR_METADATA_NATIVE_H
+
+#include "../var.h"
+
+/*
+ * Metadata is stored on the last block of an extent.
+ */
+#define HR_NATIVE_META_SIZE		1	/* in blocks */
+#define HR_NATIVE_DATA_OFF		0
+
+#define HR_NATIVE_MAGIC_STR		"HelenRAID"
+#define HR_NATIVE_MAGIC_SIZE		16
+#define HR_NATIVE_UUID_LEN		16
+#define HR_NATIVE_METADATA_VERSION	1
+
+struct hr_metadata {
+	char		magic[HR_NATIVE_MAGIC_SIZE];
+
+	uint8_t		uuid[HR_NATIVE_UUID_LEN];
+
+	/* TODO: change to blkno */
+	uint64_t	nblocks;		/* all blocks */
+	uint64_t	data_blkno;		/* usable blocks */
+
+	uint64_t	truncated_blkno;	/* usable blocks */
+	uint64_t	data_offset;
+
+	uint64_t	counter;		/* XXX: yet unused */
+	uint32_t	version;		/* XXX: yet unused */
+	uint32_t	extent_no;
+
+	uint32_t	index;			/* index of extent in volume */
+	uint32_t	level;
+	uint32_t	layout;
+	uint32_t	strip_size;
+
+	uint32_t	bsize;
+
+	char		devname[HR_DEVNAME_LEN];
+} __attribute__((packed));
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/bd/hr/raid0.c
===================================================================
--- uspace/srv/bd/hr/raid0.c	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/raid0.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -124,8 +124,9 @@
 	vol->truncated_blkno = truncated_blkno;
 	vol->nblocks = total_blkno;
-	vol->data_offset = HR_DATA_OFF;
+	vol->data_offset = vol->meta_ops->get_data_offset();
 
 	vol->data_blkno = total_blkno;
-	vol->data_blkno -= HR_META_SIZE * vol->extent_no; /* count md blocks */
+	/* count md blocks */
+	vol->data_blkno -= vol->meta_ops->get_size() * vol->extent_no;
 
 	vol->strip_size = HR_STRIP_SIZE;
@@ -200,6 +201,6 @@
 	fibril_mutex_lock(&vol->md_lock);
 
-	/* XXX: will be wrapped in md specific fcn ptrs */
-	vol->in_mem_md->counter++;
+	vol->meta_ops->inc_counter(vol->in_mem_md);
+	/* TODO: save right away */
 
 	fibril_mutex_unlock(&vol->md_lock);
Index: uspace/srv/bd/hr/raid1.c
===================================================================
--- uspace/srv/bd/hr/raid1.c	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/raid1.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -34,4 +34,5 @@
  */
 
+#include <abi/ipc/ipc.h>
 #include <bd_srv.h>
 #include <block.h>
@@ -137,6 +138,6 @@
 	vol->truncated_blkno = truncated_blkno;
 	vol->nblocks = truncated_blkno;
-	vol->data_offset = HR_DATA_OFF;
-	vol->data_blkno = truncated_blkno - HR_META_SIZE;
+	vol->data_offset = vol->meta_ops->get_data_offset();
+	vol->data_blkno = truncated_blkno - vol->meta_ops->get_size();
 	vol->strip_size = 0;
 
@@ -227,6 +228,6 @@
 	fibril_mutex_lock(&vol->md_lock);
 
-	/* XXX: will be wrapped in md specific fcn ptrs */
-	vol->in_mem_md->counter++;
+	vol->meta_ops->inc_counter(vol->in_mem_md);
+	/* XXX: save right away */
 
 	fibril_mutex_unlock(&vol->md_lock);
@@ -550,5 +551,5 @@
 	fibril_rwlock_write_unlock(&vol->states_lock);
 
-	rc = hr_metadata_save(vol, WITH_STATE_CALLBACK);
+	rc = vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
 
 end:
Index: uspace/srv/bd/hr/raid5.c
===================================================================
--- uspace/srv/bd/hr/raid5.c	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/raid5.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -139,8 +139,9 @@
 	vol->truncated_blkno = truncated_blkno;
 	vol->nblocks = total_blkno;
-	vol->data_offset = HR_DATA_OFF;
+	vol->data_offset = vol->meta_ops->get_data_offset();
 
 	vol->data_blkno = total_blkno;
-	vol->data_blkno -= HR_META_SIZE * vol->extent_no; /* count md blocks */
+	/* count md blocks */
+	vol->data_blkno -= vol->meta_ops->get_size() * vol->extent_no;
 	vol->data_blkno -= truncated_blkno; /* count parity */
 
@@ -845,5 +846,5 @@
 	hr_update_ext_status(vol, bad, HR_EXT_ONLINE);
 
-	rc = hr_metadata_save(vol, WITH_STATE_CALLBACK);
+	rc = vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
 
 end:
Index: uspace/srv/bd/hr/superblock.c
===================================================================
--- uspace/srv/bd/hr/superblock.c	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/superblock.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -45,266 +45,67 @@
 #include <stdio.h>
 #include <str.h>
-#include <types/uuid.h>
 
+#include "metadata/native.h"
 #include "superblock.h"
 #include "util.h"
 #include "var.h"
 
-errno_t hr_metadata_init(hr_volume_t *vol, hr_metadata_t *md)
+extern hr_superblock_ops_t metadata_native_ops;
+
+static hr_superblock_ops_t *hr_superblock_ops_all[] = {
+	[HR_METADATA_NATIVE] = &metadata_native_ops
+};
+
+hr_superblock_ops_t *get_type_ops(metadata_type_t type)
 {
-	HR_DEBUG("%s()", __func__);
+	assert(type >= HR_METADATA_NATIVE && type < HR_METADATA_LAST_DUMMY);
 
-	str_cpy(md->magic, HR_MAGIC_SIZE, HR_MAGIC_STR);
-
-	md->version = HR_METADATA_VERSION;
-	vol->metadata_version = md->version;
-
-	md->counter = 0;
-
-	uuid_t uuid;
-	/* rndgen */
-	fibril_usleep(1000);
-	errno_t rc = uuid_generate(&uuid);
-	if (rc != EOK)
-		return rc;
-
-	/* XXX: for now we just copy byte by byte as "encoding" */
-	memcpy(md->uuid, &uuid, HR_UUID_LEN);
-	/* uuid_encode(&uuid, metadata->uuid); */
-
-	md->nblocks = vol->nblocks;
-	md->data_blkno = vol->data_blkno;
-	md->truncated_blkno = vol->truncated_blkno;
-	md->data_offset = vol->data_offset;
-	md->extent_no = vol->extent_no;
-	md->level = vol->level;
-	md->layout = vol->layout;
-	md->strip_size = vol->strip_size;
-	md->bsize = vol->bsize;
-	memcpy(md->devname, vol->devname, HR_DEVNAME_LEN);
-
-	return EOK;
+	return hr_superblock_ops_all[type];
 }
 
-/*
- * XXX: finish this fcn documentation
- *
- * Returns ENOMEM else EOK
- */
-errno_t hr_metadata_save(hr_volume_t *vol, bool with_state_callback)
-{
-	HR_DEBUG("%s()", __func__);
-
-	errno_t rc = EOK;
-
-	void *md_block = calloc(1, vol->bsize);
-	if (md_block == NULL)
-		return ENOMEM;
-
-	fibril_rwlock_read_lock(&vol->extents_lock);
-
-	fibril_mutex_lock(&vol->md_lock);
-
-	for (size_t i = 0; i < vol->extent_no; i++) {
-		hr_extent_t *ext = &vol->extents[i];
-
-		fibril_rwlock_read_lock(&vol->states_lock);
-
-		/* TODO: special case for REBUILD */
-		if (ext->status != HR_EXT_ONLINE)
-			continue;
-
-		fibril_rwlock_read_unlock(&vol->states_lock);
-
-		vol->in_mem_md->index = i;
-		hr_encode_metadata_to_block(vol->in_mem_md, md_block);
-		rc = hr_write_metadata_block(ext->svc_id, md_block);
-		if (with_state_callback && rc != EOK)
-			vol->state_callback(vol, i, rc);
-	}
-
-	fibril_mutex_unlock(&vol->md_lock);
-
-	fibril_rwlock_read_unlock(&vol->extents_lock);
-
-	if (with_state_callback)
-		vol->hr_ops.status_event(vol);
-
-	free(md_block);
-	return EOK;
-}
-
-bool hr_valid_md_magic(const hr_metadata_t *md)
-{
-	HR_DEBUG("%s()", __func__);
-
-	if (str_lcmp(md->magic, HR_MAGIC_STR, HR_MAGIC_SIZE) != 0)
-		return false;
-
-	return true;
-}
-
-errno_t hr_write_metadata_block(service_id_t dev, const void *block)
+errno_t find_metadata(service_id_t svc_id, void **rmetadata,
+    metadata_type_t *rtype)
 {
 	HR_DEBUG("%s()", __func__);
 
 	errno_t rc;
-	uint64_t blkno;
-	size_t bsize;
+	hr_superblock_ops_t *meta_ops;
+	void *meta_block;
+	void *metadata_struct;
 
-	rc = block_get_bsize(dev, &bsize);
-	if (rc != EOK)
-		return rc;
-
-	if (bsize < sizeof(hr_metadata_t))
+	if (rmetadata == NULL)
+		return EINVAL;
+	if (rtype == NULL)
 		return EINVAL;
 
-	rc = block_get_nblocks(dev, &blkno);
-	if (rc != EOK)
-		return rc;
+	volatile metadata_type_t type = HR_METADATA_NATIVE;
+	for (; type < HR_METADATA_LAST_DUMMY; type++) {
+		meta_ops = hr_superblock_ops_all[type];
 
-	if (blkno < HR_META_SIZE)
-		return EINVAL;
+		metadata_struct = meta_ops->alloc_struct();
+		if (metadata_struct == NULL)
+			return ENOMEM;
 
-	rc = block_write_direct(dev, blkno - 1, HR_META_SIZE, block);
+		rc = meta_ops->get_block(svc_id, &meta_block);
+		if (rc == ENOMEM)
+			return ENOMEM;
+		if (rc != EOK)
+			continue;
 
-	return rc;
-}
+		meta_ops->decode(meta_block, metadata_struct);
 
-errno_t hr_get_metadata_block(service_id_t dev, void **rblock)
-{
-	HR_DEBUG("%s()", __func__);
+		free(meta_block);
 
-	errno_t rc;
-	uint64_t blkno;
-	size_t bsize;
-	void *block;
+		if (!meta_ops->has_valid_magic(metadata_struct)) {
+			free(metadata_struct);
+			continue;
+		}
 
-	rc = block_get_bsize(dev, &bsize);
-	if (rc != EOK)
-		return rc;
-
-	if (bsize < sizeof(hr_metadata_t))
-		return EINVAL;
-
-	rc = block_get_nblocks(dev, &blkno);
-	if (rc != EOK)
-		return rc;
-
-	if (blkno < HR_META_SIZE)
-		return EINVAL;
-
-	block = malloc(bsize);
-	if (block == NULL)
-		return ENOMEM;
-
-	rc = block_read_direct(dev, blkno - 1, HR_META_SIZE, block);
-	/*
-	 * XXX: here maybe call vol status event or the state callback...
-	 *
-	 * but need to pass vol pointer
-	 */
-	if (rc != EOK) {
-		free(block);
-		return rc;
+		*rmetadata = metadata_struct;
+		*rtype = type;
+		return EOK;
 	}
 
-	if (rblock == NULL) {
-		free(block);
-		return EINVAL;
-	}
-
-	*rblock = block;
-	return EOK;
-}
-
-void hr_encode_metadata_to_block(const hr_metadata_t *metadata, void *block)
-{
-	HR_DEBUG("%s()", __func__);
-
-	/*
-	 * Use scratch metadata for easier encoding without the need
-	 * for manualy specifying offsets.
-	 */
-	hr_metadata_t scratch_md;
-
-	memcpy(scratch_md.magic, metadata->magic, HR_MAGIC_SIZE);
-	memcpy(scratch_md.uuid, metadata->uuid, HR_UUID_LEN);
-	/* uuid_decode((uint8_t *)scratch_md.uuid, (uuid_t *)metadata->uuid); */
-
-	scratch_md.nblocks = host2uint64_t_le(metadata->nblocks);
-	scratch_md.data_blkno = host2uint64_t_le(metadata->data_blkno);
-	scratch_md.truncated_blkno = host2uint64_t_le(
-	    metadata->truncated_blkno);
-	scratch_md.data_offset = host2uint64_t_le(metadata->data_offset);
-	scratch_md.counter = host2uint64_t_le(metadata->counter);
-	scratch_md.version = host2uint32_t_le(metadata->version);
-	scratch_md.extent_no = host2uint32_t_le(metadata->extent_no);
-	scratch_md.index = host2uint32_t_le(metadata->index);
-	scratch_md.level = host2uint32_t_le(metadata->level);
-	scratch_md.layout = host2uint32_t_le(metadata->layout);
-	scratch_md.strip_size = host2uint32_t_le(metadata->strip_size);
-	scratch_md.bsize = host2uint32_t_le(metadata->bsize);
-	memcpy(scratch_md.devname, metadata->devname, HR_DEVNAME_LEN);
-
-	memcpy(block, &scratch_md, sizeof(hr_metadata_t));
-}
-
-void hr_decode_metadata_from_block(const void *block, hr_metadata_t *metadata)
-{
-	HR_DEBUG("%s()", __func__);
-
-	/*
-	 * Use scratch metadata for easier decoding without the need
-	 * for manualy specifying offsets.
-	 */
-	hr_metadata_t scratch_md;
-	memcpy(&scratch_md, block, sizeof(hr_metadata_t));
-
-	memcpy(metadata->magic, scratch_md.magic, HR_MAGIC_SIZE);
-	memcpy(metadata->uuid, scratch_md.uuid, HR_UUID_LEN);
-	/* uuid_decode((uint8_t *)scratch_md.uuid, (uuid_t *)metadata->uuid); */
-
-	metadata->nblocks = uint64_t_le2host(scratch_md.nblocks);
-	metadata->data_blkno = uint64_t_le2host(scratch_md.data_blkno);
-	metadata->truncated_blkno = uint64_t_le2host(
-	    scratch_md.truncated_blkno);
-	metadata->data_offset = uint64_t_le2host(scratch_md.data_offset);
-	metadata->counter = uint64_t_le2host(scratch_md.counter);
-	metadata->version = uint32_t_le2host(scratch_md.version);
-	metadata->extent_no = uint32_t_le2host(scratch_md.extent_no);
-	metadata->index = uint32_t_le2host(scratch_md.index);
-	metadata->level = uint32_t_le2host(scratch_md.level);
-	metadata->layout = uint32_t_le2host(scratch_md.layout);
-	metadata->strip_size = uint32_t_le2host(scratch_md.strip_size);
-	metadata->bsize = uint32_t_le2host(scratch_md.bsize);
-	memcpy(metadata->devname, scratch_md.devname, HR_DEVNAME_LEN);
-}
-
-void hr_metadata_dump(const hr_metadata_t *metadata)
-{
-	HR_DEBUG("%s()", __func__);
-
-	printf("\tmagic: %s\n", metadata->magic);
-	printf("\tUUID: ");
-	for (size_t i = 0; i < HR_UUID_LEN; ++i) {
-		printf("%.2X", metadata->uuid[i]);
-		if (i + 1 < HR_UUID_LEN)
-			printf(" ");
-	}
-	printf("\n");
-	printf("\tnblocks: %" PRIu64 "\n", metadata->nblocks);
-	printf("\tdata_blkno: %" PRIu64 "\n", metadata->data_blkno);
-	printf("\ttruncated_blkno: %" PRIu64 "\n", metadata->truncated_blkno);
-	printf("\tdata_offset: %" PRIu64 "\n", metadata->data_offset);
-	printf("\tcounter: %" PRIu64 "\n", metadata->counter);
-	printf("\tversion: %" PRIu32 "\n", metadata->version);
-	printf("\textent_no: %" PRIu32 "\n", metadata->extent_no);
-	printf("\tindex: %" PRIu32 "\n", metadata->index);
-	printf("\tlevel: %" PRIu32 "\n", metadata->level);
-	printf("\tlayout: %" PRIu32 "\n", metadata->layout);
-	printf("\tstrip_size: %" PRIu32 "\n", metadata->strip_size);
-	printf("\tbsize: %" PRIu32 "\n", metadata->bsize);
-	printf("\tdevname: %s\n", metadata->devname);
+	return ENOFS;
 }
 
Index: uspace/srv/bd/hr/superblock.h
===================================================================
--- uspace/srv/bd/hr/superblock.h	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/superblock.h	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -39,52 +39,36 @@
 #include "var.h"
 
-/*
- * Metadata is stored on the last block of an extent.
- */
-#define HR_META_SIZE		1	/* in blocks */
-#define HR_DATA_OFF		0
-
-#define HR_MAGIC_STR		"HelenRAID"
-#define HR_MAGIC_SIZE		16
-#define HR_UUID_LEN		16
-#define HR_METADATA_VERSION	1
-
-typedef struct hr_metadata hr_metadata_t;
 typedef struct hr_volume hr_volume_t;
 
-struct hr_metadata {
-	char		magic[HR_MAGIC_SIZE];
+typedef enum {
+	HR_METADATA_NATIVE	= 0,
+	HR_METADATA_LAST_DUMMY	= 1
+} metadata_type_t;
 
-	uint8_t		uuid[HR_UUID_LEN];
+#define HR_METADATA_HOTSPARE_SUPPORT 0x01
 
-	/* TODO: change to blkno */
-	uint64_t	nblocks;		/* all blocks */
-	uint64_t	data_blkno;		/* usable blocks */
+typedef struct hr_superblock_ops {
+	void		*(*alloc_struct)(void);
+	errno_t		 (*init_vol2meta)(const hr_volume_t *, void *);
+	errno_t		 (*init_meta2vol)(const list_t *, hr_volume_t *);
+	void		 (*encode)(const void *, void *);
+	void		 (*decode)(const void *, void *);
+	errno_t		 (*get_block)(service_id_t, void **);
+	errno_t		 (*write_block)(service_id_t, const void *);
+	bool		 (*has_valid_magic)(const void *);
+	bool		 (*compare_uuids)(const void *, const void *);
+	void		 (*inc_counter)(void *);
+	errno_t		 (*save)(hr_volume_t *, bool);
+	const char	*(*get_devname)(const void *);
+	hr_level_t	 (*get_level)(const void *);
+	uint64_t	 (*get_data_offset)(void);
+	size_t		 (*get_size)(void);
+	uint8_t		 (*get_flags)(void);
+	metadata_type_t	 (*get_type)(void);
+	void		 (*dump)(const void *);
+} hr_superblock_ops_t;
 
-	uint64_t	truncated_blkno;	/* usable blocks */
-	uint64_t	data_offset;
-
-	uint64_t	counter;		/* XXX: yet unused */
-	uint32_t	version;		/* XXX: yet unused */
-	uint32_t	extent_no;
-
-	uint32_t	index;			/* index of extent in volume */
-	uint32_t	level;
-	uint32_t	layout;
-	uint32_t	strip_size;
-
-	uint32_t	bsize;
-
-	char		devname[HR_DEVNAME_LEN];
-} __attribute__((packed));
-
-extern errno_t	hr_metadata_init(hr_volume_t *, hr_metadata_t *);
-extern errno_t	hr_metadata_save(hr_volume_t *, bool);
-extern errno_t	hr_write_metadata_block(service_id_t, const void *);
-extern errno_t	hr_get_metadata_block(service_id_t, void **);
-extern void	hr_encode_metadata_to_block(const hr_metadata_t *, void *);
-extern void	hr_decode_metadata_from_block(const void *, hr_metadata_t *);
-extern void	hr_metadata_dump(const hr_metadata_t *);
-extern bool	hr_valid_md_magic(const hr_metadata_t *);
+hr_superblock_ops_t *get_type_ops(metadata_type_t);
+extern errno_t	find_metadata(service_id_t, void **, metadata_type_t *);
 
 #endif
Index: uspace/srv/bd/hr/util.c
===================================================================
--- uspace/srv/bd/hr/util.c	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/util.c	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -55,10 +55,8 @@
 #include "var.h"
 
-struct svc_id_linked;
-
 static bool	hr_range_lock_overlap(hr_range_lock_t *, hr_range_lock_t *);
 static errno_t	hr_add_svc_linked_to_list(list_t *, service_id_t, bool,
-    hr_metadata_t *);
-static void	free_svc_id_linked(struct svc_id_linked *);
+    void *);
+static void	free_dev_list_member(struct dev_list_member *);
 static void	free_svc_id_list(list_t *);
 static errno_t	hr_fill_disk_part_svcs_list(list_t *);
@@ -66,19 +64,11 @@
 static void	block_fini_dev_list(list_t *);
 static errno_t	hr_util_get_matching_md_svcs_list(list_t *, list_t *,
-    service_id_t, hr_metadata_t *);
-static errno_t	hr_util_assemble_from_matching_list(list_t *);
+    service_id_t, metadata_type_t, void *);
+static errno_t	hr_util_assemble_from_matching_list(list_t *, metadata_type_t);
 static errno_t	hr_fill_svcs_list_from_cfg(hr_config_t *, list_t *);
 
-#define HR_RL_LIST_LOCK(vol) (fibril_mutex_lock(&vol->range_lock_list_lock))
+#define HR_RL_LIST_LOCK(vol) (fibril_mutex_lock(&(vol)->range_lock_list_lock))
 #define HR_RL_LIST_UNLOCK(vol) \
-    (fibril_mutex_unlock(&vol->range_lock_list_lock))
-
-struct svc_id_linked {
-	link_t		 link;
-	service_id_t	 svc_id;
-	hr_metadata_t	*md;
-	bool		 inited;
-	bool		 md_present;
-};
+    (fibril_mutex_unlock(&(vol)->range_lock_list_lock))
 
 extern loc_srv_t *hr_srv;
@@ -87,5 +77,5 @@
 
 errno_t hr_create_vol_struct(hr_volume_t **rvol, hr_level_t level,
-    const char *devname)
+    const char *devname, metadata_type_t metadata_type)
 {
 	errno_t rc;
@@ -98,11 +88,7 @@
 	vol->level = level;
 
+	vol->meta_ops = get_type_ops(metadata_type);
+
 	switch (level) {
-	case HR_LVL_1:
-		vol->hr_ops.create = hr_raid1_create;
-		vol->hr_ops.init = hr_raid1_init;
-		vol->hr_ops.status_event = hr_raid1_status_event;
-		vol->hr_ops.add_hotspare = hr_raid1_add_hotspare;
-		break;
 	case HR_LVL_0:
 		vol->hr_ops.create = hr_raid0_create;
@@ -110,9 +96,17 @@
 		vol->hr_ops.status_event = hr_raid0_status_event;
 		break;
+	case HR_LVL_1:
+		vol->hr_ops.create = hr_raid1_create;
+		vol->hr_ops.init = hr_raid1_init;
+		vol->hr_ops.status_event = hr_raid1_status_event;
+		if (vol->meta_ops->get_flags() & HR_METADATA_HOTSPARE_SUPPORT)
+			vol->hr_ops.add_hotspare = hr_raid1_add_hotspare;
+		break;
 	case HR_LVL_4:
 		vol->hr_ops.create = hr_raid5_create;
 		vol->hr_ops.init = hr_raid5_init;
 		vol->hr_ops.status_event = hr_raid5_status_event;
-		vol->hr_ops.add_hotspare = hr_raid5_add_hotspare;
+		if (vol->meta_ops->get_flags() & HR_METADATA_HOTSPARE_SUPPORT)
+			vol->hr_ops.add_hotspare = hr_raid5_add_hotspare;
 		break;
 	case HR_LVL_5:
@@ -120,5 +114,6 @@
 		vol->hr_ops.init = hr_raid5_init;
 		vol->hr_ops.status_event = hr_raid5_status_event;
-		vol->hr_ops.add_hotspare = hr_raid5_add_hotspare;
+		if (vol->meta_ops->get_flags() & HR_METADATA_HOTSPARE_SUPPORT)
+			vol->hr_ops.add_hotspare = hr_raid5_add_hotspare;
 		break;
 	default:
@@ -134,5 +129,5 @@
 	}
 
-	vol->in_mem_md = calloc(1, sizeof(hr_metadata_t));
+	vol->in_mem_md = vol->meta_ops->alloc_struct();
 	if (vol->in_mem_md == NULL) {
 		free(vol->fge);
@@ -221,5 +216,5 @@
 			fibril_rwlock_write_unlock(&hr_volumes_lock);
 
-			hr_metadata_save(vol, NO_STATE_CALLBACK);
+			vol->meta_ops->save(vol, NO_STATE_CALLBACK);
 
 			hr_destroy_vol_struct(vol);
@@ -584,25 +579,26 @@
 
 static errno_t hr_add_svc_linked_to_list(list_t *list, service_id_t svc_id,
-    bool inited, hr_metadata_t *md)
-{
+    bool inited, void *md)
+{
+	HR_DEBUG("%s()", __func__);
+
 	errno_t rc = EOK;
-	struct svc_id_linked *to_add;
-
-	to_add = malloc(sizeof(struct svc_id_linked));
+	struct dev_list_member *to_add;
+
+	if (list == NULL)
+		return EINVAL;
+
+	to_add = malloc(sizeof(struct dev_list_member));
 	if (to_add == NULL) {
 		rc = ENOMEM;
 		goto error;
 	}
+
 	to_add->svc_id = svc_id;
 	to_add->inited = inited;
 
 	if (md != NULL) {
-		to_add->md = malloc(sizeof(hr_metadata_t));
-		if (to_add->md == NULL) {
-			rc = ENOMEM;
-			goto error;
-		}
+		to_add->md = md;
 		to_add->md_present = true;
-		memcpy(to_add->md, md, sizeof(*md));
 	} else {
 		to_add->md_present = false;
@@ -615,6 +611,8 @@
 }
 
-static void free_svc_id_linked(struct svc_id_linked *p)
-{
+static void free_dev_list_member(struct dev_list_member *p)
+{
+	HR_DEBUG("%s()", __func__);
+
 	if (p->md_present)
 		free(p->md);
@@ -624,8 +622,10 @@
 static void free_svc_id_list(list_t *list)
 {
-	struct svc_id_linked *dev_id;
+	HR_DEBUG("%s()", __func__);
+
+	struct dev_list_member *dev_id;
 	while (!list_empty(list)) {
-		dev_id = list_pop(list, struct svc_id_linked, link);
-		free_svc_id_linked(dev_id);
+		dev_id = list_pop(list, struct dev_list_member, link);
+		free_dev_list_member(dev_id);
 	}
 }
@@ -633,4 +633,6 @@
 static errno_t hr_fill_disk_part_svcs_list(list_t *list)
 {
+	HR_DEBUG("%s()", __func__);
+
 	errno_t rc;
 	size_t disk_count;
@@ -653,5 +655,6 @@
 
 		if (disk_info.ltype == lt_none) {
-			rc = hr_add_svc_linked_to_list(list, disk_svcs[i], false, NULL);
+			rc = hr_add_svc_linked_to_list(list, disk_svcs[i],
+			    false, NULL);
 			if (rc != EOK)
 				goto error;
@@ -659,5 +662,6 @@
 			size_t part_count;
 			service_id_t *part_ids = NULL;
-			rc = vbd_label_get_parts(vbd, disk_svcs[i], &part_ids, &part_count);
+			rc = vbd_label_get_parts(vbd, disk_svcs[i], &part_ids,
+			    &part_count);
 			if (rc != EOK)
 				goto error;
@@ -665,5 +669,6 @@
 			for (size_t j = 0; j < part_count; j++) {
 				vbd_part_info_t part_info;
-				rc = vbd_part_get_info(vbd, part_ids[j], &part_info);
+				rc = vbd_part_get_info(vbd, part_ids[j],
+				    &part_info);
 				if (rc != EOK) {
 					free(part_ids);
@@ -697,7 +702,10 @@
 static errno_t block_init_dev_list(list_t *list)
 {
+	HR_DEBUG("%s()", __func__);
+
 	list_foreach_safe(*list, cur_link, next_link) {
-		struct svc_id_linked *iter;
-		iter = list_get_instance(cur_link, struct svc_id_linked, link);
+		struct dev_list_member *iter;
+		iter = list_get_instance(cur_link, struct dev_list_member,
+		    link);
 
 		if (iter->inited)
@@ -710,5 +718,5 @@
 		if (rc == EEXIST) {
 			list_remove(cur_link);
-			free_svc_id_linked(iter);
+			free_dev_list_member(iter);
 			continue;
 		}
@@ -725,5 +733,7 @@
 static void block_fini_dev_list(list_t *list)
 {
-	list_foreach(*list, link, struct svc_id_linked, iter) {
+	HR_DEBUG("%s()", __func__);
+
+	list_foreach(*list, link, struct dev_list_member, iter) {
 		if (iter->inited) {
 			block_fini(iter->svc_id);
@@ -733,33 +743,34 @@
 }
 
-static errno_t hr_util_get_matching_md_svcs_list(list_t *rlist, list_t *devlist,
-    service_id_t svc_id, hr_metadata_t *md_main)
-{
+static errno_t hr_util_get_matching_md_svcs_list(list_t *rlist, list_t *list,
+    service_id_t svc_id, metadata_type_t type, void *metadata_struct_main)
+{
+	HR_DEBUG("%s()", __func__);
+
 	errno_t rc = EOK;
 
-	list_foreach(*devlist, link, struct svc_id_linked, iter) {
+	list_foreach(*list, link, struct dev_list_member, iter) {
 		if (iter->svc_id == svc_id)
 			continue;
-		void *md_block;
-		hr_metadata_t md;
-		rc = hr_get_metadata_block(iter->svc_id, &md_block);
+
+		void *metadata_struct;
+		metadata_type_t type;
+
+		rc = find_metadata(iter->svc_id, &metadata_struct, &type);
+		if (rc == ENOFS)
+			continue;
 		if (rc != EOK)
 			goto error;
-		hr_decode_metadata_from_block(md_block, &md);
-
-		free(md_block);
-
-		if (!hr_valid_md_magic(&md))
+
+		hr_superblock_ops_t *meta_ops = get_type_ops(type);
+
+		if (!meta_ops->compare_uuids(metadata_struct_main,
+		    metadata_struct)) {
+			free(metadata_struct);
 			continue;
-
-		if (memcmp(md_main->uuid, md.uuid, HR_UUID_LEN) != 0)
-			continue;
-
-		/*
-		 * XXX: can I assume bsize and everything is fine when
-		 * UUID matches?
-		 */
-
-		rc = hr_add_svc_linked_to_list(rlist, iter->svc_id, true, &md);
+		}
+
+		rc = hr_add_svc_linked_to_list(rlist, iter->svc_id, true,
+		    metadata_struct);
 		if (rc != EOK)
 			goto error;
@@ -772,5 +783,6 @@
 }
 
-static errno_t hr_util_assemble_from_matching_list(list_t *list)
+static errno_t hr_util_assemble_from_matching_list(list_t *list,
+    metadata_type_t type)
 {
 	HR_DEBUG("%s()", __func__);
@@ -778,56 +790,19 @@
 	errno_t rc = EOK;
 
-	hr_metadata_t *main_md = NULL;
-	size_t max_counter_val = 0;
-
-	list_foreach(*list, link, struct svc_id_linked, iter) {
-		/* hr_metadata_dump(iter->md); */
-		if (iter->md->counter >= max_counter_val) {
-			max_counter_val = iter->md->counter;
-			main_md = iter->md;
-		}
-	}
-
-	assert(main_md != NULL);
+	hr_superblock_ops_t *meta_ops = get_type_ops(type);
+
+	link_t *memb_l = list_first(list);
+	struct dev_list_member *memb = list_get_instance(memb_l,
+	    struct dev_list_member, link);
+
+	hr_level_t level = meta_ops->get_level(memb->md);
+	const char *devname = meta_ops->get_devname(memb->md);
 
 	hr_volume_t *vol;
-	rc = hr_create_vol_struct(&vol, (hr_level_t)main_md->level,
-	    main_md->devname);
+	rc = hr_create_vol_struct(&vol, level, devname, type);
 	if (rc != EOK)
 		goto error;
 
-	vol->nblocks = main_md->nblocks;
-	vol->data_blkno = main_md->data_blkno;
-	vol->truncated_blkno = main_md->truncated_blkno;
-	vol->data_offset = main_md->data_offset;
-	vol->metadata_version = main_md->version;
-	vol->extent_no = main_md->extent_no;
-	/* vol->level = main_md->level; */
-	vol->layout = main_md->layout;
-	vol->strip_size = main_md->strip_size;
-	vol->bsize = main_md->bsize;
-	/* memcpy(vol->devname, main_md->devname, HR_DEVNAME_LEN); */
-
-	memcpy(vol->in_mem_md, main_md, sizeof(hr_metadata_t));
-
-	list_foreach(*list, link, struct svc_id_linked, iter) {
-		vol->extents[iter->md->index].svc_id = iter->svc_id;
-
-		uint64_t blkno;
-		rc = block_get_nblocks(iter->svc_id, &blkno);
-		if (rc != EOK)
-			goto error;
-		vol->extents[iter->md->index].blkno = blkno;
-
-		if (iter->md->counter == max_counter_val)
-			vol->extents[iter->md->index].status = HR_EXT_ONLINE;
-		else
-			vol->extents[iter->md->index].status = HR_EXT_INVALID;
-	}
-
-	for (size_t i = 0; i < vol->extent_no; i++) {
-		if (vol->extents[i].status == HR_EXT_NONE)
-			vol->extents[i].status = HR_EXT_MISSING;
-	}
+	meta_ops->init_meta2vol(list, vol);
 
 	/*
@@ -845,5 +820,5 @@
 	 * write... but for now leave it here
 	 */
-	vol->in_mem_md->counter++;
+	(void)vol->meta_ops->inc_counter(vol->in_mem_md);
 
 	rc = vol->hr_ops.create(vol);
@@ -851,17 +826,5 @@
 		goto error;
 
-	hr_metadata_save(vol, WITH_STATE_CALLBACK);
-
 	fibril_rwlock_write_lock(&hr_volumes_lock);
-
-	list_foreach(hr_volumes, lvolumes, hr_volume_t, other) {
-		uint8_t *our_uuid = vol->in_mem_md->uuid;
-		uint8_t *other_uuid = other->in_mem_md->uuid;
-		if (memcmp(our_uuid, other_uuid, HR_UUID_LEN) == 0) {
-			rc = EEXIST;
-			fibril_rwlock_write_unlock(&hr_volumes_lock);
-			goto error;
-		}
-	}
 
 	/*
@@ -884,4 +847,6 @@
 	}
 
+	(void)vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
+
 	list_append(&vol->lvolumes, &hr_volumes);
 
@@ -896,7 +861,10 @@
 static errno_t hr_fill_svcs_list_from_cfg(hr_config_t *cfg, list_t *list)
 {
+	HR_DEBUG("%s()", __func__);
+
 	errno_t rc = EOK;
 	for (size_t i = 0; i < cfg->dev_no; ++i) {
-		rc = hr_add_svc_linked_to_list(list, cfg->devs[i], false, NULL);
+		rc = hr_add_svc_linked_to_list(list, cfg->devs[i], false,
+		    NULL);
 		if (rc != EOK)
 			goto error;
@@ -944,26 +912,21 @@
 		goto error;
 
-	struct svc_id_linked *iter;
+	struct dev_list_member *iter;
 	while (!list_empty(&dev_id_list)) {
-		iter = list_pop(&dev_id_list, struct svc_id_linked, link);
-
-		void *metadata_block;
-		hr_metadata_t metadata;
-
-		rc = hr_get_metadata_block(iter->svc_id, &metadata_block);
+		iter = list_pop(&dev_id_list, struct dev_list_member, link);
+
+		void *metadata_struct_main;
+		metadata_type_t type;
+
+		rc = find_metadata(iter->svc_id, &metadata_struct_main, &type);
+		if (rc == ENOFS) {
+			block_fini(iter->svc_id);
+			free_dev_list_member(iter);
+			rc = EOK;
+			continue;
+		}
+
 		if (rc != EOK)
 			goto error;
-
-		hr_decode_metadata_from_block(metadata_block, &metadata);
-
-		free(metadata_block);
-
-		if (!hr_valid_md_magic(&metadata)) {
-			block_fini(iter->svc_id);
-			free_svc_id_linked(iter);
-			continue;
-		}
-
-		/* hr_metadata_dump(&metadata); */
 
 		char *svc_name = NULL;
@@ -981,5 +944,5 @@
 
 		rc = hr_util_get_matching_md_svcs_list(&matching_svcs_list,
-		    &dev_id_list, iter->svc_id, &metadata);
+		    &dev_id_list, iter->svc_id, type, metadata_struct_main);
 		if (rc != EOK)
 			goto error;
@@ -987,5 +950,5 @@
 		/* add current iter to list as well */
 		rc = hr_add_svc_linked_to_list(&matching_svcs_list,
-		    iter->svc_id, true, &metadata);
+		    iter->svc_id, true, metadata_struct_main);
 		if (rc != EOK) {
 			free_svc_id_list(&matching_svcs_list);
@@ -994,18 +957,19 @@
 
 		/* remove matching list members from dev_id_list */
-		list_foreach(matching_svcs_list, link, struct svc_id_linked,
+		list_foreach(matching_svcs_list, link, struct dev_list_member,
 		    iter2) {
-			struct svc_id_linked *to_remove;
+			struct dev_list_member *to_remove;
 			list_foreach_safe(dev_id_list, cur_link, next_link) {
 				to_remove = list_get_instance(cur_link,
-				    struct svc_id_linked, link);
+				    struct dev_list_member, link);
 				if (to_remove->svc_id == iter2->svc_id) {
 					list_remove(cur_link);
-					free_svc_id_linked(to_remove);
+					free_dev_list_member(to_remove);
 				}
 			}
 		}
 
-		rc = hr_util_assemble_from_matching_list(&matching_svcs_list);
+		rc = hr_util_assemble_from_matching_list(&matching_svcs_list,
+		    type);
 		switch (rc) {
 		case EOK:
@@ -1065,9 +1029,5 @@
 	}
 
-	/*
-	 * TODO: make more flexible, when will have foreign md, the calculation
-	 * will differ, maybe something new like vol->md_hs_blkno will be enough
-	 */
-	if (hs_blkno < vol->truncated_blkno - HR_META_SIZE) {
+	if (hs_blkno < vol->truncated_blkno - vol->meta_ops->get_size()) {
 		rc = EINVAL;
 		block_fini(hotspare);
Index: uspace/srv/bd/hr/util.h
===================================================================
--- uspace/srv/bd/hr/util.h	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/util.h	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -37,9 +37,19 @@
 #define _HR_UTIL_H
 
+#include <adt/list.h>
 #include <errno.h>
 #include <hr.h>
 #include <io/log.h>
 
+#include "superblock.h"
 #include "var.h"
+
+struct dev_list_member {
+	link_t		 link;
+	service_id_t	 svc_id;
+	void		*md;
+	bool		 inited;
+	bool		 md_present;
+};
 
 #define HR_DEBUG(format, ...) \
@@ -54,5 +64,5 @@
 
 extern errno_t		 hr_create_vol_struct(hr_volume_t **, hr_level_t,
-    const char *);
+    const char *, metadata_type_t);
 extern void		 hr_destroy_vol_struct(hr_volume_t *);
 extern hr_volume_t	*hr_get_volume(service_id_t);
Index: uspace/srv/bd/hr/var.h
===================================================================
--- uspace/srv/bd/hr/var.h	(revision 6f41c21f89c967e05026e3da26fffb4939be168e)
+++ uspace/srv/bd/hr/var.h	(revision 506034057bb4777c8c01885722953431aa519ae8)
@@ -53,4 +53,5 @@
 typedef struct hr_volume hr_volume_t;
 typedef struct hr_metadata hr_metadata_t;
+typedef struct hr_superblock_ops hr_superblock_ops_t;
 
 typedef struct hr_ops {
@@ -72,16 +73,8 @@
 	hr_fpool_t	*fge;			/* fibril pool */
 
-	/*
-	 * TODO: also print in info, doesn't have
-	 * to be part of hr_volume_t but just the info
-	 * that is passed to be printed
-	 *
-	 * Probably just defer this decition until foreign md
-	 * will be handled.
-	 */
-	uint32_t	 metadata_version;
+	void		*in_mem_md;
+	fibril_mutex_t	 md_lock;		/* lock protecting in_mem_md */
 
-	hr_metadata_t	*in_mem_md;
-	fibril_mutex_t	 md_lock;		/* lock protecting in_mem_md */
+	hr_superblock_ops_t *meta_ops;
 
 	/* invariants */
