jffs2: Allow selection of compression mode via a sysfs attribute

Allow selection of the compression mode for jffs2 via a sysfs
attribute. This establishes a sysfs presence for jffs2 through
which other compression options could easily be exported too.

Signed-off-by: Richard Purdie <rpurdie@openedhand.com>
---
 fs/Kconfig       |    9 ++++++++
 fs/jffs2/compr.c |   41 ++++++++++++++++++++++++++++++++++++
 fs/jffs2/compr.h |    6 +++++
 fs/jffs2/super.c |   61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 117 insertions(+), 0 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index 328a62f..9da136b 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1283,6 +1283,15 @@ config JFFS2_FS_SECURITY
 	  If you are not using a security module that requires using
 	  extended attributes for file security labels, say N.
 
+config JFFS2_SYSFS
+	bool "JFFS2 sysfs attributes"
+	depends on JFFS2_FS
+	default n
+	help
+	  Enable control of jffs2 features through sysfs.
+
+	  If unsure, say 'N'.
+
 config JFFS2_COMPRESSION_OPTIONS
 	bool "Advanced compression options for JFFS2"
 	depends on JFFS2_FS
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
index fdb0efd..8c57dc7 100644
--- a/fs/jffs2/compr.c
+++ b/fs/jffs2/compr.c
@@ -11,6 +11,7 @@
  *
  */
 
+#include <linux/kobject.h>
 #include "compr.h"
 
 static DEFINE_SPINLOCK(jffs2_compressor_list_lock);
@@ -296,6 +297,46 @@ int jffs2_unregister_compressor(struct jffs2_compressor *comp)
         return 0;
 }
 
+#ifdef CONFIG_JFFS2_SYSFS
+
+char *jffs2_get_compression_mode_name(void)
+{
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                return "none";
+        case JFFS2_COMPR_MODE_PRIORITY:
+                return "priority";
+        case JFFS2_COMPR_MODE_SIZE:
+                return "size";
+	case JFFS2_COMPR_MODE_FAVOURLZO:
+		return "favourlzo";
+        }
+        return "unkown";
+}
+
+int jffs2_set_compression_mode_name(const char *name)
+{
+        if (!strncmp("none", name, 4)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+                return 0;
+        }
+        if (!strncmp("priority", name, 8)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+                return 0;
+        }
+        if (!strncmp("size", name, 4)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+                return 0;
+        }
+	if (!strncmp("favourlzo", name, 9)) {
+		jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO;
+		return 0;
+	}
+        return -EINVAL;
+}
+
+#endif
+
 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
 {
         if (orig != comprbuf)
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
index 2b66941..917f0d6 100644
--- a/fs/jffs2/compr.h
+++ b/fs/jffs2/compr.h
@@ -77,6 +77,12 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 
 void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
 
+#ifdef CONFIG_JFFS2_SYSFS
+int jffs2_set_compression_mode_name(const char *mode_name);
+char *jffs2_get_compression_mode_name(void);
+#endif
+
+
 /* Compressor modules */
 /* These functions will be called by jffs2_compressors_init/exit */
 
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index e51164a..9a67113 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -322,6 +322,54 @@ static struct file_system_type jffs2_fs_type = {
 	.kill_sb =	jffs2_kill_sb,
 };
 
+#ifdef CONFIG_JFFS2_SYSFS
+
+static struct attribute jffs2_attr_mode = {
+	.name = "compression_mode",
+	.mode = S_IRUGO | S_IWUSR,
+};
+
+static struct attribute *jffs2_attrs[] = {
+	&jffs2_attr_mode,
+	NULL,
+};
+
+static ssize_t jffs2_attr_show(struct kobject *kobj, struct attribute *attr,
+		char *page)
+{
+	if (!strcmp("compression_mode", attr->name))
+		return sprintf(page, "%s\n", jffs2_get_compression_mode_name());
+	return 0;
+}
+
+static ssize_t jffs2_attr_store(struct kobject *kobj, struct attribute *attr,
+		const char *page, size_t count)
+{
+	int ret = -EINVAL;
+
+	if (!strcmp("compression_mode", attr->name)) {
+		ret = jffs2_set_compression_mode_name(page);
+		if (ret >= 0)
+			return count;
+	}
+	return ret;
+}
+
+static struct sysfs_ops jffs2_sysfs_ops = {
+	.show	=	jffs2_attr_show,
+	.store	=	jffs2_attr_store,
+};
+
+static struct kobj_type jffs2_subsys_type = {
+	.default_attrs	= jffs2_attrs,
+	.sysfs_ops	= &jffs2_sysfs_ops,
+};
+
+/* gives us jffs2_subsys */
+static decl_subsys(jffs2, NULL, NULL);
+
+#endif
+
 static int __init init_jffs2_fs(void)
 {
 	int ret;
@@ -371,6 +419,16 @@ static int __init init_jffs2_fs(void)
 		printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
 		goto out_slab;
 	}
+
+#ifdef CONFIG_JFFS2_SYSFS
+	/* Errors here are not fatal */
+	kset_set_kset_s(&jffs2_subsys, fs_subsys);
+	jffs2_subsys.kset.kobj.ktype = &jffs2_subsys_type;
+	ret = subsystem_register(&jffs2_subsys);
+	if (ret)
+		printk(KERN_WARNING "Error registering JFFS2 sysfs attributes\n");
+#endif
+
 	return 0;
 
  out_slab:
@@ -384,6 +442,9 @@ static int __init init_jffs2_fs(void)
 
 static void __exit exit_jffs2_fs(void)
 {
+#ifdef CONFIG_JFFS2_SYSFS
+	subsystem_unregister(&jffs2_subsys);
+#endif
 	unregister_filesystem(&jffs2_fs_type);
 	jffs2_destroy_slab_caches();
 	jffs2_compressors_exit();
-- 
1.4.4.2

