[PATCH][SCST]: Show sessions for a target on sysfs; sysfs interface to add luns to a given target.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The patch below show created sessions for a target on
/sys/kernel/scst_tgt/targets/<target_driver_name>/<target>/session
and allow creation of default LUNs (tgt->default_acg) to the target
using targets/<target_driver_name>/<target>/luns/mgmt

please review.

Signed-off-by: Daniel Debonzi <debonzi@xxxxxxxxxxxxxxxxxx>
---
include/scst.h   |    7
src/scst_lib.c   |   26 ---
src/scst_main.c  |   11 +
src/scst_priv.h  |    3
src/scst_sysfs.c |  387 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
src/scst_targ.c  |    7
6 files changed, 408 insertions(+), 33 deletions(-)

Index: scst/include/scst.h
===================================================================
--- scst/include/scst.h	(revision 887)
+++ scst/include/scst.h	(working copy)
@@ -931,6 +931,8 @@ struct scst_tgt {

 	struct scst_tgt_template *tgtt;	/* corresponding target template */

+	struct scst_acg *default_acg; /* The default acg for this target. */
+
 	/*
 	 * Maximum SG table size. Needed here, since different cards on the
 	 * same target template can have different SG table limitations.
@@ -1046,6 +1048,8 @@ struct scst_session {
 	/* Used if scst_unregister_session() called in wait mode */
 	struct completion *shutdown_compl;

+	struct kobject sess_kobj; /* kobject for this struct */
+
 	/*
 	 * Functions and data for user callbacks from scst_register_session()
 	 * and scst_unregister_session()
@@ -1631,6 +1635,9 @@ struct scst_acg_dev {

 	/* list entry in acg->acg_dev_list */
 	struct list_head acg_dev_list_entry;
+
+	/* kobject for this structure. */
+	struct kobject acg_dev_kobj;
 };

 /*
Index: scst/src/scst_main.c
===================================================================
--- scst/src/scst_main.c	(revision 887)
+++ scst/src/scst_main.c	(working copy)
@@ -394,9 +394,13 @@ struct scst_tgt *scst_register(struct sc
 			SCST_DEFAULT_TGT_NAME_SUFFIX, tgt_num++);
 	}

+	tgt->default_acg = scst_alloc_add_acg(target_name);
+	if (tgt->default_acg == NULL)
+		goto out_free_tgt_name;
+
 	rc = scst_build_proc_target_entries(tgt);
 	if (rc < 0)
-		goto out_free_tgt_name;
+		goto out_free_acg;

 	rc = scst_create_tgt_sysfs(tgt);
 	if (rc < 0)
@@ -422,6 +426,9 @@ out_clean_proc:
 out_free_tgt_name:
 	kfree(tgt->tgt_name);

+out_free_acg:
+	scst_destroy_acg(tgt->default_acg);
+
 out_free_def_name:
 	kfree(tgt->default_group_name);

@@ -499,6 +506,8 @@ again:
 	kfree(tgt->tgt_name);
 	kfree(tgt->default_group_name);

+	scst_destroy_acg(tgt->default_acg);
+
 	del_timer_sync(&tgt->retry_timer);

 	scst_cleanup_tgt_sysfs_put(tgt);
Index: scst/src/scst_priv.h
===================================================================
--- scst/src/scst_priv.h	(revision 887)
+++ scst/src/scst_priv.h	(working copy)
@@ -291,6 +291,7 @@ struct scst_acg *scst_alloc_add_acg(cons
 int scst_destroy_acg(struct scst_acg *acg);

 int scst_sess_alloc_tgt_devs(struct scst_session *sess);
+void scst_sess_free_tgt_devs(struct scst_session *sess);
 void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA);

 int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev,
@@ -390,6 +391,8 @@ int scst_create_tgtt_sysfs(struct scst_t
 void scst_cleanup_tgtt_sysfs(struct scst_tgt_template *vtt);
 int scst_create_tgt_sysfs(struct scst_tgt *tgt);
 void scst_cleanup_tgt_sysfs_put(struct scst_tgt *tgt);
+int scst_create_sess_sysfs(struct scst_session *sess);
+void scst_cleanup_sess_sysfs_put(struct scst_session *session);
 int scst_create_sgv_sysfs(struct sgv_pool *pool);
 void scst_cleanup_sgv_sysfs_put(struct sgv_pool *pool);

Index: scst/src/scst_lib.c
===================================================================
--- scst/src/scst_lib.c	(revision 887)
+++ scst/src/scst_lib.c	(working copy)
@@ -45,7 +45,6 @@ static void scst_alloc_set_UA(struct scs
 	const uint8_t *sense, int sense_len, int flags);
 static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev);
 static void scst_release_space(struct scst_cmd *cmd);
-static void scst_sess_free_tgt_devs(struct scst_session *sess);
 static void scst_unblock_cmds(struct scst_device *dev);

 #ifdef CONFIG_SCST_DEBUG_TM
@@ -925,12 +924,7 @@ static void scst_free_acg_dev(struct scs
 {
 	TRACE_ENTRY();

-	TRACE_DBG("Removing acg_dev %p from acg_dev_list and dev_acg_dev_list",
-		acg_dev);
-	list_del(&acg_dev->acg_dev_list_entry);
-	list_del(&acg_dev->dev_acg_dev_list_entry);
-
-	kmem_cache_free(scst_acgd_cachep, acg_dev);
+	kobject_put(&acg_dev->acg_dev_kobj);

 	TRACE_EXIT();
 	return;
@@ -1314,7 +1308,7 @@ out_free:
  * scst_mutex supposed to be held, there must not be parallel activity in this
  * session.
  */
-static void scst_sess_free_tgt_devs(struct scst_session *sess)
+void scst_sess_free_tgt_devs(struct scst_session *sess)
 {
 	int i;
 	struct scst_tgt_dev *tgt_dev, *t;
@@ -1890,21 +1884,7 @@ void scst_free_session(struct scst_sessi
 {
 	TRACE_ENTRY();

-	mutex_lock(&scst_mutex);
-
-	TRACE_DBG("Removing sess %p from the list", sess);
-	list_del(&sess->sess_list_entry);
-	TRACE_DBG("Removing session %p from acg %s", sess, sess->acg->acg_name);
-	list_del(&sess->acg_sess_list_entry);
-
-	scst_sess_free_tgt_devs(sess);
-
-	wake_up_all(&sess->tgt->unreg_waitQ);
-
-	mutex_unlock(&scst_mutex);
-
-	kfree(sess->initiator_name);
-	kmem_cache_free(scst_sess_cachep, sess);
+	scst_cleanup_sess_sysfs_put(sess);

 	TRACE_EXIT();
 	return;
Index: scst/src/scst_sysfs.c
===================================================================
--- scst/src/scst_sysfs.c	(revision 887)
+++ scst/src/scst_sysfs.c	(working copy)
@@ -11,6 +11,9 @@

 #define SCST_SYSFS_BLOCK_SIZE (PAGE_SIZE - 512)

+#define SCST_LUN_ACTION_ADD		 1
+#define SCST_LUN_ACTION_DEL		 2
+
 static DEFINE_MUTEX(scst_sysfs_mutex);

 static struct kobject *scst_sysfs_root_kobj;
@@ -22,7 +25,16 @@ static struct kobject *scst_back_drivers
 static struct sysfs_ops scst_sysfs_ops;

 static void scst_sysfs_release(struct kobject *kobj);
-
+static ssize_t scst_luns_mgmt_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf);
+static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
+				    struct kobj_attribute *attr,
+				    const char *buf, size_t count);
+
+/*
+ * Target Template (e.g iscsi).
+ */
 int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt)
 {
 	int retval = 0;
@@ -43,6 +55,9 @@ void scst_cleanup_tgtt_sysfs(struct scst
 	kobject_put(tgtt->tgtt_kobj);
 }

+/*
+ * Target (e.g. iscsi target name).
+ */
 static void scst_tgt_free(struct kobject *kobj)
 {
 	struct scst_tgt *tgt;
@@ -61,6 +76,10 @@ static struct kobj_type tgt_ktype = {
 	.release = scst_tgt_free,
 };

+static struct kobj_attribute scst_luns_mgmt =
+	__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_luns_mgmt_show,
+	       scst_luns_mgmt_store);
+
 int scst_create_tgt_sysfs(struct scst_tgt *tgt)
 {
 	int retval;
@@ -85,6 +104,8 @@ int scst_create_tgt_sysfs(struct scst_tg
 		PRINT_ERROR("Can't create luns kobj for tgt %s", tgt->tgt_name);
 		goto luns_kobj_err;
 	}
+	if (sysfs_create_file(tgt->tgt_luns_kobj, &scst_luns_mgmt.attr))
+		goto create_luns_mgmt_err;

 	tgt->tgt_ini_grp_kobj = kobject_create_and_add("ini_group",
 						    &tgt->tgt_kobj);
@@ -98,6 +119,7 @@ out:
 	TRACE_EXIT_RES(retval);
 	return retval;

+create_luns_mgmt_err:
 ini_grp_kobj_err:
 	kobject_del(tgt->tgt_luns_kobj);
 	kobject_put(tgt->tgt_luns_kobj);
@@ -129,7 +151,356 @@ void scst_cleanup_tgt_sysfs_put(struct s
 	return;
 }

-struct kobj_attribute sgv_stat_attr =
+/*
+ * Target sessions directory implementation
+ */
+ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj,
+			    struct kobj_attribute *attr, char *buf)
+{
+	struct scst_session *sess;
+
+	sess = container_of(kobj, struct scst_session, sess_kobj);
+
+	return sprintf(buf, "%i\n", sess->sess_cmd_count.counter);
+}
+
+struct kobj_attribute session_attr =
+	__ATTR(commands, S_IRUGO, scst_sess_sysfs_commands_show, NULL);
+
+static struct attribute *scst_session_attrs[] = {
+	&session_attr.attr,
+	NULL,
+};
+
+static void scst_session_release(struct kobject *kobj)
+{
+	struct scst_session *sess;
+
+	TRACE_ENTRY();
+
+	sess = container_of(kobj, struct scst_session, sess_kobj);
+
+	mutex_lock(&scst_mutex);
+
+	TRACE_DBG("Removing sess %p from the list", sess);
+	list_del(&sess->sess_list_entry);
+	TRACE_DBG("Removing session %p from acg %s", sess, sess->acg->acg_name);
+	list_del(&sess->acg_sess_list_entry);
+
+	scst_sess_free_tgt_devs(sess);
+
+	wake_up_all(&sess->tgt->unreg_waitQ);
+
+	mutex_unlock(&scst_mutex);
+
+	kfree(sess->initiator_name);
+	kmem_cache_free(scst_sess_cachep, sess);
+
+	TRACE_EXIT();
+	return;
+}
+
+static struct kobj_type scst_session_ktype = {
+	.sysfs_ops = &scst_sysfs_ops,
+	.release = scst_session_release,
+	.default_attrs = scst_session_attrs,
+};
+
+int scst_create_sess_sysfs(struct scst_session *sess)
+{
+	int retval = 0;
+
+	TRACE_ENTRY();
+
+	retval = kobject_init_and_add(&sess->sess_kobj, &scst_session_ktype,
+			      sess->tgt->tgt_sess_kobj, sess->initiator_name);
+	if (retval != 0) {
+		PRINT_ERROR("Can't add session %s to sysfs",
+			    sess->initiator_name);
+		goto out;
+	}
+
+out:
+	TRACE_EXIT_RES(retval);
+	return retval;
+}
+
+void scst_cleanup_sess_sysfs_put(struct scst_session *session)
+{
+	TRACE_ENTRY();
+
+	kobject_put(&session->sess_kobj);
+
+	TRACE_EXIT();
+	return;
+}
+
+/* Target luns directory implementation. */
+static void scst_acg_dev_release(struct kobject *kobj)
+{
+	struct scst_acg_dev *acg_dev;
+
+	TRACE_ENTRY();
+
+	acg_dev = container_of(kobj, struct scst_acg_dev, acg_dev_kobj);
+
+	TRACE_DBG("Removing acg_dev %p from acg_dev_list and dev_acg_dev_list",
+		acg_dev);
+	list_del(&acg_dev->acg_dev_list_entry);
+	list_del(&acg_dev->dev_acg_dev_list_entry);
+
+	kmem_cache_free(scst_acgd_cachep, acg_dev);
+
+	TRACE_EXIT();
+	return;
+}
+
+static ssize_t scst_lun_options_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	return sprintf(buf, "lun options show!!\n");
+}
+
+struct kobj_attribute lun_options_attr =
+	__ATTR(options, S_IRUGO, scst_lun_options_show, NULL);
+
+static struct attribute *lun_attrs[] = {
+	&lun_options_attr.attr,
+	NULL,
+};
+
+static struct kobj_type acg_dev_ktype = {
+	.sysfs_ops = &scst_sysfs_ops,
+	.release = scst_acg_dev_release,
+	.default_attrs = lun_attrs,
+};
+
+int scst_create_acg_dev_sysfs(struct scst_acg *acg, unsigned int virt_lun,
+			  struct kobject *parent)
+{
+	int retval;
+	struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp;
+	TRACE_ENTRY();
+
+	list_for_each_entry(acg_dev_tmp, &acg->acg_dev_list,
+			    acg_dev_list_entry) {
+		if (acg_dev_tmp->lun == virt_lun) {
+			acg_dev = acg_dev_tmp;
+			break;
+		}
+	}
+
+	if (!acg_dev) {
+		PRINT_ERROR("%s", "acg_dev lookup for kobject creation failed");
+		retval = -EINVAL;
+		goto out;
+	}
+
+	retval = kobject_init_and_add(&acg_dev->acg_dev_kobj, &acg_dev_ktype,
+				      parent, "%u", virt_lun);
+	if (retval != 0) {
+		PRINT_ERROR("Can't add acg %s to sysfs", acg->acg_name);
+		retval = -EINVAL;
+	}
+
+/* 	XXX: change the second parameter to the kobject where */
+/* 	the link will point to. */
+	sysfs_create_link(&acg_dev->acg_dev_kobj, &acg_dev->acg_dev_kobj,
+			    "device");
+
+out:
+	return retval;
+}
+
+static ssize_t scst_luns_mgmt_show(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	static char *help =
+"Usage: echo \"add|del H:C:I:L lun [READ_ONLY]\" \
+> mgmt\n"
+"       echo \"add|del V_NAME lun [READ_ONLY]\" \
+> mgmt\n";
+
+	return sprintf(buf, help);
+}
+
+static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
+				    struct kobj_attribute *attr,
+				    const char *buf, size_t count)
+{
+	int res = count, virt = 0, rc, read_only = 0, action;
+	char *buffer, *p, *e = NULL;
+	unsigned int host, channel = 0, id = 0, lun = 0, virt_lun;
+	struct scst_acg *acg;
+	struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp;
+	struct scst_device *d, *dev = NULL;
+	struct scst_tgt *tgt;
+
+	TRACE_ENTRY();
+
+	tgt = container_of(kobj->parent, struct scst_tgt, tgt_kobj);
+	acg = tgt->default_acg;
+
+	buffer = kzalloc(count+1, GFP_KERNEL);
+	memcpy(buffer, buf, count);
+	buffer[count+1] = '\0';
+	p = buffer;
+
+	p = buffer;
+	if (p[strlen(p) - 1] == '\n')
+		p[strlen(p) - 1] = '\0';
+	if (!strncasecmp("add ", p, 4)) {
+		p += 4;
+		action = SCST_LUN_ACTION_ADD;
+	} else if (!strncasecmp("del ", p, 4)) {
+		p += 4;
+		action = SCST_LUN_ACTION_DEL;
+	} else {
+		PRINT_ERROR("Unknown action \"%s\"", p);
+		res = -EINVAL;
+		goto out_free;
+	}
+
+	if (scst_suspend_activity(true) != 0) {
+		res = EINVAL;
+		goto out_free;
+	}
+
+	if (mutex_lock_interruptible(&scst_mutex) != 0) {
+		res = -EINTR;
+		goto out_free_resume;
+	}
+
+
+	while (isspace(*p) && *p != '\0')
+		p++;
+	e = p; /* save p */
+	host = simple_strtoul(p, &p, 0);
+	if (*p == ':') {
+		channel = simple_strtoul(p + 1, &p, 0);
+		id = simple_strtoul(p + 1, &p, 0);
+		lun = simple_strtoul(p + 1, &p, 0);
+		e = p;
+	} else {
+		virt++;
+		p = e; /* restore p */
+		while (!isspace(*e) && *e != '\0')
+			e++;
+		*e = 0;
+	}
+
+	list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
+		if (virt) {
+			if (d->virt_id && !strcmp(d->virt_name, p)) {
+				dev = d;
+				TRACE_DBG("Virt device %p (%s) found",
+					  dev, p);
+				break;
+			}
+		} else {
+			if (d->scsi_dev &&
+			    d->scsi_dev->host->host_no == host &&
+			    d->scsi_dev->channel == channel &&
+			    d->scsi_dev->id == id &&
+			    d->scsi_dev->lun == lun) {
+				dev = d;
+				TRACE_DBG("Dev %p (%d:%d:%d:%d) found",
+					  dev, host, channel, id, lun);
+				break;
+			}
+		}
+	}
+	if (dev == NULL) {
+		if (virt) {
+			PRINT_ERROR("Virt device %s not found", p);
+		} else {
+			PRINT_ERROR("Device %d:%d:%d:%d not found",
+				    host, channel, id, lun);
+		}
+		res = -EINVAL;
+		goto out_free_up;
+	}
+
+	switch (action) {
+	case SCST_LUN_ACTION_ADD:
+		e++;
+		while (isspace(*e) && *e != '\0')
+			e++;
+		virt_lun = simple_strtoul(e, &e, 0);
+
+		while (isspace(*e) && *e != '\0')
+			e++;
+
+		if (*e != '\0') {
+			if (!strncasecmp("READ_ONLY", e, 9))
+				read_only = 1;
+			else {
+				PRINT_ERROR("Unknown option \"%s\"", e);
+				res = -EINVAL;
+				goto out_free_up;
+			}
+		}
+
+		list_for_each_entry(acg_dev_tmp, &acg->acg_dev_list,
+				    acg_dev_list_entry) {
+			if (acg_dev_tmp->lun == virt_lun) {
+				acg_dev = acg_dev_tmp;
+				break;
+			}
+		}
+
+		if (acg_dev) {
+			acg_dev = acg_dev_tmp;
+			PRINT_ERROR("virt lun %d already exists in group %s",
+				    virt_lun, acg->acg_name);
+			res = -EINVAL;
+			goto out_free_up;
+		}
+
+
+		rc = scst_acg_add_dev(acg, dev, virt_lun, read_only);
+		if (rc) {
+			PRINT_ERROR("scst_acg_add_dev() returned %d", rc);
+			res = rc;
+			goto out_free_up;
+		}
+
+		rc = scst_create_acg_dev_sysfs(acg, virt_lun, kobj);
+		if (rc < 0) {
+			PRINT_ERROR("%s", "creation of acg_dev kobject failed");
+			goto out_remove_acg_dev;
+		}
+		break;
+	case SCST_LUN_ACTION_DEL:
+		rc = scst_acg_remove_dev(acg, dev);
+		if (rc) {
+			PRINT_ERROR("scst_acg_remove_dev() returned %d", rc);
+			res = rc;
+		}
+		break;
+	}
+out_free_up:
+	mutex_unlock(&scst_mutex);
+
+out_free_resume:
+	scst_resume_activity();
+
+out_free:
+	kfree(buffer);
+	TRACE_EXIT_RES(res);
+
+	return res;
+
+out_remove_acg_dev:
+	scst_acg_remove_dev(acg, dev);
+
+	goto out_free_up;
+}
+
+/* svg directory implementation. */
+struct kobj_attribute sgv_stat_attr =
 	__ATTR(stats, S_IRUGO, sgv_sysfs_stat_show, NULL);

 static struct attribute *sgv_attrs[] = {
@@ -183,7 +554,7 @@ void scst_cleanup_sgv_sysfs_put(struct s
 	return;
 }

-struct kobj_attribute sgv_global_stat_attr =
+struct kobj_attribute sgv_global_stat_attr =
 	__ATTR(global_stats, S_IRUGO, sgv_sysfs_global_stat_show, NULL);

 static struct attribute *sgv_default_attrs[] = {
@@ -197,6 +568,7 @@ static struct kobj_type sgv_ktype = {
 	.default_attrs = sgv_default_attrs,
 };

+/* scst sysfs root implementation. */
 static ssize_t scst_threads_show(struct kobject *kobj, struct kobj_attribute *attr,
 				 char *buf)
 {
@@ -326,7 +698,7 @@ static ssize_t scst_version_show(struct
 	return strlen(buf);
 }

-struct kobj_attribute scst_threads_attr =
+struct kobj_attribute scst_threads_attr =
 	__ATTR(threads, S_IRUGO | S_IWUSR, scst_threads_show,
 	       scst_threads_store);

@@ -334,9 +706,9 @@ struct kobj_attribute scst_trace_level_a
 	__ATTR(trace_level, S_IRUGO | S_IWUSR, scst_trace_level_show,
 	       scst_trace_level_store);

-struct kobj_attribute scst_version_attr =
+struct kobj_attribute scst_version_attr =
 	__ATTR(version, S_IRUGO, scst_version_show, NULL);
-	
+
 static struct attribute *scst_sysfs_root_default_attrs[] = {
 	&scst_threads_attr.attr,
 	&scst_trace_level_attr.attr,
@@ -378,7 +750,6 @@ static struct kobj_type scst_sysfs_root_
 	.default_attrs = scst_sysfs_root_default_attrs,
 };

-
 int __init scst_sysfs_init(void)
 {
 	int retval = 0;
@@ -420,7 +791,7 @@ int __init scst_sysfs_init(void)
 		goto back_drivers_kobj_error;


-out:	
+out:
 	TRACE_EXIT_RES(retval);
 	return retval;

Index: scst/src/scst_targ.c
===================================================================
--- scst/src/scst_targ.c	(revision 887)
+++ scst/src/scst_targ.c	(working copy)
@@ -5288,12 +5288,13 @@ static int scst_init_session(struct scst

 	mutex_lock(&scst_mutex);

+/* 	debonzi: Not fully implemented. */
 	if (sess->initiator_name)
 		acg = scst_find_acg(sess->initiator_name);
 	if ((acg == NULL) && (sess->tgt->default_group_name != NULL))
 		acg = scst_find_acg_by_name(sess->tgt->default_group_name);
 	if (acg == NULL)
-		acg = scst_default_acg;
+		acg = sess->tgt->default_acg;

 	PRINT_INFO("Using security group \"%s\" for initiator \"%s\"",
 		acg->acg_name, sess->initiator_name);
@@ -5355,6 +5356,10 @@ restart:
 			&scst_active_mgmt_cmd_list);
 		mwake = 1;
 	}
+
+	if (res == 0)
+		res = scst_create_sess_sysfs(sess);
+
 	spin_unlock(&scst_mcmd_lock);
 	sess->init_phase = SCST_SESS_IPH_READY;
 	spin_unlock_irq(&sess->sess_list_lock);
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux