[PATCH] XATTR support on JFFS2 (version. 5)

KaiGai Kohei
2006-05-06T06:51:52Z

[-- Attachment #1: Type: text/plain, Size: 1367 bytes --]

Hello,

This attached patches provide xattr support including POSIX-ACL and
SELinux support on JFFS2 (version.5).
We can apply those patches to the latest git-tree.
- xattr_on_jffs2.kernel.version-5.patch can be applied to mtd-2.6.git.
- xattr_on_jffs2.utils.version-5.patch can be applied to mtd-utils.git.

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>

We can get those patches from http://www.kaigai.gr.jp/
Any comments please. Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>

[-- Attachment #2: xattr_on_jffs2.kernel.version-5.patch --]
[-- Type: text/plain, Size: 99399 bytes --]

--- mtd-2.6.git/fs/Kconfig 2006-05-05 03:14:40.000000000 +0900
+++ mtd-2.6.0506/fs/Kconfig 2006-05-06 11:28:19.000000000 +0900
@@ -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
--- mtd-2.6.git/fs/jffs2/Makefile 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/Makefile 2006-05-06 12:50:02.000000000 +0900
@@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o bac
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
--- mtd-2.6.git/fs/jffs2/acl.c 1970-01-01 09:00:00.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/acl.c 2006-05-06 11:28:19.000000000 +0900
@@ -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,
+};
--- mtd-2.6.git/fs/jffs2/acl.h 1970-01-01 09:00:00.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/acl.h 2006-05-06 11:28:19.000000000 +0900
@@ -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__ */
--- mtd-2.6.git/fs/jffs2/build.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/build.c 2006-05-06 11:28:19.000000000 +0900
@@ -160,6 +160,7 @@ static int jffs2_build_filesystem(struct
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;
--- mtd-2.6.git/fs/jffs2/debug.h 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/debug.h 2006-05-06 11:28:19.000000000 +0900
@@ -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
--- mtd-2.6.git/fs/jffs2/dir.c 2006-05-05 03:14:40.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/dir.c 2006-05-06 11:28:19.000000000 +0900
@@ -57,7 +57,12 @@ struct inode_operations jffs2_dir_inode_
.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 *di
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 *di
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 *
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 *di
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 *di
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) {
--- mtd-2.6.git/fs/jffs2/file.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/file.c 2006-05-06 11:28:19.000000000 +0900
@@ -54,7 +54,12 @@ const struct file_operations jffs2_file_

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 =
--- mtd-2.6.git/fs/jffs2/fs.c 2006-05-06 11:27:35.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/fs.c 2006-05-06 11:28:19.000000000 +0900
@@ -185,7 +185,12 @@ static int jffs2_do_setattr (struct inod

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 *in

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_blo
}
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_blo
else
kfree(c->blocks);
out_inohash:
+ jffs2_clear_xattr_subsystem(c);
kfree(c->inocache_list);
out_wbuf:
jffs2_flash_cleanup(c);
--- mtd-2.6.git/fs/jffs2/gc.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/gc.c 2006-05-06 11:28:19.000000000 +0900
@@ -125,6 +125,7 @@ int jffs2_garbage_collect_pass(struct jf
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 jf
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 jf

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 jf

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. */
--- mtd-2.6.git/fs/jffs2/jffs2_fs_i.h 2006-05-05 03:14:40.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/jffs2_fs_i.h 2006-05-06 11:28:19.000000000 +0900
@@ -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 */
--- mtd-2.6.git/fs/jffs2/jffs2_fs_sb.h 2006-05-05 03:14:40.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/jffs2_fs_sb.h 2006-05-06 11:28:19.000000000 +0900
@@ -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;
+ struct rw_semaphore xattr_sem;
+ uint32_t xdatum_mem_usage;
+ uint32_t xdatum_mem_threshold;
+#endif
/* OS-private pointer for getting back to master superblock info */
void *os_priv;
};
--- mtd-2.6.git/fs/jffs2/malloc.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/malloc.c 2006-05-06 11:28:19.000000000 +0900
@@ -26,6 +26,10 @@ static kmem_cache_t *tmp_dnode_info_slab
static kmem_cache_t *raw_node_ref_slab;
static kmem_cache_t *node_frag_slab;
static kmem_cache_t *inode_cache_slab;
+#ifdef CONFIG_JFFS2_FS_XATTR
+static kmem_cache_t *xattr_datum_cache;
+static kmem_cache_t *xattr_ref_cache;
+#endif

int __init jffs2_create_slab_caches(void)
{
@@ -68,8 +72,24 @@ int __init jffs2_create_slab_caches(void
inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
sizeof(struct jffs2_inode_cache),
0, 0, NULL, NULL);
- if (inode_cache_slab)
- return 0;
+ if (!inode_cache_slab)
+ goto err;
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+ xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum",
+ sizeof(struct jffs2_xattr_datum),
+ 0, 0, NULL, NULL);
+ if (!xattr_datum_cache)
+ goto err;
+
+ xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref",
+ sizeof(struct jffs2_xattr_ref),
+ 0, 0, NULL, NULL);
+ if (!xattr_ref_cache)
+ goto err;
+#endif
+
+ return 0;
err:
jffs2_destroy_slab_caches();
return -ENOMEM;
@@ -91,6 +111,12 @@ void jffs2_destroy_slab_caches(void)
kmem_cache_destroy(node_frag_slab);
if(inode_cache_slab)
kmem_cache_destroy(inode_cache_slab);
+#ifdef CONFIG_JFFS2_FS_XATTR
+ if (xattr_datum_cache)
+ kmem_cache_destroy(xattr_datum_cache);
+ if (xattr_ref_cache)
+ kmem_cache_destroy(xattr_ref_cache);
+#endif
}

struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
@@ -205,3 +231,41 @@ void jffs2_free_inode_cache(struct jffs2
dbg_memalloc("%p\n", x);
kmem_cache_free(inode_cache_slab, x);
}
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
+{
+ struct jffs2_xattr_datum *xd;
+ xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL);
+ dbg_memalloc("%p\n", xd);
+
+ memset(xd, 0, sizeof(struct jffs2_xattr_datum));
+ xd->class = RAWNODE_CLASS_XATTR_DATUM;
+ INIT_LIST_HEAD(&xd->xindex);
+ return xd;
+}
+
+void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
+{
+ dbg_memalloc("%p\n", xd);
+ kmem_cache_free(xattr_datum_cache, xd);
+}
+
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
+{
+ struct jffs2_xattr_ref *ref;
+ ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL);
+ dbg_memalloc("%p\n", ref);
+
+ memset(ref, 0, sizeof(struct jffs2_xattr_ref));
+ ref->class = RAWNODE_CLASS_XATTR_REF;
+ INIT_LIST_HEAD(&ref->ilist);
+ return ref;
+}
+
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref)
+{
+ dbg_memalloc("%p\n", ref);
+ kmem_cache_free(xattr_ref_cache, ref);
+}
+#endif
--- mtd-2.6.git/fs/jffs2/nodelist.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/nodelist.c 2006-05-06 11:28:19.000000000 +0900
@@ -938,6 +938,7 @@ void jffs2_free_ino_caches(struct jffs2_
this = c->inocache_list[i];
while (this) {
next = this->next;
+ jffs2_xattr_free_inode(c, this);
jffs2_free_inode_cache(this);
this = next;
}
--- mtd-2.6.git/fs/jffs2/nodelist.h 2006-05-05 03:14:40.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/nodelist.h 2006-05-06 11:28:19.000000000 +0900
@@ -20,6 +20,8 @@
#include <linux/jffs2.h>
#include "jffs2_fs_sb.h"
#include "jffs2_fs_i.h"
+#include "xattr.h"
+#include "acl.h"
#include "summary.h"

#ifdef __ECOS
@@ -107,11 +109,16 @@ struct jffs2_inode_cache {
temporary lists of dirents, and later must be set to
NULL to mark the end of the raw_node_ref->next_in_ino
chain. */
+ u8 class; /* It's used for identification */
+ u8 flags;
+ uint16_t state;
struct jffs2_inode_cache *next;
struct jffs2_raw_node_ref *nodes;
uint32_t ino;
int nlink;
- int state;
+#ifdef CONFIG_JFFS2_FS_XATTR
+ struct list_head ilist;
+#endif
};

/* Inode states for 'state' above. We need the 'GC' state to prevent
@@ -125,6 +132,12 @@ struct jffs2_inode_cache {
#define INO_STATE_READING 5 /* In read_inode() */
#define INO_STATE_CLEARING 6 /* In clear_inode() */

+#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */
+
+#define RAWNODE_CLASS_INODE_CACHE 0
+#define RAWNODE_CLASS_XATTR_DATUM 1
+#define RAWNODE_CLASS_XATTR_REF 2
+
#define INOCACHE_HASHSIZE 128

/*
@@ -374,6 +387,12 @@ struct jffs2_node_frag *jffs2_alloc_node
void jffs2_free_node_frag(struct jffs2_node_frag *);
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
void jffs2_free_inode_cache(struct jffs2_inode_cache *);
+#ifdef CONFIG_JFFS2_FS_XATTR
+struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void);
+void jffs2_free_xattr_datum(struct jffs2_xattr_datum *);
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void);
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *);
+#endif

/* gc.c */
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
--- mtd-2.6.git/fs/jffs2/os-linux.h 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/os-linux.h 2006-05-06 11:28:19.000000000 +0900
@@ -60,6 +60,10 @@ static inline void jffs2_init_inode_info
f->target = NULL;
f->flags = 0;
f->usercompr = 0;
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+ f->i_acl_access = JFFS2_ACL_NOT_CACHED;
+ f->i_acl_default = JFFS2_ACL_NOT_CACHED;
+#endif
}

--- mtd-2.6.git/fs/jffs2/readinode.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/readinode.c 2006-05-06 11:28:19.000000000 +0900
@@ -902,6 +902,7 @@ int jffs2_do_read_inode(struct jffs2_sb_
f->inocache->ino = f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_READING;
+ init_xattr_inode_cache(f->inocache);
jffs2_add_ino_cache(c, f->inocache);
}
if (!f->inocache) {
--- mtd-2.6.git/fs/jffs2/scan.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/scan.c 2006-05-06 11:28:19.000000000 +0900
@@ -306,6 +306,136 @@ int jffs2_scan_classify_jeb(struct jffs2
return BLK_STATE_ALLDIRTY;
}

+#ifdef CONFIG_JFFS2_FS_XATTR
+static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_xattr *rx, uint32_t ofs,
+ struct jffs2_summary *s)
+{
+ struct jffs2_xattr_datum *xd;
+ struct jffs2_raw_node_ref *raw;
+ uint32_t totlen, crc;
+
+ crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4);
+ if (crc != je32_to_cpu(rx->node_crc)) {
+ if (je32_to_cpu(rx->node_crc) != 0xffffffff)
+ JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ofs, je32_to_cpu(rx->node_crc), crc);
+ DIRTY_SPACE(je32_to_cpu(rx->totlen));
+ return 0;
+ }
+
+ totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len));
+ if (totlen != je32_to_cpu(rx->totlen)) {
+ JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
+ ofs, je32_to_cpu(rx->totlen), totlen);
+ DIRTY_SPACE(je32_to_cpu(rx->totlen));
+ return 0;
+ }
+
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+ return -ENOMEM;
+
+ xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version));
+ if (IS_ERR(xd)) {
+ jffs2_free_raw_node_ref(raw);
+ if (PTR_ERR(xd) == -EEXIST) {
+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen)));
+ return 0;
+ }
+ return PTR_ERR(xd);
+ }
+ xd->xprefix = rx->xprefix;
+ xd->name_len = rx->name_len;
+ xd->value_len = je16_to_cpu(rx->value_len);
+ xd->data_crc = je32_to_cpu(rx->data_crc);
+ xd->node = raw;
+
+ raw->__totlen = totlen;
+ raw->flash_offset = ofs | REF_PRISTINE;
+ raw->next_phys = NULL;
+ raw->next_in_ino = (void *)xd;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ USED_SPACE(PAD(je32_to_cpu(rx->totlen)));
+ if (jffs2_sum_active())
+ jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
+ dbg_xattr("scaning xdatum at %#08x (xid=%u, version=%u)\n",
+ ofs, xd->xid, xd->version);
+ return 0;
+}
+
+static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_xref *rr, uint32_t ofs,
+ struct jffs2_summary *s)
+{
+ struct jffs2_xattr_ref *ref;
+ struct jffs2_raw_node_ref *raw;
+ uint32_t crc;
+
+ crc = crc32(0, rr, sizeof(*rr) - 4);
+ if (crc != je32_to_cpu(rr->node_crc)) {
+ if (je32_to_cpu(rr->node_crc) != 0xffffffff)
+ JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ofs, je32_to_cpu(rr->node_crc), crc);
+ DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen)));
+ return 0;
+ }
+
+ if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) {
+ JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
+ ofs, je32_to_cpu(rr->totlen),
+ PAD(sizeof(struct jffs2_raw_xref)));
+ DIRTY_SPACE(je32_to_cpu(rr->totlen));
+ return 0;
+ }
+
+ ref = jffs2_alloc_xattr_ref();
+ if (!ref)
+ return -ENOMEM;
+
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ jffs2_free_xattr_ref(ref);
+ return -ENOMEM;
+ }
+
+ /* BEFORE jffs2_build_xattr_subsystem() called,
+ * ref->xid is used to store 32bit xid, xd is not used
+ * ref->ino is used to store 32bit inode-number, ic is not used
+ * Thoes variables are declared as union, thus using those
+ * are exclusive. In a similar way, ref->ilist is temporarily
+ * used to chain all xattr_ref object. It's re-chained to
+ * jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly.
+ */
+ ref->node = raw;
+ ref->ino = je32_to_cpu(rr->ino);
+ ref->xid = je32_to_cpu(rr->xid);
+ list_add_tail(&ref->ilist, &c->xattr_temp);
+
+ raw->__totlen = PAD(je32_to_cpu(rr->totlen));
+ raw->flash_offset = ofs | REF_PRISTINE;
+ raw->next_phys = NULL;
+ raw->next_in_ino = (void *)ref;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ USED_SPACE(PAD(je32_to_cpu(rr->totlen)));
+ if (jffs2_sum_active())
+ jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset);
+ dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n",
+ ofs, ref->xid, ref->ino);
+ return 0;
+}
+#endif
+
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
struct jffs2_unknown_node *node;
@@ -614,6 +744,43 @@ scan_more:
ofs += PAD(je32_to_cpu(node->totlen));
break;

+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR:
+ if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+ D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)"
+ " left to end of buf. Reading 0x%x at 0x%08x\n",
+ je32_to_cpu(node->totlen), buf_len, ofs));
+ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+ if (err)
+ return err;
+ buf_ofs = ofs;
+ node = (void *)buf;
+ }
+ err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s);
+ if (err)
+ return err;
+ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+ case JFFS2_NODETYPE_XREF:
+ if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+ D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)"
+ " left to end of buf. Reading 0x%x at 0x%08x\n",
+ je32_to_cpu(node->totlen), buf_len, ofs));
+ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+ if (err)
+ return err;
+ buf_ofs = ofs;
+ node = (void *)buf;
+ }
+ err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s);
+ if (err)
+ return err;
+ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+#endif /* CONFIG_JFFS2_FS_XATTR */
+
case JFFS2_NODETYPE_CLEANMARKER:
D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
@@ -721,6 +888,7 @@ struct jffs2_inode_cache *jffs2_scan_mak

ic->ino = ino;
ic->nodes = (void *)ic;
+ init_xattr_inode_cache(ic);
jffs2_add_ino_cache(c, ic);
if (ino == 1)
ic->nlink = 1;
--- mtd-2.6.git/fs/jffs2/security.c 1970-01-01 09:00:00.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/security.c 2006-05-06 11:28:19.000000000 +0900
@@ -0,0 +1,82 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/security.c
+ * Security Labels 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/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/crc32.h>
+#include <linux/jffs2.h>
+#include <linux/xattr.h>
+#include <linux/mtd/mtd.h>
+#include <linux/security.h>
+#include "nodelist.h"
+
+/* ---- Initial Security Label Attachment -------------- */
+int jffs2_init_security(struct inode *inode, struct inode *dir)
+{
+ int rc;
+ size_t len;
+ void *value;
+ char *name;
+
+ rc = security_inode_init_security(inode, dir, &name, &value, &len);
+ if (rc) {
+ if (rc == -EOPNOTSUPP)
+ return 0;
+ return rc;
+ }
+ rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
+
+ kfree(name);
+ kfree(value);
+ return rc;
+}
+
+/* ---- XATTR Handler for "security.*" ----------------- */
+static int jffs2_security_getxattr(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (!strcmp(name, ""))
+ return -EINVAL;
+
+ return do_jffs2_getxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size);
+}
+
+static int jffs2_security_setxattr(struct inode *inode, const char *name, const void *buffer,
+ size_t size, int flags)
+{
+ if (!strcmp(name, ""))
+ return -EINVAL;
+
+ return do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size, flags);
+}
+
+static size_t jffs2_security_listxattr(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1;
+
+ if (list && retlen <= list_size) {
+ strcpy(list, XATTR_SECURITY_PREFIX);
+ strcpy(list + XATTR_SECURITY_PREFIX_LEN, name);
+ }
+
+ return retlen;
+}
+
+struct xattr_handler jffs2_security_xattr_handler = {
+ .prefix = XATTR_SECURITY_PREFIX,
+ .list = jffs2_security_listxattr,
+ .set = jffs2_security_setxattr,
+ .get = jffs2_security_getxattr
+};
--- mtd-2.6.git/fs/jffs2/summary.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/summary.c 2006-05-06 11:28:19.000000000 +0900
@@ -5,6 +5,7 @@
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
+ * 2005 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@@ -81,6 +82,19 @@ static int jffs2_sum_add_mem(struct jffs
dbg_summary("dirent (%u) added to summary\n",
je32_to_cpu(item->d.ino));
break;
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR:
+ s->sum_size += JFFS2_SUMMARY_XATTR_SIZE;
+ s->sum_num++;
+ dbg_summary("xattr (xid=%u, version=%u) added to summary\n",
+ je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version));
+ break;
+ case JFFS2_NODETYPE_XREF:
+ s->sum_size += JFFS2_SUMMARY_XREF_SIZE;
+ s->sum_num++;
+ dbg_summary("xref added to summary\n");
+ break;
+#endif
default:
JFFS2_WARNING("UNKNOWN node type %u\n",
je16_to_cpu(item->u.nodetype));
@@ -141,6 +155,40 @@ int jffs2_sum_add_dirent_mem(struct jffs
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
}

+#ifdef CONFIG_JFFS2_FS_XATTR
+int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs)
+{
+ struct jffs2_sum_xattr_mem *temp;
+
+ temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ temp->nodetype = rx->nodetype;
+ temp->xid = rx->xid;
+ temp->version = rx->version;
+ temp->offset = cpu_to_je32(ofs);
+ temp->totlen = rx->totlen;
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+
+int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs)
+{
+ struct jffs2_sum_xref_mem *temp;
+
+ temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ temp->nodetype = rr->nodetype;
+ temp->offset = cpu_to_je32(ofs);
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
+}
+#endif
/* Cleanup every collected summary information */

static void jffs2_sum_clean_collected(struct jffs2_summary *s)
@@ -259,7 +307,40 @@ int jffs2_sum_add_kvec(struct jffs2_sb_i

return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
}
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR: {
+ struct jffs2_sum_xattr_mem *temp;
+ if (je32_to_cpu(node->x.version) == 0xffffffff)
+ return 0;
+ temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
+ if (!temp)
+ goto no_mem;

+ temp->nodetype = node->x.nodetype;
+ temp->xid = node->x.xid;
+ temp->version = node->x.version;
+ temp->totlen = node->x.totlen;
+ temp->offset = cpu_to_je32(ofs);
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+ }
+ case JFFS2_NODETYPE_XREF: {
+ struct jffs2_sum_xref_mem *temp;
+
+ if (je32_to_cpu(node->r.ino) == 0xffffffff
+ && je32_to_cpu(node->r.xid) == 0xffffffff)
+ return 0;
+ temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
+ if (!temp)
+ goto no_mem;
+ temp->nodetype = node->r.nodetype;
+ temp->offset = cpu_to_je32(ofs);
+ temp->next = NULL;
+
+ return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
+ }
+#endif
case JFFS2_NODETYPE_PADDING:
dbg_summary("node PADDING\n");
c->summary->sum_padded += je32_to_cpu(node->u.totlen);
@@ -408,8 +489,94 @@ static int jffs2_sum_process_sum_data(st

break;
}
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR: {
+ struct jffs2_xattr_datum *xd;
+ struct jffs2_sum_xattr_flash *spx;
+ uint32_t ofs;
+
+ spx = (struct jffs2_sum_xattr_flash *)sp;
+ ofs = jeb->offset + je32_to_cpu(spx->offset);
+ dbg_summary("xattr at %#08x (xid=%u, version=%u)\n", ofs,
+ je32_to_cpu(spx->xid), je32_to_cpu(spx->version));
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ JFFS2_NOTICE("allocation of node reference failed\n");
+ kfree(summary);
+ return -ENOMEM;
+ }
+ xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
+ je32_to_cpu(spx->version));
+ if (IS_ERR(xd)) {
+ JFFS2_NOTICE("allocation of xattr_datum failed\n");
+ jffs2_free_raw_node_ref(raw);
+ kfree(summary);
+ return PTR_ERR(xd);
+ }
+ xd->node = raw;
+
+ raw->flash_offset = ofs | REF_UNCHECKED;
+ raw->__totlen = PAD(je32_to_cpu(spx->totlen));
+ raw->next_phys = NULL;
+ raw->next_in_ino = (void *)xd;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ *pseudo_random += je32_to_cpu(spx->xid);
+ UNCHECKED_SPACE(je32_to_cpu(spx->totlen));
+ sp += JFFS2_SUMMARY_XATTR_SIZE;
+
+ break;
+ }
+ case JFFS2_NODETYPE_XREF: {
+ struct jffs2_xattr_ref *ref;
+ struct jffs2_sum_xref_flash *spr;
+ uint32_t ofs;
+
+ spr = (struct jffs2_sum_xref_flash *)sp;
+ ofs = jeb->offset + je32_to_cpu(spr->offset);
+ dbg_summary("xref at %#08x (xid=%u, ino=%u)\n", ofs,
+ je32_to_cpu(spr->xid), je32_to_cpu(spr->ino));
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ JFFS2_NOTICE("allocation of node reference failed\n");
+ kfree(summary);
+ return -ENOMEM;
+ }
+ ref = jffs2_alloc_xattr_ref();
+ if (!ref) {
+ JFFS2_NOTICE("allocation of xattr_datum failed\n");
+ jffs2_free_raw_node_ref(raw);
+ kfree(summary);
+ return -ENOMEM;
+ }
+ ref->ino = 0xfffffffe;
+ ref->xid = 0xfffffffd;
+ ref->node = raw;
+ list_add_tail(&ref->ilist, &c->xattr_temp);

+ raw->__totlen = PAD(sizeof(struct jffs2_raw_xref));
+ raw->flash_offset = ofs | REF_UNCHECKED;
+ raw->next_phys = NULL;
+ raw->next_in_ino = (void *)ref;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ UNCHECKED_SPACE(PAD(sizeof(struct jffs2_raw_xref)));
+ *pseudo_random += ofs;
+ sp += JFFS2_SUMMARY_XREF_SIZE;
+
+ break;
+ }
+#endif
default : {
+printk("nodetype = %#04x\n",je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype));
JFFS2_WARNING("Unsupported node type found in summary! Exiting...");
kfree(summary);
return -EIO;
@@ -616,7 +783,31 @@ static int jffs2_sum_write_data(struct j

break;
}
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case JFFS2_NODETYPE_XATTR: {
+ struct jffs2_sum_xattr_flash *sxattr_ptr = wpage;
+
+ temp = c->summary->sum_list_head;
+ sxattr_ptr->nodetype = temp->x.nodetype;
+ sxattr_ptr->xid = temp->x.xid;
+ sxattr_ptr->version = temp->x.version;
+ sxattr_ptr->offset = temp->x.offset;
+ sxattr_ptr->totlen = temp->x.totlen;
+
+ wpage += JFFS2_SUMMARY_XATTR_SIZE;
+ break;
+ }
+ case JFFS2_NODETYPE_XREF: {
+ struct jffs2_sum_xref_flash *sxref_ptr = wpage;
+
+ temp = c->summary->sum_list_head;
+ sxref_ptr->nodetype = temp->r.nodetype;
+ sxref_ptr->offset = temp->r.offset;

+ wpage += JFFS2_SUMMARY_XREF_SIZE;
+ break;
+ }
+#endif
default : {
BUG(); /* unknown node in summary information */
}
--- mtd-2.6.git/fs/jffs2/summary.h 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/summary.h 2006-05-06 11:28:19.000000000 +0900
@@ -45,6 +45,8 @@
#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
+#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))

/* Summary structures used on flash */

@@ -75,11 +77,28 @@ struct jffs2_sum_dirent_flash
uint8_t name[0]; /* dirent name */
} __attribute__((packed));

+struct jffs2_sum_xattr_flash
+{
+ jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */
+ jint32_t xid; /* xattr identifier */
+ jint32_t version; /* version number */
+ jint32_t offset; /* offset on jeb */
+ jint32_t totlen; /* node length */
+} __attribute__((packed));
+
+struct jffs2_sum_xref_flash
+{
+ jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */
+ jint32_t offset; /* offset on jeb */
+} __attribute__((packed));
+
union jffs2_sum_flash
{
struct jffs2_sum_unknown_flash u;
struct jffs2_sum_inode_flash i;
struct jffs2_sum_dirent_flash d;
+ struct jffs2_sum_xattr_flash x;
+ struct jffs2_sum_xref_flash r;
};

/* Summary structures used in the memory */
@@ -114,11 +133,30 @@ struct jffs2_sum_dirent_mem
uint8_t name[0]; /* dirent name */
} __attribute__((packed));

+struct jffs2_sum_xattr_mem
+{
+ union jffs2_sum_mem *next;
+ jint16_t nodetype;
+ jint32_t xid;
+ jint32_t version;
+ jint32_t offset;
+ jint32_t totlen;
+} __attribute__((packed));
+
+struct jffs2_sum_xref_mem
+{
+ union jffs2_sum_mem *next;
+ jint16_t nodetype;
+ jint32_t offset;
+} __attribute__((packed));
+
union jffs2_sum_mem
{
struct jffs2_sum_unknown_mem u;
struct jffs2_sum_inode_mem i;
struct jffs2_sum_dirent_mem d;
+ struct jffs2_sum_xattr_mem x;
+ struct jffs2_sum_xref_mem r;
};

/* Summary related information stored in superblock */
@@ -159,6 +197,8 @@ int jffs2_sum_write_sumnode(struct jffs2
int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
+int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs);
+int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs);
int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
uint32_t ofs, uint32_t *pseudo_random);

@@ -176,6 +216,8 @@ int jffs2_sum_scan_sumnode(struct jffs2_
#define jffs2_sum_add_padding_mem(a,b)
#define jffs2_sum_add_inode_mem(a,b,c)
#define jffs2_sum_add_dirent_mem(a,b,c)
+#define jffs2_sum_add_xattr_mem(a,b,c)
+#define jffs2_sum_add_xref_mem(a,b,c)
#define jffs2_sum_scan_sumnode(a,b,c,d) (0)

#endif /* CONFIG_JFFS2_SUMMARY */
--- mtd-2.6.git/fs/jffs2/super.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/super.c 2006-05-06 11:28:19.000000000 +0900
@@ -151,7 +151,10 @@ static struct super_block *jffs2_get_sb_

sb->s_op = &jffs2_super_operations;
sb->s_flags = flags | MS_NOATIME;
-
+ sb->s_xattr = jffs2_xattr_handlers;
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+ sb->s_flags |= MS_POSIXACL;
+#endif
ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);

if (ret) {
@@ -293,6 +296,7 @@ static void jffs2_put_super (struct supe
kfree(c->blocks);
jffs2_flash_cleanup(c);
kfree(c->inocache_list);
+ jffs2_clear_xattr_subsystem(c);
if (c->mtd->sync)
c->mtd->sync(c->mtd);

--- mtd-2.6.git/fs/jffs2/symlink.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/symlink.c 2006-05-06 11:28:19.000000000 +0900
@@ -24,7 +24,12 @@ struct inode_operations jffs2_symlink_in
{
.readlink = generic_readlink,
.follow_link = jffs2_follow_link,
- .setattr = jffs2_setattr
+ .permission = jffs2_permission,
+ .setattr = jffs2_setattr,
+ .setxattr = jffs2_setxattr,
+ .getxattr = jffs2_getxattr,
+ .listxattr = jffs2_listxattr,
+ .removexattr = jffs2_removexattr
};

static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
--- mtd-2.6.git/fs/jffs2/write.c 2006-04-30 18:06:51.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/write.c 2006-05-06 11:28:19.000000000 +0900
@@ -36,7 +36,7 @@ int jffs2_do_new_inode(struct jffs2_sb_i
f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_PRESENT;
-
+ init_xattr_inode_cache(f->inocache);

jffs2_add_ino_cache(c, f->inocache);
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
--- mtd-2.6.git/fs/jffs2/xattr.c 1970-01-01 09:00:00.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/xattr.c 2006-05-06 11:28:19.000000000 +0900
@@ -0,0 +1,1271 @@
+/* -------------------------------------------------------------------------
+ * File: fs/jffs2/xattr.c
+ * XATTR 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/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/crc32.h>
+#include <linux/jffs2.h>
+#include <linux/xattr.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+/* -------- xdatum related functions ----------------
+ * xattr_datum_hashkey(xprefix, xname, xvalue, xsize)
+ * is used to calcurate xdatum hashkey. The reminder of hashkey into XATTRINDEX_HASHSIZE is
+ * the index of the xattr name/value pair cache (c->xattrindex).
+ * unload_xattr_datum(c, xd)
+ * is used to release xattr name/value pair and detach from c->xattrindex.
+ * reclaim_xattr_datum(c)
+ * is used to reclaim xattr name/value pairs on the xattr name/value pair cache when
+ * memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold
+ * is hard coded as 32KiB.
+ * delete_xattr_datum_node(c, xd)
+ * is used to delete a jffs2 node is dominated by xdatum. When EBS(Erase Block Summary) is
+ * enabled, it overwrites the obsolete node by myself.
+ * delete_xattr_datum(c, xd)
+ * is used to delete jffs2_xattr_datum object. It must be called with 0-value of reference
+ * counter. (It means how many jffs2_xattr_ref object refers this xdatum.)
+ * do_verify_xattr_datum(c, xd)
+ * is used to load the xdatum informations without name/value pair from the medium.
+ * It's necessary once, because those informations are not collected during mounting
+ * process when EBS is enabled.
+ * 0 will be returned, if success. An negative return value means recoverable error, and
+ * positive return value means unrecoverable error. Thus, caller must remove this xdatum
+ * and xref when it returned positive value.
+ * do_load_xattr_datum(c, xd)
+ * is used to load name/value pair from the medium.
+ * The meanings of return value is same as do_verify_xattr_datum().
+ * load_xattr_datum(c, xd)
+ * is used to be as a wrapper of do_verify_xattr_datum() and do_load_xattr_datum().
+ * If xd need to call do_verify_xattr_datum() at first, it's called before calling
+ * do_load_xattr_datum(). The meanings of return value is same as do_verify_xattr_datum().
+ * save_xattr_datum(c, xd, phys_ofs)
+ * is used to write xdatum to medium. xd->version will be incremented.
+ * create_xattr_datum(c, xprefix, xname, xvalue, xsize, phys_ofs)
+ * is used to create new xdatum and write to medium.
+ * -------------------------------------------------- */
+
+static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize)
+{
+ int name_len = strlen(xname);
+
+ return crc32(xprefix, xname, name_len) ^ crc32(xprefix, xvalue, xsize);
+}
+
+static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem) */
+ D1(dbg_xattr("%s: xid=%u, version=%u\n", __FUNCTION__, xd->xid, xd->version));
+ if (xd->xname) {
+ c->xdatum_mem_usage -= (xd->name_len + 1 + xd->value_len);
+ kfree(xd->xname);
+ }
+
+ list_del_init(&xd->xindex);
+ xd->hashkey = 0;
+ xd->xname = NULL;
+ xd->xvalue = NULL;
+}
+
+static void reclaim_xattr_datum(struct jffs2_sb_info *c)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_xattr_datum *xd, *_xd;
+ uint32_t target, before;
+ static int index = 0;
+ int count;
+
+ if (c->xdatum_mem_threshold > c->xdatum_mem_usage)
+ return;
+
+ before = c->xdatum_mem_usage;
+ target = c->xdatum_mem_usage * 4 / 5; /* 20% reduction */
+ for (count = 0; count < XATTRINDEX_HASHSIZE; count++) {
+ list_for_each_entry_safe(xd, _xd, &c->xattrindex[index], xindex) {
+ if (xd->flags & JFFS2_XFLAGS_HOT) {
+ xd->flags &= ~JFFS2_XFLAGS_HOT;
+ } else if (!(xd->flags & JFFS2_XFLAGS_BIND)) {
+ unload_xattr_datum(c, xd);
+ }
+ if (c->xdatum_mem_usage <= target)
+ goto out;
+ }
+ index = (index+1) % XATTRINDEX_HASHSIZE;
+ }
+ out:
+ JFFS2_NOTICE("xdatum_mem_usage from %u byte to %u byte (%u byte reclaimed)\n",
+ before, c->xdatum_mem_usage, before - c->xdatum_mem_usage);
+}
+
+static void delete_xattr_datum_node(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_raw_xattr rx;
+ uint32_t length;
+ int rc;
+
+ if (!xd->node) {
+ JFFS2_WARNING("xdatum (xid=%u) is removed twice.\n", xd->xid);
+ return;
+ }
+ if (jffs2_sum_active()) {
+ memset(&rx, 0xff, sizeof(struct jffs2_raw_xattr));
+ rc = jffs2_flash_read(c, ref_offset(xd->node),
+ sizeof(struct jffs2_unknown_node),
+ &length, (char *)&rx);
+ if (rc || length != sizeof(struct jffs2_unknown_node)) {
+ JFFS2_ERROR("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n",
+ rc, sizeof(struct jffs2_unknown_node),
+ length, ref_offset(xd->node));
+ }
+ rc = jffs2_flash_write(c, ref_offset(xd->node), sizeof(rx),
+ &length, (char *)&rx);
+ if (rc || length != sizeof(struct jffs2_raw_xattr)) {
+ JFFS2_ERROR("jffs2_flash_write()=%d, req=%u, wrote=%u ar %#08x\n",
+ rc, sizeof(rx), length, ref_offset(xd->node));
+ }
+ }
+ spin_lock(&c->erase_completion_lock);
+ xd->node->next_in_ino = NULL;
+ spin_unlock(&c->erase_completion_lock);
+ jffs2_mark_node_obsolete(c, xd->node);
+ xd->node = NULL;
+}
+
+static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem) */
+ BUG_ON(xd->refcnt);
+
+ unload_xattr_datum(c, xd);
+ if (xd->node) {
+ delete_xattr_datum_node(c, xd);
+ xd->node = NULL;
+ }
+ jffs2_free_xattr_datum(xd);
+}
+
+static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_xattr rx;
+ size_t readlen;
+ uint32_t crc, totlen;
+ int rc;
+
+ BUG_ON(!xd->node);
+ BUG_ON(ref_flags(xd->node) != REF_UNCHECKED);
+
+ rc = jffs2_flash_read(c, ref_offset(xd->node), sizeof(rx), &readlen, (char *)&rx);
+ if (rc || readlen != sizeof(rx)) {
+ JFFS2_WARNING("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n",
+ rc, sizeof(rx), readlen, ref_offset(xd->node));
+ return rc ? rc : -EIO;
+ }
+ crc = crc32(0, &rx, sizeof(rx) - 4);
+ if (crc != je32_to_cpu(rx.node_crc)) {
+ if (je32_to_cpu(rx.node_crc) != 0xffffffff)
+ JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ref_offset(xd->node), je32_to_cpu(rx.hdr_crc), crc);
+ return EIO;
+ }
+ totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len));
+ if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK
+ || je16_to_cpu(rx.nodetype) != JFFS2_NODETYPE_XATTR
+ || je32_to_cpu(rx.totlen) != totlen
+ || je32_to_cpu(rx.xid) != xd->xid
+ || je32_to_cpu(rx.version) != xd->version) {
+ JFFS2_ERROR("inconsistent xdatum at %#08x, magic=%#04x/%#04x, "
+ "nodetype=%#04x/%#04x, totlen=%u/%u, xid=%u/%u, version=%u/%u\n",
+ ref_offset(xd->node), je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK,
+ je16_to_cpu(rx.nodetype), JFFS2_NODETYPE_XATTR,
+ je32_to_cpu(rx.totlen), totlen,
+ je32_to_cpu(rx.xid), xd->xid,
+ je32_to_cpu(rx.version), xd->version);
+ return EIO;
+ }
+ xd->xprefix = rx.xprefix;
+ xd->name_len = rx.name_len;
+ xd->value_len = je16_to_cpu(rx.value_len);
+ xd->data_crc = je32_to_cpu(rx.data_crc);
+
+ /* This JFFS2_NODETYPE_XATTR node is checked */
+ jeb = &c->blocks[ref_offset(xd->node) / c->sector_size];
+ totlen = PAD(je32_to_cpu(rx.totlen));
+
+ spin_lock(&c->erase_completion_lock);
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ xd->node->flash_offset = ref_offset(xd->node) | REF_PRISTINE;
+ spin_unlock(&c->erase_completion_lock);
+
+ /* unchecked xdatum is chained with c->xattr_unchecked */
+ list_del_init(&xd->xindex);
+
+ dbg_xattr("success on verfying xdatum (xid=%u, version=%u)\n",
+ xd->xid, xd->version);
+
+ return 0;
+}
+
+static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem) */
+ char *data;
+ size_t readlen;
+ uint32_t crc, length;
+ int i, ret, retry = 0;
+
+ BUG_ON(!xd->node);
+ BUG_ON(ref_flags(xd->node) != REF_PRISTINE);
+ BUG_ON(!list_empty(&xd->xindex));
+ retry:
+ length = xd->name_len + 1 + xd->value_len;
+ data = kmalloc(length, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = jffs2_flash_read(c, ref_offset(xd->node)+sizeof(struct jffs2_raw_xattr),
+ length, &readlen, data);
+
+ if (ret || length!=readlen) {
+ JFFS2_WARNING("jffs2_flash_read() returned %d, request=%d, readlen=%d, at %#08x\n",
+ ret, length, readlen, ref_offset(xd->node));
+ kfree(data);
+ return ret ? ret : -EIO;
+ }
+
+ data[xd->name_len] = '\0';
+ crc = crc32(0, data, length);
+ if (crc != xd->data_crc) {
+ JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XREF)"
+ " at %#08x, read: 0x%08x calculated: 0x%08x\n",
+ ref_offset(xd->node), xd->data_crc, crc);
+ kfree(data);
+ return EIO;
+ }
+
+ xd->flags |= JFFS2_XFLAGS_HOT;
+ xd->xname = data;
+ xd->xvalue = data + xd->name_len+1;
+
+ c->xdatum_mem_usage += length;
+
+ xd->hashkey = xattr_datum_hashkey(xd->xprefix, xd->xname, xd->xvalue, xd->value_len);
+ i = xd->hashkey % XATTRINDEX_HASHSIZE;
+ list_add(&xd->xindex, &c->xattrindex[i]);
+ if (!retry) {
+ retry = 1;
+ reclaim_xattr_datum(c);
+ if (!xd->xname)
+ goto retry;
+ }
+
+ dbg_xattr("success on loading xdatum (xid=%u, xprefix=%u, xname='%s')\n",
+ xd->xid, xd->xprefix, xd->xname);
+
+ return 0;
+}
+
+static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem);
+ * rc < 0 : recoverable error, try again
+ * rc = 0 : success
+ * rc > 0 : Unrecoverable error, this node should be deleted.
+ */
+ int rc = 0;
+ BUG_ON(xd->xname);
+ if (!xd->node)
+ return EIO;
+ if (unlikely(ref_flags(xd->node) != REF_PRISTINE)) {
+ rc = do_verify_xattr_datum(c, xd);
+ if (rc > 0) {
+ list_del_init(&xd->xindex);
+ delete_xattr_datum_node(c, xd);
+ }
+ }
+ if (!rc)
+ rc = do_load_xattr_datum(c, xd);
+ return rc;
+}
+
+static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, uint32_t phys_ofs)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_raw_xattr rx;
+ struct jffs2_raw_node_ref *raw;
+ struct kvec vecs[2];
+ uint32_t length;
+ int rc, totlen;
+
+ BUG_ON(!xd->xname);
+
+ vecs[0].iov_base = &rx;
+ vecs[0].iov_len = PAD(sizeof(rx));
+ vecs[1].iov_base = xd->xname;
+ vecs[1].iov_len = xd->name_len + 1 + xd->value_len;
+ totlen = vecs[0].iov_len + vecs[1].iov_len;
+
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+ return -ENOMEM;
+ raw->flash_offset = phys_ofs;
+ raw->__totlen = PAD(totlen);
+ raw->next_phys = NULL;
+ raw->next_in_ino = (void *)xd;
+
+ /* Setup raw-xattr */
+ rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
+ rx.totlen = cpu_to_je32(PAD(totlen));
+ rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4));
+
+ rx.xid = cpu_to_je32(xd->xid);
+ rx.version = cpu_to_je32(++xd->version);
+ rx.xprefix = xd->xprefix;
+ rx.name_len = xd->name_len;
+ rx.value_len = cpu_to_je16(xd->value_len);
+ rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len));
+ rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr) - 4));
+
+ rc = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0);
+ if (rc || totlen != length) {
+ JFFS2_WARNING("jffs2_flash_writev()=%d, req=%u, wrote=%u, at %#08x\n",
+ rc, totlen, length, phys_ofs);
+ rc = rc ? rc : -EIO;
+ if (length) {
+ raw->flash_offset |= REF_OBSOLETE;
+ raw->next_in_ino = NULL;
+ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ jffs2_free_raw_node_ref(raw);
+ }
+ return rc;
+ }
+ BUG_ON(raw->__totlen < sizeof(struct jffs2_raw_xattr));
+ /* success */
+ raw->flash_offset |= REF_PRISTINE;
+ jffs2_add_physical_node_ref(c, raw);
+ if (xd->node)
+ delete_xattr_datum_node(c, xd);
+ xd->node = raw;
+
+ dbg_xattr("success on saving xdatum (xid=%u, version=%u, xprefix=%u, xname='%s')\n",
+ xd->xid, xd->version, xd->xprefix, xd->xname);
+
+ return 0;
+}
+
+static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
+ int xprefix, const char *xname,
+ const char *xvalue, int xsize,
+ uint32_t phys_ofs)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_xattr_datum *xd;
+ uint32_t hashkey, name_len;
+ char *data;
+ int i, rc;
+
+ /* Search xattr_datum has same xname/xvalue by index */
+ hashkey = xattr_datum_hashkey(xprefix, xname, xvalue, xsize);
+ i = hashkey % XATTRINDEX_HASHSIZE;
+ list_for_each_entry(xd, &c->xattrindex[i], xindex) {
+ if (xd->hashkey==hashkey
+ && xd->xprefix==xprefix
+ && xd->value_len==xsize
+ && !strcmp(xd->xname, xname)
+ && !memcmp(xd->xvalue, xvalue, xsize)) {
+ xd->refcnt++;
+ return xd;
+ }
+ }
+
+ /* Not found, Create NEW XATTR-Cache */
+ name_len = strlen(xname);
+
+ xd = jffs2_alloc_xattr_datum();
+ if (!xd)
+ return ERR_PTR(-ENOMEM);
+
+ data = kmalloc(name_len + 1 + xsize, GFP_KERNEL);
+ if (!data) {
+ jffs2_free_xattr_datum(xd);
+ return ERR_PTR(-ENOMEM);
+ }
+ strcpy(data, xname);
+ memcpy(data + name_len + 1, xvalue, xsize);
+
+ xd->refcnt = 1;
+ xd->xid = ++c->highest_xid;
+ xd->flags |= JFFS2_XFLAGS_HOT;
+ xd->xprefix = xprefix;
+
+ xd->hashkey = hashkey;
+ xd->xname = data;
+ xd->xvalue = data + name_len + 1;
+ xd->name_len = name_len;
+ xd->value_len = xsize;
+ xd->data_crc = crc32(0, data, xd->name_len + 1 + xd->value_len);
+
+ rc = save_xattr_datum(c, xd, phys_ofs);
+ if (rc) {
+ kfree(xd->xname);
+ jffs2_free_xattr_datum(xd);
+ return ERR_PTR(rc);
+ }
+
+ /* Insert Hash Index */
+ i = hashkey % XATTRINDEX_HASHSIZE;
+ list_add(&xd->xindex, &c->xattrindex[i]);
+
+ c->xdatum_mem_usage += (xd->name_len + 1 + xd->value_len);
+ reclaim_xattr_datum(c);
+
+ return xd;
+}
+
+/* -------- xdatum related functions ----------------
+ * verify_xattr_ref(c, ref)
+ * is used to load xref information from medium. Because summary data does not
+ * contain xid/ino, it's necessary to verify once while mounting process.
+ * delete_xattr_ref_node(c, ref)
+ * is used to delete a jffs2 node is dominated by xref. When EBS is enabled,
+ * it overwrites the obsolete node by myself.
+ * delete_xattr_ref(c, ref)
+ * is used to delete jffs2_xattr_ref object. If the reference counter of xdatum
+ * is refered by this xref become 0, delete_xattr_datum() is called later.
+ * save_xattr_ref(c, ref, phys_ofs)
+ * is used to write xref to medium.
+ * create_xattr_ref(c, ic, xd, phys_ofs)
+ * is used to create a new xref and write to medium.
+ * jffs2_xattr_delete_inode(c, ic)
+ * is called to remove xrefs related to obsolete inode when inode is unlinked.
+ * jffs2_xattr_free_inode(c, ic)
+ * is called to release xattr related objects when unmounting.
+ * check_xattr_ref_ilist(c, ic)
+ * is used to confirm inode does not have duplicate xattr name/value pair.
+ * -------------------------------------------------- */
+static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_xref rr;
+ size_t readlen;
+ uint32_t crc, totlen;
+ int rc;
+
+ BUG_ON(ref_flags(ref->node) != REF_UNCHECKED);
+
+ rc = jffs2_flash_read(c, ref_offset(ref->node), sizeof(rr), &readlen, (char *)&rr);
+ if (rc || sizeof(rr) != readlen) {
+ JFFS2_WARNING("jffs2_flash_read()=%d, req=%u, read=%u, at %#08x\n",
+ rc, sizeof(rr), readlen, ref_offset(ref->node));
+ return rc ? rc : -EIO;
+ }
+ /* obsolete node */
+ crc = crc32(0, &rr, sizeof(rr) - 4);
+ if (crc != je32_to_cpu(rr.node_crc)) {
+ if (je32_to_cpu(rr.node_crc) != 0xffffffff)
+ JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ref_offset(ref->node), je32_to_cpu(rr.node_crc), crc);
+ return EIO;
+ }
+ if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK
+ || je16_to_cpu(rr.nodetype) != JFFS2_NODETYPE_XREF
+ || je32_to_cpu(rr.totlen) != PAD(sizeof(rr))) {
+ JFFS2_ERROR("inconsistent xref at %#08x, magic=%#04x/%#04x, "
+ "nodetype=%#04x/%#04x, totlen=%u/%u\n",
+ ref_offset(ref->node), je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK,
+ je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF,
+ je32_to_cpu(rr.totlen), PAD(sizeof(rr)));
+ return EIO;
+ }
+ ref->ino = je32_to_cpu(rr.ino);
+ ref->xid = je32_to_cpu(rr.xid);
+
+ /* fixup superblock/eraseblock info */
+ jeb = &c->blocks[ref_offset(ref->node) / c->sector_size];
+ totlen = PAD(sizeof(rr));
+
+ spin_lock(&c->erase_completion_lock);
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ ref->node->flash_offset = ref_offset(ref->node) | REF_PRISTINE;
+ spin_unlock(&c->erase_completion_lock);
+
+ dbg_xattr("success on verifying xref (ino=%u, xid=%u) at %#08x\n",
+ ref->ino, ref->xid, ref_offset(ref->node));
+ return 0;
+}
+
+static void delete_xattr_ref_node(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ struct jffs2_raw_xref rr;
+ uint32_t length;
+ int rc;
+
+ if (jffs2_sum_active()) {
+ memset(&rr, 0xff, sizeof(rr));
+ rc = jffs2_flash_read(c, ref_offset(ref->node),
+ sizeof(struct jffs2_unknown_node),
+ &length, (char *)&rr);
+ if (rc || length != sizeof(struct jffs2_unknown_node)) {
+ JFFS2_ERROR("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n",
+ rc, sizeof(struct jffs2_unknown_node),
+ length, ref_offset(ref->node));
+ }
+ rc = jffs2_flash_write(c, ref_offset(ref->node), sizeof(rr),
+ &length, (char *)&rr);
+ if (rc || length != sizeof(struct jffs2_raw_xref)) {
+ JFFS2_ERROR("jffs2_flash_write()=%d, req=%u, wrote=%u at %#08x\n",
+ rc, sizeof(rr), length, ref_offset(ref->node));
+ }
+ }
+ spin_lock(&c->erase_completion_lock);
+ ref->node->next_in_ino = NULL;
+ spin_unlock(&c->erase_completion_lock);
+ jffs2_mark_node_obsolete(c, ref->node);
+ ref->node = NULL;
+}
+
+static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_xattr_datum *xd;
+
+ BUG_ON(!ref->node);
+ delete_xattr_ref_node(c, ref);
+
+ list_del(&ref->ilist);
+ xd = ref->xd;
+ xd->refcnt--;
+ if (!xd->refcnt)
+ delete_xattr_datum(c, xd);
+ jffs2_free_xattr_ref(ref);
+}
+
+static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, uint32_t phys_ofs)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_raw_xref rr;
+ uint32_t length;
+ int ret;
+
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+ return -ENOMEM;
+ raw->flash_offset = phys_ofs;
+ raw->__totlen = PAD(sizeof(rr));
+ raw->next_phys = NULL;
+ raw->next_in_ino = (void *)ref;
+
+ rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ rr.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF);
+ rr.totlen = cpu_to_je32(PAD(sizeof(rr)));
+ rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node) - 4));
+
+ rr.ino = cpu_to_je32(ref->ic->ino);
+ rr.xid = cpu_to_je32(ref->xd->xid);
+ rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(rr) - 4));
+
+ ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr);
+ if (ret || sizeof(rr) != length) {
+ JFFS2_WARNING("jffs2_flash_write() returned %d, request=%u, retlen=%u, at %#08x\n",
+ ret, sizeof(rr), length, phys_ofs);
+ ret = ret ? ret : -EIO;
+ if (length) {
+ raw->flash_offset |= REF_OBSOLETE;
+ raw->next_in_ino = NULL;
+ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ jffs2_free_raw_node_ref(raw);
+ }
+ return ret;
+ }
+ raw->flash_offset |= REF_PRISTINE;
+
+ jffs2_add_physical_node_ref(c, raw);
+ if (ref->node)
+ delete_xattr_ref_node(c, ref);
+ ref->node = raw;
+
+ dbg_xattr("success on saving xref (ino=%u, xid=%u)\n", ref->ic->ino, ref->xd->xid);
+
+ return 0;
+}
+
+static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic,
+ struct jffs2_xattr_datum *xd, uint32_t phys_ofs)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_xattr_ref *ref;
+ int ret;
+
+ ref = jffs2_alloc_xattr_ref();
+ if (!ref)
+ return ERR_PTR(-ENOMEM);
+ ref->ic = ic;
+ ref->xd = xd;
+
+ ret = save_xattr_ref(c, ref, phys_ofs);
+ if (ret) {
+ jffs2_free_xattr_ref(ref);
+ return ERR_PTR(ret);
+ }
+
+ /* Chain to inode */
+ list_add(&ref->ilist, &ic->ilist);
+
+ return ref; /* success */
+}
+
+void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ /* It's called from jffs2_clear_inode() on inode removing.
+ When an inode with XATTR is removed, those XATTRs must be removed. */
+ struct jffs2_xattr_ref *ref, *_ref;
+
+ if (!ic || ic->nlink > 0)
+ return;
+
+ down_write(&c->xattr_sem);
+ list_for_each_entry_safe(ref, _ref, &ic->ilist, ilist)
+ delete_xattr_ref(c, ref);
+ up_write(&c->xattr_sem);
+}
+
+void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ /* It's called from jffs2_free_ino_caches() until unmounting FS. */
+ struct jffs2_xattr_datum *xd;
+ struct jffs2_xattr_ref *ref, *_ref;
+
+ down_write(&c->xattr_sem);
+ list_for_each_entry_safe(ref, _ref, &ic->ilist, ilist) {
+ list_del(&ref->ilist);
+ xd = ref->xd;
+ xd->refcnt--;
+ if (!xd->refcnt) {
+ unload_xattr_datum(c, xd);
+ jffs2_free_xattr_datum(xd);
+ }
+ jffs2_free_xattr_ref(ref);
+ }
+ up_write(&c->xattr_sem);
+}
+
+static int check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ /* success of check_xattr_ref_ilist() means taht inode (ic) dose not have
+ * duplicate name/value pairs. If duplicate name/value pair would be found,
+ * one will be removed.
+ */
+ struct jffs2_xattr_ref *ref, *cmp;
+ int rc = 0;
+
+ if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED))
+ return 0;
+ down_write(&c->xattr_sem);
+ retry:
+ rc = 0;
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ if (!ref->xd->xname) {
+ rc = load_xattr_datum(c, ref->xd);
+ if (unlikely(rc > 0)) {
+ delete_xattr_ref(c, ref);
+ goto retry;
+ } else if (unlikely(rc < 0))
+ goto out;
+ }
+ cmp = ref;
+ list_for_each_entry_continue(cmp, &ic->ilist, ilist) {
+ if (!cmp->xd->xname) {
+ ref->xd->flags |= JFFS2_XFLAGS_BIND;
+ rc = load_xattr_datum(c, cmp->xd);
+ ref->xd->flags &= ~JFFS2_XFLAGS_BIND;
+ if (unlikely(rc > 0)) {
+ delete_xattr_ref(c, cmp);
+ goto retry;
+ } else if (unlikely(rc < 0))
+ goto out;
+ }
+ if (ref->xd->xprefix == cmp->xd->xprefix
+ && !strcmp(ref->xd->xname, cmp->xd->xname)) {
+ delete_xattr_ref(c, cmp);
+ goto retry;
+ }
+ }
+ }
+ ic->flags |= INO_FLAGS_XATTR_CHECKED;
+ out:
+ up_write(&c->xattr_sem);
+
+ return rc;
+}
+
+/* -------- xattr subsystem functions ---------------
+ * jffs2_init_xattr_subsystem(c)
+ * is used to initialize semaphore and list_head, and some variables.
+ * jffs2_find_xattr_datum(c, xid)
+ * is used to lookup xdatum while scanning process.
+ * jffs2_clear_xattr_subsystem(c)
+ * is used to release any xattr related objects.
+ * jffs2_build_xattr_subsystem(c)
+ * is used to associate xdatum and xref while super block building process.
+ * jffs2_setup_xattr_datum(c, xid, version)
+ * is used to insert xdatum while scanning process.
+ * -------------------------------------------------- */
+void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c)
+{
+ int i;
+
+ for (i=0; i < XATTRINDEX_HASHSIZE; i++)
+ INIT_LIST_HEAD(&c->xattrindex[i]);
+ INIT_LIST_HEAD(&c->xattr_temp);
+ INIT_LIST_HEAD(&c->xattr_unchecked);
+
+ init_rwsem(&c->xattr_sem);
+ c->xdatum_mem_usage = 0;
+ c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */
+}
+
+static struct jffs2_xattr_datum *jffs2_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid)
+{
+ struct jffs2_xattr_datum *xd;
+ int i = xid % XATTRINDEX_HASHSIZE;
+
+ /* It's only used in scanning/building process. */
+ BUG_ON(!(c->flags & (JFFS2_SB_FLAG_SCANNING|JFFS2_SB_FLAG_BUILDING)));
+
+ list_for_each_entry(xd, &c->xattrindex[i], xindex) {
+ if (xd->xid==xid)
+ return xd;
+ }
+ return NULL;
+}
+
+void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c)
+{
+ struct jffs2_xattr_datum *xd, *_xd;
+ struct jffs2_xattr_ref *ref, *_ref;
+ int i;
+
+ list_for_each_entry_safe(ref, _ref, &c->xattr_temp, ilist)
+ jffs2_free_xattr_ref(ref);
+
+ for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
+ list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
+ list_del(&xd->xindex);
+ if (xd->xname)
+ kfree(xd->xname);
+ jffs2_free_xattr_datum(xd);
+ }
+ }
+}
+
+void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
+{
+ struct jffs2_xattr_ref *ref, *_ref;
+ struct jffs2_xattr_datum *xd, *_xd;
+ struct jffs2_inode_cache *ic;
+ int i, xdatum_count =0, xdatum_unchecked_count = 0, xref_count = 0;
+
+ BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING));
+
+ /* Phase.1 */
+ list_for_each_entry_safe(ref, _ref, &c->xattr_temp, ilist) {
+ list_del_init(&ref->ilist);
+ /* checking REF_UNCHECKED nodes */
+ if (ref_flags(ref->node) != REF_PRISTINE) {
+ if (verify_xattr_ref(c, ref)) {
+ delete_xattr_ref_node(c, ref);
+ jffs2_free_xattr_ref(ref);
+ continue;
+ }
+ }
+ /* At this point, ref->xid and ref->ino contain XID and inode number.
+ ref->xd and ref->ic are not valid yet. */
+ xd = jffs2_find_xattr_datum(c, ref->xid);
+ ic = jffs2_get_ino_cache(c, ref->ino);
+ if (!xd || !ic) {
+ if (ref_flags(ref->node) != REF_UNCHECKED)
+ JFFS2_WARNING("xref(ino=%u, xid=%u) is orphan. \n",
+ ref->ino, ref->xid);
+ delete_xattr_ref_node(c, ref);
+ jffs2_free_xattr_ref(ref);
+ continue;
+ }
+ ref->xd = xd;
+ ref->ic = ic;
+ xd->refcnt++;
+ list_add_tail(&ref->ilist, &ic->ilist);
+ xref_count++;
+ }
+ /* After this, ref->xid/ino are NEVER used. */
+
+ /* Phase.2 */
+ for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
+ list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
+ list_del_init(&xd->xindex);
+ if (!xd->refcnt) {
+ if (ref_flags(xd->node) != REF_UNCHECKED)
+ JFFS2_WARNING("orphan xdatum(xid=%u, version=%u) at %#08x\n",
+ xd->xid, xd->version, ref_offset(xd->node));
+ delete_xattr_datum(c, xd);
+ continue;
+ }
+ if (ref_flags(xd->node) != REF_PRISTINE) {
+ dbg_xattr("unchecked xdatum(xid=%u) at %#08x\n",
+ xd->xid, ref_offset(xd->node));
+ list_add(&xd->xindex, &c->xattr_unchecked);
+ xdatum_unchecked_count++;
+ }
+ xdatum_count++;
+ }
+ }
+ /* build complete */
+ JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum (%u unchecked) and "
+ "%u of xref found.\n", xdatum_count, xdatum_unchecked_count, xref_count);
+}
+
+struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
+ uint32_t xid, uint32_t version)
+{
+ struct jffs2_xattr_datum *xd, *_xd;
+
+ _xd = jffs2_find_xattr_datum(c, xid);
+ if (_xd) {
+ dbg_xattr("duplicate xdatum (xid=%u, version=%u/%u) at %#08x\n",
+ xid, version, _xd->version, ref_offset(_xd->node));
+ if (version < _xd->version)
+ return ERR_PTR(-EEXIST);
+ }
+ xd = jffs2_alloc_xattr_datum();
+ if (!xd)
+ return ERR_PTR(-ENOMEM);
+ xd->xid = xid;
+ xd->version = version;
+ if (xd->xid > c->highest_xid)
+ c->highest_xid = xd->xid;
+ list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]);
+
+ if (_xd) {
+ list_del_init(&_xd->xindex);
+ delete_xattr_datum_node(c, _xd);
+ jffs2_free_xattr_datum(_xd);
+ }
+ return xd;
+}
+
+/* -------- xattr subsystem functions ---------------
+ * xprefix_to_handler(xprefix)
+ * is used to translate xprefix into xattr_handler.
+ * jffs2_listxattr(dentry, buffer, size)
+ * is an implementation of listxattr handler on jffs2.
+ * do_jffs2_getxattr(inode, xprefix, xname, buffer, size)
+ * is an implementation of getxattr handler on jffs2.
+ * do_jffs2_setxattr(inode, xprefix, xname, buffer, size, flags)
+ * is an implementation of setxattr handler on jffs2.
+ * -------------------------------------------------- */
+struct xattr_handler *jffs2_xattr_handlers[] = {
+ &jffs2_user_xattr_handler,
+#ifdef CONFIG_JFFS2_FS_SECURITY
+ &jffs2_security_xattr_handler,
+#endif
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+ &jffs2_acl_access_xattr_handler,
+ &jffs2_acl_default_xattr_handler,
+#endif
+ &jffs2_trusted_xattr_handler,
+ NULL
+};
+
+static struct xattr_handler *xprefix_to_handler(int xprefix) {
+ struct xattr_handler *ret;
+
+ switch (xprefix) {
+ case JFFS2_XPREFIX_USER:
+ ret = &jffs2_user_xattr_handler;
+ break;
+#ifdef CONFIG_JFFS2_FS_SECURITY
+ case JFFS2_XPREFIX_SECURITY:
+ ret = &jffs2_security_xattr_handler;
+ break;
+#endif
+#ifdef CONFIG_JFFS2_FS_POSIX_ACL
+ case JFFS2_XPREFIX_ACL_ACCESS:
+ ret = &jffs2_acl_access_xattr_handler;
+ break;
+ case JFFS2_XPREFIX_ACL_DEFAULT:
+ ret = &jffs2_acl_default_xattr_handler;
+ break;
+#endif
+ case JFFS2_XPREFIX_TRUSTED:
+ ret = &jffs2_trusted_xattr_handler;
+ break;
+ default:
+ ret = NULL;
+ break;
+ }
+ return ret;
+}
+
+ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+ struct inode *inode = dentry->d_inode;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_inode_cache *ic = f->inocache;
+ struct jffs2_xattr_ref *ref;
+ struct jffs2_xattr_datum *xd;
+ struct xattr_handler *xhandle;
+ ssize_t len, rc;
+ int retry = 0;
+
+ rc = check_xattr_ref_ilist(c, ic);
+ if (unlikely(rc))
+ return rc;
+
+ down_read(&c->xattr_sem);
+ retry:
+ len = 0;
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ BUG_ON(ref->ic != ic);
+ xd = ref->xd;
+ if (!xd->xname) {
+ /* xdatum is unchached */
+ if (!retry) {
+ retry = 1;
+ up_read(&c->xattr_sem);
+ down_write(&c->xattr_sem);
+ goto retry;
+ } else {
+ rc = load_xattr_datum(c, xd);
+ if (unlikely(rc > 0)) {
+ delete_xattr_ref(c, ref);
+ goto retry;
+ } else if (unlikely(rc < 0))
+ goto out;
+ }
+ }
+ xhandle = xprefix_to_handler(xd->xprefix);
+ if (!xhandle)
+ continue;
+ if (buffer) {
+ rc = xhandle->list(inode, buffer+len, size-len, xd->xname, xd->name_len);
+ } else {
+ rc = xhandle->list(inode, NULL, 0, xd->xname, xd->name_len);
+ }
+ if (rc < 0)
+ goto out;
+ len += rc;
+ }
+ rc = len;
+ out:
+ if (!retry) {
+ up_read(&c->xattr_sem);
+ } else {
+ up_write(&c->xattr_sem);
+ }
+ return rc;
+}
+
+int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
+ char *buffer, size_t size)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_inode_cache *ic = f->inocache;
+ struct jffs2_xattr_datum *xd;
+ struct jffs2_xattr_ref *ref;
+ int rc, retry = 0;
+
+ rc = check_xattr_ref_ilist(c, ic);
+ if (unlikely(rc))
+ return rc;
+
+ down_read(&c->xattr_sem);
+ retry:
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ BUG_ON(ref->ic!=ic);
+
+ xd = ref->xd;
+ if (xd->xprefix != xprefix)
+ continue;
+ if (!xd->xname) {
+ /* xdatum is unchached */
+ if (!retry) {
+ retry = 1;
+ up_read(&c->xattr_sem);
+ down_write(&c->xattr_sem);
+ goto retry;
+ } else {
+ rc = load_xattr_datum(c, xd);
+ if (unlikely(rc > 0)) {
+ delete_xattr_ref(c, ref);
+ goto retry;
+ } else if (unlikely(rc < 0)) {
+ goto out;
+ }
+ }
+ }
+ if (!strcmp(xname, xd->xname)) {
+ rc = xd->value_len;
+ if (buffer) {
+ if (size < rc) {
+ rc = -ERANGE;
+ } else {
+ memcpy(buffer, xd->xvalue, rc);
+ }
+ }
+ goto out;
+ }
+ }
+ rc = -ENODATA;
+ out:
+ if (!retry) {
+ up_read(&c->xattr_sem);
+ } else {
+ up_write(&c->xattr_sem);
+ }
+ return rc;
+}
+
+int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
+ const char *buffer, size_t size, int flags)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_inode_cache *ic = f->inocache;
+ struct jffs2_xattr_datum *xd;
+ struct jffs2_xattr_ref *ref, *newref;
+ uint32_t phys_ofs, length, request;
+ int rc;
+
+ rc = check_xattr_ref_ilist(c, ic);
+ if (unlikely(rc))
+ return rc;
+
+ request = PAD(sizeof(struct jffs2_raw_xattr) + strlen(xname) + 1 + size);
+ rc = jffs2_reserve_space(c, request, &phys_ofs, &length,
+ ALLOC_NORMAL, JFFS2_SUMMARY_XATTR_SIZE);
+ if (rc) {
+ JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
+ return rc;
+ }
+
+ /* Find existing xattr */
+ down_write(&c->xattr_sem);
+ retry:
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ xd = ref->xd;
+ if (xd->xprefix != xprefix)
+ continue;
+ if (!xd->xname) {
+ rc = load_xattr_datum(c, xd);
+ if (unlikely(rc > 0)) {
+ delete_xattr_ref(c, ref);
+ goto retry;
+ } else if (unlikely(rc < 0))
+ goto out;
+ }
+ if (!strcmp(xd->xname, xname)) {
+ if (flags & XATTR_CREATE) {
+ rc = -EEXIST;
+ goto out;
+ }
+ if (!buffer) {
+ delete_xattr_ref(c, ref);
+ rc = 0;
+ goto out;
+ }
+ goto found;
+ }
+ }
+ /* not found */
+ ref = NULL;
+ if (flags & XATTR_REPLACE) {
+ rc = -ENODATA;
+ goto out;
+ }
+ if (!buffer) {
+ rc = -EINVAL;
+ goto out;
+ }
+ found:
+ xd = create_xattr_datum(c, xprefix, xname, buffer, size, phys_ofs);
+ if (IS_ERR(xd)) {
+ rc = PTR_ERR(xd);
+ goto out;
+ }
+ up_write(&c->xattr_sem);
+ jffs2_complete_reservation(c);
+
+ /* create xattr_ref */
+ request = PAD(sizeof(struct jffs2_raw_xref));
+ rc = jffs2_reserve_space(c, request, &phys_ofs, &length,
+ ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE);
+ if (rc) {
+ JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
+ down_write(&c->xattr_sem);
+ xd->refcnt--;
+ if (!xd->refcnt)
+ delete_xattr_datum(c, xd);
+ up_write(&c->xattr_sem);
+ return rc;
+ }
+ down_write(&c->xattr_sem);
+ newref = create_xattr_ref(c, ic, xd, phys_ofs);
+ if (IS_ERR(newref)) {
+ rc = PTR_ERR(newref);
+ xd->refcnt--;
+ if (!xd->refcnt)
+ delete_xattr_datum(c, xd);
+ } else if (ref) {
+ /* If replaced xattr_ref exists */
+ delete_xattr_ref(c, ref);
+ }
+ out:
+ up_write(&c->xattr_sem);
+ jffs2_complete_reservation(c);
+ return rc;
+}
+
+/* -------- garbage collector functions -------------
+ * jffs2_garbage_collect_xattr_datum(c, xd)
+ * is used to move xdatum into new node.
+ * jffs2_garbage_collect_xattr_ref(c, ref)
+ * is used to move xref into new node.
+ * jffs2_garbage_collect_xattr(c, ic)
+ * is used to call appropriate garbage collector function, if argument
+ * pointer (ic) is the reference of xdatum/xref.
+ * jffs2_verify_xattr(c)
+ * is used to call do_verify_xattr_datum() before garbage collecting.
+ * -------------------------------------------------- */
+static int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c,
+ struct jffs2_xattr_datum *xd)
+{
+ /* must be called under down_write(xattr_sem), and called from GC thread */
+ uint32_t phys_ofs, totlen, length, old_ofs;
+ int rc;
+
+ BUG_ON(!xd->node);
+
+ old_ofs = ref_offset(xd->node);
+ totlen = ref_totlen(c, c->gcblock, xd->node);
+ if (totlen < sizeof(struct jffs2_raw_xattr))
+ return -EINVAL;
+
+ if (!xd->xname) {
+ rc = load_xattr_datum(c, xd);
+ if (unlikely(rc > 0)) {
+ delete_xattr_datum_node(c, xd);
+ return 0;
+ } else if (unlikely(rc < 0))
+ return -EINVAL;
+ }
+ rc = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_XATTR_SIZE);
+ if (rc || length < totlen) {
+ JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, totlen);
+ return rc ? rc : -EBADFD;
+ }
+ rc = save_xattr_datum(c, xd, phys_ofs);
+ if (!rc)
+ dbg_xattr("xdatum (xid=%u, version=%u) GC'ed from %#08x to %08x\n",
+ xd->xid, xd->version, old_ofs, ref_offset(xd->node));
+ return rc;
+}
+
+
+static int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c,
+ struct jffs2_xattr_ref *ref)
+{
+ /* must be called under down(alloc_sem) */
+ uint32_t phys_ofs, totlen, length, old_ofs;
+ int rc;
+
+ BUG_ON(!ref->node);
+
+ old_ofs = ref_offset(ref->node);
+ totlen = ref_totlen(c, c->gcblock, ref->node);
+ if (totlen != sizeof(struct jffs2_raw_xref))
+ return -EINVAL;
+ rc = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_XREF_SIZE);
+ if (rc || length < totlen) {
+ JFFS2_WARNING("%s: jffs2_reserve_space() = %d, request = %u\n",
+ __FUNCTION__, rc, totlen);
+ return rc ? rc : -EBADFD;
+ }
+ rc = save_xattr_ref(c, ref, phys_ofs);
+ if (!rc)
+ dbg_xattr("xref (ino=%u, xid=%u) GC'ed from %#08x to %08x\n",
+ ref->ic->ino, ref->xd->xid, old_ofs, ref_offset(ref->node));
+ return rc;
+}
+
+int jffs2_garbage_collect_xattr(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_xattr_datum *xd;
+ struct jffs2_xattr_ref *ref;
+ int ret;
+
+ switch (ic->class) {
+ case RAWNODE_CLASS_XATTR_DATUM:
+ spin_unlock(&c->erase_completion_lock);
+
+ down_write(&c->xattr_sem);
+ xd = (struct jffs2_xattr_datum *)ic;
+ ret = xd ? jffs2_garbage_collect_xattr_datum(c, xd) : 0;
+ up_write(&c->xattr_sem);
+ break;
+ case RAWNODE_CLASS_XATTR_REF:
+ spin_unlock(&c->erase_completion_lock);
+
+ down_write(&c->xattr_sem);
+ ref = (struct jffs2_xattr_ref *)ic;
+ ret = ref ? jffs2_garbage_collect_xattr_ref(c, ref) : 0;
+ up_write(&c->xattr_sem);
+ break;
+ default:
+ /* This node is not xattr_datum/xattr_ref */
+ ret = 1;
+ break;
+ }
+ return ret;
+}
+
+int jffs2_verify_xattr(struct jffs2_sb_info *c)
+{
+ struct jffs2_xattr_datum *xd, *_xd;
+ int rc;
+
+ down_write(&c->xattr_sem);
+ list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) {
+ rc = do_verify_xattr_datum(c, xd);
+ if (rc == 0) {
+ list_del_init(&xd->xindex);
+ break;
+ } else if (rc > 0) {
+ list_del_init(&xd->xindex);
+ delete_xattr_datum_node(c, xd);
+ }
+ }
+ up_write(&c->xattr_sem);
+
+ return list_empty(&c->xattr_unchecked) ? 1 : 0;
+}
--- mtd-2.6.git/fs/jffs2/xattr.h 1970-01-01 09:00:00.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/xattr.h 2006-05-06 11:28:19.000000000 +0900
@@ -0,0 +1,120 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/xattr.c
+ * XATTR 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.
+ *-------------------------------------------------------------------------*/
+
+#ifndef _JFFS2_FS_XATTR_H_
+#define _JFFS2_FS_XATTR_H_
+
+#include <linux/xattr.h>
+
+#define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */
+#define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */
+
+struct jffs2_xattr_datum
+{
+ void *always_null;
+ u8 class;
+ u8 flags;
+ u16 xprefix; /* see JFFS2_XATTR_PREFIX_* */
+
+ struct jffs2_raw_node_ref *node;
+ struct list_head xindex; /* chained from c->xattrindex[n] */
+ uint32_t refcnt; /* # of xattr_ref refers this */
+ uint32_t xid;
+ uint32_t version;
+
+ uint32_t data_crc;
+ uint32_t hashkey;
+ char *xname; /* XATTR name without prefix */
+ uint32_t name_len; /* length of xname */
+ char *xvalue; /* XATTR value */
+ uint32_t value_len; /* length of xvalue */
+};
+
+struct jffs2_inode_cache; /* forward refence */
+struct jffs2_xattr_ref
+{
+ void *always_null;
+ u8 class;
+ u8 flags; /* Currently unused */
+ u16 unused;
+
+ struct jffs2_raw_node_ref *node;
+ union {
+ struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */
+ uint32_t ino; /* only used in scanning/building */
+ };
+ union {
+ struct jffs2_xattr_datum *xd; /* reference to jffs2_xattr_datum */
+ uint32_t xid; /* only used in sccanning/building */
+ };
+ struct list_head ilist; /* chained from ic->ilist */
+};
+
+#ifdef CONFIG_JFFS2_FS_XATTR
+
+extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c);
+extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
+extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
+
+extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
+ uint32_t xid, uint32_t version);
+
+extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+
+extern int jffs2_garbage_collect_xattr(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+extern int jffs2_verify_xattr(struct jffs2_sb_info *c);
+
+extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
+ char *buffer, size_t size);
+extern int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
+ const char *buffer, size_t size, int flags);
+
+extern struct xattr_handler *jffs2_xattr_handlers[];
+extern struct xattr_handler jffs2_user_xattr_handler;
+extern struct xattr_handler jffs2_trusted_xattr_handler;
+
+extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t);
+#define jffs2_getxattr generic_getxattr
+#define jffs2_setxattr generic_setxattr
+#define jffs2_removexattr generic_removexattr
+
+/*---- Any inline initialize functions ----*/
+#define init_xattr_inode_cache(x) INIT_LIST_HEAD(&((x)->ilist))
+
+#else
+
+#define jffs2_init_xattr_subsystem(c)
+#define jffs2_build_xattr_subsystem(c)
+#define jffs2_clear_xattr_subsystem(c)
+
+#define jffs2_xattr_delete_inode(c, ic)
+#define jffs2_xattr_free_inode(c, ic)
+#define jffs2_garbage_collect_xattr(c, ic) (1)
+#define jffs2_verify_xattr(c) (1)
+
+#define jffs2_xattr_handlers NULL
+#define jffs2_listxattr NULL
+#define jffs2_getxattr NULL
+#define jffs2_setxattr NULL
+#define jffs2_removexattr NULL
+
+#define init_xattr_inode_cache(x)
+
+#endif /* CONFIG_JFFS2_FS_XATTR */
+
+#ifdef CONFIG_JFFS2_FS_SECURITY
+extern int jffs2_init_security(struct inode *inode, struct inode *dir);
+extern struct xattr_handler jffs2_security_xattr_handler;
+#else
+#define jffs2_init_security(inode,dir) (0)
+#endif /* CONFIG_JFFS2_FS_SECURITY */
+
+#endif /* _JFFS2_FS_XATTR_H_ */
--- mtd-2.6.git/fs/jffs2/xattr_trusted.c 1970-01-01 09:00:00.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/xattr_trusted.c 2006-05-06 11:28:19.000000000 +0900
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/xattr_trusted.c
+ * XATTR 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/fs.h>
+#include <linux/jffs2.h>
+#include <linux/xattr.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+static int jffs2_trusted_getxattr(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (!strcmp(name, ""))
+ return -EINVAL;
+ return do_jffs2_getxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size);
+}
+
+static int jffs2_trusted_setxattr(struct inode *inode, const char *name, const void *buffer,
+ size_t size, int flags)
+{
+ if (!strcmp(name, ""))
+ return -EINVAL;
+ return do_jffs2_setxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size, flags);
+}
+
+static size_t jffs2_trusted_listxattr(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ size_t retlen = XATTR_TRUSTED_PREFIX_LEN + name_len + 1;
+
+ if (list && retlen<=list_size) {
+ strcpy(list, XATTR_TRUSTED_PREFIX);
+ strcpy(list + XATTR_TRUSTED_PREFIX_LEN, name);
+ }
+
+ return retlen;
+}
+
+struct xattr_handler jffs2_trusted_xattr_handler = {
+ .prefix = XATTR_TRUSTED_PREFIX,
+ .list = jffs2_trusted_listxattr,
+ .set = jffs2_trusted_setxattr,
+ .get = jffs2_trusted_getxattr
+};
--- mtd-2.6.git/fs/jffs2/xattr_user.c 1970-01-01 09:00:00.000000000 +0900
+++ mtd-2.6.0506/fs/jffs2/xattr_user.c 2006-05-06 11:28:19.000000000 +0900
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/xattr_user.c
+ * XATTR 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/fs.h>
+#include <linux/jffs2.h>
+#include <linux/xattr.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+static int jffs2_user_getxattr(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (!strcmp(name, ""))
+ return -EINVAL;
+ return do_jffs2_getxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size);
+}
+
+static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer,
+ size_t size, int flags)
+{
+ if (!strcmp(name, ""))
+ return -EINVAL;
+ return do_jffs2_setxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size, flags);
+}
+
+static size_t jffs2_user_listxattr(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ size_t retlen = XATTR_USER_PREFIX_LEN + name_len + 1;
+
+ if (list && retlen <= list_size) {
+ strcpy(list, XATTR_USER_PREFIX);
+ strcpy(list + XATTR_USER_PREFIX_LEN, name);
+ }
+
+ return retlen;
+}
+
+struct xattr_handler jffs2_user_xattr_handler = {
+ .prefix = XATTR_USER_PREFIX,
+ .list = jffs2_user_listxattr,
+ .set = jffs2_user_setxattr,
+ .get = jffs2_user_getxattr
+};
--- mtd-2.6.git/include/linux/jffs2.h 2006-04-30 18:06:54.000000000 +0900
+++ mtd-2.6.0506/include/linux/jffs2.h 2006-05-06 11:28:19.000000000 +0900
@@ -65,6 +65,18 @@

#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)

+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
+
+/* XATTR Related */
+#define JFFS2_XPREFIX_USER 1 /* for "user." */
+#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */
+#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */
+#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */
+#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */
+
+#define JFFS2_ACL_VERSION 0x0001
+
// Maybe later...
//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@@ -151,6 +163,32 @@ struct jffs2_raw_inode
uint8_t data[0];
} __attribute__((packed));

+struct jffs2_raw_xattr {
+ jint16_t magic;
+ jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t xid; /* XATTR identifier number */
+ jint32_t version;
+ uint8_t xprefix;
+ uint8_t name_len;
+ jint16_t value_len;
+ jint32_t data_crc;
+ jint32_t node_crc;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xref
+{
+ jint16_t magic;
+ jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t ino; /* inode number */
+ jint32_t xid; /* XATTR identifier number */
+ jint32_t node_crc;
+} __attribute__((packed));
+
struct jffs2_raw_summary
{
jint16_t magic;
@@ -169,6 +207,8 @@ union jffs2_node_union
{
struct jffs2_raw_inode i;
struct jffs2_raw_dirent d;
+ struct jffs2_raw_xattr x;
+ struct jffs2_raw_xref r;
struct jffs2_raw_summary s;
struct jffs2_unknown_node u;
};

[-- Attachment #3: xattr_on_jffs2.utils.version-5.patch --]
[-- Type: text/plain, Size: 24162 bytes --]

diff -prNU3 mtd-utils.git/include/linux/jffs2.h mtd-utils.xattr/include/linux/jffs2.h
--- mtd-utils.git/include/linux/jffs2.h 2006-05-05 20:24:18.000000000 +0900
+++ mtd-utils.xattr/include/linux/jffs2.h 2006-05-05 21:33:48.000000000 +0900
@@ -65,6 +65,18 @@

#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)

+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
+
+/* XATTR Related */
+#define JFFS2_XPREFIX_USER 1 /* for "user." */
+#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */
+#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */
+#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */
+#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */
+
+#define JFFS2_ACL_VERSION 0x0001
+
// Maybe later...
//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
@@ -151,6 +163,32 @@ struct jffs2_raw_inode
uint8_t data[0];
} __attribute__((packed));

+struct jffs2_raw_xattr {
+ jint16_t magic;
+ jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t xid; /* XATTR identifier number */
+ jint32_t version;
+ uint8_t xprefix;
+ uint8_t name_len;
+ jint16_t value_len;
+ jint32_t data_crc;
+ jint32_t node_crc;
+ uint8_t data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xref
+{
+ jint16_t magic;
+ jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */
+ jint32_t totlen;
+ jint32_t hdr_crc;
+ jint32_t ino; /* inode number */
+ jint32_t xid; /* XATTR identifier number */
+ jint32_t node_crc;
+} __attribute__((packed));
+
struct jffs2_raw_summary
{
jint16_t magic;
@@ -169,6 +207,8 @@ union jffs2_node_union
{
struct jffs2_raw_inode i;
struct jffs2_raw_dirent d;
+ struct jffs2_raw_xattr x;
+ struct jffs2_raw_xref r;
struct jffs2_raw_summary s;
struct jffs2_unknown_node u;
};
diff -prNU3 mtd-utils.git/include/mtd/jffs2-user.h mtd-utils.xattr/include/mtd/jffs2-user.h
--- mtd-utils.git/include/mtd/jffs2-user.h 2006-05-05 20:24:18.000000000 +0900
+++ mtd-utils.xattr/include/mtd/jffs2-user.h 2006-05-05 23:29:13.000000000 +0900
@@ -32,4 +32,51 @@ extern int target_endian;
#define je32_to_cpu(x) (t32((x).v32))
#define jemode_to_cpu(x) (t32((x).m))

+#define le16_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x))
+#define le32_to_cpu(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x))
+#define cpu_to_le16(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_16(x))
+#define cpu_to_le32(x) (__BYTE_ORDER==__LITTLE_ENDIAN ? (x) : bswap_32(x))
+
+/* XATTR/POSIX-ACL related definition */
+/* Namespaces copied from xattr.h and posix_acl_xattr.h */
+#define XATTR_USER_PREFIX "user."
+#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
+#define XATTR_SECURITY_PREFIX "security."
+#define XATTR_SECURITY_PREFIX_LEN (sizeof (XATTR_SECURITY_PREFIX) - 1)
+#define POSIX_ACL_XATTR_ACCESS "system.posix_acl_access"
+#define POSIX_ACL_XATTR_ACCESS_LEN (sizeof (POSIX_ACL_XATTR_ACCESS) - 1)
+#define POSIX_ACL_XATTR_DEFAULT "system.posix_acl_default"
+#define POSIX_ACL_XATTR_DEFAULT_LEN (sizeof (POSIX_ACL_XATTR_DEFAULT) - 1)
+#define XATTR_TRUSTED_PREFIX "trusted."
+#define XATTR_TRUSTED_PREFIX_LEN (sizeof (XATTR_TRUSTED_PREFIX) - 1)
+
+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;
+
+/* copied from include/linux/posix_acl_xattr.h */
+#define POSIX_ACL_XATTR_VERSION 0x0002
+
+typedef struct {
+ uint16_t e_tag;
+ uint16_t e_perm;
+ uint32_t e_id;
+} posix_acl_xattr_entry;
+
+typedef struct {
+ uint32_t a_version;
+ posix_acl_xattr_entry a_entries[0];
+} posix_acl_xattr_header;
+
#endif /* __JFFS2_USER_H__ */
--- mtd-utils.git/mkfs.jffs2.1 2006-05-05 20:24:18.000000000 +0900
+++ mtd-utils.xattr/mkfs.jffs2.1 2006-05-05 21:12:47.000000000 +0900
@@ -49,6 +49,15 @@ mkfs.jffs2 \- Create a JFFS2 file system
.B -P,--squash-perms
]
[
+.B --with-xattr
+]
+[
+.B --with-selinux
+]
+[
+.B --with-posix-acl
+]
+[
.B -m,--compression-mode=MODE
]
[
@@ -178,6 +187,15 @@ Squash owners making all files be owned
.B -P, --squash-perms
Squash permissions, removing write permission for \'group\' and \'other\'.
.TP
+.B --with-xattr
+Enables xattr, stuff all xattr entries into jffs2 image file.
+.TP
+.B --with-selinux
+Enables xattr, stuff only SELinux Labels into jffs2 image file.
+.TP
+.B --with-posix-acl
+Enable xattr, stuff only POSIX ACL entries into jffs2 image file.
+.TP
.B -m, --compression-mode=MODE
Set the default compression mode. The default mode is
.B priority
--- mtd-utils.git/mkfs.jffs2.c 2006-05-05 20:24:18.000000000 +0900
+++ mtd-utils.xattr/mkfs.jffs2.c 2006-05-06 10:44:26.000000000 +0900
@@ -7,6 +7,7 @@
* 2002 Axis Communications AB
* 2001, 2002 Erik Andersen <andersen@codepoet.org>
* 2004 University of Szeged, Hungary
+ * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -63,6 +64,8 @@
#include <ctype.h>
#include <time.h>
#include <getopt.h>
+#include <sys/xattr.h>
+#include <sys/acl.h>
#include <byteswap.h>
#define crc32 __complete_crap
#include <zlib.h>
@@ -1022,6 +1025,233 @@ static void write_special_file(struct fi
padword();
}

+typedef struct xattr_entry {
+ struct xattr_entry *next;
+ uint32_t xid;
+ int xprefix;
+ char *xname;
+ char *xvalue;
+ int name_len;
+ int value_len;
+} xattr_entry_t;
+
+#define XATTR_BUFFER_SIZE (64 * 1024) /* 64KB */
+static uint32_t enable_xattr = 0;
+static uint32_t highest_xid = 0;
+
+static struct {
+ int xprefix;
+ char *string;
+ int length;
+} xprefix_tbl[] = {
+ { JFFS2_XPREFIX_USER, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN },
+ { JFFS2_XPREFIX_SECURITY, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN },
+ { JFFS2_XPREFIX_ACL_ACCESS, POSIX_ACL_XATTR_ACCESS, POSIX_ACL_XATTR_ACCESS_LEN },
+ { JFFS2_XPREFIX_ACL_DEFAULT, POSIX_ACL_XATTR_DEFAULT, POSIX_ACL_XATTR_DEFAULT_LEN },
+ { JFFS2_XPREFIX_TRUSTED, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN },
+ { 0, NULL, 0 }
+};
+
+static void formalize_posix_acl(char *xvalue, int *value_len)
+{
+ posix_acl_xattr_header *pacl_header;
+ posix_acl_xattr_entry *pent, *plim;
+ jffs2_acl_header *jacl_header;
+ jffs2_acl_entry *jent;
+ jffs2_acl_entry_short *jent_s;
+ char buffer[XATTR_BUFFER_SIZE];
+ int offset = 0;
+
+ pacl_header = (posix_acl_xattr_header *)xvalue;;
+ pent = pacl_header->a_entries;
+ plim = (posix_acl_xattr_entry *)(xvalue + *value_len);
+
+ jacl_header = (jffs2_acl_header *)buffer;
+ offset += sizeof(jffs2_acl_header);
+ jacl_header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
+
+ while (pent < plim) {
+ switch(le16_to_cpu(pent->e_tag)) {
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ jent_s = (jffs2_acl_entry_short *)(buffer + offset);
+ offset += sizeof(jffs2_acl_entry_short);
+ jent_s->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag));
+ jent_s->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm));
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ jent = (jffs2_acl_entry *)(buffer + offset);
+ offset += sizeof(jffs2_acl_entry);
+ jent->e_tag = cpu_to_je16(le16_to_cpu(pent->e_tag));
+ jent->e_perm = cpu_to_je16(le16_to_cpu(pent->e_perm));
+ jent->e_id = cpu_to_je32(le32_to_cpu(pent->e_id));
+ break;
+ default:
+ printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(pent->e_tag));
+ exit(1);
+ }
+ pent++;
+ }
+ if (offset > *value_len) {
+ printf("Length of JFFS2 ACL expression(%u) is longer than general one(%u).\n",
+ offset, *value_len);
+ exit(1);
+ }
+ memcpy(xvalue, buffer, offset);
+ *value_len = offset;
+}
+
+static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len)
+{
+ xattr_entry_t *xe;
+ struct jffs2_raw_xattr rx;
+ int name_len;
+
+ /* create xattr entry */
+ name_len = strlen(xname);
+ xe = xcalloc(1, sizeof(xattr_entry_t) + name_len + 1 + value_len);
+ xe->next = NULL;
+ xe->xid = ++highest_xid;
+ xe->xprefix = xprefix;
+ xe->xname = ((char *)xe) + sizeof(xattr_entry_t);
+ xe->xvalue = xe->xname + name_len + 1;
+ xe->name_len = name_len;
+ xe->value_len = value_len;
+ strcpy(xe->xname, xname);
+ memcpy(xe->xvalue, xvalue, value_len);
+
+ /* write xattr node */
+ memset(&rx, 0, sizeof(rx));
+ rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
+ rx.totlen = cpu_to_je32(PAD(sizeof(rx) + xe->name_len + 1 + xe->value_len));
+ rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4));
+
+ rx.xid = cpu_to_je32(xe->xid);
+ rx.version = cpu_to_je32(1); /* initial version */
+ rx.xprefix = xprefix;
+ rx.name_len = xe->name_len;
+ rx.value_len = cpu_to_je16(xe->value_len);
+ rx.data_crc = cpu_to_je32(crc32(0, xe->xname, xe->name_len + 1 + xe->value_len));
+ rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 4));
+
+ pad_block_if_less_than(sizeof(rx) + xe->name_len + 1 + xe->value_len);
+ full_write(out_fd, &rx, sizeof(rx));
+ full_write(out_fd, xe->xname, xe->name_len + 1 + xe->value_len);
+ padword();
+
+ return xe;
+}
+
+#define XATTRENTRY_HASHSIZE 57
+static xattr_entry_t *find_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len)
+{
+ static xattr_entry_t **xentry_hash = NULL;
+ xattr_entry_t *xe;
+ int index, name_len;
+
+ /* create hash table */
+ if (!xentry_hash)
+ xentry_hash = xcalloc(1, sizeof(xe) * XATTRENTRY_HASHSIZE);
+
+ if (xprefix == JFFS2_XPREFIX_ACL_ACCESS
+ || xprefix == JFFS2_XPREFIX_ACL_DEFAULT)
+ formalize_posix_acl(xvalue, &value_len);
+
+ name_len = strlen(xname);
+ index = (crc32(0, xname, name_len) ^ crc32(0, xvalue, value_len)) % XATTRENTRY_HASHSIZE;
+ for (xe = xentry_hash[index]; xe; xe = xe->next) {
+ if (xe->xprefix == xprefix
+ && xe->value_len == value_len
+ && !strcmp(xe->xname, xname)
+ && !memcmp(xe->xvalue, xvalue, value_len))
+ break;
+ }
+ if (!xe) {
+ xe = create_xattr_entry(xprefix, xname, xvalue, value_len);
+ xe->next = xentry_hash[index];
+ xentry_hash[index] = xe;
+ }
+ return xe;
+}
+
+static void write_xattr_entry(struct filesystem_entry *e)
+{
+ struct jffs2_raw_xref ref;
+ struct xattr_entry *xe;
+ char xlist[XATTR_BUFFER_SIZE], xvalue[XATTR_BUFFER_SIZE];
+ char *xname, *prefix_str;
+ int i, xprefix, prefix_len;
+ int list_sz, offset, name_len, value_len;
+
+ if (!enable_xattr)
+ return;
+
+ list_sz = llistxattr(e->hostname, xlist, XATTR_BUFFER_SIZE);
+ if (list_sz < 0) {
+ if (verbose)
+ printf("llistxattr('%s') = %d : %s\n",
+ e->hostname, errno, strerror(errno));
+ return;
+ }
+
+ for (offset = 0; offset < list_sz; offset += name_len) {
+ xname = xlist + offset;
+ name_len = strlen(xname) + 1;
+
+ for (i = 0; (xprefix = xprefix_tbl[i].xprefix); i++) {
+ prefix_str = xprefix_tbl[i].string;
+ prefix_len = xprefix_tbl[i].length;
+ if (prefix_str[prefix_len - 1] == '.') {
+ if (!strncmp(xname, prefix_str, prefix_len - 1))
+ break;
+ } else {
+ if (!strcmp(xname, prefix_str))
+ break;
+ }
+ }
+ if (!xprefix) {
+ if (verbose)
+ printf("%s: xattr '%s' is not supported.\n",
+ e->hostname, xname);
+ continue;
+ }
+ if ((enable_xattr & (1 << xprefix)) == 0)
+ continue;
+
+ value_len = lgetxattr(e->hostname, xname, xvalue, XATTR_BUFFER_SIZE);
+ if (value_len < 0) {
+ if (verbose)
+ printf("lgetxattr('%s', '%s') = %d : %s\n",
+ e->hostname, xname, errno, strerror(errno));
+ continue;
+ }
+ xe = find_xattr_entry(xprefix, xname + prefix_len, xvalue, value_len);
+ if (!xe) {
+ if (verbose)
+ printf("%s : xattr '%s' was ignored.\n",
+ e->hostname, xname);
+ continue;
+ }
+
+ memset(&ref, 0, sizeof(ref));
+ ref.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ ref.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF);
+ ref.totlen = cpu_to_je32(sizeof(ref));
+ ref.hdr_crc = cpu_to_je32(crc32(0, &ref, sizeof(struct jffs2_unknown_node) - 4));
+ ref.ino = cpu_to_je32(e->sb.st_ino);
+ ref.xid = cpu_to_je32(xe->xid);
+ ref.node_crc = cpu_to_je32(crc32(0, &ref, sizeof(ref) - 4));
+
+ pad_block_if_less_than(sizeof(ref));
+ full_write(out_fd, &ref, sizeof(ref));
+ padword();
+ }
+}
+
static void recursive_populate_directory(struct filesystem_entry *dir)
{
struct filesystem_entry *e;
@@ -1029,6 +1259,8 @@ static void recursive_populate_directory
if (verbose) {
printf("%s\n", dir->fullname);
}
+ write_xattr_entry(dir); /* for '/' */
+
e = dir->files;
while (e) {

@@ -1041,6 +1273,7 @@ static void recursive_populate_directory
e->name);
}
write_pipe(e);
+ write_xattr_entry(e);
break;
case S_IFSOCK:
if (verbose) {
@@ -1049,6 +1282,7 @@ static void recursive_populate_directory
(int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
}
write_pipe(e);
+ write_xattr_entry(e);
break;
case S_IFIFO:
if (verbose) {
@@ -1057,6 +1291,7 @@ static void recursive_populate_directory
(int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
}
write_pipe(e);
+ write_xattr_entry(e);
break;
case S_IFCHR:
if (verbose) {
@@ -1066,6 +1301,7 @@ static void recursive_populate_directory
(int) e->sb.st_gid, e->name);
}
write_special_file(e);
+ write_xattr_entry(e);
break;
case S_IFBLK:
if (verbose) {
@@ -1075,6 +1311,7 @@ static void recursive_populate_directory
(int) e->sb.st_gid, e->name);
}
write_special_file(e);
+ write_xattr_entry(e);
break;
case S_IFLNK:
if (verbose) {
@@ -1084,6 +1321,7 @@ static void recursive_populate_directory
e->link);
}
write_symlink(e);
+ write_xattr_entry(e);
break;
case S_IFREG:
if (verbose) {
@@ -1092,6 +1330,7 @@ static void recursive_populate_directory
(int) e->sb.st_uid, (int) e->sb.st_gid, e->name);
}
write_regular_file(e);
+ write_xattr_entry(e);
break;
default:
error_msg("Unknown mode %o for %s", e->sb.st_mode,
@@ -1169,6 +1408,9 @@ static struct option long_options[] = {
{"test-compression", 0, NULL, 't'},
{"compressor-priority", 1, NULL, 'y'},
{"incremental", 1, NULL, 'i'},
+ {"with-xattr", 0, NULL, 1000 },
+ {"with-selinux", 0, NULL, 1001 },
+ {"with-posix-acl", 0, NULL, 1002 },
{NULL, 0, NULL, 0}
};

@@ -1201,6 +1443,9 @@ static char *helptext =
" -q, --squash Squash permissions and owners making all files be owned by root\n"
" -U, --squash-uids Squash owners making all files be owned by root\n"
" -P, --squash-perms Squash permissions on all files\n"
+ " --with-xattr stuff all xattr entries into image\n"
+ " --with-selinux stuff only SELinux Labels into jffs2 image\n"
+ " --with-posix-acl stuff only POSIX ACL entries into jffs2 image\n"
" -h, --help Display this help text\n"
" -v, --verbose Verbose operation\n"
" -V, --version Display version information\n"
@@ -1519,6 +1764,20 @@ int main(int argc, char **argv)
perror_msg_and_die("cannot open (incremental) file");
}
break;
+ case 1000: /* --with-xattr */
+ enable_xattr |= (1 << JFFS2_XPREFIX_USER)
+ | (1 << JFFS2_XPREFIX_SECURITY)
+ | (1 << JFFS2_XPREFIX_ACL_ACCESS)
+ | (1 << JFFS2_XPREFIX_ACL_DEFAULT)
+ | (1 << JFFS2_XPREFIX_TRUSTED);
+ break;
+ case 1001: /* --with-selinux */
+ enable_xattr |= (1 << JFFS2_XPREFIX_SECURITY);
+ break;
+ case 1002: /* --with-posix-acl */
+ enable_xattr |= (1 << JFFS2_XPREFIX_ACL_ACCESS)
+ | (1 << JFFS2_XPREFIX_ACL_DEFAULT);
+ break;
}
}
if (out_fd == -1) {
--- mtd-utils.git/summary.h 2006-05-05 20:24:18.000000000 +0900
+++ mtd-utils.xattr/summary.h 2006-05-06 10:15:03.000000000 +0900
@@ -45,6 +45,8 @@
#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
+#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
+#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))

/* Summary structures used on flash */

@@ -75,11 +77,28 @@ struct jffs2_sum_dirent_flash
uint8_t name[0]; /* dirent name */
} __attribute__((packed));

+struct jffs2_sum_xattr_flash
+{
+ jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */
+ jint32_t xid; /* xattr identifier */
+ jint32_t version; /* version number */
+ jint32_t offset; /* offset on jeb */
+ jint32_t totlen; /* node length */
+} __attribute__((packed));
+
+struct jffs2_sum_xref_flash
+{
+ jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */
+ jint32_t offset; /* offset on jeb */
+} __attribute__((packed));
+
union jffs2_sum_flash
{
struct jffs2_sum_unknown_flash u;
struct jffs2_sum_inode_flash i;
struct jffs2_sum_dirent_flash d;
+ struct jffs2_sum_xattr_flash x;
+ struct jffs2_sum_xref_flash r;
};

/* Summary structures used in the memory */
@@ -114,11 +133,30 @@ struct jffs2_sum_dirent_mem
uint8_t name[0]; /* dirent name */
} __attribute__((packed));

+struct jffs2_sum_xattr_mem
+{
+ union jffs2_sum_mem *next;
+ jint16_t nodetype;
+ jint32_t xid;
+ jint32_t version;
+ jint32_t offset;
+ jint32_t totlen;
+} __attribute__((packed));
+
+struct jffs2_sum_xref_mem
+{
+ union jffs2_sum_mem *next;
+ jint16_t nodetype;
+ jint32_t offset;
+} __attribute__((packed));
+
union jffs2_sum_mem
{
struct jffs2_sum_unknown_mem u;
struct jffs2_sum_inode_mem i;
struct jffs2_sum_dirent_mem d;
+ struct jffs2_sum_xattr_mem x;
+ struct jffs2_sum_xref_mem r;
};

struct jffs2_summary
--- mtd-utils.git/sumtool.c 2006-05-05 20:24:18.000000000 +0900
+++ mtd-utils.xattr/sumtool.c 2006-05-06 10:27:50.000000000 +0900
@@ -4,6 +4,7 @@
* Copyright (C) 2004 Zoltan Sogor <weth@inf.u-szeged.hu>,
* Ferenc Havasi <havasi@inf.u-szeged.hu>
* University of Szeged, Hungary
+ * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* $Id: sumtool.c,v 1.9 2006/01/23 08:22:45 havasi Exp $
*
@@ -442,6 +443,29 @@ void dump_sum_records()
break;
}

+ case JFFS2_NODETYPE_XATTR: {
+ struct jffs2_sum_xattr_flash *sxattr_ptr = wpage;
+
+ sxattr_ptr->nodetype = sum_collected->sum_list_head->x.nodetype;
+ sxattr_ptr->xid = sum_collected->sum_list_head->x.xid;
+ sxattr_ptr->version = sum_collected->sum_list_head->x.version;
+ sxattr_ptr->offset = sum_collected->sum_list_head->x.offset;
+ sxattr_ptr->totlen = sum_collected->sum_list_head->x.totlen;
+
+ wpage += JFFS2_SUMMARY_XATTR_SIZE;
+ break;
+ }
+
+ case JFFS2_NODETYPE_XREF: {
+ struct jffs2_sum_xref_flash *sxref_ptr = wpage;
+
+ sxref_ptr->nodetype = sum_collected->sum_list_head->r.nodetype;
+ sxref_ptr->offset = sum_collected->sum_list_head->r.offset;
+
+ wpage += JFFS2_SUMMARY_XREF_SIZE;
+ break;
+ }
+
default : {
printf("Unknown node type!\n");
}
@@ -577,6 +601,16 @@ int add_sum_mem(union jffs2_sum_mem *ite
sum_collected->sum_num++;
break;

+ case JFFS2_NODETYPE_XATTR:
+ sum_collected->sum_size += JFFS2_SUMMARY_XATTR_SIZE;
+ sum_collected->sum_num++;
+ break;
+
+ case JFFS2_NODETYPE_XREF:
+ sum_collected->sum_size += JFFS2_SUMMARY_XREF_SIZE;
+ sum_collected->sum_num++;
+ break;
+
default:
error_msg_and_die("__jffs2_add_sum_mem(): UNKNOWN node type %d\n", je16_to_cpu(item->u.nodetype));
}
@@ -622,6 +656,37 @@ void add_sum_dirent_mem(union jffs2_node
add_sum_mem((union jffs2_sum_mem *) temp);
}

+void add_sum_xattr_mem(union jffs2_node_union *node)
+{
+ struct jffs2_sum_xattr_mem *temp = (struct jffs2_sum_xattr_mem *)
+ malloc(sizeof(struct jffs2_sum_xattr_mem));
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ temp->nodetype = node->x.nodetype;
+ temp->xid = node->x.xid;
+ temp->version = node->x.version;
+ temp->offset = cpu_to_je32(data_ofs);
+ temp->totlen = node->x.totlen;
+ temp->next = NULL;
+
+ add_sum_mem((union jffs2_sum_mem *) temp);
+}
+
+void add_sum_xref_mem(union jffs2_node_union *node)
+{
+ struct jffs2_sum_xref_mem *temp = (struct jffs2_sum_xref_mem *)
+ malloc(sizeof(struct jffs2_sum_xref_mem));
+ if (!temp)
+ error_msg_and_die("Can't allocate memory for summary information!\n");
+
+ temp->nodetype = node->r.nodetype;
+ temp->offset = cpu_to_je32(data_ofs);
+ temp->next = NULL;
+
+ add_sum_mem((union jffs2_sum_mem *) temp);
+}
+
void write_dirent_to_buff(union jffs2_node_union *node)
{
pad_block_if_less_than(je32_to_cpu (node->d.totlen),JFFS2_SUMMARY_DIRENT_SIZE(node->d.nsize));
@@ -639,11 +704,27 @@ void write_inode_to_buff(union jffs2_nod
padword();
}

+void write_xattr_to_buff(union jffs2_node_union *node)
+{
+ pad_block_if_less_than(je32_to_cpu(node->x.totlen), JFFS2_SUMMARY_XATTR_SIZE);
+ add_sum_xattr_mem(node); /* Add xdatum summary mem to summary list */
+ full_write(data_buffer + data_ofs, &(node->x), je32_to_cpu(node->x.totlen));
+ padword();
+}
+
+void write_xref_to_buff(union jffs2_node_union *node)
+{
+ pad_block_if_less_than(je32_to_cpu(node->r.totlen), JFFS2_SUMMARY_XREF_SIZE);
+ add_sum_xref_mem(node); /* Add xref summary mem to summary list */
+ full_write(data_buffer + data_ofs, &(node->r), je32_to_cpu(node->r.totlen));
+ padword();
+}
+
void create_summed_image(int inp_size)
{
uint8_t *p = file_buffer;
union jffs2_node_union *node;
- uint32_t crc;
+ uint32_t crc, length;
uint16_t type;
int bitchbitmask = 0;
int obsolete;
@@ -743,6 +824,56 @@ void create_summed_image(int inp_size)
p += PAD(je32_to_cpu (node->d.totlen));
break;

+ case JFFS2_NODETYPE_XATTR:
+ if (je32_to_cpu(node->x.node_crc) == 0xffffffff)
+ obsolete = 1;
+ if (verbose)
+ printf("%8s Xdatum node at 0x%08x, totlen 0x%08x, "
+ "#xid %5u, version %5u\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu (node->x.totlen),
+ je32_to_cpu(node->x.xid), je32_to_cpu(node->x.version));
+ crc = crc32(0, node, sizeof (struct jffs2_raw_xattr) - 4);
+ if (crc != je32_to_cpu(node->x.node_crc)) {
+ printf("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n",
+ p - file_buffer, je32_to_cpu(node->x.node_crc), crc);
+ p += PAD(je32_to_cpu (node->x.totlen));
+ continue;
+ }
+ length = node->x.name_len + 1 + je16_to_cpu(node->x.value_len);
+ crc = crc32(0, node->x.data, length);
+ if (crc != je32_to_cpu(node->x.data_crc)) {
+ printf("Wrong data_crc at 0x%08x, 0x%08x instead of 0x%08x\n",
+ p - file_buffer, je32_to_cpu(node->x.data_crc), crc);
+ p += PAD(je32_to_cpu (node->x.totlen));
+ continue;
+ }
+
+ write_xattr_to_buff(node);
+ p += PAD(je32_to_cpu (node->x.totlen));
+ break;
+
+ case JFFS2_NODETYPE_XREF:
+ if (je32_to_cpu(node->r.node_crc) == 0xffffffff)
+ obsolete = 1;
+ if (verbose)
+ printf("%8s Xref node at 0x%08x, totlen 0x%08x, "
+ "#ino %5u, xid %5u\n",
+ obsolete ? "Obsolete" : "",
+ p - file_buffer, je32_to_cpu(node->r.totlen),
+ je32_to_cpu(node->r.ino), je32_to_cpu(node->r.xid));
+ crc = crc32(0, node, sizeof (struct jffs2_raw_xref) - 4);
+ if (crc != je32_to_cpu(node->r.node_crc)) {
+ printf("Wrong node_crc at 0x%08x, 0x%08x instead of 0x%08x\n",
+ p - file_buffer, je32_to_cpu(node->r.node_crc), crc);
+ p += PAD(je32_to_cpu (node->r.totlen));
+ continue;
+ }
+
+ write_xref_to_buff(node);
+ p += PAD(je32_to_cpu (node->r.totlen));
+ break;
+
case JFFS2_NODETYPE_CLEANMARKER:
if (verbose) {
printf ("%8s Cleanmarker at 0x%08x, totlen 0x%08x\n",