Index: .gitignore
===================================================================
--- .gitignore	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ .gitignore	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -427,3 +427,4 @@
 uspace/srv/test/chardev-test/chardev-test
 uspace/srv/vfs/vfs
+uspace/srv/volsrv/test-volsrv
 uspace/srv/volsrv/volsrv
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ boot/Makefile.common	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -243,5 +243,6 @@
 	$(USPACE_PATH)/drv/bus/usb/xhci/test-xhci \
 	$(USPACE_PATH)/app/bdsh/test-bdsh \
-	$(USPACE_PATH)/srv/net/tcp/test-tcp
+	$(USPACE_PATH)/srv/net/tcp/test-tcp \
+	$(USPACE_PATH)/srv/volsrv/test-volsrv \
 
 RD_DATA_ESSENTIAL = \
Index: uspace/app/fdisk/fdisk.c
===================================================================
--- uspace/app/fdisk/fdisk.c	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/app/fdisk/fdisk.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2015 Jiri Svoboda
+ * Copyright (c) 2018 Jiri Svoboda
  * All rights reserved.
  *
@@ -432,4 +432,5 @@
 	char *smcap = NULL;
 	char *label = NULL;
+	char *mountp = NULL;
 
 	if (pkind == lpk_logical)
@@ -506,4 +507,32 @@
 	}
 
+	/* Ask for mount point */
+	tinput = tinput_new();
+	if (tinput == NULL) {
+		rc = ENOMEM;
+		goto error;
+	}
+
+	rc = tinput_set_prompt(tinput, "?> ");
+	if (rc != EOK)
+		goto error;
+
+	while (true) {
+		printf("Enter mount point for new partition (Auto, None or /path).\n");
+		rc = tinput_read_i(tinput, "Auto", &mountp);
+		if (rc != EOK)
+			goto error;
+
+		rc = vol_mountp_validate(mountp);
+		if (rc == EOK)
+			break;
+
+		free(mountp);
+		mountp = NULL;
+	}
+
+	tinput_destroy(tinput);
+	tinput = NULL;
+
 	fdisk_pspec_init(&pspec);
 	pspec.capacity = cap;
@@ -511,4 +540,5 @@
 	pspec.fstype = fstype;
 	pspec.label = label;
+	pspec.mountp = mountp;
 
 	rc = fdisk_part_create(dev, &pspec, NULL);
@@ -519,8 +549,10 @@
 
 	free(label);
+	free(mountp);
 	return EOK;
 error:
 	free(smcap);
 	free(label);
+	free(mountp);
 	if (tinput != NULL)
 		tinput_destroy(tinput);
Index: uspace/app/sysinst/sysinst.c
===================================================================
--- uspace/app/sysinst/sysinst.c	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/app/sysinst/sysinst.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -132,4 +132,5 @@
 	pspec.pkind = lpk_primary;
 	pspec.fstype = fs_minix;
+	pspec.mountp = "";
 
 	rc = fdisk_part_create(fdev, &pspec, &part);
Index: uspace/lib/c/generic/vol.c
===================================================================
--- uspace/lib/c/generic/vol.c	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/lib/c/generic/vol.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -40,4 +40,5 @@
 #include <stdlib.h>
 #include <str.h>
+#include <vfs/vfs.h>
 #include <vol.h>
 
@@ -220,7 +221,7 @@
 	exch = async_exchange_begin(vol->sess);
 	aid_t req = async_send_1(exch, VOL_PART_INFO, sid, &answer);
+
 	errno_t rc = async_data_read_start(exch, vinfo, sizeof(vol_part_info_t));
 	async_exchange_end(exch);
-
 	if (rc != EOK) {
 		async_forget(req);
@@ -294,7 +295,16 @@
 }
 
-/** Create file system. */
+/** Create file system.
+ *
+ * @param vol Volume service
+ * @param sid Partition service ID
+ * @param fstype File system type
+ * @param label Volume label
+ * @param mountp Mount point
+ *
+ * @return EOK on success or an error code
+ */
 errno_t vol_part_mkfs(vol_t *vol, service_id_t sid, vol_fstype_t fstype,
-    const char *label)
+    const char *label, const char *mountp)
 {
 	async_exch_t *exch;
@@ -304,12 +314,20 @@
 	exch = async_exchange_begin(vol->sess);
 	aid_t req = async_send_2(exch, VOL_PART_MKFS, sid, fstype, &answer);
+
 	retval = async_data_write_start(exch, label, str_size(label));
-	async_exchange_end(exch);
-
 	if (retval != EOK) {
+		async_exchange_end(exch);
 		async_forget(req);
 		return retval;
 	}
 
+	retval = async_data_write_start(exch, mountp, str_size(mountp));
+	if (retval != EOK) {
+		async_exchange_end(exch);
+		async_forget(req);
+		return retval;
+	}
+
+	async_exchange_end(exch);
 	async_wait_for(req, &retval);
 
@@ -394,4 +412,37 @@
 }
 
+/** Validate mount point.
+ *
+ * Verify that mount point is valid. A valid mount point is
+ * one of:
+ *  - 'Auto'
+ *  - 'None'
+ *  - /path (string beginning with '/') to an existing directory
+ *
+ * @return EOK if mount point is in valid, EINVAL if the format is invalid,
+ *         ENOENT if the directory does not exist
+ */
+errno_t vol_mountp_validate(const char *mountp)
+{
+	errno_t rc;
+	vfs_stat_t stat;
+
+	if (str_cmp(mountp, "Auto") == 0 || str_cmp(mountp, "auto") == 0)
+		return EOK;
+
+	if (str_casecmp(mountp, "None") == 0 || str_cmp(mountp, "none") == 0)
+		return EOK;
+
+	if (mountp[0] == '/') {
+		rc = vfs_stat_path(mountp, &stat);
+		if (rc != EOK || !stat.is_directory)
+			return ENOENT;
+
+		return EOK;
+	}
+
+	return EINVAL;
+}
+
 /** @}
  */
Index: uspace/lib/c/include/ipc/vol.h
===================================================================
--- uspace/lib/c/include/ipc/vol.h	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/lib/c/include/ipc/vol.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -37,4 +37,5 @@
 
 #define VOL_LABEL_MAXLEN 63
+#define VOL_MOUNTP_MAXLEN 4096
 
 typedef enum {
Index: uspace/lib/c/include/vol.h
===================================================================
--- uspace/lib/c/include/vol.h	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/lib/c/include/vol.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -51,8 +51,10 @@
 extern errno_t vol_part_empty(vol_t *, service_id_t);
 extern errno_t vol_part_get_lsupp(vol_t *, vol_fstype_t, vol_label_supp_t *);
-extern errno_t vol_part_mkfs(vol_t *, service_id_t, vol_fstype_t, const char *);
+extern errno_t vol_part_mkfs(vol_t *, service_id_t, vol_fstype_t, const char *,
+    const char *);
 
 extern errno_t vol_fstype_format(vol_fstype_t, char **);
 extern errno_t vol_pcnt_fs_format(vol_part_cnt_t, vol_fstype_t, char **);
+extern errno_t vol_mountp_validate(const char *);
 
 #endif
Index: uspace/lib/fdisk/include/types/fdisk.h
===================================================================
--- uspace/lib/fdisk/include/types/fdisk.h	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/lib/fdisk/include/types/fdisk.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -156,5 +156,7 @@
 	vol_fstype_t fstype;
 	/** Volume label */
-	char *label;
+	const char *label;
+	/** Mount point */
+	const char *mountp;
 } fdisk_part_spec_t;
 
Index: uspace/lib/fdisk/src/fdisk.c
===================================================================
--- uspace/lib/fdisk/src/fdisk.c	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/lib/fdisk/src/fdisk.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -729,4 +729,14 @@
 }
 
+/** Create partition.
+ *
+ * Create new partition based on a specification.
+ *
+ * @param dev Fdisk device
+ * @param pspec Partition specification
+ * @param rpart Place to store pointer to new partition
+ *
+ * @return EOK on success or error code
+ */
 errno_t fdisk_part_create(fdisk_dev_t *dev, fdisk_part_spec_t *pspec,
     fdisk_part_t **rpart)
@@ -737,7 +747,9 @@
 	vol_part_info_t vpinfo;
 	const char *label;
+	const char *mountp;
 	errno_t rc;
 
 	label = pspec->label != NULL ? pspec->label : "";
+	mountp = pspec->mountp != NULL ? pspec->mountp : "";
 
 	rc = fdisk_part_spec_prepare(dev, pspec, &vpspec);
@@ -761,5 +773,5 @@
 	if (part->svc_id != 0) {
 		rc = vol_part_mkfs(dev->fdisk->vol, part->svc_id, pspec->fstype,
-		    label);
+		    label, mountp);
 		if (rc != EOK && rc != ENOTSUP) {
 			rc = EIO;
Index: uspace/srv/volsrv/Makefile
===================================================================
--- uspace/srv/volsrv/Makefile	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/srv/volsrv/Makefile	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -37,5 +37,11 @@
 	mkfs.c \
 	part.c \
-	volsrv.c
+	volsrv.c \
+	volume.c
+
+TEST_SOURCES = \
+	volume.c \
+	test/main.c \
+	test/volume.c
 
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/volsrv/part.c
===================================================================
--- uspace/srv/volsrv/part.c	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/srv/volsrv/part.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -45,4 +45,5 @@
 #include <str_error.h>
 #include <vfs/vfs.h>
+#include <vol.h>
 
 #include "empty.h"
@@ -50,4 +51,5 @@
 #include "part.h"
 #include "types/part.h"
+#include "volume.h"
 
 static errno_t vol_part_add_locked(vol_parts_t *, service_id_t);
@@ -191,4 +193,7 @@
 		return;
 
+	if (part->volume != NULL)
+		vol_volume_del_ref(part->volume);
+
 	free(part->cur_mp);
 	free(part->svc_name);
@@ -202,4 +207,5 @@
 	struct fsname_type *fst;
 	char *label;
+	vol_volume_t *volume;
 	errno_t rc;
 
@@ -250,4 +256,10 @@
 	}
 
+	/* Look up new or existing volume. */
+	rc = vol_volume_lookup_ref(part->parts->volumes, part->label, &volume);
+	if (rc != EOK)
+		goto error;
+
+	part->volume = volume;
 	return EOK;
 
@@ -275,36 +287,68 @@
 }
 
+/** Determine the default mount point for a partition.
+ *
+ * @param part Partition
+ * @return Pointer to the constant string "Auto" or "None"
+ */
+static const char *vol_part_def_mountp(vol_part_t *part)
+{
+	return vol_part_allow_mount_by_def(part) ? "Auto" : "None";
+}
+
+/** Mount partition.
+ *
+ * @param part Partition
+ */
 static errno_t vol_part_mount(vol_part_t *part)
 {
+	const char *cfg_mp;
 	char *mp;
 	int err;
-	errno_t rc;
-
-	if (str_size(part->label) < 1) {
-		/* Don't mount nameless volumes */
-		log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting nameless partition.");
+	bool mp_auto;
+	errno_t rc;
+
+	/* Get configured mount point */
+	if (str_size(part->volume->mountp) > 0) {
+		cfg_mp = part->volume->mountp;
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Configured mount point '%s",
+		    cfg_mp);
+	} else {
+		cfg_mp = vol_part_def_mountp(part);
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Default mount point '%s",
+		    cfg_mp);
+	}
+
+	if (str_cmp(cfg_mp, "Auto") == 0 || str_cmp(cfg_mp, "auto") == 0) {
+
+		if (str_size(part->label) < 1) {
+			/* Don't mount nameless volumes */
+			log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting nameless volume.");
+			return EOK;
+		}
+
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Determine MP label='%s'", part->label);
+		err = asprintf(&mp, "/vol/%s", part->label);
+		if (err < 0) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory");
+			return ENOMEM;
+		}
+
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Create mount point '%s'", mp);
+		rc = vfs_link_path(mp, KIND_DIRECTORY, NULL);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating mount point '%s'",
+			    mp);
+			free(mp);
+			return EIO;
+		}
+
+		mp_auto = true;
+	} else if (str_cmp(cfg_mp, "None") == 0 || str_cmp(cfg_mp, "none") == 0) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting volume.");
 		return EOK;
-	}
-
-	if (!vol_part_allow_mount_by_def(part)) {
-		/* Don't mount partition by default */
-		log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting per default policy.");
-		return EOK;
-	}
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "Determine MP label='%s'", part->label);
-	err = asprintf(&mp, "/vol/%s", part->label);
-	if (err < 0) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory");
-		return ENOMEM;
-	}
-
-	log_msg(LOG_DEFAULT, LVL_NOTE, "Create mount point '%s'", mp);
-	rc = vfs_link_path(mp, KIND_DIRECTORY, NULL);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating mount point '%s'",
-		    mp);
-		free(mp);
-		return EIO;
+	} else {
+		mp = str_dup(cfg_mp);
+		mp_auto = false;
 	}
 
@@ -319,5 +363,5 @@
 
 	part->cur_mp = mp;
-	part->cur_mp_auto = true;
+	part->cur_mp_auto = mp_auto;
 
 	return rc;
@@ -385,5 +429,5 @@
 }
 
-errno_t vol_part_add(vol_parts_t *parts, service_id_t sid)
+errno_t vol_part_add_part(vol_parts_t *parts, service_id_t sid)
 {
 	errno_t rc;
@@ -403,5 +447,5 @@
 }
 
-errno_t vol_parts_create(vol_parts_t **rparts)
+errno_t vol_parts_create(vol_volumes_t *volumes, vol_parts_t **rparts)
 {
 	vol_parts_t *parts;
@@ -413,4 +457,5 @@
 	fibril_mutex_initialize(&parts->lock);
 	list_initialize(&parts->parts);
+	parts->volumes = volumes;
 
 	*rparts = parts;
@@ -557,6 +602,39 @@
 }
 
+/** Set mount point.
+ *
+ * Verify and set a mount point. If the value of the mount point is
+ * the same as the default value, we will actually unset the mount point
+ * value (therefore effectively changing it to use the default).
+ *
+ * @return EOK on success, error code otherwise
+ */
+static errno_t vol_part_mountp_set(vol_part_t *part, const char *mountp)
+{
+	errno_t rc;
+	const char *def_mp;
+	const char *mp;
+
+	rc = vol_mountp_validate(mountp);
+	if (rc != EOK)
+		return rc;
+
+	def_mp = vol_part_def_mountp(part);
+
+	/* If the value is the same as default, set to empty string. */
+	if (str_cmp(def_mp, mountp) == 0)
+		mp = "";
+	else
+		mp = mountp;
+
+	rc = vol_volume_set_mountp(part->volume, mp);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
 errno_t vol_part_mkfs_part(vol_part_t *part, vol_fstype_t fstype,
-    const char *label)
+    const char *label, const char *mountp)
 {
 	errno_t rc;
@@ -585,4 +663,10 @@
 	}
 
+	rc = vol_part_mountp_set(part, mountp);
+	if (rc != EOK) {
+		fibril_mutex_unlock(&part->parts->lock);
+		return rc;
+	}
+
 	rc = vol_part_mount(part);
 	if (rc != EOK) {
Index: uspace/srv/volsrv/part.h
===================================================================
--- uspace/srv/volsrv/part.h	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/srv/volsrv/part.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -42,9 +42,10 @@
 #include <types/vol.h>
 #include "types/part.h"
+#include "types/volume.h"
 
-extern errno_t vol_parts_create(vol_parts_t **);
+extern errno_t vol_parts_create(vol_volumes_t *, vol_parts_t **);
 extern void vol_parts_destroy(vol_parts_t *);
 extern errno_t vol_part_discovery_start(vol_parts_t *);
-extern errno_t vol_part_add(vol_parts_t *, service_id_t);
+extern errno_t vol_part_add_part(vol_parts_t *, service_id_t);
 extern errno_t vol_part_get_ids(vol_parts_t *, service_id_t *, size_t,
     size_t *);
@@ -54,5 +55,6 @@
 extern errno_t vol_part_eject_part(vol_part_t *);
 extern errno_t vol_part_empty_part(vol_part_t *);
-extern errno_t vol_part_mkfs_part(vol_part_t *, vol_fstype_t, const char *);
+extern errno_t vol_part_mkfs_part(vol_part_t *, vol_fstype_t, const char *,
+    const char *);
 extern errno_t vol_part_get_info(vol_part_t *, vol_part_info_t *);
 
Index: uspace/srv/volsrv/test/main.c
===================================================================
--- uspace/srv/volsrv/test/main.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
+++ uspace/srv/volsrv/test/main.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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.
+ */
+
+#include <pcut/pcut.h>
+
+PCUT_INIT;
+
+PCUT_IMPORT(volume);
+
+PCUT_MAIN();
Index: uspace/srv/volsrv/test/volume.c
===================================================================
--- uspace/srv/volsrv/test/volume.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
+++ uspace/srv/volsrv/test/volume.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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.
+ */
+
+#include <errno.h>
+#include <pcut/pcut.h>
+#include <str.h>
+
+#include "../volume.h"
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(volume);
+
+/** Volumes list creation and destruction. */
+PCUT_TEST(volumes_basic)
+{
+	vol_volumes_t *volumes;
+	errno_t rc;
+
+	rc = vol_volumes_create(&volumes);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	vol_volumes_destroy(volumes);
+}
+
+/** Two references to the same volume, reference to a different volume. */
+PCUT_TEST(two_same_different)
+{
+	vol_volumes_t *volumes;
+	vol_volume_t *va, *vb, *va1;
+	errno_t rc;
+
+	rc = vol_volumes_create(&volumes);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = vol_volume_lookup_ref(volumes, "foo", &va);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = vol_volume_lookup_ref(volumes, "bar", &vb);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = vol_volume_lookup_ref(volumes, "foo", &va1);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_TRUE(va1 == va);
+
+	vol_volume_del_ref(va);
+	vol_volume_del_ref(vb);
+	vol_volume_del_ref(va1);
+
+	vol_volumes_destroy(volumes);
+}
+
+/** Reference the same volume twice, making sure it persists. */
+PCUT_TEST(same_twice)
+{
+	vol_volumes_t *volumes;
+	vol_volume_t *va;
+	errno_t rc;
+
+	rc = vol_volumes_create(&volumes);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Look up a volume */
+	rc = vol_volume_lookup_ref(volumes, "foo", &va);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Setting mount point forces it to persist after dropping the ref. */
+	rc = vol_volume_set_mountp(va, "/xyz");
+
+	/* Drop the reference */
+	vol_volume_del_ref(va);
+
+	/* Look up volume again */
+	rc = vol_volume_lookup_ref(volumes, "foo", &va);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* The mount point should still be set */
+	PCUT_ASSERT_NOT_NULL(va->mountp);
+	PCUT_ASSERT_TRUE(str_cmp(va->mountp, "/xyz") == 0);
+
+	vol_volume_del_ref(va);
+
+	vol_volumes_destroy(volumes);
+}
+
+PCUT_EXPORT(volume);
Index: uspace/srv/volsrv/types/part.h
===================================================================
--- uspace/srv/volsrv/types/part.h	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/srv/volsrv/types/part.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -66,4 +66,6 @@
 	/** Mounted at automatic mount point */
 	bool cur_mp_auto;
+	/** Volume */
+	struct vol_volume *volume;
 } vol_part_t;
 
@@ -74,4 +76,6 @@
 	/** Partitions (list of vol_part_t) */
 	list_t parts;
+	/** Underlying volumes */
+	struct vol_volumes *volumes;
 } vol_parts_t;
 
Index: uspace/srv/volsrv/types/volume.h
===================================================================
--- uspace/srv/volsrv/types/volume.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
+++ uspace/srv/volsrv/types/volume.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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 volsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef TYPES_VOLUME_H_
+#define TYPES_VOLUME_H_
+
+#include <adt/list.h>
+#include <atomic.h>
+#include <fibril_synch.h>
+
+/** Volume */
+typedef struct vol_volume {
+	/** Containing volume list */
+	struct vol_volumes *volumes;
+	/** Link to vol_volumes */
+	link_t lvolumes;
+	/** Reference count */
+	atomic_t refcnt;
+	/** Volume label */
+	char *label;
+	/** Mount point */
+	char *mountp;
+} vol_volume_t;
+
+/** Partitions */
+typedef struct vol_volumes {
+	/** Synchronize access to list of volumes */
+	fibril_mutex_t lock;
+	/** Volumes (list of vol_volume_t) */
+	list_t volumes;
+} vol_volumes_t;
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/volsrv/volsrv.c
===================================================================
--- uspace/srv/volsrv/volsrv.c	(revision 05208d9c60c285297a494eec8c02e0e77414a3b7)
+++ uspace/srv/volsrv/volsrv.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -49,4 +49,5 @@
 #include "mkfs.h"
 #include "part.h"
+#include "volume.h"
 
 #define NAME  "volsrv"
@@ -57,9 +58,14 @@
 {
 	errno_t rc;
+	vol_volumes_t *volumes = NULL;
 	vol_parts_t *parts = NULL;
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_init()");
 
-	rc = vol_parts_create(&parts);
+	rc = vol_volumes_create(&volumes);
+	if (rc != EOK)
+		goto error;
+
+	rc = vol_parts_create(volumes, &parts);
 	if (rc != EOK)
 		goto error;
@@ -87,4 +93,5 @@
 	return EOK;
 error:
+	vol_volumes_destroy(volumes);
 	vol_parts_destroy(parts);
 	return rc;
@@ -131,5 +138,5 @@
 	sid = IPC_GET_ARG1(*icall);
 
-	rc = vol_part_add(parts, sid);
+	rc = vol_part_add_part(parts, sid);
 	if (rc != EOK) {
 		async_answer_0(icall, rc);
@@ -285,4 +292,5 @@
 	vol_fstype_t fstype;
 	char *label;
+	char *mountp;
 	errno_t rc;
 
@@ -304,4 +312,17 @@
 	}
 
+	rc = async_data_write_accept((void **)&mountp, true, 0, VOL_MOUNTP_MAXLEN,
+	    0, NULL);
+	if (rc != EOK) {
+		free(label);
+		async_answer_0(icall, rc);
+		return;
+	}
+
+	if (mountp != NULL) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "vol_part_mkfs_srv: mountp='%s'",
+		    mountp);
+	}
+
 	rc = vol_part_find_by_id_ref(parts, sid, &part);
 	if (rc != EOK) {
@@ -311,5 +332,5 @@
 	}
 
-	rc = vol_part_mkfs_part(part, fstype, label);
+	rc = vol_part_mkfs_part(part, fstype, label, mountp);
 	if (rc != EOK) {
 		free(label);
Index: uspace/srv/volsrv/volume.c
===================================================================
--- uspace/srv/volsrv/volume.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
+++ uspace/srv/volsrv/volume.c	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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 volsrv
+ * @{
+ */
+/**
+ * @file Volume handling
+ * @brief
+ *
+ * Volumes are the file systems (or similar) contained in partitions.
+ * Each vol_volume_t can be considered the configuration entry
+ * for a volume. Each partition has an associated vol_volume_t.
+ *
+ * If there is any non-default configuration to be remembered for a
+ * volume, the vol_volume_t structure is kept around even after the partition
+ * is disassociated from it. Otherwise it is deleted once no longer
+ * referenced.
+ */
+
+#include <adt/list.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <io/log.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <str.h>
+
+#include "volume.h"
+#include "types/volume.h"
+
+static void vol_volume_delete(vol_volume_t *);
+static void vol_volume_add_locked(vol_volumes_t *, vol_volume_t *);
+static errno_t vol_volume_lookup_ref_locked(vol_volumes_t *, const char *,
+    vol_volume_t **);
+
+/** Allocate new volume structure.
+ *
+ * @return Pointer to new volume structure
+ */
+static vol_volume_t *vol_volume_new(void)
+{
+	vol_volume_t *volume = calloc(1, sizeof(vol_volume_t));
+
+	if (volume == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating volume "
+		    "structure. Out of memory.");
+		return NULL;
+	}
+
+	volume->label = str_dup("");
+	volume->mountp = str_dup("");
+
+	if (volume->label == NULL || volume->mountp == NULL) {
+		vol_volume_delete(volume);
+		return NULL;
+	}
+
+	atomic_set(&volume->refcnt, 1);
+	link_initialize(&volume->lvolumes);
+	volume->volumes = NULL;
+
+	return volume;
+}
+
+/** Delete volume structure.
+ *
+ * @param volume Volume structure
+ */
+static void vol_volume_delete(vol_volume_t *volume)
+{
+	log_msg(LOG_DEFAULT, LVL_NOTE, "Freeing volume %p", volume);
+
+	free(volume->label);
+	free(volume->mountp);
+	free(volume);
+}
+
+/** Create list of volumes.
+ *
+ * @param rvolumes Place to store pointer to list of volumes.
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t vol_volumes_create(vol_volumes_t **rvolumes)
+{
+	vol_volumes_t *volumes;
+
+	volumes = calloc(1, sizeof(vol_volumes_t));
+	if (volumes == NULL)
+		return ENOMEM;
+
+	fibril_mutex_initialize(&volumes->lock);
+	list_initialize(&volumes->volumes);
+
+	*rvolumes = volumes;
+	return EOK;
+}
+
+/** Destroy list of volumes.
+ *
+ * @param volumes List of volumes or @c NULL
+ */
+void vol_volumes_destroy(vol_volumes_t *volumes)
+{
+	link_t *link;
+	vol_volume_t *volume;
+
+	if (volumes == NULL)
+		return;
+
+	link = list_first(&volumes->volumes);
+	while (link != NULL) {
+		volume = list_get_instance(link, vol_volume_t, lvolumes);
+
+		list_remove(&volume->lvolumes);
+		vol_volume_delete(volume);
+
+		link = list_first(&volumes->volumes);
+	}
+
+	free(volumes);
+}
+
+/** Add new volume structure to list of volumes.
+ *
+ * @param volumes List of volumes
+ * @param volume Volume structure
+ */
+static void vol_volume_add_locked(vol_volumes_t *volumes,
+    vol_volume_t *volume)
+{
+	assert(fibril_mutex_is_locked(&volumes->lock));
+	log_msg(LOG_DEFAULT, LVL_NOTE, "vol_volume_add_locked(%p)", volume);
+
+	volume->volumes = volumes;
+	list_append(&volume->lvolumes, &volumes->volumes);
+}
+
+/** Look up volume structure with locked volumes lock.
+ *
+ * If a matching existing volume is found, it is returned. Otherwise
+ * a new volume structure is created.
+ *
+ * @param volumes List of volumes
+ * @param label Volume label
+ * @param rvolume Place to store pointer to volume structure (existing or new)
+ *
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static errno_t vol_volume_lookup_ref_locked(vol_volumes_t *volumes,
+    const char *label, vol_volume_t **rvolume)
+{
+	vol_volume_t *volume;
+
+	assert(fibril_mutex_is_locked(&volumes->lock));
+
+	list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
+		if (str_cmp(volume->label, label) == 0 &&
+		    str_size(label) > 0) {
+			/* Add reference */
+			atomic_inc(&volume->refcnt);
+			*rvolume = volume;
+			return EOK;
+		}
+	}
+
+	/* No existing volume found. Create a new one. */
+	volume = vol_volume_new();
+	if (volume == NULL)
+		return ENOMEM;
+
+	volume->label = str_dup(label);
+	vol_volume_add_locked(volumes, volume);
+
+	*rvolume = volume;
+	return EOK;
+}
+
+/** Look up volume structure.
+ *
+ * If a matching existing volume is found, it is returned. Otherwise
+ * a new volume structure is created.
+ *
+ * @param volumes List of volumes
+ * @param label Volume label
+ * @param rvolume Place to store pointer to volume structure (existing or new)
+ *
+ * @return EOK on success, ENOMEM if out of memory
+ */
+errno_t vol_volume_lookup_ref(vol_volumes_t *volumes, const char *label,
+    vol_volume_t **rvolume)
+{
+	errno_t rc;
+
+	fibril_mutex_lock(&volumes->lock);
+	rc = vol_volume_lookup_ref_locked(volumes, label, rvolume);
+	fibril_mutex_unlock(&volumes->lock);
+
+	return rc;
+}
+
+/** Determine if volume has non-default settings that need to persist.
+ *
+ * @param volume Volume
+ * @return @c true iff volume has settings that need to persist
+ */
+static bool vol_volume_is_persist(vol_volume_t *volume)
+{
+	return str_size(volume->mountp) > 0;
+}
+
+/** Delete reference to volume.
+ *
+ * @param volume Volume
+ */
+void vol_volume_del_ref(vol_volume_t *volume)
+{
+	if (atomic_predec(&volume->refcnt) == 0) {
+		/* No more references. Check if volume is persistent. */
+		if (!vol_volume_is_persist(volume)) {
+			list_remove(&volume->lvolumes);
+			vol_volume_delete(volume);
+		}
+	}
+}
+
+/** Set volume mount point.
+ *
+ * @param volume Volume
+ * @param mountp Mount point
+ *
+ * @return EOK on success or error code
+ */
+errno_t vol_volume_set_mountp(vol_volume_t *volume, const char *mountp)
+{
+	char *mp;
+
+	mp = str_dup(mountp);
+	if (mp == NULL)
+		return ENOMEM;
+
+	free(volume->mountp);
+	volume->mountp = mp;
+
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/srv/volsrv/volume.h
===================================================================
--- uspace/srv/volsrv/volume.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
+++ uspace/srv/volsrv/volume.h	(revision 64ffd83f5f89887b8cfde324d0603dcecba12312)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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 volsrv
+ * @{
+ */
+/**
+ * @file
+ * @brief
+ */
+
+#ifndef VOLUME_H_
+#define VOLUME_H_
+
+#include "types/volume.h"
+
+extern errno_t vol_volumes_create(vol_volumes_t **);
+extern void vol_volumes_destroy(vol_volumes_t *);
+extern errno_t vol_volume_lookup_ref(vol_volumes_t *, const char *,
+    vol_volume_t **);
+extern void vol_volume_del_ref(vol_volume_t *);
+extern errno_t vol_volume_set_mountp(vol_volume_t *, const char *);
+
+#endif
+
+/** @}
+ */
