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;
 }
