summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaiGai Kohei <kaigai@ak.jp.nec.com>2006-05-13 15:09:47 +0900
committerKaiGai Kohei <kaigai@ak.jp.nec.com>2006-05-13 15:09:47 +0900
commitaa98d7cf59b5b0764d3502662053489585faf2fe (patch)
treee98e83f3e69ebe3a1112394a19d440419e899749
parent4992a9e88886b0c5ebc3d27eb74d0344c873eeea (diff)
[JFFS2][XATTR] XATTR support on JFFS2 (version. 5)
This attached patches provide xattr support including POSIX-ACL and SELinux support on JFFS2 (version.5). There are some significant differences from previous version posted at last December. The biggest change is addition of EBS(Erase Block Summary) support. Currently, both kernel and usermode utility (sumtool) can recognize xattr nodes which have JFFS2_NODETYPE_XATTR/_XREF nodetype. In addition, some bugs are fixed. - A potential race condition was fixed. - Unexpected fail when updating a xattr by same name/value pair was fixed. - A bug when removing xattr name/value pair was fixed. The fundamental structures (such as using two new nodetypes and exclusion mechanism by rwsem) are unchanged. But most of implementation were reviewed and updated if necessary. Espacially, we had to change several internal implementations related to load_xattr_datum() to avoid a potential race condition. [1/2] xattr_on_jffs2.kernel.version-5.patch [2/2] xattr_on_jffs2.utils.version-5.patch Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r--fs/Kconfig38
-rw-r--r--fs/jffs2/Makefile3
-rw-r--r--fs/jffs2/acl.c483
-rw-r--r--fs/jffs2/acl.h46
-rw-r--r--fs/jffs2/build.c2
-rw-r--r--fs/jffs2/debug.h6
-rw-r--r--fs/jffs2/dir.c62
-rw-r--r--fs/jffs2/file.c7
-rw-r--r--fs/jffs2/fs.c11
-rw-r--r--fs/jffs2/gc.c16
-rw-r--r--fs/jffs2/jffs2_fs_i.h5
-rw-r--r--fs/jffs2/jffs2_fs_sb.h10
-rw-r--r--fs/jffs2/malloc.c68
-rw-r--r--fs/jffs2/nodelist.c1
-rw-r--r--fs/jffs2/nodelist.h21
-rw-r--r--fs/jffs2/os-linux.h4
-rw-r--r--fs/jffs2/readinode.c1
-rw-r--r--fs/jffs2/scan.c168
-rw-r--r--fs/jffs2/security.c82
-rw-r--r--fs/jffs2/summary.c191
-rw-r--r--fs/jffs2/summary.h42
-rw-r--r--fs/jffs2/super.c6
-rw-r--r--fs/jffs2/symlink.c7
-rw-r--r--fs/jffs2/write.c2
-rw-r--r--fs/jffs2/xattr.c1271
-rw-r--r--fs/jffs2/xattr.h120
-rw-r--r--fs/jffs2/xattr_trusted.c51
-rw-r--r--fs/jffs2/xattr_user.c51
-rw-r--r--include/linux/jffs2.h40
29 files changed, 2800 insertions, 15 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index f9b5842c8d2d..2496ccbe2604 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1075,6 +1075,44 @@ config JFFS2_FS_DEBUG
If reporting bugs, please try to have available a full dump of the
messages at debug level 1 while the misbehaviour was occurring.
+config JFFS2_FS_XATTR
+ bool "JFFS2 XATTR support"
+ depends on JFFS2_FS
+ default n
+ help
+ Extended attributes are name:value pairs associated with inodes by
+ the kernel or by users (see the attr(5) manual page, or visit
+ <http://acl.bestbits.at/> for details).
+
+ If unsure, say N.
+
+config JFFS2_FS_POSIX_ACL
+ bool "JFFS2 POSIX Access Control Lists"
+ depends on JFFS2_FS_XATTR
+ default y
+ select FS_POSIX_ACL
+ help
+ Posix Access Control Lists (ACLs) support permissions for users and
+ groups beyond the owner/group/world scheme.
+
+ To learn more about Access Control Lists, visit the Posix ACLs for
+ Linux website <http://acl.bestbits.at/>.
+
+ If you don't know what Access Control Lists are, say N
+
+config JFFS2_FS_SECURITY
+ bool "JFFS2 Security Labels"
+ depends on JFFS2_FS_XATTR
+ default y
+ help
+ Security labels support alternative access control models
+ implemented by security modules like SELinux. This option
+ enables an extended attribute handler for file security
+ labels in the jffs2 filesystem.
+
+ If you are not using a security module that requires using
+ extended attributes for file security labels, say N.
+
config JFFS2_FS_WRITEBUFFER
bool "JFFS2 write-buffering support"
depends on JFFS2_FS
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
index 77dc5561a04e..7f28ee0bd132 100644
--- a/fs/jffs2/Makefile
+++ b/fs/jffs2/Makefile
@@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o debug.o
jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
+jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o xattr_trusted.o xattr_user.o
+jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o
+jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
new file mode 100644
index 000000000000..080bb51e4b65
--- /dev/null
+++ b/fs/jffs2/acl.c
@@ -0,0 +1,483 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/acl.c
+ * POSIX ACL support on JFFS2 FileSystem
+ *
+ * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * Copyright (C) 2006 NEC Corporation
+ *
+ * For licensing information, see the file 'LICENCE' in the jffs2 directory.
+ *-------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/crc32.h>
+#include <linux/jffs2.h>
+#include <linux/xattr.h>
+#include <linux/posix_acl_xattr.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+static size_t jffs2_acl_size(int count)
+{
+ if (count <= 4) {
+ return sizeof(jffs2_acl_header)
+ + count * sizeof(jffs2_acl_entry_short);
+ } else {
+ return sizeof(jffs2_acl_header)
+ + 4 * sizeof(jffs2_acl_entry_short)
+ + (count - 4) * sizeof(jffs2_acl_entry);
+ }
+}
+
+static int jffs2_acl_count(size_t size)
+{
+ size_t s;
+
+ size -= sizeof(jffs2_acl_header);
+ s = size - 4 * sizeof(jffs2_acl_entry_short);
+ if (s < 0) {
+ if (size % sizeof(jffs2_acl_entry_short))
+ return -1;
+ return size / sizeof(jffs2_acl_entry_short);
+ } else {
+ if (s % sizeof(jffs2_acl_entry))
+ return -1;
+ return s / sizeof(jffs2_acl_entry) + 4;
+ }
+}
+
+static struct posix_acl *jffs2_acl_from_medium(const void *value, size_t size)
+{
+ const char *end = (char *)value + size;
+ struct posix_acl *acl;
+ uint32_t ver;
+ int i, count;
+
+ if (!value)
+ return NULL;
+ if (size < sizeof(jffs2_acl_header))
+ return ERR_PTR(-EINVAL);
+ ver = je32_to_cpu(((jffs2_acl_header *)value)->a_version);
+ if (ver != JFFS2_ACL_VERSION) {
+ JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
+ return ERR_PTR(-EINVAL);
+ }
+
+ value = (char *)value + sizeof(jffs2_acl_header);
+ count = jffs2_acl_count(size);
+ if (count < 0)
+ return ERR_PTR(-EINVAL);
+ if (count == 0)
+ return NULL;
+
+ acl = posix_acl_alloc(count, GFP_KERNEL);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+
+ for (i=0; i < count; i++) {
+ jffs2_acl_entry *entry = (jffs2_acl_entry *)value;
+ if ((char *)value + sizeof(jffs2_acl_entry_short) > end)
+ goto fail;
+ acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
+ acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
+ switch (acl->a_entries[i].e_tag) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ value = (char *)value + sizeof(jffs2_acl_entry_short);
+ acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
+ break;
+
+ case ACL_USER:
+ case ACL_GROUP:
+ value = (char *)value + sizeof(jffs2_acl_entry);
+ if ((char *)value > end)
+ goto fail;
+ acl->a_entries[i].e_id = je32_to_cpu(entry->e_id);
+ break;
+
+ default:
+ goto fail;
+ }
+ }
+ if (value != end)
+ goto fail;
+ return acl;
+ fail:
+ posix_acl_release(acl);
+ return ERR_PTR(-EINVAL);
+}
+
+static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
+{
+ jffs2_acl_header *jffs2_acl;
+ char *e;
+ size_t i;
+
+ *size = jffs2_acl_size(acl->a_count);
+ jffs2_acl = (jffs2_acl_header *)kmalloc(sizeof(jffs2_acl_header)
+ + acl->a_count * sizeof(jffs2_acl_entry),
+ GFP_KERNEL);
+ if (!jffs2_acl)
+ return ERR_PTR(-ENOMEM);
+ jffs2_acl->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
+ e = (char *)jffs2_acl + sizeof(jffs2_acl_header);
+ for (i=0; i < acl->a_count; i++) {
+ jffs2_acl_entry *entry = (jffs2_acl_entry *)e;
+ entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag);
+ entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm);
+ switch(acl->a_entries[i].e_tag) {
+ case ACL_USER:
+ case ACL_GROUP:
+ entry->e_id = cpu_to_je32(acl->a_entries[i].e_id);
+ e += sizeof(jffs2_acl_entry);
+ break;
+
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ e += sizeof(jffs2_acl_entry_short);
+ break;
+
+ default:
+ goto fail;
+ }
+ }
+ return (char *)jffs2_acl;
+ fail:
+ kfree(jffs2_acl);
+ return ERR_PTR(-EINVAL);
+}
+
+static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl)
+{
+ struct posix_acl *acl = JFFS2_ACL_NOT_CACHED;
+
+ spin_lock(&inode->i_lock);
+ if (*i_acl != JFFS2_ACL_NOT_CACHED)
+ acl = posix_acl_dup(*i_acl);
+ spin_unlock(&inode->i_lock);
+ return acl;
+}
+
+static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl)
+{
+ spin_lock(&inode->i_lock);
+ if (*i_acl != JFFS2_ACL_NOT_CACHED)
+ posix_acl_release(*i_acl);
+ *i_acl = posix_acl_dup(acl);
+ spin_unlock(&inode->i_lock);
+}
+
+static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct posix_acl *acl;
+ char *value = NULL;
+ int rc, xprefix;
+
+ switch (type) {
+ case ACL_TYPE_ACCESS:
+ acl = jffs2_iget_acl(inode, &f->i_acl_access);
+ if (acl != JFFS2_ACL_NOT_CACHED)
+ return acl;
+ xprefix = JFFS2_XPREFIX_ACL_ACCESS;
+ break;
+ case ACL_TYPE_DEFAULT:
+ acl = jffs2_iget_acl(inode, &f->i_acl_default);
+ if (acl != JFFS2_ACL_NOT_CACHED)
+ return acl;
+ xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+ rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
+ if (rc > 0) {
+ value = kmalloc(rc, GFP_KERNEL);
+ if (!value)
+ return ERR_PTR(-ENOMEM);
+ rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
+ }
+ if (rc > 0) {
+ acl = jffs2_acl_from_medium(value, rc);
+ } else if (rc == -ENODATA || rc == -ENOSYS) {
+ acl = NULL;
+ } else {
+ acl = ERR_PTR(rc);
+ }
+ if (value)
+ kfree(value);
+ if (!IS_ERR(acl)) {
+ switch (type) {
+ case ACL_TYPE_ACCESS:
+ jffs2_iset_acl(inode, &f->i_acl_access, acl);
+ break;
+ case ACL_TYPE_DEFAULT:
+ jffs2_iset_acl(inode, &f->i_acl_default, acl);
+ break;
+ }
+ }
+ return acl;
+}
+
+static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ size_t size = 0;
+ char *value = NULL;
+ int rc, xprefix;
+
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case ACL_TYPE_ACCESS:
+ xprefix = JFFS2_XPREFIX_ACL_ACCESS;
+ if (acl) {
+ mode_t mode = inode->i_mode;
+ rc = posix_acl_equiv_mode(acl, &mode);
+ if (rc < 0)
+ return rc;
+ if (inode->i_mode != mode) {
+ inode->i_mode = mode;
+ jffs2_dirty_inode(inode);
+ }
+ if (rc == 0)
+ acl = NULL;
+ }
+ break;
+ case ACL_TYPE_DEFAULT:
+ xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
+ if (!S_ISDIR(inode->i_mode))
+ return acl ? -EACCES : 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (acl) {
+ value = jffs2_acl_to_medium(acl, &size);
+ if (IS_ERR(value))
+ return PTR_ERR(value);
+ }
+
+ rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
+ if (value)
+ kfree(value);
+ if (!rc) {
+ switch(type) {
+ case ACL_TYPE_ACCESS:
+ jffs2_iset_acl(inode, &f->i_acl_access, acl);
+ break;
+ case ACL_TYPE_DEFAULT:
+ jffs2_iset_acl(inode, &f->i_acl_default, acl);
+ break;
+ }
+ }
+ return rc;
+}
+
+static int jffs2_check_acl(struct inode *inode, int mask)
+{
+ struct posix_acl *acl;
+ int rc;
+
+ acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl) {
+ rc = posix_acl_permission(inode, acl, mask);
+ posix_acl_release(acl);
+ return rc;
+ }
+ return -EAGAIN;
+}
+
+int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+ return generic_permission(inode, mask, jffs2_check_acl);
+}
+
+int jffs2_init_acl(struct inode *inode, struct inode *dir)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct posix_acl *acl = NULL, *clone;
+ mode_t mode;
+ int rc = 0;
+
+ f->i_acl_access = JFFS2_ACL_NOT_CACHED;
+ f->i_acl_default = JFFS2_ACL_NOT_CACHED;
+ if (!S_ISLNK(inode->i_mode)) {
+ acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (!acl)
+ inode->i_mode &= ~current->fs->umask;
+ }
+ if (acl) {
+ if (S_ISDIR(inode->i_mode)) {
+ rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
+ if (rc)
+ goto cleanup;
+ }
+ clone = posix_acl_clone(acl, GFP_KERNEL);
+ rc = -ENOMEM;
+ if (!clone)
+ goto cleanup;
+ mode = inode->i_mode;
+ rc = posix_acl_create_masq(clone, &mode);
+ if (rc >= 0) {
+ inode->i_mode = mode;
+ if (rc > 0)
+ rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
+ }
+ posix_acl_release(clone);
+ }
+ cleanup:
+ posix_acl_release(acl);
+ return rc;
+}
+
+void jffs2_clear_acl(struct inode *inode)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+
+ if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) {
+ posix_acl_release(f->i_acl_access);
+ f->i_acl_access = JFFS2_ACL_NOT_CACHED;
+ }
+ if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) {
+ posix_acl_release(f->i_acl_default);
+ f->i_acl_default = JFFS2_ACL_NOT_CACHED;
+ }
+}
+
+int jffs2_acl_chmod(struct inode *inode)
+{
+ struct posix_acl *acl, *clone;
+ int rc;
+
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+ acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
+ if (IS_ERR(acl) || !acl)
+ return PTR_ERR(acl);
+ clone = posix_acl_clone(acl, GFP_KERNEL);
+ posix_acl_release(acl);
+ if (!clone)
+ return -ENOMEM;
+ rc = posix_acl_chmod_masq(clone, inode->i_mode);
+ if (!rc)
+ rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
+ posix_acl_release(clone);
+ return rc;
+}
+
+static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
+
+ if (list && retlen <= list_size)
+ strcpy(list, POSIX_ACL_XATTR_ACCESS);
+ return retlen;
+}
+
+static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
+
+ if (list && retlen <= list_size)
+ strcpy(list, POSIX_ACL_XATTR_DEFAULT);
+ return retlen;
+}
+
+static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
+{
+ struct posix_acl *acl;
+ int rc;
+
+ acl = jffs2_get_acl(inode, type);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (!acl)
+ return -ENODATA;
+ rc = posix_acl_to_xattr(acl, buffer, size);
+ posix_acl_release(acl);
+
+ return rc;
+}
+
+static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
+{
+ if (name[0] != '\0')
+ return -EINVAL;
+ return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
+}
+
+static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
+{
+ if (name[0] != '\0')
+ return -EINVAL;
+ return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
+}
+
+static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
+{
+ struct posix_acl *acl;
+ int rc;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+
+ if (value) {
+ acl = posix_acl_from_xattr(value, size);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl) {
+ rc = posix_acl_valid(acl);
+ if (rc)
+ goto out;
+ }
+ } else {
+ acl = NULL;
+ }
+ rc = jffs2_set_acl(inode, type, acl);
+ out:
+ posix_acl_release(acl);
+ return rc;
+}
+
+static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
+ const void *buffer, size_t size, int flags)
+{
+ if (name[0] != '\0')
+ return -EINVAL;
+ return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
+}
+
+static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
+ const void *buffer, size_t size, int flags)
+{
+ if (name[0] != '\0')
+ return -EINVAL;
+ return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
+}
+
+struct xattr_handler jffs2_acl_access_xattr_handler = {
+ .prefix = POSIX_ACL_XATTR_ACCESS,
+ .list = jffs2_acl_access_listxattr,
+ .get = jffs2_acl_access_getxattr,
+ .set = jffs2_acl_access_setxattr,
+};
+
+struct xattr_handler jffs2_acl_default_xattr_handler = {
+ .prefix = POSIX_ACL_XATTR_DEFAULT,
+ .list = jffs2_acl_default_listxattr,
+ .get = jffs2_acl_default_getxattr,
+ .set = jffs2_acl_default_setxattr,
+};
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h
new file mode 100644
index 000000000000..c98610b4e81c
--- /dev/null
+++ b/fs/jffs2/acl.h
@@ -0,0 +1,46 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/acl.h
+ * POSIX ACL support on JFFS2 FileSystem
+ *
+ * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * Copyright (C) 2006 NEC Corporation
+ *
+ * For licensing information, see the file 'LICENCE' in the jffs2 directory.
+ *-------------------------------------------------------------------------*/
+typedef struct {
+ jint16_t e_tag;
+ jint16_t e_perm;
+ jint32_t e_id;
+} jffs2_acl_entry;
+
+typedef struct {
+ jint16_t e_tag;
+ jint16_t e_perm;
+} jffs2_acl_entry_short;
+
+typedef struct {
+ jint32_t a_version;
+} jffs2_acl_header;
+
+#ifdef __KERNEL__
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+
+#define JFFS2_ACL_NOT_CACHED ((void *)-1)
+
+extern int jffs2_permission(struct inode *, int, struct nameidata *);
+extern int jffs2_acl_chmod(struct inode *);
+extern int jffs2_init_acl(struct inode *, struct inode *);
+extern void jffs2_clear_acl(struct inode *);
+
+extern struct xattr_handler jffs2_acl_access_xattr_handler;
+extern struct xattr_handler jffs2_acl_default_xattr_handler;
+
+#else
+
+#define jffs2_permission NULL
+#define jffs2_acl_chmod(inode) (0)
+#define jffs2_init_acl(inode,dir) (0)
+#define jffs2_clear_acl(inode)
+
+#endif /* CONFIG_JFFS2_FS_POSIX_ACL */
+#endif /* __KERNEL__ */
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
index 70f7a896c04a..02826967ab58 100644
--- a/fs/jffs2/build.c
+++ b/fs/jffs2/build.c
@@ -160,6 +160,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
ic->scan_dents = NULL;
cond_resched();
}
+ jffs2_build_xattr_subsystem(c);
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
dbg_fsbuild("FS build complete\n");
@@ -178,6 +179,7 @@ exit:
jffs2_free_full_dirent(fd);
}
}
+ jffs2_clear_xattr_subsystem(c);
}
return ret;
diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h
index 162af6dfe292..5fa494a792b2 100644
--- a/fs/jffs2/debug.h
+++ b/fs/jffs2/debug.h
@@ -171,6 +171,12 @@
#define dbg_memalloc(fmt, ...)
#endif
+/* Watch the XATTR subsystem */
+#ifdef JFFS2_DBG_XATTR_MESSAGES
+#define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
+#else
+#define dbg_xattr(fmt, ...)
+#endif
/* "Sanity" checks */
void
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 1c8e8c0f6cea..f1b18b99a3cd 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -57,7 +57,12 @@ struct inode_operations jffs2_dir_inode_operations =
.rmdir = jffs2_rmdir,
.mknod = jffs2_mknod,
.rename = jffs2_rename,
+ .permission = jffs2_permission,
.setattr = jffs2_setattr,
+ .setxattr = jffs2_setxattr,
+ .getxattr = jffs2_getxattr,
+ .listxattr = jffs2_listxattr,
+ .removexattr = jffs2_removexattr
};
/***********************************************************************/
@@ -209,12 +214,15 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
ret = jffs2_do_create(c, dir_f, f, ri,
dentry->d_name.name, dentry->d_name.len);
- if (ret) {
- make_bad_inode(inode);
- iput(inode);
- jffs2_free_raw_inode(ri);
- return ret;
- }
+ if (ret)
+ goto fail;
+
+ ret = jffs2_init_security(inode, dir_i);
+ if (ret)
+ goto fail;
+ ret = jffs2_init_acl(inode, dir_i);
+ if (ret)
+ goto fail;
dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
@@ -224,6 +232,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
return 0;
+
+ fail:
+ make_bad_inode(inode);
+ iput(inode);
+ jffs2_free_raw_inode(ri);
+ return ret;
}
/***********************************************************************/
@@ -374,6 +388,18 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
up(&f->sem);
jffs2_complete_reservation(c);
+
+ ret = jffs2_init_security(inode, dir_i);
+ if (ret) {
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+ ret = jffs2_init_acl(inode, dir_i);
+ if (ret) {
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
if (ret) {
@@ -504,6 +530,18 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
up(&f->sem);
jffs2_complete_reservation(c);
+
+ ret = jffs2_init_security(inode, dir_i);
+ if (ret) {
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+ ret = jffs2_init_acl(inode, dir_i);
+ if (ret) {
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
if (ret) {
@@ -660,6 +698,18 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
up(&f->sem);
jffs2_complete_reservation(c);
+
+ ret = jffs2_init_security(inode, dir_i);
+ if (ret) {
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+ ret = jffs2_init_acl(inode, dir_i);
+ if (ret) {
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
if (ret) {
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
index 9f4171213e58..e92187f34d5f 100644
--- a/fs/jffs2/file.c
+++ b/fs/jffs2/file.c
@@ -54,7 +54,12 @@ const struct file_operations jffs2_file_operations =
struct inode_operations jffs2_file_inode_operations =
{
- .setattr = jffs2_setattr
+ .permission = jffs2_permission,
+ .setattr = jffs2_setattr,
+ .setxattr = jffs2_setxattr,
+ .getxattr = jffs2_getxattr,
+ .listxattr = jffs2_listxattr,
+ .removexattr = jffs2_removexattr
};
struct address_space_operations jffs2_file_address_operations =
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index ea1f37d4fc58..4607cdc4c46d 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -185,7 +185,12 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
{
- return jffs2_do_setattr(dentry->d_inode, iattr);
+ int rc;
+
+ rc = jffs2_do_setattr(dentry->d_inode, iattr);
+ if (!rc && (iattr->ia_valid & ATTR_MODE))
+ rc = jffs2_acl_chmod(dentry->d_inode);
+ return rc;
}
int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
@@ -224,6 +229,7 @@ void jffs2_clear_inode (struct inode *inode)
D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+ jffs2_xattr_delete_inode(c, f->inocache);
jffs2_do_clear_inode(c, f);
}
@@ -497,6 +503,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
}
memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
+ jffs2_init_xattr_subsystem(c);
+
if ((ret = jffs2_do_mount_fs(c)))
goto out_inohash;
@@ -531,6 +539,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
else
kfree(c->blocks);
out_inohash:
+ jffs2_clear_xattr_subsystem(c);
kfree(c->inocache_list);
out_wbuf:
jffs2_flash_cleanup(c);
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 967fb2cf8e21..4ea1b7f0ae78 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -125,6 +125,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *raw;
int ret = 0, inum, nlink;
+ int xattr = 0;
if (down_interruptible(&c->alloc_sem))
return -EINTR;
@@ -138,7 +139,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
the node CRCs etc. Do it now. */
/* checked_ino is protected by the alloc_sem */
- if (c->checked_ino > c->highest_ino) {
+ if (c->checked_ino > c->highest_ino && xattr) {
printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
c->unchecked_size);
jffs2_dbg_dump_block_lists_nolock(c);
@@ -148,6 +149,9 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
spin_unlock(&c->erase_completion_lock);
+ if (!xattr)
+ xattr = jffs2_verify_xattr(c);
+
spin_lock(&c->inocache_lock);
ic = jffs2_get_ino_cache(c, c->checked_ino++);
@@ -262,6 +266,16 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
ic = jffs2_raw_ref_to_ic(raw);
+ /* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr.
+ We can decide whether this node is inode or xattr by ic->class.
+ ret = 0 : ic is xattr_datum/xattr_ref, and GC was SUCCESSED.
+ ret < 0 : ic is xattr_datum/xattr_ref, but GC was FAILED.
+ ret > 0 : ic is NOT xattr_datum/xattr_ref.
+ */
+ ret = jffs2_garbage_collect_xattr(c, ic);
+ if (ret <= 0)
+ goto release_sem;
+
/* We need to hold the inocache. Either the erase_completion_lock or
the inocache_lock are sufficient; we trade down since the inocache_lock
causes less contention. */
diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h
index ad565bf9dcc1..2e0cc8e00b85 100644
--- a/fs/jffs2/jffs2_fs_i.h
+++ b/fs/jffs2/jffs2_fs_i.h
@@ -5,6 +5,7 @@
#include <linux/version.h>
#include <linux/rbtree.h>
+#include <linux/posix_acl.h>
#include <asm/semaphore.h>
struct jffs2_inode_info {
@@ -45,6 +46,10 @@ struct jffs2_inode_info {
struct inode vfs_inode;
#endif
#endif
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+ struct posix_acl *i_acl_access;
+ struct posix_acl *i_acl_default;
+#endif
};
#endif /* _JFFS2_FS_I */
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index 4bcfb5570221..3b4e0edd6dbb 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -115,6 +115,16 @@ struct jffs2_sb_info {
struct jffs2_summary *summary; /* Summary information */
+#ifdef CONFIG_JFFS2_FS_XATTR
+#define XATTRINDEX_HASHSIZE (57)
+ uint32_t highest_xid;
+ struct list_head xattrindex[XATTRINDEX_HASHSIZE];
+ struct list_head xattr_temp;
+ struct list_head xattr_unchecked;