Index: uspace/app/fdisk/fdisk.c
===================================================================
--- uspace/app/fdisk/fdisk.c	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/app/fdisk/fdisk.c	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -343,4 +343,5 @@
 
 	tinput_destroy(tinput);
+	tinput = NULL;
 
 	rc = fdsk_select_fstype(&fstype);
Index: uspace/lib/block/block.c
===================================================================
--- uspace/lib/block/block.c	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/lib/block/block.c	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -789,4 +789,22 @@
 }
 
+/** Synchronize blocks to persistent storage.
+ *
+ * @param service_id	Service ID of the block device.
+ * @param ba		Address of first block (physical).
+ * @param cnt		Number of blocks.
+ *
+ * @return		EOK on success or negative error code on failure.
+ */
+int block_sync_cache(service_id_t service_id, aoff64_t ba, size_t cnt)
+{
+	devcon_t *devcon;
+
+	devcon = devcon_search(service_id);
+	assert(devcon);
+
+	return bd_sync_cache(devcon->bd, ba, cnt);
+}
+
 /** Get device block size.
  *
Index: uspace/lib/block/block.h
===================================================================
--- uspace/lib/block/block.h	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/lib/block/block.h	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -120,4 +120,5 @@
 extern int block_read_bytes_direct(service_id_t, aoff64_t, size_t, void *);
 extern int block_write_direct(service_id_t, aoff64_t, size_t, const void *);
+extern int block_sync_cache(service_id_t, aoff64_t, size_t);
 
 #endif
Index: uspace/lib/label/include/label.h
===================================================================
--- uspace/lib/label/include/label.h	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/lib/label/include/label.h	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -49,4 +49,5 @@
 extern label_part_t *label_part_first(label_t *);
 extern label_part_t *label_part_next(label_part_t *);
+extern void label_part_get_info(label_part_t *, label_part_info_t *);
 
 extern int label_part_create(label_t *, label_part_spec_t *,
Index: uspace/lib/label/include/types/liblabel.h
===================================================================
--- uspace/lib/label/include/types/liblabel.h	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/lib/label/include/types/liblabel.h	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -39,4 +39,5 @@
 #include <adt/list.h>
 #include <types/label.h>
+#include <sys/types.h>
 #include <vol.h>
 
@@ -48,26 +49,27 @@
 } label_info_t;
 
+typedef struct {
+	/** Address of first block */
+	aoff64_t block0;
+	/** Number of blocks */
+	aoff64_t nblocks;
+} label_part_info_t;
+
 /** Partition */
 typedef struct {
 	/** Containing label */
 	struct label *label;
-	/** Link to fdisk_dev_t.parts */
-	link_t ldev;
-	/** Capacity */
-//	fdisk_cap_t capacity;
-	/** File system type */
-//	fdisk_fstype_t fstype;
+	/** Link to label_t.parts */
+	link_t llabel;
 } label_part_t;
 
 /** Specification of new partition */
 typedef struct {
-	/** Desired capacity */
-//	fdisk_cap_t capacity;
-	/** File system type */
-//	fdisk_fstype_t fstype;
 } label_part_spec_t;
 
 /** Label instance */
 typedef struct label {
+	/** Partitions */
+	list_t parts; /* of label_part_t */
 } label_t;
 
Index: uspace/lib/label/src/label.c
===================================================================
--- uspace/lib/label/src/label.c	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/lib/label/src/label.c	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -34,4 +34,5 @@
  */
 
+#include <adt/list.h>
 #include <errno.h>
 #include <label.h>
@@ -47,4 +48,5 @@
 		return ENOMEM;
 
+	list_initialize(&label->parts);
 	*rlabel = label;
 	return EOK;
@@ -59,4 +61,5 @@
 		return ENOMEM;
 
+	list_initialize(&label->parts);
 	*rlabel = label;
 	return EOK;
@@ -65,4 +68,7 @@
 void label_close(label_t *label)
 {
+	if (label == NULL)
+		return;
+
 	free(label);
 }
@@ -83,21 +89,49 @@
 label_part_t *label_part_first(label_t *label)
 {
-	return NULL;
+	link_t *link;
+
+	link = list_first(&label->parts);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, label_part_t, llabel);
 }
 
-label_part_t *label_part_next(label_part_t *oart)
+label_part_t *label_part_next(label_part_t *part)
 {
-	return NULL;
+	link_t *link;
+
+	link = list_next(&part->llabel, &part->label->parts);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, label_part_t, llabel);
 }
 
+void label_part_get_info(label_part_t *part, label_part_info_t *pinfo)
+{
+	pinfo->block0 = 0;
+	pinfo->nblocks = 0;
+}
 
 int label_part_create(label_t *label, label_part_spec_t *pspec,
     label_part_t **rpart)
 {
-	return ENOTSUP;
+	label_part_t *part;
+
+	part = calloc(1, sizeof(label_part_t));
+	if (part == NULL)
+		return ENOMEM;
+
+	part->label = label;
+	list_append(&part->llabel, &label->parts);
+	*rpart = part;
+	return EOK;
 }
 
 int label_part_destroy(label_part_t *part)
 {
+	list_remove(&part->llabel);
+	free(part);
 	return EOK;
 }
Index: uspace/srv/bd/vbd/Makefile
===================================================================
--- uspace/srv/bd/vbd/Makefile	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/srv/bd/vbd/Makefile	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -34,5 +34,6 @@
 
 EXTRA_CFLAGS = \
-	-I$(LIBLABEL_PREFIX)/include
+	-I$(LIBLABEL_PREFIX)/include \
+	-I$(LIBBLOCK_PREFIX)
 
 BINARY = vbd
Index: uspace/srv/bd/vbd/disk.c
===================================================================
--- uspace/srv/bd/vbd/disk.c	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/srv/bd/vbd/disk.c	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -35,6 +35,10 @@
 
 #include <adt/list.h>
+#include <bd_srv.h>
+#include <block.h>
 #include <errno.h>
 #include <io/log.h>
+#include <loc.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <task.h>
@@ -44,8 +48,36 @@
 
 static list_t vbds_disks; /* of vbds_disk_t */
+static list_t vbds_parts; /* of vbds_part_t */
+
+static int vbds_bd_open(bd_srvs_t *, bd_srv_t *);
+static int vbds_bd_close(bd_srv_t *);
+static int vbds_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
+static int vbds_bd_sync_cache(bd_srv_t *, aoff64_t, size_t);
+static int vbds_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *,
+    size_t);
+static int vbds_bd_get_block_size(bd_srv_t *, size_t *);
+static int vbds_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
+
+static int vbds_bsa_translate(vbds_part_t *, aoff64_t, size_t, aoff64_t *);
+
+static bd_ops_t vbds_bd_ops = {
+	.open = vbds_bd_open,
+	.close = vbds_bd_close,
+	.read_blocks = vbds_bd_read_blocks,
+	.sync_cache = vbds_bd_sync_cache,
+	.write_blocks = vbds_bd_write_blocks,
+	.get_block_size = vbds_bd_get_block_size,
+	.get_num_blocks = vbds_bd_get_num_blocks
+};
+
+static vbds_part_t *bd_srv_part(bd_srv_t *bd)
+{
+	return (vbds_part_t *)bd->srvs->sarg;
+}
 
 void vbds_disks_init(void)
 {
 	list_initialize(&vbds_disks);
+	list_initialize(&vbds_parts);
 }
 
@@ -62,10 +94,27 @@
 }
 
+static int vbds_part_by_id(vbds_part_id_t partid, vbds_part_t **rpart)
+{
+	list_foreach(vbds_parts, lparts, vbds_part_t, part) {
+		if (part->id == partid) {
+			*rpart = part;
+			return EOK;
+		}
+	}
+
+	return ENOENT;
+}
+
 static int vbds_part_add(vbds_disk_t *disk, label_part_t *lpart)
 {
 	vbds_part_t *part;
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_part_add(%p, %p)",
-	    disk, lpart);
+	service_id_t psid;
+	label_part_info_t lpinfo;
+	char *name;
+	int pno;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_part_add(%s, %p)",
+	    disk->svc_name, lpart);
 
 	part = calloc(1, sizeof(vbds_part_t));
@@ -75,7 +124,35 @@
 	}
 
+	/* XXX Proper service name */
+	pno = list_count(&disk->parts);
+	rc = asprintf(&name, "%sp%u", disk->svc_name, pno);
+	if (rc < 0) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory.");
+		return ENOMEM;
+	}
+
+	rc = loc_service_register(name, &psid);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering "
+		    "service %s.", name);
+		free(name);
+		free(part);
+		return EIO;
+	}
+
+	free(name);
+	label_part_get_info(lpart, &lpinfo);
+
 	part->lpart = lpart;
 	part->disk = disk;
+	part->block0 = lpinfo.block0;
+	part->nblocks = lpinfo.nblocks;
+
+	bd_srvs_init(&part->bds);
+	part->bds.ops = &vbds_bd_ops;
+	part->bds.sarg = part;
+
 	list_append(&part->ldisk, &disk->parts);
+	list_append(&part->lparts, &vbds_parts);
 
 	return EOK;
@@ -86,5 +163,7 @@
 	label_t *label;
 	label_part_t *part;
-	vbds_disk_t *disk;
+	vbds_disk_t *disk = NULL;
+	bool block_inited = false;
+	size_t block_size;
 	int rc;
 
@@ -100,10 +179,39 @@
 		return ENOMEM;
 
+	rc = loc_service_get_name(sid, &disk->svc_name);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting disk service name.");
+		rc = EIO;
+		goto error;
+	}
+
+	rc = block_init(EXCHANGE_SERIALIZE, sid, 2048);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed opening block device %s.",
+		    disk->svc_name);
+		rc = EIO;
+		goto error;
+	}
+
+	rc = block_get_bsize(sid, &block_size);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting block size of %s.",
+		    disk->svc_name);
+		rc = EIO;
+		goto error;
+	}
+
+	block_inited = true;
+
 	rc = label_open(sid, &label);
-	if (rc != EOK)
-		goto error;
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
 
 	disk->svc_id = sid;
 	disk->label = label;
+	disk->block_size = block_size;
+
 	list_initialize(&disk->parts);
 	list_append(&disk->ldisks, &vbds_disks);
@@ -113,5 +221,6 @@
 		rc = vbds_part_add(disk, part);
 		if (rc != EOK) {
-			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding partition.");
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding partitio "
+			    "(disk %s)", disk->svc_name);
 		}
 
@@ -121,4 +230,9 @@
 	return EOK;
 error:
+	label_close(label);
+	if (block_inited)
+		block_fini(sid);
+	if (disk != NULL)
+		free(disk->svc_name);
 	free(disk);
 	return rc;
@@ -181,5 +295,4 @@
 int vbds_label_delete(service_id_t sid)
 {
-	return EOK;
 	vbds_disk_t *disk;
 	int rc;
@@ -202,16 +315,186 @@
 }
 
-int vbds_part_get_info(vbds_part_id_t part, vbds_part_info_t *pinfo)
-{
-	return EOK;
-}
-
-int vbds_part_create(service_id_t disk, vbds_part_id_t *rpart)
-{
-	return EOK;
-}
-
-int vbds_part_delete(vbds_part_id_t part)
-{
+int vbds_part_get_info(vbds_part_id_t partid, vbds_part_info_t *pinfo)
+{
+	vbds_part_t *part;
+	int rc;
+
+	rc = vbds_part_by_id(partid, &part);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+int vbds_part_create(service_id_t sid, vbds_part_id_t *rpart)
+{
+	vbds_disk_t *disk;
+	vbds_part_t *part;
+	label_part_spec_t pspec;
+	label_part_t *lpart;
+	int rc;
+
+	part = calloc(1, sizeof(vbds_part_t));
+	if (part == NULL)
+		return ENOMEM;
+
+	rc = vbds_disk_by_svcid(sid, &disk);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Partition %zu not found",
+		    sid);
+		goto error;
+	}
+
+	label_pspec_init(&pspec);
+	rc = label_part_create(disk->label, &pspec, &lpart);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating partition.");
+		goto error;
+	}
+
+	rc = vbds_part_add(disk, lpart);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed while creating "
+		    "partition.");
+		rc = label_part_destroy(lpart);
+		if (rc != EOK)
+			log_msg(LOG_DEFAULT, LVL_ERROR,
+			    "Cannot roll back partition creation.");
+		rc = EIO;
+		goto error;
+	}
+
+	return EOK;
+error:
+	free(part);
+	return rc;
+}
+
+int vbds_part_delete(vbds_part_id_t partid)
+{
+	vbds_part_t *part;
+	int rc;
+
+	rc = vbds_part_by_id(partid, &part);
+	if (rc != EOK)
+		return rc;
+
+	rc = label_part_destroy(part->lpart);
+	if (rc != EOK)
+		return rc;
+
+	list_remove(&part->ldisk);
+	list_remove(&part->lparts);
+	free(part);
+	return EOK;
+}
+
+static int vbds_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
+{
+	vbds_part_t *part = bd_srv_part(bd);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_bd_open()");
+	part->open_cnt++;
+	return EOK;
+}
+
+static int vbds_bd_close(bd_srv_t *bd)
+{
+	vbds_part_t *part = bd_srv_part(bd);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_bd_close()");
+	part->open_cnt--;
+	return EOK;
+}
+
+static int vbds_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    void *buf, size_t size)
+{
+	vbds_part_t *part = bd_srv_part(bd);
+	aoff64_t gba;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_bd_read_blocks()");
+
+	if (cnt * part->disk->block_size < size)
+		return EINVAL;
+
+	if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK)
+		return ELIMIT;
+
+	return block_read_direct(part->disk->svc_id, gba, cnt, buf);
+}
+
+static int vbds_bd_sync_cache(bd_srv_t *bd, aoff64_t ba, size_t cnt)
+{
+	vbds_part_t *part = bd_srv_part(bd);
+	aoff64_t gba;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_bd_sync_cache()");
+
+	/* XXX Allow full-disk sync? */
+	if (ba != 0 || cnt != 0) {
+		if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK)
+			return ELIMIT;
+	} else {
+		gba = 0;
+	}
+
+	return block_sync_cache(part->disk->svc_id, gba, cnt);
+}
+
+static int vbds_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
+    const void *buf, size_t size)
+{
+	vbds_part_t *part = bd_srv_part(bd);
+	aoff64_t gba;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_bd_write_blocks()");
+
+	if (cnt * part->disk->block_size < size)
+		return EINVAL;
+
+	if (vbds_bsa_translate(part, ba, cnt, &gba) != EOK)
+		return ELIMIT;
+
+	return block_write_direct(part->disk->svc_id, gba, cnt, buf);
+}
+
+static int vbds_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
+{
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_bd_get_block_size()");
+	return EOK;
+}
+
+static int vbds_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
+{
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_bd_get_num_blocks()");
+	return EOK;
+}
+
+void vbds_bd_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	vbds_part_t *part;
+	int rc;
+	service_id_t partid;
+
+	partid = IPC_GET_ARG1(*icall);
+
+	rc = vbds_part_by_id(partid, &part);
+	if (rc != EOK) {
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	bd_conn(iid, icall, &part->bds);
+}
+
+/** Translate block segment address with range checking. */
+static int vbds_bsa_translate(vbds_part_t *part, aoff64_t ba, size_t cnt,
+    aoff64_t *gba)
+{
+	if (ba + cnt > part->nblocks)
+		return ELIMIT;
+
+	*gba = part->block0 + ba;
 	return EOK;
 }
Index: uspace/srv/bd/vbd/disk.h
===================================================================
--- uspace/srv/bd/vbd/disk.h	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/srv/bd/vbd/disk.h	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -50,4 +50,5 @@
 extern int vbds_part_create(service_id_t, vbds_part_id_t *);
 extern int vbds_part_delete(vbds_part_id_t);
+extern void vbds_bd_conn(ipc_callid_t, ipc_call_t *, void *);
 
 #endif
Index: uspace/srv/bd/vbd/types/vbd.h
===================================================================
--- uspace/srv/bd/vbd/types/vbd.h	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/srv/bd/vbd/types/vbd.h	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -39,7 +39,10 @@
 
 #include <adt/list.h>
+#include <bd_srv.h>
 #include <label.h>
 #include <loc.h>
 #include <types/label.h>
+
+typedef sysarg_t vbds_part_id_t;
 
 /** Disk info */
@@ -57,6 +60,18 @@
 	/** Link to vbds_disk_t.parts */
 	link_t ldisk;
+	/** Link to vbds_parts */
+	link_t lparts;
+	/** Partition ID */
+	vbds_part_id_t id;
 	/** Label partition */
 	label_part_t *lpart;
+	/** Block device service */
+	bd_srvs_t bds;
+	/** Number of times the device is open */
+	int open_cnt;
+	/** Address of first block */
+	aoff64_t block0;
+	/** Number of blocks */
+	aoff64_t nblocks;
 } vbds_part_t;
 
@@ -67,8 +82,12 @@
 	/** Service ID */
 	service_id_t svc_id;
+	/** Disk service name */
+	char *svc_name;
 	/** Label */
 	label_t *label;
 	/** Partitions */
 	list_t parts; /* of vbds_part_t */
+	/** Block size */
+	size_t block_size;
 } vbds_disk_t;
 
@@ -76,6 +95,4 @@
 } vbds_part_info_t;
 
-typedef sysarg_t vbds_part_id_t;
-
 #endif
 
Index: uspace/srv/bd/vbd/vbd.c
===================================================================
--- uspace/srv/bd/vbd/vbd.c	(revision 28ed0d952e53138af66cace5b8439cbfaeda61ca)
+++ uspace/srv/bd/vbd/vbd.c	(revision 78d50bd06d1f1eb12182cb42a78d0182b9256f3d)
@@ -51,4 +51,6 @@
 static void vbds_client_conn(ipc_callid_t, ipc_call_t *, void *);
 
+static service_id_t ctl_sid;
+
 static int vbds_init(void)
 {
@@ -66,6 +68,5 @@
 	}
 
-	service_id_t sid;
-	rc = loc_service_register(SERVICE_NAME_VBD, &sid);
+	rc = loc_service_register(SERVICE_NAME_VBD, &ctl_sid);
 	if (rc != EOK) {
 		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service (%d).", rc);
@@ -191,5 +192,5 @@
 }
 
-static void vbds_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+static void vbds_ctl_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
 	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_client_conn()");
@@ -243,4 +244,18 @@
 }
 
+static void vbds_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	service_id_t sid;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vbds_client_conn()");
+
+	sid = (service_id_t)IPC_GET_ARG1(*icall);
+
+	if (sid == ctl_sid)
+		vbds_ctl_conn(iid, icall, arg);
+	else
+		vbds_bd_conn(iid, icall, arg);
+}
+
 int main(int argc, char *argv[])
 {
