Index: uspace/srv/bd/hr/hr.c
===================================================================
--- uspace/srv/bd/hr/hr.c	(revision f18e36e2bbc50b38693ef734c8811bf0cdf01584)
+++ uspace/srv/bd/hr/hr.c	(revision e0695cef2a4a4392f323e3f0212caa45f2b409ee)
@@ -155,7 +155,5 @@
 		goto error;
 
-	rc = vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
-	if (rc != EOK)
-		goto error;
+	vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
 
 	rc = hr_register_volume(vol);
Index: uspace/srv/bd/hr/metadata/native.c
===================================================================
--- uspace/srv/bd/hr/metadata/native.c	(revision f18e36e2bbc50b38693ef734c8811bf0cdf01584)
+++ uspace/srv/bd/hr/metadata/native.c	(revision e0695cef2a4a4392f323e3f0212caa45f2b409ee)
@@ -171,8 +171,17 @@
 		iter->fini = false;
 
-		if (iter_meta->counter == max_counter_val)
-			vol->extents[iter_meta->index].state = HR_EXT_ONLINE;
-		else
-			vol->extents[iter_meta->index].state = HR_EXT_INVALID;
+		hr_ext_state_t final_ext_state = HR_EXT_INVALID;
+		if (iter_meta->counter == max_counter_val) {
+			if (iter_meta->rebuild_pos > 0) {
+				final_ext_state = HR_EXT_REBUILD;
+				vol->rebuild_blk = iter_meta->rebuild_pos;
+				printf("REBUILD SHOULD RESUME at %lu\n",
+				    vol->rebuild_blk);
+			} else {
+				final_ext_state = HR_EXT_ONLINE;
+			}
+		}
+
+		vol->extents[iter_meta->index].state = final_ext_state;
 	}
 
@@ -206,4 +215,5 @@
 	scratch_md.data_offset = host2uint64_t_le(metadata->data_offset);
 	scratch_md.counter = host2uint64_t_le(metadata->counter);
+	scratch_md.rebuild_pos = host2uint64_t_le(metadata->rebuild_pos);
 	scratch_md.version = host2uint32_t_le(metadata->version);
 	scratch_md.extent_no = host2uint32_t_le(metadata->extent_no);
@@ -240,4 +250,5 @@
 	metadata->data_offset = uint64_t_le2host(scratch_md.data_offset);
 	metadata->counter = uint64_t_le2host(scratch_md.counter);
+	metadata->rebuild_pos = uint64_t_le2host(scratch_md.rebuild_pos);
 	metadata->version = uint32_t_le2host(scratch_md.version);
 	metadata->extent_no = uint32_t_le2host(scratch_md.extent_no);
@@ -249,4 +260,7 @@
 	memcpy(metadata->devname, scratch_md.devname, HR_DEVNAME_LEN);
 
+	if (metadata->version != 1)
+		return EINVAL;
+
 	return EOK;
 }
@@ -394,7 +408,7 @@
 
 		fibril_rwlock_read_lock(&vol->states_lock);
-
-		/* TODO: special case for REBUILD */
-		if (ext->state != HR_EXT_ONLINE) {
+		hr_ext_state_t s = ext->state;
+
+		if (s != HR_EXT_ONLINE && s != HR_EXT_REBUILD) {
 			fibril_rwlock_read_unlock(&vol->states_lock);
 			continue;
@@ -404,4 +418,8 @@
 
 		md->index = i;
+		if (s == HR_EXT_REBUILD)
+			md->rebuild_pos = vol->rebuild_blk;
+		else
+			md->rebuild_pos = 0;
 		meta_native_encode(md, md_block);
 		rc = meta_native_write_block(ext->svc_id, md_block);
Index: uspace/srv/bd/hr/metadata/native.h
===================================================================
--- uspace/srv/bd/hr/metadata/native.h	(revision f18e36e2bbc50b38693ef734c8811bf0cdf01584)
+++ uspace/srv/bd/hr/metadata/native.h	(revision e0695cef2a4a4392f323e3f0212caa45f2b409ee)
@@ -61,4 +61,6 @@
 	uint64_t counter;
 
+	uint64_t rebuild_pos;
+
 	uint32_t version; /* XXX: yet unused */
 	uint32_t extent_no;
Index: uspace/srv/bd/hr/raid1.c
===================================================================
--- uspace/srv/bd/hr/raid1.c	(revision f18e36e2bbc50b38693ef734c8811bf0cdf01584)
+++ uspace/srv/bd/hr/raid1.c	(revision e0695cef2a4a4392f323e3f0212caa45f2b409ee)
@@ -153,5 +153,5 @@
 
 	vol->meta_ops->inc_counter(vol);
-	(void)vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
+	vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
 
 	hr_raid1_vol_state_eval_forced(vol);
@@ -194,4 +194,6 @@
 	size_t invalid_no = hr_count_extents(vol, HR_EXT_INVALID);
 
+	size_t rebuild_no = hr_count_extents(vol, HR_EXT_REBUILD);
+
 	fibril_mutex_lock(&vol->hotspare_lock);
 	size_t hs_no = vol->hotspare_no;
@@ -216,5 +218,5 @@
 
 		if (old_state != HR_VOL_REBUILD) {
-			if (hs_no > 0 || invalid_no > 0) {
+			if (hs_no > 0 || invalid_no > 0 || rebuild_no > 0) {
 				fid_t fib = fibril_create(hr_raid1_rebuild,
 				    vol);
@@ -443,8 +445,4 @@
 }
 
-/*
- * Put the last HOTSPARE extent in place
- * of first that != ONLINE, and start the rebuild.
- */
 static errno_t hr_raid1_rebuild(void *arg)
 {
@@ -463,10 +461,10 @@
 	rebuild_ext = &vol->extents[rebuild_idx];
 
-	size_t left = vol->data_blkno;
+	size_t left = vol->data_blkno - vol->rebuild_blk;
 	size_t max_blks = DATA_XFER_LIMIT / vol->bsize;
 	buf = hr_malloc_waitok(max_blks * vol->bsize);
 
 	size_t cnt;
-	uint64_t ba = 0;
+	uint64_t ba = vol->rebuild_blk;
 	hr_add_data_offset(vol, &ba);
 
@@ -487,4 +485,8 @@
 	hr_range_lock_t *rl = NULL;
 
+	HR_NOTE("\"%s\": REBUILD started on extent no. %zu at block %lu.\n",
+	    vol->devname, rebuild_idx, ba);
+
+	uint64_t written = 0;
 	unsigned int percent, old_percent = 100;
 	while (left != 0) {
@@ -517,6 +519,12 @@
 		}
 
+		if (written * vol->bsize > HR_REBUILD_SAVE_BYTES) {
+			vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
+			written = 0;
+		}
+
 		hr_range_lock_release(rl);
 
+		written += cnt;
 		ba += cnt;
 		left -= cnt;
@@ -531,10 +539,9 @@
 	hr_update_ext_state(vol, rebuild_idx, HR_EXT_ONLINE);
 
+	atomic_store_explicit(&vol->rebuild_blk, 0, memory_order_relaxed);
+
 	hr_mark_vol_state_dirty(vol);
 
 	fibril_rwlock_write_unlock(&vol->states_lock);
-
-	/* (void)vol->meta_ops->save(vol, WITH_STATE_CALLBACK); */
-
 end:
 	fibril_rwlock_read_unlock(&vol->extents_lock);
@@ -542,6 +549,5 @@
 	hr_raid1_vol_state_eval(vol);
 
-	if (buf != NULL)
-		free(buf);
+	free(buf);
 
 	return rc;
Index: uspace/srv/bd/hr/raid5.c
===================================================================
--- uspace/srv/bd/hr/raid5.c	(revision f18e36e2bbc50b38693ef734c8811bf0cdf01584)
+++ uspace/srv/bd/hr/raid5.c	(revision e0695cef2a4a4392f323e3f0212caa45f2b409ee)
@@ -150,5 +150,5 @@
 
 	vol->meta_ops->inc_counter(vol);
-	(void)vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
+	vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
 
 	hr_raid5_vol_state_eval_forced(vol);
@@ -610,4 +610,6 @@
 
 	size_t invalid_no = hr_count_extents(vol, HR_EXT_INVALID);
+
+	size_t rebuild_no = hr_count_extents(vol, HR_EXT_REBUILD);
 
 	fibril_mutex_lock(&vol->hotspare_lock);
@@ -625,5 +627,5 @@
 
 		if (state != HR_VOL_REBUILD) {
-			if (hs_no > 0 || invalid_no > 0) {
+			if (hs_no > 0 || invalid_no > 0 || rebuild_no > 0) {
 				fid_t fib = fibril_create(hr_raid5_rebuild,
 				    vol);
@@ -720,5 +722,6 @@
 
 	uint64_t max_blks = DATA_XFER_LIMIT / vol->bsize;
-	uint64_t left = vol->data_blkno / (vol->extent_no - 1);
+	uint64_t left =
+	    vol->data_blkno / (vol->extent_no - 1) - vol->rebuild_blk;
 	buf = hr_malloc_waitok(max_blks * vol->bsize);
 	xorbuf = hr_malloc_waitok(max_blks * vol->bsize);
@@ -726,5 +729,6 @@
 	uint64_t strip_size = vol->strip_size / vol->bsize; /* in blocks */
 
-	uint64_t ba = 0, cnt;
+	size_t cnt;
+	uint64_t ba = vol->rebuild_blk;
 	hr_add_data_offset(vol, &ba);
 
@@ -747,4 +751,8 @@
 	    false);
 
+	HR_NOTE("\"%s\": REBUILD started on extent no. %zu at block %lu.\n",
+	    vol->devname, rebuild_idx, ba);
+
+	uint64_t written = 0;
 	unsigned int percent, old_percent = 100;
 	while (left != 0) {
@@ -810,9 +818,16 @@
 		}
 
+		if (written * vol->bsize > HR_REBUILD_SAVE_BYTES) {
+			vol->meta_ops->save(vol, WITH_STATE_CALLBACK);
+			written = 0;
+		}
+
 		hr_range_lock_release(rl);
 		hr_reset_stripe(stripe);
 
+		written += cnt;
 		ba += cnt;
 		left -= cnt;
+		old_percent = percent;
 
 		/*
@@ -829,10 +844,9 @@
 	hr_update_ext_state(vol, rebuild_idx, HR_EXT_ONLINE);
 
+	atomic_store_explicit(&vol->rebuild_blk, 0, memory_order_relaxed);
+
 	hr_mark_vol_state_dirty(vol);
 
 	fibril_rwlock_write_unlock(&vol->states_lock);
-
-	/* (void)vol->meta_ops->save(vol, WITH_STATE_CALLBACK); */
-
 end:
 	fibril_rwlock_read_unlock(&vol->extents_lock);
Index: uspace/srv/bd/hr/util.c
===================================================================
--- uspace/srv/bd/hr/util.c	(revision f18e36e2bbc50b38693ef734c8811bf0cdf01584)
+++ uspace/srv/bd/hr/util.c	(revision e0695cef2a4a4392f323e3f0212caa45f2b409ee)
@@ -281,5 +281,5 @@
 
 	/* save metadata, but we don't care about states anymore */
-	(void)vol->meta_ops->save(vol, NO_STATE_CALLBACK);
+	vol->meta_ops->save(vol, NO_STATE_CALLBACK);
 
 	HR_NOTE("deactivating volume \"%s\"\n", vol->devname);
@@ -1117,4 +1117,5 @@
 {
 	errno_t rc = EOK;
+	size_t bad = vol->extent_no;
 
 	if (vol->level == HR_LVL_0)
@@ -1125,16 +1126,21 @@
 	fibril_mutex_lock(&vol->hotspare_lock);
 
-	size_t bad = vol->extent_no;
+	if (vol->state != HR_VOL_DEGRADED) {
+		rc = EINVAL;
+		goto error;
+	}
+
+	size_t rebuild = vol->extent_no;
 	for (size_t i = 0; i < vol->extent_no; i++) {
-		if (vol->extents[i].state != HR_EXT_ONLINE) {
-			bad = i;
+		if (vol->extents[i].state == HR_EXT_REBUILD) {
+			rebuild = i;
 			break;
 		}
 	}
 
-	if (bad == vol->extent_no)
-		rc = EINVAL;
-	else if (vol->state != HR_VOL_DEGRADED)
-		rc = EINVAL;
+	if (rebuild < vol->extent_no) {
+		bad = rebuild;
+		goto init_rebuild;
+	}
 
 	size_t invalid = vol->extent_no;
@@ -1146,31 +1152,37 @@
 	}
 
-	if (invalid < vol->extent_no)
+	if (invalid < vol->extent_no) {
 		bad = invalid;
-
-	if (bad != invalid && vol->hotspare_no == 0)
+		goto init_rebuild;
+	}
+
+	for (size_t i = 0; i < vol->extent_no; i++) {
+		if (vol->extents[i].state != HR_EXT_ONLINE) {
+			bad = i;
+			break;
+		}
+	}
+
+	if (bad == vol->extent_no || vol->hotspare_no == 0) {
 		rc = EINVAL;
-
-	if (rc != EOK)
-		goto error;
-
-	if (bad != invalid) {
-		size_t hotspare_idx = vol->hotspare_no - 1;
-
-		hr_ext_state_t hs_state = vol->hotspares[hotspare_idx].state;
-		if (hs_state != HR_EXT_HOTSPARE) {
-			HR_ERROR("hr_raid1_rebuild(): invalid hotspare"
-			    "state \"%s\", aborting rebuild\n",
-			    hr_get_ext_state_str(hs_state));
-			rc = EINVAL;
-			goto error;
-		}
-
-		rc = hr_swap_hs(vol, bad, hotspare_idx);
-		if (rc != EOK) {
-			HR_ERROR("hr_raid1_rebuild(): swapping "
-			    "hotspare failed, aborting rebuild\n");
-			goto error;
-		}
+		goto error;
+	}
+
+	size_t hotspare_idx = vol->hotspare_no - 1;
+
+	hr_ext_state_t hs_state = vol->hotspares[hotspare_idx].state;
+	if (hs_state != HR_EXT_HOTSPARE) {
+		HR_ERROR("hr_raid1_rebuild(): invalid hotspare"
+		    "state \"%s\", aborting rebuild\n",
+		    hr_get_ext_state_str(hs_state));
+		rc = EINVAL;
+		goto error;
+	}
+
+	rc = hr_swap_hs(vol, bad, hotspare_idx);
+	if (rc != EOK) {
+		HR_ERROR("hr_raid1_rebuild(): swapping "
+		    "hotspare failed, aborting rebuild\n");
+		goto error;
 	}
 
@@ -1180,6 +1192,5 @@
 	    "(%"  PRIun  ")\n", bad, rebuild_ext->svc_id);
 
-	atomic_store_explicit(&vol->rebuild_blk, 0, memory_order_relaxed);
-
+init_rebuild:
 	hr_update_ext_state(vol, bad, HR_EXT_REBUILD);
 	hr_update_vol_state(vol, HR_VOL_REBUILD);
Index: uspace/srv/bd/hr/var.h
===================================================================
--- uspace/srv/bd/hr/var.h	(revision f18e36e2bbc50b38693ef734c8811bf0cdf01584)
+++ uspace/srv/bd/hr/var.h	(revision e0695cef2a4a4392f323e3f0212caa45f2b409ee)
@@ -49,4 +49,11 @@
 #define NAME "hr"
 #define HR_STRIP_SIZE DATA_XFER_LIMIT
+
+/*
+ * During a rebuild operation, we save the rebuild
+ * position this each many bytes. Currently each
+ * 10 MiB.
+ */
+#define HR_REBUILD_SAVE_BYTES (10U * 1024 * 1024)
 
 struct hr_volume;
