* [PATCH] XATTR issues on JFFS2
@ 2005-08-23 10:24 Kaigai Kohei
2005-08-23 12:46 ` Jörn Engel
0 siblings, 1 reply; 28+ messages in thread
From: Kaigai Kohei @ 2005-08-23 10:24 UTC (permalink / raw)
To: linux-mtd; +Cc: dwmw2
[-- Attachment #1: Type: text/plain, Size: 5234 bytes --]
Hello,
The attached patch provids XATTR support in JFFS2.
We can apply it to mtd-snapshot-20050822.tar.bz2 tree.
I hope your worthwhile comments for improvement.
# By coincidence, two patches for similar functionality are posted in same day :)
# I changed the subject of this mail a bit to avoid confusion.
This implementation added two new node types JFFS2_NODETYPE_XATTR
and JFFS2_NODETYPE_XREF.
JFFS2_NODETYPE_XATTR node has a set of XATTR entry which contains
name and value. It's represented as jffs2_raw_xattr structure on MTD-device.
JFFS2_NODETYPE_XREF node has a set of reference to associate inode with XATTR.
It's represented as jffs2_raw_xref structure on MTD-device.
* Data Structure
- jffs2_xattr_cache
This object contains XATTR name/value. It must be refered by an inode
at least. When the last inode which refers jffs2_xattr_cache is unlinked,
jffs2_xattr_cache will be deleted.
XATTR name/value is not loaded until inode object is allocated.
- jffs2_xattr_ref
This object refers jffs2_inode_cache and jffs2_xattr_cache, and chains each other.
When we walk on ref->ilist from jffs2_inode_cache, we can scan all the XATTR which
is bound to this inode. Adversely, we can scan all the inode which refers this
XATTR, when we walk on ref->xlist from jffs2_xattr_cache.
struct jffs2_xattr_cache
{
struct list_head xcache; <-- It's chained from struct jffs2_sb_info (global hash list).
struct list_head xlist; <-- jffs2_xattr_ref is chained on this list.
struct jffs2_raw_node_ref *node;
uint32_t xid; <-- Unique Identifier
uint32_t version;
uint32_t crc;
char *xname; /* XATTR name */ <--
char *xvalue; /* XATTR value */
int xsize; /* size of XATTR value */
};
struct jffs2_xattr_ref
{
uint32_t seqno; /* sequencial number */
uint32_t flags;
struct list_head ilist; /* list from jffs2_inode_cache */
struct list_head xlist; /* list from jffs2_xattr_cache */
struct jffs2_xattr_cache *xc;
struct jffs2_inode_cache *ic;
struct jffs2_raw_node_ref *node;
};
* How to work
[1] FS mounting
When jffs2 is mounting, xattrcache which is array of list_head
is created by kmalloc() on jffs2_sb_info first.
Then jffs2_scan_eraseblock() is called on each erase block,
and jffs2_scan_xattr_node() and jffs2_scan_xref_node() are
used to treat two new node-type.
jffs2_scan_xattr_node() create jffs2_xattr_cache object and chain
it to xattrcache. jffs2_scan_xref_node() create jffs2_xattr_ref
object and chain it to temporary list.
Next, jffs2_build_xattr_caches() is called. This function associate
jffs2_inode_cache with jffs2_xattr_cache by using jffs2_xattr_ref.
(struct jffs2_xattr_ref *)ref->ilist ... Chained jffs2_inode_cache
(struct jffs2_xattr_ref *)ref->xlist ... Chained jffs2_xattr_cache
[2] inode-creation
When inode object is created, jffs2_get_xattr_inode() will be called.
It walks on ilist on inode-cache and calls do_load_xattr_cache() for
related XATTR object to load XATTR name/value.
[3] getxattr/setxattr
We can refer an jffs2_inode_cache object in {get|set}xattr handler,
and walks on jffs2_inode_cache->ilist for looking up target XATTR.
In setxattr case, do_create_xattr_cache() is called for creation
new XATTR-node, then do_delete_xattr_ref() is used for old-node.
* Example of use
[root@saba ~]# dd if=jffs2.xattr.img of=/dev/mtdblock0
8192+0 records in
8192+0 records out
[root@saba ~]# mount -t jffs2 /dev/mtdblock0 /mnt/0
[root@saba ~]# ls -lZ /mnt/0
drwxr-xr-x root root system_u:object_r:bin_t bin/
drwxr-xr-x root root system_u:object_r:mnt_t dev/
drwxr-xr-x root root system_u:object_r:etc_t etc/
-rw-r--r-- root root system_u:object_r:mnt_t hoge
-rwxr-xr-x root root system_u:object_r:mnt_t init*
drwxr-xr-x root root system_u:object_r:lib_t lib/
drwxr-xr-x root root system_u:object_r:mnt_t loopfs/
drwxr-xr-x root root system_u:object_r:mnt_t proc/
lrwxrwxrwx root root system_u:object_r:bin_t sbin
drwxr-xr-x root root system_u:object_r:mnt_t sys/
drwxr-xr-x root root system_u:object_r:mnt_t sysroot/
[root@saba ~]#
* ToDo List
(1) stabilization
o more tests and debugging are necessary. I hope any trouble reporting.
I don't have real MTD device. This implementation is developed on
MTD-RAM pseudo device. Does it work on real MTD-devices ?
(2) performance & scalability
o Currently, any XATTR objects are guarded by a xattr_sem. But this
implementation is not scale, I guess. To divid locking is necessary.
o When setxattr() is called, the system scans XATTR cache to look for
same combination of name/value and share it.
But it's so expensive process, to reduce finding-step is necessary.
(3) more functionality
o Currently, only XATTR has "security.*" prefix is supported.
It's enough to use SELinux, but that's more useful if we can use
POSIX-ACL and so on.
o modifying mkfs.jffs2 command.
Thanks,
--
Linux Promotion Center, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>
[-- Attachment #2: jffs2_xattr_take-1.patch --]
[-- Type: text/x-patch, Size: 45733 bytes --]
diff -rpNU3 mtd/fs/Kconfig mtd-xattr/fs/Kconfig
--- mtd/fs/Kconfig 2005-05-10 07:00:10.000000000 +0900
+++ mtd-xattr/fs/Kconfig 2005-08-23 10:30:27.000000000 +0900
@@ -51,6 +51,15 @@ 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_XATTR
+ bool "JFFS2 XATTR support (EXPERIMENTAL)"
+ depends on JFFS2_FS
+ default n
+ help
+ This enables the XATTR support in JFFS2.
+
+ It's neccesary for persistent security context in SELinux.
+
config JFFS2_FS_WRITEBUFFER
bool "JFFS2 write-buffering support"
depends on JFFS2_FS
diff -rpNU3 mtd/fs/jffs2/Makefile.common mtd-xattr/fs/jffs2/Makefile.common
--- mtd/fs/jffs2/Makefile.common 2005-07-18 07:00:13.000000000 +0900
+++ mtd-xattr/fs/jffs2/Makefile.common 2005-08-23 10:31:18.000000000 +0900
@@ -11,6 +11,7 @@ jffs2-y += read.o nodemgmt.o readinode.o
jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
jffs2-y += super.o debug.o
+jffs2-$(CONFIG_JFFS2_XATTR) += xattr.o
jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
diff -rpNU3 mtd/fs/jffs2/build.c mtd-xattr/fs/jffs2/build.c
--- mtd/fs/jffs2/build.c 2005-07-31 07:00:11.000000000 +0900
+++ mtd-xattr/fs/jffs2/build.c 2005-08-23 11:02:27.000000000 +0900
@@ -165,6 +165,7 @@ static int jffs2_build_filesystem(struct
ic->scan_dents = NULL;
cond_resched();
}
+ jffs2_build_xattr_caches(c);
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
D1(printk(KERN_DEBUG "Pass 3 complete\n"));
@@ -184,6 +185,7 @@ exit:
jffs2_free_full_dirent(fd);
}
}
+ jffs2_clear_xattr_caches(c);
}
return ret;
@@ -353,6 +355,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
if (jffs2_build_filesystem(c)) {
D1(printk(KERN_DEBUG "build_fs failed\n"));
+ jffs2_clear_xattr_caches(c);
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
#ifndef __ECOS
diff -rpNU3 mtd/fs/jffs2/dir.c mtd-xattr/fs/jffs2/dir.c
--- mtd/fs/jffs2/dir.c 2005-08-18 07:00:11.000000000 +0900
+++ mtd-xattr/fs/jffs2/dir.c 2005-08-23 11:02:27.000000000 +0900
@@ -58,6 +58,10 @@ struct inode_operations jffs2_dir_inode_
.mknod = jffs2_mknod,
.rename = jffs2_rename,
.setattr = jffs2_setattr,
+ .setxattr = jffs2_setxattr,
+ .getxattr = jffs2_getxattr,
+ .listxattr = jffs2_listxattr,
+ .removexattr = jffs2_removexattr
};
/***********************************************************************/
@@ -236,8 +240,11 @@ static int jffs2_unlink(struct inode *di
ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name,
dentry->d_name.len, dead_f, now);
- if (dead_f->inocache)
+ if (dead_f->inocache) {
dentry->d_inode->i_nlink = dead_f->inocache->nlink;
+ if (!dead_f->inocache->nlink)
+ jffs2_unlink_xattr_inode(c, dead_f->inocache);
+ }
if (!ret)
dir_i->i_mtime = dir_i->i_ctime = ITIME(now);
return ret;
diff -rpNU3 mtd/fs/jffs2/file.c mtd-xattr/fs/jffs2/file.c
--- mtd/fs/jffs2/file.c 2005-07-07 07:00:13.000000000 +0900
+++ mtd-xattr/fs/jffs2/file.c 2005-08-23 11:02:27.000000000 +0900
@@ -57,7 +57,11 @@ struct file_operations jffs2_file_operat
struct inode_operations jffs2_file_inode_operations =
{
- .setattr = jffs2_setattr
+ .setattr = jffs2_setattr,
+ .setxattr = jffs2_setxattr,
+ .getxattr = jffs2_getxattr,
+ .listxattr = jffs2_listxattr,
+ .removexattr = jffs2_removexattr
};
struct address_space_operations jffs2_file_address_operations =
diff -rpNU3 mtd/fs/jffs2/fs.c mtd-xattr/fs/jffs2/fs.c
--- mtd/fs/jffs2/fs.c 2005-08-07 07:00:16.000000000 +0900
+++ mtd-xattr/fs/jffs2/fs.c 2005-08-23 11:02:27.000000000 +0900
@@ -310,6 +310,15 @@ void jffs2_read_inode (struct inode *ino
printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
}
+#ifdef CONFIG_JFFS2_XATTR
+ ret = jffs2_get_xattr_inode(c, f->inocache);
+ if (ret) {
+ up(&f->sem);
+ jffs2_do_clear_inode(c, f);
+ make_bad_inode(inode);
+ return;
+ }
+#endif
up(&f->sem);
D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
@@ -504,6 +513,20 @@ int jffs2_do_fill_super(struct super_blo
}
memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
+#ifdef CONFIG_JFFS2_XATTR
+ /* Initialization of XATTR related members */
+ c->xattrcache = kmalloc(XATTRCACHE_HASHSIZE * sizeof(struct list_head), GFP_KERNEL);
+ if (!c->xattrcache) {
+ kfree(c->inocache_list);
+ ret = -ENOMEM;
+ goto out_wbuf;
+ }
+ for (ret=0; ret<XATTRCACHE_HASHSIZE; ret++)
+ INIT_LIST_HEAD(&c->xattrcache[ret]);
+ INIT_LIST_HEAD(&c->xattr_temp);
+ init_rwsem(&c->xattr_sem);
+#endif
+
if ((ret = jffs2_do_mount_fs(c)))
goto out_inohash;
@@ -531,6 +554,7 @@ int jffs2_do_fill_super(struct super_blo
out_root_i:
iput(root_i);
+ jffs2_clear_xattr_caches(c);
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
@@ -538,6 +562,9 @@ int jffs2_do_fill_super(struct super_blo
else
kfree(c->blocks);
out_inohash:
+#ifdef CONFIG_JFFS2_XATTR
+ kfree(c->xattrcache);
+#endif
kfree(c->inocache_list);
out_wbuf:
jffs2_flash_cleanup(c);
diff -rpNU3 mtd/fs/jffs2/malloc.c mtd-xattr/fs/jffs2/malloc.c
--- mtd/fs/jffs2/malloc.c 2005-07-28 07:00:18.000000000 +0900
+++ mtd-xattr/fs/jffs2/malloc.c 2005-08-23 11:02:27.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_XATTR
+static kmem_cache_t *xattr_cache_slab;
+static kmem_cache_t *xattr_ref_slab;
+#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_XATTR
+ xattr_cache_slab = kmem_cache_create("jffs2_xattr_cache",
+ sizeof(struct jffs2_xattr_cache),
+ 0, 0, NULL, NULL);
+ if (!xattr_cache_slab)
+ goto err;
+
+ xattr_ref_slab = kmem_cache_create("jffs2_xattr_ref",
+ sizeof(struct jffs2_xattr_ref),
+ 0, 0, NULL, NULL);
+ if (!xattr_ref_slab)
+ 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_XATTR
+ if(xattr_cache_slab)
+ kmem_cache_destroy(xattr_cache_slab);
+ if(xattr_ref_slab)
+ kmem_cache_destroy(xattr_ref_slab);
+#endif
}
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
@@ -205,3 +231,35 @@ void jffs2_free_inode_cache(struct jffs2
JFFS2_DBG_MEMALLOC("%p\n", x);
kmem_cache_free(inode_cache_slab, x);
}
+
+#ifdef CONFIG_JFFS2_XATTR
+
+struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void)
+{
+ struct jffs2_xattr_cache *xc;
+ xc = kmem_cache_alloc(xattr_cache_slab, GFP_KERNEL);
+ JFFS2_DBG_MEMALLOC("%p\n", xc);
+ return xc;
+}
+
+void jffs2_free_xattr_cache(struct jffs2_xattr_cache *xc)
+{
+ JFFS2_DBG_MEMALLOC("%p\n", xc);
+ kmem_cache_free(xattr_cache_slab, xc);
+}
+
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
+{
+ struct jffs2_xattr_ref *ref;
+ ref = kmem_cache_alloc(xattr_ref_slab, GFP_KERNEL);
+ JFFS2_DBG_MEMALLOC("%p\n", ref);
+ return ref;
+}
+
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref)
+{
+ JFFS2_DBG_MEMALLOC("%p\n", ref);
+ kmem_cache_free(xattr_ref_slab, ref);
+}
+
+#endif
diff -rpNU3 mtd/fs/jffs2/nodelist.h mtd-xattr/fs/jffs2/nodelist.h
--- mtd/fs/jffs2/nodelist.h 2005-08-18 07:00:12.000000000 +0900
+++ mtd-xattr/fs/jffs2/nodelist.h 2005-08-23 11:02:27.000000000 +0900
@@ -20,6 +20,7 @@
#include <linux/jffs2.h>
#include <linux/jffs2_fs_sb.h>
#include <linux/jffs2_fs_i.h>
+#include <linux/jffs2_fs_x.h>
#ifdef __ECOS
#include "os-ecos.h"
@@ -111,6 +112,9 @@ struct jffs2_inode_cache {
uint32_t ino;
int nlink;
int state;
+#ifdef CONFIG_JFFS2_XATTR
+ struct list_head ilist;
+#endif
};
/* Inode states for 'state' above. We need the 'GC' state to prevent
@@ -367,6 +371,13 @@ void jffs2_free_node_frag(struct jffs2_n
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
void jffs2_free_inode_cache(struct jffs2_inode_cache *);
+#ifdef CONFIG_JFFS2_XATTR
+struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void);
+void jffs2_free_xattr_cache(struct jffs2_xattr_cache *);
+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void);
+void jffs2_free_xattr_ref(struct jffs2_xattr_ref *);
+#endif /* CONFIG_JFFS2_XATTR */
+
/* gc.c */
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
diff -rpNU3 mtd/fs/jffs2/readinode.c mtd-xattr/fs/jffs2/readinode.c
--- mtd/fs/jffs2/readinode.c 2005-08-18 07:00:12.000000000 +0900
+++ mtd-xattr/fs/jffs2/readinode.c 2005-08-23 13:08:59.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;
+ initxattr_jffs2_inode_cache(f->inocache);
jffs2_add_ino_cache(c, f->inocache);
}
if (!f->inocache) {
@@ -965,6 +966,8 @@ void jffs2_do_clear_inode(struct jffs2_s
jffs2_free_full_dirent(fd);
}
+ jffs2_put_xattr_inode(c, f->inocache);
+
if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
if (f->inocache->nodes == (void *)f->inocache)
diff -rpNU3 mtd/fs/jffs2/scan.c mtd-xattr/fs/jffs2/scan.c
--- mtd/fs/jffs2/scan.c 2005-07-21 07:00:12.000000000 +0900
+++ mtd-xattr/fs/jffs2/scan.c 2005-08-23 11:02:27.000000000 +0900
@@ -57,6 +57,12 @@ static int jffs2_scan_inode_node(struct
struct jffs2_raw_inode *ri, uint32_t ofs);
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_raw_dirent *rd, uint32_t ofs);
+#ifdef CONFIG_JFFS2_XATTR
+static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_xattr *rx, uint32_t ofs);
+static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct jffs2_raw_xref *rr, uint32_t ofs);
+#endif
#define BLK_STATE_ALLFF 0
#define BLK_STATE_CLEAN 1
@@ -552,7 +558,40 @@ scan_more:
if (err) return err;
ofs += PAD(je32_to_cpu(node->totlen));
break;
-
+#ifdef CONFIG_JFFS2_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);
+ 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);
+ if (err)
+ return err;
+ ofs += PAD(je32_to_cpu(node->totlen));
+ break;
+#endif
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) {
@@ -665,6 +704,7 @@ static struct jffs2_inode_cache *jffs2_s
ic->ino = ino;
ic->nodes = (void *)ic;
+ initxattr_jffs2_inode_cache(ic);
jffs2_add_ino_cache(c, ic);
if (ino == 1)
ic->nlink = 1;
@@ -820,6 +860,184 @@ static int jffs2_scan_dirent_node(struct
return 0;
}
+#ifdef CONFIG_JFFS2_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_raw_node_ref *raw;
+ struct jffs2_xattr_cache *xc;
+ uint32_t crc;
+ int nlen, vlen;
+
+ crc = crc32(0, rx, sizeof(*rx)-8);
+ if (crc != je32_to_cpu(rx->node_crc)) {
+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Node CRC failed"
+ " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ofs, je32_to_cpu(rx->node_crc), crc);
+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen)));
+ return 0;
+ }
+
+ nlen = XATTR_NAMELEN(je32_to_cpu(rx->length));
+ vlen = XATTR_VALUELEN(je32_to_cpu(rx->length));
+ if (je32_to_cpu(rx->totlen) != sizeof(*rx)+PAD(nlen+1)+PAD(vlen)) {
+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): length mimatch!"
+ " totlen=%d length=0x%08x nlen=%d vlen=%d on 0x%08x\n",
+ je32_to_cpu(rx->totlen), je32_to_cpu(rx->length), nlen, vlen, ofs);
+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen)));
+ return 0;
+ }
+
+ crc = crc32(0, rx->data, PAD(nlen+1)+PAD(vlen));
+ if (crc != je32_to_cpu(rx->data_crc)) {
+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Data CRC failed"
+ " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ofs, je32_to_cpu(rx->data_crc), crc);
+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen)));
+ return 0;
+ }
+
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+ return -ENOMEM;
+ raw->__totlen = PAD(je32_to_cpu(rx->totlen));
+ raw->flash_offset = ofs | REF_PRISTINE;
+ raw->next_phys = NULL;
+ raw->next_in_ino = NULL;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ xc = jffs2_find_xattr_cache(c, je32_to_cpu(rx->xid));
+ if (!xc) {
+ xc = jffs2_alloc_xattr_cache();
+ if (!xc) {
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
+ initxattr_jffs2_xattr_cache(xc);
+
+ xc->xid = je32_to_cpu(rx->xid);
+ xc->version = je32_to_cpu(rx->version);
+
+ if (xc->xid > c->highest_xid)
+ c->highest_xid = xc->xid;
+
+ jffs2_attach_xattr_cache(c, xc);
+ }
+
+ USED_SPACE(PAD(je32_to_cpu(rx->totlen)));
+ if (xc->version > je32_to_cpu(rx->version)) {
+ /* This node is obsolete */
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ if (xc->node)
+ jffs2_mark_node_obsolete(c, xc->node);
+ xc->version = je32_to_cpu(rx->version);
+ xc->node = raw;
+ }
+ BUG_ON(!xc->node);
+
+ printk(KERN_NOTICE "XATTR_CACHE: XID=%d inserted\n", xc->xid);
+ return 0;
+}
+
+static inline int jffs2_scan_xref_node_helper(struct jffs2_sb_info *c, struct jffs2_xattr_ref *new,
+ struct jffs2_xattr_ref *old)
+{
+ /* (uint32_t)new->xc == (uint32_t)old->xc has been made sure. */
+ if ((uint32_t)new->ic == (uint32_t)old->ic) {
+ if (new->seqno > old->seqno) {
+ jffs2_mark_node_obsolete(c, old->node);
+ old->seqno = new->seqno;
+ old->node = new->node;
+ } else {
+ jffs2_mark_node_obsolete(c, new->node);
+ }
+ return 1; /* 'new' should be released. */
+ }
+ 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_raw_node_ref *raw;
+ struct jffs2_xattr_ref *ref, *pos, *pos2;
+ uint32_t crc;
+
+ crc = crc32(0, rr, sizeof(*rr)-4);
+ if (crc != je32_to_cpu(rr->node_crc)) {
+ printk(KERN_NOTICE "jffs2_scan_xref_node(): Node CRC failed"
+ " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ ofs, je32_to_cpu(rr->node_crc), crc);
+ DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen)));
+ return 0;
+ }
+
+ ref = jffs2_alloc_xattr_ref();
+ if (!ref)
+ return -ENOMEM;
+ initxattr_jffs2_xattr_ref(ref);
+
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ jffs2_free_xattr_ref(ref);
+ return -ENOMEM;
+ }
+
+ ref->seqno = je32_to_cpu(rr->seqno);
+ if (ref->seqno > c->highest_seqno)
+ c->highest_seqno = ref->seqno;
+ ref->ic = (void *)je32_to_cpu(rr->ino);
+ ref->xc = (void *)je32_to_cpu(rr->xid);
+
+ raw->__totlen = PAD(je32_to_cpu(rr->totlen));
+ raw->flash_offset = ofs | REF_PRISTINE;
+ raw->next_phys = NULL;
+ raw->next_in_ino = NULL;
+ ref->node = raw;
+
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ /* chaining to x->xattr_temp */
+ list_for_each_entry(pos, &c->xattr_temp, xlist) {
+ if ((uint32_t)pos->xc == (uint32_t)ref->xc) {
+ /* Duplicate Checking */
+ if (jffs2_scan_xref_node_helper(c, ref, pos)) {
+ jffs2_free_xattr_ref(ref);
+ DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen)));
+ return 0;
+ }
+ list_for_each_entry(pos2, &pos->ilist, ilist) {
+ if (jffs2_scan_xref_node_helper(c, ref, pos2)) {
+ jffs2_free_xattr_ref(ref);
+ DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen)));
+ return 0;
+ }
+ }
+ /* No Duplication Nodes exist */
+ list_add_tail(&ref->ilist, &pos->ilist);
+ USED_SPACE(PAD(je32_to_cpu(rr->totlen)));
+ printk(KERN_NOTICE "XATTR_REF: XID=%d INO=%d inserted\n",
+ (uint32_t)ref->xc, (uint32_t)ref->ic);
+ return 0;
+ }
+ }
+ list_add_tail(&ref->xlist, &c->xattr_temp);
+ USED_SPACE(PAD(je32_to_cpu(rr->totlen)));
+ printk(KERN_NOTICE "XATTR_REF: XID=%d INO=%d inserted\n",
+ (uint32_t)ref->xc, (uint32_t)ref->ic);
+ return 0;
+}
+#endif /* CONFIG_JFFS2_XATTR */
+
static int count_list(struct list_head *l)
{
uint32_t count = 0;
diff -rpNU3 mtd/fs/jffs2/super.c mtd-xattr/fs/jffs2/super.c
--- mtd/fs/jffs2/super.c 2005-07-13 07:00:11.000000000 +0900
+++ mtd-xattr/fs/jffs2/super.c 2005-08-23 11:02:27.000000000 +0900
@@ -282,6 +282,7 @@ static void jffs2_put_super (struct supe
down(&c->alloc_sem);
jffs2_flush_wbuf_pad(c);
up(&c->alloc_sem);
+ jffs2_clear_xattr_caches(c);
jffs2_free_ino_caches(c);
jffs2_free_raw_node_refs(c);
if (c->mtd->flags & MTD_NO_VIRTBLOCKS)
@@ -290,6 +291,9 @@ static void jffs2_put_super (struct supe
kfree(c->blocks);
jffs2_flash_cleanup(c);
kfree(c->inocache_list);
+#ifdef CONFIG_JFFS2_XATTR
+ kfree(c->xattrcache);
+#endif
if (c->mtd->sync)
c->mtd->sync(c->mtd);
diff -rpNU3 mtd/fs/jffs2/symlink.c mtd-xattr/fs/jffs2/symlink.c
--- mtd/fs/jffs2/symlink.c 2005-07-18 07:00:20.000000000 +0900
+++ mtd-xattr/fs/jffs2/symlink.c 2005-08-23 11:02:27.000000000 +0900
@@ -24,7 +24,11 @@ struct inode_operations jffs2_symlink_in
{
.readlink = generic_readlink,
.follow_link = jffs2_follow_link,
- .setattr = jffs2_setattr
+ .setattr = jffs2_setattr,
+ .setxattr = jffs2_setxattr,
+ .getxattr = jffs2_getxattr,
+ .listxattr = jffs2_listxattr,
+ .removexattr = jffs2_removexattr
};
static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
diff -rpNU3 mtd/fs/jffs2/write.c mtd-xattr/fs/jffs2/write.c
--- mtd/fs/jffs2/write.c 2005-08-18 07:00:12.000000000 +0900
+++ mtd-xattr/fs/jffs2/write.c 2005-08-23 11:02:27.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;
-
+ initxattr_jffs2_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));
diff -rpNU3 mtd/fs/jffs2/xattr.c mtd-xattr/fs/jffs2/xattr.c
--- mtd/fs/jffs2/xattr.c 1970-01-01 09:00:00.000000000 +0900
+++ mtd-xattr/fs/jffs2/xattr.c 2005-08-23 12:47:01.000000000 +0900
@@ -0,0 +1,704 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/xattr.c
+ * XATTR support on JFFS2 FileSystem
+ *
+ * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * Copyright (C) 2005 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"
+
+/*---- decralation of internal use functions ----*/
+static int do_load_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc);
+static void do_unload_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc);
+static int do_save_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc);
+static int do_create_xattr_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic,
+ const char *name, const char *buffer, size_t size);
+static void do_delete_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc);
+
+static struct jffs2_xattr_ref *do_create_xattr_ref(struct jffs2_sb_info *c,
+ struct jffs2_inode_cache *ic, struct jffs2_xattr_cache *xc);
+static void do_delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);
+
+/*---- Implementations of XATTR functions ----*/
+void jffs2_build_xattr_caches(struct jffs2_sb_info *c)
+{
+ struct jffs2_xattr_ref *xref, *_xref, *iref, *_iref;
+ struct jffs2_inode_cache *ic;
+ struct jffs2_xattr_cache *xc;
+ uint32_t xid, ino;
+ struct list_head temp_list;
+
+ BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING));
+
+ down_write(&c->xattr_sem);
+
+ INIT_LIST_HEAD(&temp_list);
+
+ list_for_each_entry_safe(xref, _xref, &c->xattr_temp, xlist) {
+ list_del_init(&xref->xlist);
+ list_add(&temp_list, &xref->ilist);
+
+ xid = (uint32_t)xref->xc;
+
+ xc = jffs2_find_xattr_cache(c, xid);
+ if (!xc) {
+ printk(KERN_NOTICE "xid = %d is not found on xattr_cache\n", xid);
+ list_for_each_entry_safe(iref, _iref, &temp_list, ilist) {
+ list_del(&iref->ilist);
+ jffs2_mark_node_obsolete(c, iref->node);
+ jffs2_free_xattr_ref(iref);
+ }
+ continue;
+ }
+
+ list_for_each_entry_safe(iref, _iref, &temp_list, ilist) {
+ list_del_init(&iref->ilist);
+
+ BUG_ON(xid!=(uint32_t)iref->xc);
+
+ ino = (uint32_t)iref->ic;
+ ic = jffs2_get_ino_cache(c, ino);
+ if (!ic) {
+ D1(printk(KERN_NOTICE "ino = %d is not found on inode_cache\n", ino));
+ jffs2_mark_node_obsolete(c, iref->node);
+ jffs2_free_xattr_ref(iref);
+ continue;
+ }
+ iref->ic = ic;
+ iref->xc = xc;
+ list_add_tail(&iref->ilist, &ic->ilist);
+ list_add_tail(&iref->xlist, &xc->xlist);
+ D1(printk(KERN_NOTICE "Bind XATTR-REF(xid=%d, ino=%d)\n", xc->xid, ic->ino));
+ }
+ BUG_ON(!list_empty(&temp_list));
+ }
+ up_write(&c->xattr_sem);
+}
+
+void jffs2_clear_xattr_caches(struct jffs2_sb_info *c)
+{
+ struct jffs2_xattr_cache *xc, *_xc;
+ struct jffs2_xattr_ref *ref, *_ref;
+ int i;
+
+ down_write(&c->xattr_sem);
+ list_for_each_entry_safe(ref, _ref, &c->xattr_temp, xlist)
+ jffs2_free_xattr_ref(ref);
+
+ for (i=0; i<XATTRCACHE_HASHSIZE; i++) {
+ list_for_each_entry_safe(xc, _xc, &c->xattrcache[i], xcache) {
+ jffs2_dettach_xattr_cache(c, xc);
+ list_for_each_entry_safe(ref, _ref, &xc->xlist, xlist) {
+ list_del(&ref->xlist);
+ list_del(&ref->ilist);
+ jffs2_free_xattr_ref(ref);
+ }
+ if (xc->xname)
+ kfree(xc->xname);
+ jffs2_free_xattr_cache(xc);
+ }
+ }
+ up_write(&c->xattr_sem);
+}
+
+struct jffs2_xattr_cache *jffs2_find_xattr_cache(struct jffs2_sb_info *c, uint32_t xid)
+{
+ struct jffs2_xattr_cache *xc;
+ int i = xid % XATTRCACHE_HASHSIZE;
+
+ list_for_each_entry(xc, &c->xattrcache[i], xcache) {
+ if (xc->xid==xid)
+ return xc;
+ }
+ return NULL;
+}
+
+static int is_active_xattr_cache(struct jffs2_xattr_cache *xc)
+{
+ struct jffs2_xattr_ref *ref;
+
+ list_for_each_entry(ref, &xc->xlist, xlist) {
+ if (ref->flags & XREF_FLAGS_ACTIVE)
+ return 1; /* Active */
+ }
+ return 0; /* Not Active */
+}
+
+static int do_load_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc)
+{
+ struct jffs2_raw_xattr rx;
+ size_t length;
+ uint32_t crc;
+ char *data;
+ int ret, nlen, vlen, dlen;
+
+ if (xc->xname)
+ return 0; /* No need to read */
+
+ BUG_ON(!xc->node);
+
+ ret = jffs2_flash_read(c, ref_offset(xc->node), sizeof(rx), &length, (char *)&rx);
+ if (ret || length!=sizeof(rx)) {
+ ret = ret ? ret : -EIO;
+ return ret;
+ }
+
+ crc = crc32(0, &rx, sizeof(rx)-8);
+ if (crc != je32_to_cpu(rx.node_crc))
+ return -EIO;
+
+ nlen = XATTR_NAMELEN(je32_to_cpu(rx.length));
+ vlen = XATTR_VALUELEN(je32_to_cpu(rx.length));
+ dlen = PAD(nlen+1)+PAD(vlen);
+
+ data = kmalloc(dlen, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = jffs2_flash_read(c, ref_offset(xc->node)+sizeof(rx), dlen, &length, data);
+ if (ret || length!=dlen) {
+ ret = ret ? ret : -EIO;
+ kfree(data);
+ return ret;
+ }
+ memset(data+nlen, 0, PAD(nlen+1)-nlen); /* PADDING and NULL terminate string */
+ memset(data+dlen, 0, PAD(dlen)-dlen);
+
+ crc = crc32(0, data, dlen);
+ if (crc != je32_to_cpu(rx.data_crc)) {
+ kfree(data);
+ return -EIO;
+ }
+
+ xc->crc = crc;
+ xc->xname = data;
+ xc->xvalue = data+PAD(nlen+1);
+ xc->xsize = vlen;
+
+ D1(printk("do_load_xattr_cache(xid=%d, xname='%s')\n", xc->xid, xc->xname));
+
+ return 0;
+}
+
+static void do_unload_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc)
+{
+ D1(printk("do_unload_xattr_cache(xid=%d, xname='%s')\n", xc->xid, xc->xname));
+
+ if (xc->xname)
+ kfree(xc->xname);
+ xc->xname = NULL;
+ xc->xvalue = NULL;
+ xc->xsize = 0;
+ xc->crc = 0;
+}
+
+static int do_save_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc)
+{
+ struct jffs2_raw_xattr rx;
+ struct jffs2_raw_node_ref *raw;
+ struct kvec vecs[2];
+ uint32_t length, phys_ofs;
+ int ret, nlen, totlen;
+
+ if (!xc->xname)
+ return -ENODATA;
+
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+ return -ENOMEM;
+
+ nlen = strlen(xc->xname);
+ vecs[0].iov_base = ℞
+ vecs[0].iov_len = PAD(sizeof(rx));
+ vecs[1].iov_base = xc->xname;
+ vecs[1].iov_len = PAD(nlen+1)+PAD(xc->xsize);
+ totlen = vecs[0].iov_len + vecs[1].iov_len;
+
+ /* Setup raw-xattr */
+ rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
+ rx.totlen = cpu_to_je32(totlen);
+ rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node)-4));
+
+ rx.xid = cpu_to_je32(xc->xid);
+ rx.version = cpu_to_je32(++xc->version);
+ rx.length = cpu_to_je32(XATTR_TOTLEN(nlen, xc->xsize));
+ rx.data_crc = cpu_to_je32(xc->crc);
+ rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr)-8));
+
+ ret = jffs2_reserve_space(c, totlen, &phys_ofs, &length, ALLOC_NORMAL);
+ if (ret) {
+ printk("%s:%d jffs2_reserve_space() = %d phys_ofs = %08x totlen = %d length = %d\n",
+ __FILE__, __LINE__, ret, phys_ofs, totlen, length);
+ jffs2_free_raw_node_ref(raw);
+ return ret;
+ }
+ raw->flash_offset = phys_ofs;
+ raw->__totlen = vecs[0].iov_len + vecs[1].iov_len;
+ raw->next_phys = NULL;
+ raw->next_in_ino = NULL;
+
+ ret = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0);
+ if (ret || totlen!=length) {
+ printk(KERN_NOTICE "%s:%d Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
+ __FILE__, __LINE__, totlen, phys_ofs, ret, length);
+ ret = ret ? ret : -EIO;
+ if (length) {
+ raw->flash_offset |= REF_OBSOLETE;
+ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ jffs2_free_raw_node_ref(raw);
+ }
+ jffs2_complete_reservation(c);
+ return ret;
+ }
+ /* success */
+ raw->flash_offset |= REF_PRISTINE;
+ jffs2_add_physical_node_ref(c, raw);
+ if (xc->node)
+ jffs2_mark_node_obsolete(c, xc->node);
+
+ xc->node = raw;
+
+ jffs2_complete_reservation(c);
+
+ return 0;
+}
+
+static int do_create_xattr_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic,
+ const char *name, const char *buffer, size_t size)
+{
+ struct jffs2_xattr_cache *xc;
+ struct jffs2_xattr_ref *ref;
+ char *data;
+ int i, ret, length;
+
+ /* Looking up same name/value XATTR */
+ /* TODO: reduce seraching cost */
+ for (i=0; i<XATTRCACHE_HASHSIZE; i++) {
+ list_for_each_entry(xc, &c->xattrcache[i], xcache) {
+ if (!xc->xname || xc->xsize!=size)
+ continue;
+ if (strcmp(name, xc->xname))
+ continue;
+ if (memcmp(buffer, xc->xvalue, size))
+ continue;
+ D1(printk(KERN_NOTICE "Duplicate XATTR-Cache Found XID=%d\n", xc->xid));
+ goto found;
+ }
+ }
+
+ /* Not found, Create NEW XATTR-Cache */
+ xc = jffs2_alloc_xattr_cache();
+ if (!xc)
+ return -ENOMEM;
+ initxattr_jffs2_xattr_cache(xc);
+
+ length = strlen(name)+1;
+ data = kmalloc(PAD(length)+PAD(size), GFP_KERNEL);
+ if (!data) {
+ jffs2_free_xattr_cache(xc);
+ return -ENOMEM;
+ }
+
+ memset(data, 0, PAD(length)+PAD(size));
+ strcpy(data, name);
+ memcpy(data+PAD(length), buffer, size);
+
+ xc->xid = ++c->highest_xid;
+ xc->version = 1;
+
+ xc->crc = crc32(0, data, PAD(length)+PAD(size));
+ xc->xname = data;
+ xc->xvalue = data+PAD(length);
+ xc->xsize = size;
+
+ jffs2_attach_xattr_cache(c, xc);
+
+ D1(printk(KERN_NOTICE "Create New XATTR-Cache XID=%d\n", xc->xid));
+
+ found:
+ ref = do_create_xattr_ref(c, ic, xc);
+ if (IS_ERR(ref)) {
+ if (!is_active_xattr_cache(xc)) {
+ if (xc->xname)
+ kfree(xc->xname);
+ xc->crc = 0;
+ xc->xname = NULL;
+ xc->xvalue = NULL;
+ xc->xsize = 0;
+ }
+ if (list_empty(&xc->xlist)) {
+ jffs2_dettach_xattr_cache(c, xc);
+ jffs2_free_xattr_cache(xc);
+ }
+ return (int)ref;
+ }
+ ref->flags |= XREF_FLAGS_ACTIVE;
+
+ ret = do_save_xattr_cache(c, xc);
+ if (ret)
+ do_delete_xattr_ref(c, ref);
+
+ D1(printk(KERN_NOTICE "do_save_xattr_cache() = %d\n", ret));
+
+ return ret;
+}
+
+static void do_delete_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc)
+{
+ BUG_ON(!list_empty(&xc->xlist));
+
+ D1(printk(KERN_NOTICE "do_delete_xattr_cache() XID=%d XNAME=%s\n",
+ xc->xid, xc->xname ? xc->xname : "<NULL>"));
+
+ jffs2_dettach_xattr_cache(c, xc);
+ if (xc->node)
+ jffs2_mark_node_obsolete(c, xc->node);
+
+ if (xc->xname)
+ kfree(xc->xname);
+ jffs2_free_xattr_cache(xc);
+}
+
+static struct jffs2_xattr_ref *do_create_xattr_ref(struct jffs2_sb_info *c,
+ struct jffs2_inode_cache *ic, struct jffs2_xattr_cache *xc)
+{
+ struct jffs2_xattr_ref *tmp, *ref = NULL;
+ struct jffs2_raw_node_ref *raw = NULL;
+ struct jffs2_raw_xref rr;
+ int ret = -ENOMEM;
+ uint32_t phys_ofs, length;
+
+ D1(printk("do_create_xattr_ref(xid=%d, ino=%d)\n", xc->xid, ic->ino));
+
+ /* Duplicate Check */
+ list_for_each_entry(tmp, &ic->ilist, ilist) {
+ if (tmp->xc == xc)
+ return ERR_PTR(-EINVAL);
+ }
+
+ ref = jffs2_alloc_xattr_ref();
+ if (!ref)
+ goto error;
+ initxattr_jffs2_xattr_ref(ref);
+ ref->seqno = ++c->highest_seqno;
+
+ /* Setup raw-node-ref */
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+ goto error;
+
+ ret = jffs2_reserve_space(c, sizeof(rr), &phys_ofs, &length, ALLOC_NORMAL);
+ if (ret) {
+ printk(KERN_NOTICE "%s:%d jffs2_reserve_space() = %d length =%d sizeof(rr) = %d\n",
+ __FILE__, __LINE__, ret, length, sizeof(rr));
+ ret = ret ? ret : -EIO;
+ goto error;
+ }
+ raw->flash_offset = phys_ofs;
+ raw->__totlen = sizeof(rr);
+ raw->next_phys = NULL;
+ raw->next_in_ino = NULL;
+
+ /* write JFFS2_NODETYPE_XREF */
+ rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+ rr.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF);
+ rr.totlen = cpu_to_je32(sizeof(rr));
+ rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node)-4));
+
+ rr.seqno = cpu_to_je32(ref->seqno);
+ rr.ino = cpu_to_je32(ic->ino);
+ rr.xid = cpu_to_je32(xc->xid);
+ rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_raw_xref)-4));
+
+ ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr);
+ if (ret || sizeof(rr)!=length) {
+ printk(KERN_NOTICE "%s:%d Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
+ __FILE__, __LINE__, sizeof(rr), phys_ofs, ret, length);
+ ret = ret ? ret : -EIO;
+ if (length) {
+ raw->flash_offset |= REF_OBSOLETE;
+ jffs2_add_physical_node_ref(c, raw);
+ jffs2_mark_node_obsolete(c, raw);
+ raw = NULL; /* do not free in error-path */
+ }
+ jffs2_complete_reservation(c);
+ goto error;
+ }
+
+ raw->flash_offset |= REF_PRISTINE;
+ jffs2_add_physical_node_ref(c, raw);
+ jffs2_complete_reservation(c);
+
+ /* Setup Reference */
+ ref->ic = ic;
+ ref->xc = xc;
+ list_add(&ref->ilist, &ic->ilist);
+ list_add(&ref->xlist, &xc->xlist);
+ ref->node = raw;
+
+ return ref; /* success */
+
+ error:
+ if (raw)
+ jffs2_free_raw_node_ref(raw);
+ if (ref)
+ jffs2_free_xattr_ref(ref);
+ return ERR_PTR(ret);
+}
+
+static void do_delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ struct jffs2_xattr_cache *xc;
+
+ BUG_ON(!ref->node);
+
+ xc = ref->xc;
+ list_del(&ref->ilist);
+ list_del(&ref->xlist);
+ jffs2_mark_node_obsolete(c, ref->node);
+ D1(printk(KERN_NOTICE "do_delete_xattr_ref(xid=%d, ino=%d)\n", ref->xc->xid, ref->ic->ino));
+ if (list_empty(&xc->xlist))
+ do_delete_xattr_cache(c, xc);
+ jffs2_free_xattr_ref(ref);
+}
+
+int jffs2_get_xattr_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_xattr_ref *ref;
+ int ret = 0;
+
+ D1(printk("jffs2_get_xattr_inode(ino=%d)\n", ic->ino));
+
+ down_write(&c->xattr_sem);
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ ret = do_load_xattr_cache(c, ref->xc);
+ if (ret) {
+ struct jffs2_xattr_ref *pos;
+ list_for_each_entry(pos, &ic->ilist, ilist) {
+ if (pos==ref)
+ break;
+ if (!is_active_xattr_cache(pos->xc))
+ do_unload_xattr_cache(c, pos->xc);
+ }
+ goto out;
+ }
+ ref->flags |= XREF_FLAGS_ACTIVE;
+ }
+ out:
+ up_write(&c->xattr_sem);
+
+ return ret;
+}
+
+void jffs2_put_xattr_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_xattr_ref *ref;
+
+ D1(printk("jffs2_put_xattr_inode(ino=%d)\n", ic->ino));
+
+ down_write(&c->xattr_sem);
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ if (!is_active_xattr_cache(ref->xc))
+ do_unload_xattr_cache(c, ref->xc);
+ }
+ up_write(&c->xattr_sem);
+}
+
+void jffs2_unlink_xattr_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_xattr_ref *ref, *tmp;
+
+ D1(printk("jffs2_unlink_xattr_inode(ino=%d)\n", ic->ino));
+
+ down_write(&c->xattr_sem);
+
+ list_for_each_entry_safe(ref, tmp, &ic->ilist, ilist)
+ do_delete_xattr_ref(c, ref);
+
+ up_write(&c->xattr_sem);
+}
+
+static int do_jffs2_getxattr(struct inode *inode, const char *name, void *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_ref *ref;
+ struct jffs2_xattr_cache *xc;
+ int ret;
+
+ if (!name)
+ return -EINVAL;
+
+ down_read(&c->xattr_sem);
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ xc = ref->xc;
+ BUG_ON(ref->ic!=ic || !xc->xname);
+ if (!strcmp(name, xc->xname)) {
+ if (!buffer) {
+ ret = xc->xsize;
+ } else if (xc->xsize > size) {
+ ret = -ERANGE;
+ } else {
+ memcpy(buffer, xc->xvalue, xc->xsize);
+ ret = xc->xsize;
+ }
+ goto out;
+ }
+ }
+ ret = -ENODATA; /* Not Found */
+ out:
+ up_read(&c->xattr_sem);
+
+ return ret;
+}
+
+static int do_jffs2_setxattr(struct inode *inode, const char *name,
+ const void *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_ref *ref;
+ struct jffs2_xattr_cache *xc;
+ int ret = 0;
+
+ if (!name)
+ return -EINVAL;
+
+ down_write(&c->xattr_sem);
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ xc = ref->xc;
+ BUG_ON(ref->ic!=ic || !xc->xname);
+ if (!strcmp(xc->xname, name)) {
+ /* XATTR found */
+ if (flags & XATTR_CREATE) {
+ ret = -EEXIST;
+ goto out;
+ }
+
+ if (buffer) {
+ ret = do_create_xattr_cache(c, ic, name, buffer, size);
+ if (ret)
+ goto out;
+ }
+
+ do_delete_xattr_ref(c, ref);
+ goto out;
+ }
+ }
+ /* Not Found */
+ if (flags & XATTR_REPLACE) {
+ ret = -ENODATA;
+ goto out;
+ }
+ if (!buffer) {
+ ret = -EINVAL;
+ goto out;
+ }
+ /* Create New XATTR Node */
+ ret = do_create_xattr_cache(c, ic, name, buffer, size);
+ out:
+ up_write(&c->xattr_sem);
+
+ return ret;
+}
+
+/* ---- Security Related XATTR handlers -------------------------------------- */
+static ssize_t jffs2_security_getxattr(struct inode *inode, const char *name, char *buffer, size_t size)
+{
+ if (name[sizeof(XATTR_SECURITY_PREFIX)-1]=='\0')
+ return -EINVAL;
+
+ return do_jffs2_getxattr(inode, name, buffer, size);
+}
+
+static int jffs2_security_setxattr(struct inode *inode, const char *name, const char *buffer, size_t size, int flags)
+{
+ if (name[sizeof(XATTR_SECURITY_PREFIX)-1]=='\0')
+ return -EINVAL;
+
+ return do_jffs2_setxattr(inode, name, buffer, size, flags);
+}
+
+/* ---- JFFS2 inode_operations related to XATTR ------------------------------ */
+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_cache *xc;
+ int ret, nlen;
+
+ down_read(&c->xattr_sem);
+ ret = 0;
+ list_for_each_entry(ref, &ic->ilist, ilist) {
+ xc = ref->xc;
+ BUG_ON(ref->ic!=ic || !xc->xname);
+ nlen = strlen(xc->xname);
+ if (!buffer) {
+ ret += nlen+1;
+ } else if (ret+nlen+1 > size) {
+ ret = -ERANGE;
+ goto out;
+ } else {
+ memcpy(buffer+ret, xc->xname, nlen+1);
+ ret += nlen+1;
+ }
+ }
+ out:
+ up_read(&c->xattr_sem);
+ return ret;
+}
+
+ssize_t jffs2_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size)
+{
+ ssize_t ret = -EOPNOTSUPP;
+
+ if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX)-1)) {
+ ret = jffs2_security_getxattr(dentry->d_inode, name, (char *)buffer, size);
+#ifdef JFFS2_POSIX_ACL
+ } else if (...) {
+ /* TODO: POSIX ACL Support */
+#endif
+ }
+
+ return ret;
+}
+
+int jffs2_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)
+{
+ int ret = -EOPNOTSUPP;
+
+ if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX)-1)) {
+ ret = jffs2_security_setxattr(dentry->d_inode, name, (void *)value, size, flags);
+#ifdef JFFS2_POSIX_ACL
+ /* TODO: POSIX ACL Support */
+#endif
+ }
+
+ return ret;
+}
+
+int jffs2_removexattr(struct dentry *dentry, const char *name){
+ return jffs2_setxattr(dentry, name, NULL, 0, XATTR_REPLACE);
+}
+
diff -rpNU3 mtd/include/linux/jffs2.h mtd-xattr/include/linux/jffs2.h
--- mtd/include/linux/jffs2.h 2005-07-27 07:00:09.000000000 +0900
+++ mtd-xattr/include/linux/jffs2.h 2005-08-23 11:02:27.000000000 +0900
@@ -59,6 +59,8 @@
#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
// Maybe later...
//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
@@ -146,9 +148,40 @@ 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; /* XATTR version number */
+ jint32_t length; /* upper 12bit = the length of xname */
+ /* lower 20bit = the length of xvalue */
+ 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 seqno; /* sequencial number */
+ jint32_t ino; /* inode number */
+ jint32_t xid; /* XATTR identifier number */
+ jint32_t node_crc;
+} __attribute__((packed));
+
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_unknown_node u;
};
diff -rpNU3 mtd/include/linux/jffs2_fs_sb.h mtd-xattr/include/linux/jffs2_fs_sb.h
--- mtd/include/linux/jffs2_fs_sb.h 2005-05-20 07:00:14.000000000 +0900
+++ mtd-xattr/include/linux/jffs2_fs_sb.h 2005-08-23 11:02:27.000000000 +0900
@@ -112,6 +112,13 @@ struct jffs2_sb_info {
uint32_t fsdata_len;
#endif
+#ifdef CONFIG_JFFS2_XATTR
+ uint32_t highest_xid; /* version number for XATTR-Node */
+ uint32_t highest_seqno; /* seqno for XREF-Node */
+ struct list_head *xattrcache;
+ struct list_head xattr_temp; /* temporary use in mounting */
+ struct rw_semaphore xattr_sem;
+#endif
/* OS-private pointer for getting back to master superblock info */
void *os_priv;
};
diff -rpNU3 mtd/include/linux/jffs2_fs_x.h mtd-xattr/include/linux/jffs2_fs_x.h
--- mtd/include/linux/jffs2_fs_x.h 1970-01-01 09:00:00.000000000 +0900
+++ mtd-xattr/include/linux/jffs2_fs_x.h 2005-08-23 12:47:21.000000000 +0900
@@ -0,0 +1,119 @@
+/*-------------------------------------------------------------------------*
+ * File: fs/jffs2/xattr.c
+ * XATTR support on JFFS2 FileSystem
+ *
+ * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * Copyright (C) 2005 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 XATTRCACHE_HASHSIZE (64)
+#define XATTR_NAMELEN(x) (((x)>>20) & 0x00000fff)
+#define XATTR_VALUELEN(x) ( (x) & 0x000fffff)
+#define XATTR_TOTLEN(n,v) ((((n)<<20)&0xfff00000) | ((v)&0x000fffff))
+
+struct jffs2_xattr_cache
+{
+ /* hash list chain from sb->xattrcache */
+ struct list_head xcache;
+ /* link list to jffs2_xattr_ref */
+ /* when list_empty(&xc->xlist) is true, no-inode refers this XATR */
+ struct list_head xlist;
+ /* reference to raw-ndoe */
+ struct jffs2_raw_node_ref *node;
+
+ uint32_t xid;
+ uint32_t version;
+
+ uint32_t crc;
+ char *xname; /* XATTR name */
+ char *xvalue; /* XATTR value */
+ int xsize; /* size of XATTR value */
+};
+
+#define XREF_FLAGS_ACTIVE (0x0001)
+
+struct jffs2_inode_cache; /* forward refence */
+struct jffs2_xattr_ref
+{
+ uint32_t seqno; /* sequencial number */
+ uint32_t flags;
+ struct list_head ilist; /* list from jffs2_inode_cache */
+ struct list_head xlist; /* list from jffs2_xattr_cache */
+ struct jffs2_xattr_cache *xc;
+ struct jffs2_inode_cache *ic;
+ struct jffs2_raw_node_ref *node;
+};
+
+#ifdef CONFIG_JFFS2_XATTR
+
+extern void jffs2_build_xattr_caches(struct jffs2_sb_info *c);
+extern void jffs2_clear_xattr_caches(struct jffs2_sb_info *c);
+extern struct jffs2_xattr_cache *jffs2_find_xattr_cache(struct jffs2_sb_info *c, uint32_t xid);
+
+extern int jffs2_get_xattr_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+extern void jffs2_put_xattr_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+extern void jffs2_unlink_xattr_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+
+extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t);
+extern ssize_t jffs2_getxattr(struct dentry *, const char *, void *, size_t);
+extern int jffs2_setxattr(struct dentry *, const char *, const void *, size_t, int);
+extern int jffs2_removexattr(struct dentry *, const char *);
+
+#define initxattr_jffs2_inode_cache(x) INIT_LIST_HEAD(&((x)->ilist))
+
+static inline void initxattr_jffs2_xattr_cache(struct jffs2_xattr_cache *xc)
+{
+ memset(xc, 0, sizeof(struct jffs2_xattr_cache));
+ INIT_LIST_HEAD(&xc->xcache);
+ INIT_LIST_HEAD(&xc->xlist);
+}
+
+static inline void initxattr_jffs2_xattr_ref(struct jffs2_xattr_ref *ref)
+{
+ memset(ref, 0, sizeof(struct jffs2_xattr_ref));
+ INIT_LIST_HEAD(&ref->ilist);
+ INIT_LIST_HEAD(&ref->xlist);
+}
+
+static inline void jffs2_attach_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc)
+{
+ int i = xc->xid % XATTRCACHE_HASHSIZE;
+ list_add(&xc->xcache, &c->xattrcache[i]);
+}
+
+static inline void jffs2_dettach_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc)
+{
+ list_del(&xc->xcache);
+}
+
+#else /* CONFIG_JFFS2_XATTR */
+
+#define initxattr_jffs2_inode_cache(x)
+#define initxattr_jffs2_xattr_cache(x) BUG()
+#define initxattr_jffs2_xattr_ref(x) BUG()
+static inline void jffs2_build_xattr_caches(void *c){}
+static inline void jffs2_clear_xattr_caches(void *c){}
+static inline void jffs2_attach_xattr_cache(void *c, void *xc){ BUG(); }
+static inline void jffs2_dettach_xattr_cache(void *c, void *xc){ BUG(); }
+static inline void *jffs2_find_xattr_cache(void *c, uint32_t xid){ BUG(); return NULL; }
+
+static inline int jffs2_get_xattr_inode(void *c, void *ic){ return 0; }
+static inline void jffs2_put_xattr_inode(void *c, void *ic){}
+static inline void jffs2_unlink_xattr_inode(void *c, void *ic){}
+
+#define jffs2_listxattr NULL
+#define jffs2_getxattr NULL
+#define jffs2_setxattr NULL
+#define jffs2_removexattr NULL
+#define jffs2_xattr_handlers NULL
+
+#endif /* CONFIG_JFFS2_XATTR */
+
+#endif /* _JFFS2_FS_XATTR_H_ */
^ permalink raw reply [flat|nested] 28+ messages in thread* Re: [PATCH] XATTR issues on JFFS2 2005-08-23 10:24 [PATCH] XATTR issues on JFFS2 Kaigai Kohei @ 2005-08-23 12:46 ` Jörn Engel 2005-08-23 12:52 ` David Woodhouse 0 siblings, 1 reply; 28+ messages in thread From: Jörn Engel @ 2005-08-23 12:46 UTC (permalink / raw) To: Kaigai Kohei; +Cc: dwmw2, linux-mtd, Ma Yun On Tue, 23 August 2005 19:24:31 +0900, Kaigai Kohei wrote: > > The attached patch provids XATTR support in JFFS2. > We can apply it to mtd-snapshot-20050822.tar.bz2 tree. > > I hope your worthwhile comments for improvement. > # By coincidence, two patches for similar functionality are posted in same day :) Which is quite nice. Would you two mind reading the others patch and stating what is bad at that one and why your's is better? If there is a clear winner in this batter, we should merge that one. Most likely, the result is that both patches are lacking details and a new one with elements from both should be used instead. At this moment, we just don't know yet, what those details are. Jörn -- Mundie uses a textbook tactic of manipulation: start with some reasonable talk, and lead the audience to an unreasonable conclusion. -- Bruce Perens ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-08-23 12:46 ` Jörn Engel @ 2005-08-23 12:52 ` David Woodhouse 2005-08-24 9:49 ` Kaigai Kohei 0 siblings, 1 reply; 28+ messages in thread From: David Woodhouse @ 2005-08-23 12:52 UTC (permalink / raw) To: Jörn Engel; +Cc: Ma Yun, linux-mtd, Kaigai Kohei On Tue, 2005-08-23 at 14:46 +0200, Jörn Engel wrote: > Which is quite nice. Would you two mind reading the others patch and > stating what is bad at that one and why your's is better? If there is > a clear winner in this batter, we should merge that one. I'm happier with inventing new node types for xattrs instead of re-using existing nodes, I think. And I'd also like to make sure we can share xattr data on the medium where appropriate. In an selinux system, you'll end up with a _multitude_ of xattrs used for selinux, many of which will have precisely the same contents. Each should be stored only once on the flash, like hard links. The ext2 implementation manages this. -- dwmw2 ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-08-23 12:52 ` David Woodhouse @ 2005-08-24 9:49 ` Kaigai Kohei 2005-08-25 10:28 ` Kaigai Kohei 2005-09-07 5:14 ` Kaigai Kohei 0 siblings, 2 replies; 28+ messages in thread From: Kaigai Kohei @ 2005-08-24 9:49 UTC (permalink / raw) To: David Woodhouse; +Cc: James Morris, linux-mtd, Stephen Smalley, Ma Yun Hello, Thanks for your comments. David Woodhouse wrote: > I'm happier with inventing new node types for xattrs instead of re-using > existing nodes, I think. And I'd also like to make sure we can share > xattr data on the medium where appropriate. In an selinux system, you'll > end up with a _multitude_ of xattrs used for selinux, many of which will > have precisely the same contents. Each should be stored only once on the > flash, like hard links. The ext2 implementation manages this. Indeed, it is key concept to separate the relationship between inode and XATTR from XATTR-entry which contain name/value. In my implementation, an increase/decrease of inode which shares XATTR-entry does not need to update the XATTR-entry. It's represented by an existance of XATTR-reference node. Therefore, it's possible to update less frequently. If this approach is acceptable, I'm willing to implement something to do. * Dividing the global xattr_sem semaphore into more small lock. * Indexing the XATTR-entry by its contains, for fast looking-up shared entry. and, Stephen suggested as follows: * Using generic_{set|get|list}xattr method like ext2/3. Thanks, -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-08-24 9:49 ` Kaigai Kohei @ 2005-08-25 10:28 ` Kaigai Kohei 2005-08-25 14:12 ` Jörn Engel 2005-09-07 5:14 ` Kaigai Kohei 1 sibling, 1 reply; 28+ messages in thread From: Kaigai Kohei @ 2005-08-25 10:28 UTC (permalink / raw) To: linux-mtd Cc: James Morris, Ma Yun, David Woodhouse, Stephen Smalley, Kaigai Kohei Hi, Today, I think about appropriate locking in JFFS2's XATTR. I hope to get some comments about dividing locking. > * Dividing the global xattr_sem semaphore into more small lock. xattr_sem is a global read/write semaphore stored in jffs2_sb_info to protect any XATTR structure. In common case, dividing lock/semaphore has good effects for scalability toward the number of CPUs. But JFFS2 is frequently used for small and embedded region in my understanding. If global semaphore is divided into many per-entry locks, we may be able to execute setxattr() concurrently. But each locking/un-locking cost may also exceed the scalability effect. Since I don't have much experience in this region, I hope any professional's worthwhile comments. Now, I think one global read/write semaphore is enough for 1-2 CPUs, if assumption of the XATTR processing with wrinting does not happen so much is permitted. In almost case, we can execute get/listxattr cuncurrently with one lock/un-locking, I think. Thanks, -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-08-25 10:28 ` Kaigai Kohei @ 2005-08-25 14:12 ` Jörn Engel 0 siblings, 0 replies; 28+ messages in thread From: Jörn Engel @ 2005-08-25 14:12 UTC (permalink / raw) To: Kaigai Kohei Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley, Ma Yun On Thu, 25 August 2005 19:28:42 +0900, Kaigai Kohei wrote: > > Today, I think about appropriate locking in JFFS2's XATTR. > I hope to get some comments about dividing locking. > > >* Dividing the global xattr_sem semaphore into more small lock. > > xattr_sem is a global read/write semaphore stored in jffs2_sb_info > to protect any XATTR structure. > In common case, dividing lock/semaphore has good effects for scalability > toward the number of CPUs. But JFFS2 is frequently used for small > and embedded region in my understanding. > > If global semaphore is divided into many per-entry locks, we may be able > to execute setxattr() concurrently. But each locking/un-locking cost > may also exceed the scalability effect. > Since I don't have much experience in this region, I hope any professional's > worthwhile comments. > > Now, I think one global read/write semaphore is enough for 1-2 CPUs, > if assumption of the XATTR processing with wrinting does not happen > so much is permitted. In almost case, we can execute get/listxattr > cuncurrently with one lock/un-locking, I think. Your idea to go to fine-grained locking surprised me a bit when I first saw it. JFFS2 is hardly the fs that cares much about SMP scalability. Unless you can prove real contentions around the xattr_lock in realistic benchmarks, I would ignore this issue. Correctness is another issue. I guess you have read fs/jffs2/README.Locking and started a graph on paper to visualize locking order. This is more important, as a deadlock is about the worst performance you can get. ;) Jörn -- He that composes himself is wiser than he that composes a book. -- B. Franklin ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-08-24 9:49 ` Kaigai Kohei 2005-08-25 10:28 ` Kaigai Kohei @ 2005-09-07 5:14 ` Kaigai Kohei 2005-09-08 19:49 ` Jörn Engel 1 sibling, 1 reply; 28+ messages in thread From: Kaigai Kohei @ 2005-09-07 5:14 UTC (permalink / raw) To: linux-mtd Cc: James Morris, Ma Yun, David Woodhouse, Stephen Smalley, Kaigai Kohei [-- Attachment #1: Type: text/plain, Size: 2444 bytes --] Hello, This attached patch enables XATTR support in JFFS2. (take-2) # The contribution has slowed due to the trouble related to GC :-( The my first patch posted in Aug 23 is revised at the points as follows. - get/setxattr are implemented as a method of generic XATTR handler. - new methods for "user.*" and "trusted.*" prefix were added. - Time to retrieve the duplicate XATTR-cache shortened, when setxattr(). In first implementation, we must scan all XATTR-cache to find the duplicate one, and it's so expensive. Now, we cau use the hash-list of XATTR-cache which is indexed by own contents. - Now, Garbage Collector can deal JFFS2_NODETYPE_XATTR and JFFS2_NODETYPE_XREF. It seems to me that GC can mark unconditionally any nodes without inode as obsolete. But new JFFS2_NODETYPE_XATTR/XREF should be an exception. GC must not mark them as obsolete, although their "next_in_ino" is NULL. ----[ jffs2_garbage_collect_pass() in gc.c ]------------------------ 249 if (!raw->next_in_ino) { <-- unconditionally mark it as obsolete. 250 /* Inode-less node. Clean marker, snapshot or something like that */ 251 /* FIXME: If it's something that needs to be copied, including something 252 we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ 253 spin_unlock(&c->erase_completion_lock); 254 jffs2_mark_node_obsolete(c, raw); 255 up(&c->alloc_sem); 256 goto eraseit_lock; 257 } -------------------------------------------------------------------- I added a new variable 'owner' into jffs2_raw_node_ref to resolve this. It refers jffs2_xattr_cache or jffs2_xattr_ref object when raw-node reflect XATTR-node. Thus, we can distinguish XATTR-node and others by whether 'owner' is NULL or not. - Consistency has improved. check_xattr_ref_ilist() checks XATTRs which are binded to same inode. If multiple XATTR has same name exist, older one is marked as obsolete. In first implementation, there are possibilities to be created multiple XATTRs have same name in an inode, when system-crash happens durling setxattr() The remaining work is as follows: - Test & Stabilization. - The implementation for POSIX ACL support. - The implementation for XATTR support in mkfs.jffs2. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Thanks, -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: jffs2_xattr_take-2.patch --] [-- Type: text/x-patch, Size: 61039 bytes --] diff -rpNU3 mtd-0829/fs/Kconfig mtd-0829.xattr/fs/Kconfig --- mtd-0829/fs/Kconfig 2005-05-09 18:00:10.000000000 -0400 +++ mtd-0829.xattr/fs/Kconfig 2005-09-06 10:30:52.000000000 -0400 @@ -64,6 +64,22 @@ config JFFS2_FS_WRITEBUFFER - NOR flash with transparent ECC - DataFlash +config JFFS2_XATTR + bool "JFFS2 XATTR support (EXPERIMENTAL)" + depends on JFFS2_FS + default n + help + This enables the XATTR support in JFFS2. + + It's neccesary for persistent security context in SELinux. + Now, we can use the following kinds of prefix. + + - security.* + - user.* + - trust.* + + - system.posix_acl_{access|default} are in TODO list now. + config JFFS2_COMPRESSION_OPTIONS bool "Advanced compression options for JFFS2" default n diff -rpNU3 mtd-0829/fs/jffs2/Makefile.common mtd-0829.xattr/fs/jffs2/Makefile.common --- mtd-0829/fs/jffs2/Makefile.common 2005-07-17 18:00:13.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/Makefile.common 2005-09-06 10:31:26.000000000 -0400 @@ -12,6 +12,7 @@ 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_XATTR) += xattr.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff -rpNU3 mtd-0829/fs/jffs2/build.c mtd-0829.xattr/fs/jffs2/build.c --- mtd-0829/fs/jffs2/build.c 2005-07-30 18:00:11.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/build.c 2005-09-06 10:28:53.000000000 -0400 @@ -165,6 +165,7 @@ static int jffs2_build_filesystem(struct ic->scan_dents = NULL; cond_resched(); } + jffs2_build_xattr_caches(c); c->flags &= ~JFFS2_SB_FLAG_BUILDING; D1(printk(KERN_DEBUG "Pass 3 complete\n")); @@ -184,6 +185,7 @@ exit: jffs2_free_full_dirent(fd); } } + jffs2_clear_xattr_caches(c); } return ret; diff -rpNU3 mtd-0829/fs/jffs2/dir.c mtd-0829.xattr/fs/jffs2/dir.c --- mtd-0829/fs/jffs2/dir.c 2005-08-17 18:00:11.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/dir.c 2005-09-06 10:28:53.000000000 -0400 @@ -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 }; /***********************************************************************/ diff -rpNU3 mtd-0829/fs/jffs2/file.c mtd-0829.xattr/fs/jffs2/file.c --- mtd-0829/fs/jffs2/file.c 2005-07-06 18:00:13.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/file.c 2005-09-06 10:28:53.000000000 -0400 @@ -57,7 +57,12 @@ struct file_operations jffs2_file_operat struct inode_operations jffs2_file_inode_operations = { - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; struct address_space_operations jffs2_file_address_operations = diff -rpNU3 mtd-0829/fs/jffs2/fs.c mtd-0829.xattr/fs/jffs2/fs.c --- mtd-0829/fs/jffs2/fs.c 2005-08-06 18:00:16.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/fs.c 2005-09-06 10:28:53.000000000 -0400 @@ -217,6 +217,8 @@ 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)); + if (f->inocache && !f->inocache->nlink) + jffs2_xattr_clear_inode(c, f->inocache); jffs2_do_clear_inode(c, f); } @@ -504,6 +506,9 @@ int jffs2_do_fill_super(struct super_blo } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + if ((ret = jffs2_init_xattr_caches(c))) + goto out_inohash; + if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; @@ -537,6 +542,7 @@ int jffs2_do_fill_super(struct super_blo vfree(c->blocks); else kfree(c->blocks); + jffs2_clear_xattr_caches(c); out_inohash: kfree(c->inocache_list); out_wbuf: diff -rpNU3 mtd-0829/fs/jffs2/gc.c mtd-0829.xattr/fs/jffs2/gc.c --- mtd-0829/fs/jffs2/gc.c 2005-08-17 18:00:12.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/gc.c 2005-09-06 10:28:53.000000000 -0400 @@ -251,7 +251,12 @@ int jffs2_garbage_collect_pass(struct jf /* FIXME: If it's something that needs to be copied, including something we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ spin_unlock(&c->erase_completion_lock); - jffs2_mark_node_obsolete(c, raw); + if (raw->owner) { + /* JFFS2_NODETYPE_XATTR or JFFS2_NODETYPE_XREF */ + ret = jffs2_garbage_collect_xattr(c, raw); + } else { + jffs2_mark_node_obsolete(c, raw); + } up(&c->alloc_sem); goto eraseit_lock; } diff -rpNU3 mtd-0829/fs/jffs2/malloc.c mtd-0829.xattr/fs/jffs2/malloc.c --- mtd-0829/fs/jffs2/malloc.c 2005-07-27 18:00:18.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/malloc.c 2005-09-06 10:28:53.000000000 -0400 @@ -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_XATTR +static kmem_cache_t *xattr_cache_slab; +static kmem_cache_t *xattr_ref_slab; +#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_XATTR + xattr_cache_slab = kmem_cache_create("jffs2_xattr_cache", + sizeof(struct jffs2_xattr_cache), + 0, 0, NULL, NULL); + if (!xattr_cache_slab) + goto err; + + xattr_ref_slab = kmem_cache_create("jffs2_xattr_ref", + sizeof(struct jffs2_xattr_ref), + 0, 0, NULL, NULL); + if (!xattr_ref_slab) + 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_XATTR + if(xattr_cache_slab) + kmem_cache_destroy(xattr_cache_slab); + if(xattr_ref_slab) + kmem_cache_destroy(xattr_ref_slab); +#endif } struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) @@ -169,6 +195,7 @@ struct jffs2_raw_node_ref *jffs2_alloc_r struct jffs2_raw_node_ref *ret; ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); JFFS2_DBG_MEMALLOC("%p\n", ret); + memset(ret, 0, sizeof(*ret)); /* to guarantee owner==NULL */ return ret; } @@ -205,3 +232,35 @@ void jffs2_free_inode_cache(struct jffs2 JFFS2_DBG_MEMALLOC("%p\n", x); kmem_cache_free(inode_cache_slab, x); } + +#ifdef CONFIG_JFFS2_XATTR + +struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void) +{ + struct jffs2_xattr_cache *xc; + xc = kmem_cache_alloc(xattr_cache_slab, GFP_KERNEL); + JFFS2_DBG_MEMALLOC("%p\n", xc); + return xc; +} + +void jffs2_free_xattr_cache(struct jffs2_xattr_cache *xc) +{ + JFFS2_DBG_MEMALLOC("%p\n", xc); + kmem_cache_free(xattr_cache_slab, xc); +} + +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) +{ + struct jffs2_xattr_ref *ref; + ref = kmem_cache_alloc(xattr_ref_slab, GFP_KERNEL); + JFFS2_DBG_MEMALLOC("%p\n", ref); + return ref; +} + +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref) +{ + JFFS2_DBG_MEMALLOC("%p\n", ref); + kmem_cache_free(xattr_ref_slab, ref); +} + +#endif diff -rpNU3 mtd-0829/fs/jffs2/nodelist.c mtd-0829.xattr/fs/jffs2/nodelist.c --- mtd-0829/fs/jffs2/nodelist.c 2005-08-22 18:00:15.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/nodelist.c 2005-09-06 10:28:53.000000000 -0400 @@ -934,6 +934,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; } diff -rpNU3 mtd-0829/fs/jffs2/nodelist.h mtd-0829.xattr/fs/jffs2/nodelist.h --- mtd-0829/fs/jffs2/nodelist.h 2005-08-17 18:00:12.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/nodelist.h 2005-09-06 10:28:53.000000000 -0400 @@ -20,6 +20,7 @@ #include <linux/jffs2.h> #include <linux/jffs2_fs_sb.h> #include <linux/jffs2_fs_i.h> +#include <linux/jffs2_fs_x.h> #ifdef __ECOS #include "os-ecos.h" @@ -80,6 +81,7 @@ struct jffs2_raw_node_ref struct jffs2_raw_node_ref *next_phys; uint32_t flash_offset; uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ + void *owner; }; /* flash_offset & 3 always has to be zero, because nodes are @@ -111,6 +113,10 @@ struct jffs2_inode_cache { uint32_t ino; int nlink; int state; +#ifdef CONFIG_JFFS2_XATTR + struct list_head ilist; + uint8_t ilist_checked; +#endif }; /* Inode states for 'state' above. We need the 'GC' state to prevent @@ -367,6 +373,13 @@ void jffs2_free_node_frag(struct jffs2_n struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); void jffs2_free_inode_cache(struct jffs2_inode_cache *); +#ifdef CONFIG_JFFS2_XATTR +struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void); +void jffs2_free_xattr_cache(struct jffs2_xattr_cache *); +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void); +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *); +#endif /* CONFIG_JFFS2_XATTR */ + /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); diff -rpNU3 mtd-0829/fs/jffs2/readinode.c mtd-0829.xattr/fs/jffs2/readinode.c --- mtd-0829/fs/jffs2/readinode.c 2005-08-17 18:00:12.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/readinode.c 2005-09-06 10:28:53.000000000 -0400 @@ -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; + initxattr_jffs2_inode_cache(f->inocache); jffs2_add_ino_cache(c, f->inocache); } if (!f->inocache) { diff -rpNU3 mtd-0829/fs/jffs2/scan.c mtd-0829.xattr/fs/jffs2/scan.c --- mtd-0829/fs/jffs2/scan.c 2005-07-20 18:00:12.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/scan.c 2005-09-06 10:28:53.000000000 -0400 @@ -57,6 +57,12 @@ static int jffs2_scan_inode_node(struct struct jffs2_raw_inode *ri, uint32_t ofs); static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs); +#ifdef CONFIG_JFFS2_XATTR +static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_xattr *rx, uint32_t ofs); +static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_xref *rr, uint32_t ofs); +#endif #define BLK_STATE_ALLFF 0 #define BLK_STATE_CLEAN 1 @@ -552,7 +558,40 @@ scan_more: if (err) return err; ofs += PAD(je32_to_cpu(node->totlen)); break; - +#ifdef CONFIG_JFFS2_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); + 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); + if (err) + return err; + ofs += PAD(je32_to_cpu(node->totlen)); + break; +#endif 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) { @@ -665,6 +704,7 @@ static struct jffs2_inode_cache *jffs2_s ic->ino = ino; ic->nodes = (void *)ic; + initxattr_jffs2_inode_cache(ic); jffs2_add_ino_cache(c, ic); if (ino == 1) ic->nlink = 1; @@ -820,6 +860,190 @@ static int jffs2_scan_dirent_node(struct return 0; } +#ifdef CONFIG_JFFS2_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_raw_node_ref *raw; + struct jffs2_xattr_cache *xc; + uint32_t crc; + int xtype, nlen, vlen; + + crc = crc32(0, rx, sizeof(*rx)-8); + if (crc != je32_to_cpu(rx->node_crc)) { + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Node CRC failed" + " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rx->node_crc), crc); + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); + return 0; + } + + nlen = JFFS2_XATTR_NAMELEN(je32_to_cpu(rx->length)); + vlen = JFFS2_XATTR_VALUELEN(je32_to_cpu(rx->length)); + if (je32_to_cpu(rx->totlen) != sizeof(*rx)+PAD(nlen+1)+PAD(vlen)) { + printk(KERN_NOTICE "jffs2_scan_xattr_node(): length mimatch!" + " totlen=%d length=0x%08x nlen=%d vlen=%d on 0x%08x\n", + je32_to_cpu(rx->totlen), je32_to_cpu(rx->length), nlen, vlen, ofs); + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); + return 0; + } + + crc = crc32(0, rx->data, PAD(nlen+1)+PAD(vlen)); + if (crc != je32_to_cpu(rx->data_crc)) { + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Data CRC failed" + " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rx->data_crc), crc); + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); + return 0; + } + + xc = jffs2_find_xattr_cache_xid(c, je32_to_cpu(rx->xid)); + if (xc) { + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Duplicate XID Found" + " on node at 0x%08x XID=%d Later one ignored\n", ofs, xc->xid); + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); + return 0; + } + + xtype = jffs2_xattr_prefix_to_xtype(rx->data, nlen); + if (JFFS2_XATTRTYPE_INVALID==xtype) { + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Unsupported xattr type" + " on node at 0x%08x\n", ofs); + USED_SPACE(PAD(je32_to_cpu(rx->totlen))); + return 0; + } + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) + return -ENOMEM; + + xc = jffs2_alloc_xattr_cache(); + if (!xc) { + jffs2_free_raw_node_ref(raw); + return -ENOMEM; + } + initxattr_jffs2_xattr_cache(xc); + + xc->xid = je32_to_cpu(rx->xid); + if (xc->xid > c->highest_xid) + c->highest_xid = xc->xid; + xc->xtype = xtype; + xc->node = raw; + xc->name_len = nlen; + xc->value_len = vlen; + xc->data_crc = crc; + + raw->__totlen = PAD(je32_to_cpu(rx->totlen)); + raw->flash_offset = ofs | REF_PRISTINE; + raw->next_phys = NULL; + raw->next_in_ino = NULL; + 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))); + + jffs2_attach_xattr_cache(c, xc); + + D1(printk(KERN_NOTICE "XATTR_CACHE: XID=%d inserted\n", xc->xid)); + return 0; +} + +static inline int jffs2_scan_xref_node_helper(struct jffs2_sb_info *c, + struct jffs2_xattr_ref *new, struct jffs2_xattr_ref *old) +{ + /* (uint32_t)new->xc == (uint32_t)old->xc has been made sure. */ + if ((uint32_t)new->ic == (uint32_t)old->ic) { + if (new->seqno > old->seqno) { + jffs2_mark_node_obsolete(c, old->node); + old->seqno = new->seqno; + old->node = new->node; + } else { + jffs2_mark_node_obsolete(c, new->node); + } + return 1; /* 'new' should be released. */ + } + 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_raw_node_ref *raw; + struct jffs2_xattr_ref *ref, *pos, *pos2; + uint32_t crc; + + crc = crc32(0, rr, sizeof(*rr)-4); + if (crc != je32_to_cpu(rr->node_crc)) { + printk(KERN_NOTICE "jffs2_scan_xref_node(): Node CRC failed" + " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rr->node_crc), crc); + DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen))); + return 0; + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return -ENOMEM; + initxattr_jffs2_xattr_ref(ref); + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + jffs2_free_xattr_ref(ref); + return -ENOMEM; + } + + ref->seqno = je32_to_cpu(rr->seqno); + if (ref->seqno > c->highest_seqno) + c->highest_seqno = ref->seqno; + ref->ic = (void *)je32_to_cpu(rr->ino); + ref->xc = (void *)je32_to_cpu(rr->xid); + + raw->__totlen = PAD(je32_to_cpu(rr->totlen)); + raw->flash_offset = ofs | REF_PRISTINE; + raw->next_phys = NULL; + raw->next_in_ino = NULL; + ref->node = raw; + + if (!jeb->first_node) + jeb->first_node = raw; + if (jeb->last_node) + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + + /* chaining to x->xattr_temp */ + list_for_each_entry(pos, &c->xattr_temp, xlist) { + if ((uint32_t)pos->xc == (uint32_t)ref->xc) { + /* Duplicate Checking */ + if (jffs2_scan_xref_node_helper(c, ref, pos)) { + jffs2_free_xattr_ref(ref); + DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen))); + return 0; + } + list_for_each_entry(pos2, &pos->ilist, ilist) { + if (jffs2_scan_xref_node_helper(c, ref, pos2)) { + jffs2_free_xattr_ref(ref); + DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen))); + return 0; + } + } + /* No Duplication Nodes exist */ + list_add_tail(&ref->ilist, &pos->ilist); + USED_SPACE(PAD(je32_to_cpu(rr->totlen))); + D1(printk(KERN_NOTICE "XATTR_REF: XID=%d INO=%d inserted\n", + (uint32_t)ref->xc, (uint32_t)ref->ic)); + return 0; + } + } + list_add_tail(&ref->xlist, &c->xattr_temp); + USED_SPACE(PAD(je32_to_cpu(rr->totlen))); + D1(printk(KERN_NOTICE "XATTR_REF: XID=%d INO=%d inserted\n", + (uint32_t)ref->xc, (uint32_t)ref->ic)); + return 0; +} +#endif /* CONFIG_JFFS2_XATTR */ + static int count_list(struct list_head *l) { uint32_t count = 0; diff -rpNU3 mtd-0829/fs/jffs2/super.c mtd-0829.xattr/fs/jffs2/super.c --- mtd-0829/fs/jffs2/super.c 2005-07-12 18:00:11.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/super.c 2005-09-06 10:28:53.000000000 -0400 @@ -151,6 +151,7 @@ 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; ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); @@ -290,6 +291,7 @@ static void jffs2_put_super (struct supe kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); + jffs2_clear_xattr_caches(c); if (c->mtd->sync) c->mtd->sync(c->mtd); diff -rpNU3 mtd-0829/fs/jffs2/symlink.c mtd-0829.xattr/fs/jffs2/symlink.c --- mtd-0829/fs/jffs2/symlink.c 2005-07-17 18:00:20.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/symlink.c 2005-09-06 10:28:53.000000000 -0400 @@ -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 int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) diff -rpNU3 mtd-0829/fs/jffs2/write.c mtd-0829.xattr/fs/jffs2/write.c --- mtd-0829/fs/jffs2/write.c 2005-08-17 18:00:12.000000000 -0400 +++ mtd-0829.xattr/fs/jffs2/write.c 2005-09-06 10:28:53.000000000 -0400 @@ -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; - + initxattr_jffs2_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)); diff -rpNU3 mtd-0829/fs/jffs2/xattr.c mtd-0829.xattr/fs/jffs2/xattr.c --- mtd-0829/fs/jffs2/xattr.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-0829.xattr/fs/jffs2/xattr.c 2005-09-06 10:28:53.000000000 -0400 @@ -0,0 +1,1170 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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" + +/* ---- decralation of internal use functions ----------------- */ +static uint32_t jffs2_xattr_cache_hashkey(int xtype, const char *xname, const char *xvalue, int xsize); +static void reclaim_xattr_cache(struct jffs2_sb_info *c); +static void unload_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc); + +static int create_xattr_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, + int xtype, const char *xname, const char *xvalue, int xsize); +static void delete_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc); + +static struct jffs2_xattr_ref * +create_xattr_ref(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_xattr_cache *xc); +static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref); + +static int do_jffs2_getxattr(struct inode *inode, int xtype, const char *xname, + char *buffer, size_t size); +static int do_jffs2_setxattr(struct inode *inode, int xtype, const char *xname, + const char *buffer, size_t size, int flags); + +/* ---- XATTR Handler for "user.*" --------------------- */ +static int jffs2_user_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + int ret; + + if (!strcmp(name, "")) + return -EINVAL; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + return do_jffs2_getxattr(inode, JFFS2_XATTRTYPE_USER, name, buffer, size); +} + +static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags) +{ + int ret; + + if (!strcmp(name, "") == 0) + return -EINVAL; + + if ( !S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + return do_jffs2_setxattr(inode, JFFS2_XATTRTYPE_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) +{ + int ret; + + if (!strcmp(name, "")) + return -EINVAL; + ret = permission(inode, MAY_READ, NULL); + if (ret) + return ret; + + return 1; /* permit to copy */ +} + +static struct xattr_handler jffs2_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .list = jffs2_user_listxattr, + .set = jffs2_user_setxattr, + .get = jffs2_user_getxattr +}; + +/* ---- XATTR Handler for "trusted.*" ------------------ */ +static int jffs2_trusted_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + return do_jffs2_getxattr(inode, JFFS2_XATTRTYPE_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; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + return do_jffs2_setxattr(inode, JFFS2_XATTRTYPE_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) +{ + if (!capable(CAP_SYS_ADMIN)) + return 0; /* ignore this entry */ + return 1; +} + +static struct xattr_handler jffs2_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .list = jffs2_trusted_listxattr, + .set = jffs2_trusted_setxattr, + .get = jffs2_trusted_getxattr +}; + +/* ---- 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_XATTRTYPE_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_XATTRTYPE_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) +{ + return 1; /* permit to copy */ +} + +static struct xattr_handler jffs2_security_xattr_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .list = jffs2_security_listxattr, + .set = jffs2_security_setxattr, + .get = jffs2_security_getxattr +}; +/* ----------------------------------------------------- */ +struct xattr_handler *jffs2_xattr_handlers[] = { + &jffs2_user_xattr_handler, + &jffs2_security_xattr_handler, + &jffs2_trusted_xattr_handler, + NULL +}; + +/* ---- Build-up and Destruct XATTR-Cache functions ----------- */ +int jffs2_init_xattr_caches(struct jffs2_sb_info *c) +{ + int i; + + c->xattrcache = kmalloc((XATTRCACHE_HASHSIZE+1)*sizeof(struct list_head), GFP_KERNEL); + if (!c->xattrcache) + return -ENOMEM; + + init_rwsem(&c->xattr_sem); + + for (i=0; i<=XATTRCACHE_HASHSIZE; i++) + INIT_LIST_HEAD(&c->xattrcache[i]); + + c->xcache_mem_threshold = 64 * 1024; /* Default 64KB */ + + return 0; +} + +void jffs2_clear_xattr_caches(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_cache *xc, *_xc; + struct jffs2_xattr_ref *ref, *_ref; + int i; + + if (!c->xattrcache) + return; /* not initialized yet */ + + if ( !(c->flags & (JFFS2_SB_FLAG_SCANNING|JFFS2_SB_FLAG_BUILDING)) ) + goto free_xcache; + + list_for_each_entry_safe(ref, _ref, &c->xattr_temp, xlist) + jffs2_free_xattr_ref(ref); + + for (i=0; i<XATTRCACHE_HASHSIZE; i++) { + list_for_each_entry_safe(xc, _xc, &c->xattrcache[i], xcache) { + list_del(&xc->xcache); + list_for_each_entry_safe(ref, _ref, &xc->xlist, xlist) { + list_del(&ref->xlist); + list_del(&ref->ilist); + jffs2_free_xattr_ref(ref); + } + if (xc->xname) + kfree(xc->xname); + jffs2_free_xattr_cache(xc); + } + } + + free_xcache: + kfree(c->xattrcache); + c->xattrcache = NULL; +} + +void jffs2_build_xattr_caches(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_ref *xref, *_xref, *iref, *_iref; + struct jffs2_inode_cache *ic; + struct jffs2_xattr_cache *xc, *_xc; + uint32_t xid, ino; + struct list_head temp_list; + int i; + + BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING)); + + INIT_LIST_HEAD(&temp_list); + + /* Phase.1 */ + list_for_each_entry_safe(xref, _xref, &c->xattr_temp, xlist) { + list_del_init(&xref->xlist); + list_add(&temp_list, &xref->ilist); + + xid = (uint32_t)xref->xc; + + xc = jffs2_find_xattr_cache_xid(c, xid); + if (!xc) { + printk(KERN_NOTICE "xid = %d is not found on xattr_cache\n", xid); + list_for_each_entry_safe(iref, _iref, &temp_list, ilist) { + list_del(&iref->ilist); + jffs2_mark_node_obsolete(c, iref->node); + jffs2_free_xattr_ref(iref); + } + continue; + } + + list_for_each_entry_safe(iref, _iref, &temp_list, ilist) { + list_del_init(&iref->ilist); + + BUG_ON(xid!=(uint32_t)iref->xc); + + ino = (uint32_t)iref->ic; + ic = jffs2_get_ino_cache(c, ino); + if (!ic) { + printk(KERN_NOTICE "ino = %d is not found on inode_cache\n", ino); + jffs2_mark_node_obsolete(c, iref->node); + jffs2_free_xattr_ref(iref); + continue; + } + iref->ic = ic; + iref->xc = xc; + list_add_tail(&iref->ilist, &ic->ilist); + list_add_tail(&iref->xlist, &xc->xlist); + D2(printk(KERN_NOTICE "Bind XATTR-REF(xid=%d, ino=%d)\n", xc->xid, ic->ino)); + } + BUG_ON(!list_empty(&temp_list)); + } + /* Phase.2 */ + for (i=0; i<XATTRCACHE_HASHSIZE; i++) { + list_for_each_entry_safe(xc, _xc, &c->xattrcache[i], xcache) { + list_del_init(&xc->xcache); + if (list_empty(&xc->xlist)) { + printk(KERN_NOTICE "Unrefered XATTR-Cache found XID=%d\n", xc->xid); + jffs2_free_xattr_cache(xc); + } + } + } + /* After this steps, xattrcache is used as index of XATTR-entry */ +} + +struct jffs2_xattr_cache *jffs2_find_xattr_cache_xid(struct jffs2_sb_info *c, uint32_t xid) +{ + struct jffs2_xattr_cache *xc; + int i = xid % XATTRCACHE_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(xc, &c->xattrcache[i], xcache) { + if (xc->xid==xid) + return xc; + } + return NULL; +} + +void jffs2_attach_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc) +{ + int i = xc->xid % XATTRCACHE_HASHSIZE; + + /* It's only used in scanning process. */ + BUG_ON(!(c->flags & JFFS2_SB_FLAG_SCANNING)); + + list_add(&xc->xcache, &c->xattrcache[i]); +} + +/*---- internal utility functions -------------------------------------------------*/ +struct +{ + int xtype; + int length; + char *prefix; + struct xattr_handler *handler; +} __xattr_prefix[] = { + [JFFS2_XATTRTYPE_USER] = {JFFS2_XATTRTYPE_USER, sizeof(XATTR_USER_PREFIX)-1, + XATTR_USER_PREFIX, &jffs2_user_xattr_handler}, + [JFFS2_XATTRTYPE_SECURITY] = {JFFS2_XATTRTYPE_SECURITY, sizeof(XATTR_SECURITY_PREFIX)-1, + XATTR_SECURITY_PREFIX, &jffs2_security_xattr_handler}, + [JFFS2_XATTRTYPE_TRUSTED] = {JFFS2_XATTRTYPE_TRUSTED, sizeof(XATTR_TRUSTED_PREFIX)-1, + XATTR_TRUSTED_PREFIX, &jffs2_trusted_xattr_handler}, + [JFFS2_XATTRTYPE_INVALID] = {JFFS2_XATTRTYPE_INVALID, 0, NULL, NULL} +}; + +char *jffs2_xattr_xtype_to_prefix(int xtype) +{ + if (xtype>0 && xtype<JFFS2_XATTRTYPE_INVALID) + return __xattr_prefix[xtype].prefix; + return NULL; +} + +int jffs2_xattr_prefix_to_xtype(const char *xname, int nlen) +{ + int i; + + for (i=1; i<JFFS2_XATTRTYPE_INVALID; i++) { + if (!__xattr_prefix[i].prefix || __xattr_prefix[i].length>nlen) + continue; + if (!strncmp(__xattr_prefix[i].prefix, xname, __xattr_prefix[i].length)) + return __xattr_prefix[i].xtype; + } + return JFFS2_XATTRTYPE_INVALID; +} + +int jffs2_xattr_xtype_to_length(int xtype) +{ + if (xtype>0 && xtype<JFFS2_XATTRTYPE_INVALID) { + if (!__xattr_prefix[xtype].length) + return -EINVAL; + return __xattr_prefix[xtype].length; + } + return -EINVAL; +} + +struct xattr_handler *jffs2_xattr_xtype_to_handler(int xtype) +{ + if (xtype>0 && xtype<JFFS2_XATTRTYPE_INVALID) + return __xattr_prefix[xtype].handler; + return NULL; +} + +static uint32_t jffs2_xattr_cache_hashkey(int xtype, const char *xname, const char *xvalue, int xsize) +{ + int name_len = strlen(xname); + + return crc32(xtype, xname, name_len) ^ crc32(xtype, xvalue, xsize); +} + +/* ---- Internal XATTR related functions ------------------------------------ */ +static void reclaim_xattr_cache(struct jffs2_sb_info *c) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_cache *xc, *_xc; + int target, before; + static int index = 0; + + before = c->xcache_mem_usage; + target = c->xcache_mem_threshold * 4 / 5; /* 20% reduction */ + while(1) { + list_for_each_entry_safe(xc, _xc, &c->xattrcache[index], xcache) { + if (xc->flags & JFFS2_XCACHE_FLAGS_ACTIVE) { + xc->flags &= ~JFFS2_XCACHE_FLAGS_ACTIVE; + } else { + unload_xattr_cache(c, xc); + } + if (c->xcache_mem_usage <= target) + goto out; + } + index = (index+1) % XATTRCACHE_HASHSIZE; + } + out: + printk(KERN_NOTICE "JFFS2: reclaim_xattr_cache() before: %d Byte after: %d Byte\n", + before, c->xcache_mem_usage); +} + +static int do_load_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc) +{ + /* must be called under down_write(xattr_sem) */ + char *data; + size_t readlen; + uint32_t crc; + int i, ret, length, retry = 0; + + D2(printk(KERN_NOTICE "do_load_xattr_cache(xid=%d xtype=%d)\n", xc->xid, xc->xtype)); + + retry: + length = PAD(xc->name_len+1) + PAD(xc->value_len); + data = kmalloc(length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* c->alloc_sem should be hold when xc->node is refered */ + down(&c->alloc_sem); + + BUG_ON(!xc->node); + ret = jffs2_flash_read(c, ref_offset(xc->node)+sizeof(struct jffs2_raw_xattr), + length, &readlen, data); + up(&c->alloc_sem); + + if (ret || length!=readlen) { + kfree(data); + return ret ? ret : -EIO; + } + + memset(data+xc->name_len, 0, PAD(xc->name_len+1)-xc->name_len); + memset(data+PAD(xc->name_len+1) + xc->value_len, 0, PAD(xc->value_len)-xc->value_len); + + crc = crc32(0, data, length); + if (crc != xc->data_crc) { + kfree(data); + return -EIO; + } + + if (jffs2_xattr_prefix_to_xtype(data, xc->name_len) != xc->xtype) { + kfree(data); + return -EIO; + } + + xc->flags = JFFS2_XCACHE_FLAGS_ACTIVE; + + xc->xname = data; + xc->xvalue = data+PAD(xc->name_len+1); + c->xcache_mem_usage += length; + + xc->hashkey = jffs2_xattr_cache_hashkey(xc->xtype, xc->xname, xc->xvalue, xc->value_len); + i = xc->hashkey % XATTRCACHE_HASHSIZE; + list_add(&xc->xcache, &c->xattrcache[i]); + + if (c->xcache_mem_usage > c->xcache_mem_threshold && retry==0) { + reclaim_xattr_cache(c); + if (!xc->xname) { + retry = 1; + goto retry; + } + } + + return 0; +} + +static inline int load_xattr_cache_nolock(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc) +{ + /* must be called under down_write(xattr_sem); */ + if (unlikely(!xc->xname)) + return do_load_xattr_cache(c, xc); + return 0; +} + +static inline int load_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc) +{ + /* must be called under down_read(xattr_sem); */ + int ret; + + if (likely(xc->xname)) + return 0; + + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + ret = load_xattr_cache_nolock(c, xc); + downgrade_write(&c->xattr_sem); + + return ret; +} + +static void unload_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc) +{ + /* must be called under down_write(xattr_sem) */ + D2(printk(KERN_NOTICE "do_unload_xattr_cache(xid=%d xtype=%d)\n", xc->xid, xc->xtype)); + + if (xc->xname) { + c->xcache_mem_usage -= (PAD(xc->name_len+1)+PAD(xc->value_len)); + kfree(xc->xname); + } + + list_del_init(&xc->xcache); + xc->hashkey = 0; + xc->xname = NULL; + xc->xvalue = NULL; +} + +static int save_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc) +{ + /* must be called under down_write(xattr_sem) */ + /* gc=1, if called from GC */ + + struct jffs2_raw_xattr rx; + struct jffs2_raw_node_ref *raw; + struct kvec vecs[2]; + uint32_t length, phys_ofs; + int ret, totlen; + + BUG_ON(!xc->xname); + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) + return -ENOMEM; + + vecs[0].iov_base = ℞ + vecs[0].iov_len = PAD(sizeof(rx)); + vecs[1].iov_base = xc->xname; + vecs[1].iov_len = PAD(xc->name_len+1)+PAD(xc->value_len); + totlen = vecs[0].iov_len + vecs[1].iov_len; + + /* Setup raw-xattr */ + rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); + rx.totlen = cpu_to_je32(totlen); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node)-4)); + + rx.xid = cpu_to_je32(xc->xid); + rx.length = cpu_to_je32(JFFS2_XATTR_LENGTH(xc->name_len, xc->value_len)); + rx.data_crc = cpu_to_je32(crc32(0, xc->xname, vecs[1].iov_len)); + rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr)-8)); + + ret = jffs2_reserve_space(c, totlen, &phys_ofs, &length, ALLOC_NORMAL); + if (ret) { + printk("%s:%d jffs2_reserve_space() = %d phys_ofs = %08x totlen = %d length = %d\n", + __FILE__, __LINE__, ret, phys_ofs, totlen, length); + jffs2_free_raw_node_ref(raw); + return ret; + } + raw->flash_offset = phys_ofs; + raw->__totlen = vecs[0].iov_len + vecs[1].iov_len; + raw->next_phys = NULL; + raw->next_in_ino = NULL; + raw->owner = xc; + + ret = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); + if (ret || totlen!=length) { + printk(KERN_NOTICE "%s:%d Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", + __FILE__, __LINE__, totlen, phys_ofs, ret, length); + ret = ret ? ret : -EIO; + if (length) { + raw->flash_offset |= REF_OBSOLETE; + jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + jffs2_free_raw_node_ref(raw); + } + jffs2_complete_reservation(c); + return ret; + } + /* success */ + raw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, raw); + if (xc->node) { + xc->node->owner = NULL; + jffs2_mark_node_obsolete(c, xc->node); + } + + xc->node = raw; + + jffs2_complete_reservation(c); + + return 0; +} + +static int create_xattr_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, + int xtype, const char *xname, const char *xvalue, int xsize) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_cache *xc; + struct jffs2_xattr_ref *ref; + uint32_t hashkey; + int i, ret, prlen, nlen, dlen; + char *data, *prefix; + + /* Index search */ + if ((prlen=jffs2_xattr_xtype_to_length(xtype))<0) + return -EINVAL; + + hashkey = jffs2_xattr_cache_hashkey(xtype, xname, xvalue, xsize); + i = hashkey % XATTRCACHE_HASHSIZE; + list_for_each_entry(xc, &c->xattrcache[i], xcache) { + if (xc->hashkey==hashkey + && xc->xtype==xtype + && xc->value_len==xsize + && !strcmp(xc->xname+prlen, xname) + && !memcmp(xc->xvalue, xvalue, xsize)) { + /* Shared-XATTR found! */ + D2(printk(KERN_NOTICE "Shared XATTR found XID=%d\n", xc->xid)); + ref = create_xattr_ref(c, ic, xc); + return IS_ERR(ref) ? PTR_ERR(ref) : 0; + } + } + + /* Not found, Create NEW XATTR-Cache */ + if (!(prefix=jffs2_xattr_xtype_to_prefix(xtype))) + return -EINVAL; + + nlen = prlen + strlen(xname); + dlen = PAD(nlen+1)+PAD(xsize); + + xc = jffs2_alloc_xattr_cache(); + if (!xc) + return -ENOMEM; + initxattr_jffs2_xattr_cache(xc); + + data = kmalloc(dlen, GFP_KERNEL); + if (!data) { + jffs2_free_xattr_cache(xc); + return -ENOMEM; + } + memset(data, 0, dlen); + strcpy(data, prefix); + strcpy(data+prlen, xname); + memcpy(data+PAD(nlen+1), xvalue, xsize); + + xc->xid = ++c->highest_xid; + xc->xtype = xtype; + xc->flags = JFFS2_XCACHE_FLAGS_ACTIVE; + xc->data_crc = crc32(0, data, dlen); + + xc->hashkey = hashkey; + xc->xname = data; + xc->name_len = nlen; + xc->xvalue = data + PAD(nlen+1); + xc->value_len = xsize; + + /* Create XREF */ + ref = create_xattr_ref(c, ic, xc); + if (IS_ERR(ref)) { + kfree(data); + jffs2_free_xattr_cache(xc); + return PTR_ERR(ref); + } + + ret = save_xattr_cache(c, xc); + if (ret) + delete_xattr_ref(c, ref); + + /* Insert Hash Index */ + i = hashkey % XATTRCACHE_HASHSIZE; + list_add(&xc->xcache, &c->xattrcache[i]); + + c->xcache_mem_usage += (PAD(xc->name_len+1)+PAD(xc->value_len)); + + if (c->xcache_mem_usage > c->xcache_mem_threshold) + reclaim_xattr_cache(c); + + return ret; +} + +static void delete_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc) +{ + /* must be called under down_write(xattr_sem) and down(alloc_sem) */ + BUG_ON(!list_empty(&xc->xlist)); + + unload_xattr_cache(c, xc); + if (xc->node) { + xc->node->owner = NULL; + jffs2_mark_node_obsolete(c, xc->node); + } + jffs2_free_xattr_cache(xc); +} + +static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + /* must be called under down_write(xattr_sem) */ + /* gc=1, if called from GC */ + struct jffs2_raw_node_ref *raw; + struct jffs2_raw_xref rr; + uint32_t phys_ofs, length; + int ret; + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) + return -ENOMEM; + + ret = jffs2_reserve_space(c, sizeof(rr), &phys_ofs, &length, ALLOC_NORMAL); + if (ret) { + printk(KERN_NOTICE "%s:%d jffs2_reserve_space() = %d length =%d sizeof(rr) = %d\n", + __FILE__, __LINE__, ret, length, sizeof(rr)); + return ret; + } + + rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rr.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); + rr.totlen = cpu_to_je32(sizeof(rr)); + rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node)-4)); + + rr.seqno = cpu_to_je32(ref->seqno); + rr.ino = cpu_to_je32(ref->ic->ino); + rr.xid = cpu_to_je32(ref->xc->xid); + rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_raw_xref)-4)); + + raw->flash_offset = phys_ofs; + raw->__totlen = sizeof(rr); + raw->next_phys = NULL; + raw->next_in_ino = NULL; + raw->owner = ref; + + ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); + if (ret || sizeof(rr)!=length) { + printk(KERN_NOTICE "%s:%d Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + __FILE__, __LINE__, sizeof(rr), phys_ofs, ret, length); + ret = ret ? ret : -EIO; + if (length) { + raw->flash_offset |= REF_OBSOLETE; + jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + jffs2_free_raw_node_ref(raw); + } + jffs2_complete_reservation(c); + return ret; + } + raw->flash_offset |= REF_PRISTINE; + + jffs2_add_physical_node_ref(c, raw); + if (ref->node) { + ref->node->owner = NULL; + jffs2_mark_node_obsolete(c, ref->node); + } + ref->node = raw; + + jffs2_complete_reservation(c); + + return 0; +} + +static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, + struct jffs2_xattr_cache *xc) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_ref *ref; + int ret; + + /* Duplication Check */ + list_for_each_entry(ref, &ic->ilist, ilist) { + if (ref->xc == xc) + return ERR_PTR(-EINVAL); + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return ERR_PTR(-ENOMEM); + initxattr_jffs2_xattr_ref(ref); + + ref->seqno = ++c->highest_seqno; + ref->ic = ic; + ref->xc = xc; + + ret = save_xattr_ref(c, ref); + if (ret) { + jffs2_free_xattr_ref(ref); + return ERR_PTR(ret); + } + + /* Setup Reference */ + list_add(&ref->ilist, &ic->ilist); + list_add(&ref->xlist, &xc->xlist); + + return ref; /* success */ +} + +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_cache *xc; + + + xc = ref->xc; + list_del(&ref->ilist); + list_del(&ref->xlist); + + down(&c->alloc_sem); + + BUG_ON(!ref->node); + ref->node->owner = NULL; + jffs2_mark_node_obsolete(c, ref->node); + + if (list_empty(&xc->xlist)) + delete_xattr_cache(c, xc); + up(&c->alloc_sem); + + jffs2_free_xattr_ref(ref); +} + +/* ---- External XATTR Handler ------------------------- */ +void jffs2_xattr_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_xattr_ref *ref, *_ref; + + 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() */ + struct jffs2_xattr_cache *xc; + struct jffs2_xattr_ref *ref, *_ref; + + down_write(&c->xattr_sem); + list_for_each_entry_safe(ref, _ref, &ic->ilist, ilist) { + xc=ref->xc; + list_del(&ref->ilist); + list_del(&ref->xlist); + if (list_empty(&xc->xlist)) { + unload_xattr_cache(c, xc); + jffs2_free_xattr_cache(xc); + } + jffs2_free_xattr_ref(ref); + } + up_write(&c->xattr_sem); +} + +static int do_check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_xattr_ref *ref, *cmp; + int ret = 0; + + down_write(&c->xattr_sem); + retry: + list_for_each_entry(ref, &ic->ilist, ilist) { + if ((ret = load_xattr_cache_nolock(c, ref->xc))) + goto out; + cmp = ref; + list_for_each_entry_continue(cmp, &ic->ilist, ilist) { + if ((ret = load_xattr_cache_nolock(c, cmp->xc))) + goto out; + if (ref->xc->xtype == cmp->xc->xtype + && !strcmp(ref->xc->xname, cmp->xc->xname)) { + delete_xattr_ref(c, ref->seqno > cmp->seqno ? cmp : ref); + goto retry; + } + } + } + out: + up_write(&c->xattr_sem); + + D2(printk(KERN_NOTICE "%s ino=%d ret=%d\n", __FUNCTION__, ic->ino, ret)); + + ic->ilist_checked = ret ? 0 : 1; + + return ret; +} + +static inline int check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + if (likely(!ic->ilist_checked)) + return do_check_xattr_ref_ilist(c, ic); + return 0; +} + +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_cache *xc; + struct xattr_handler *xhandle; + int len, ret; + + if (unlikely((ret=check_xattr_ref_ilist(c, ic)))) + return ret; + + down_read(&c->xattr_sem); + len = 0; + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + xc = ref->xc; + ret = load_xattr_cache(c, xc); + if (ret<0) + goto out; + + xhandle = jffs2_xattr_xtype_to_handler(xc->xtype); + if (xhandle) { + ret = xhandle->list(inode, NULL, 0, xc->xname, xc->name_len); + if (!ret) + continue; + if (ret<0) + goto out; + if (buffer) { + if (size < len+xc->name_len+1) { + ret = -ERANGE; + goto out; + } + strcpy(buffer+len, xc->xname); + } + len += xc->name_len+1; + } + } + out: + up_read(&c->xattr_sem); + return ret; +} + +static int do_jffs2_getxattr(struct inode *inode, int xtype, 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_cache *xc; + struct jffs2_xattr_ref *ref; + int ret, prlen; + + if ((prlen=jffs2_xattr_xtype_to_length(xtype))<0) + return prlen; + + if (unlikely((ret=check_xattr_ref_ilist(c, ic)))) + return ret; + + down_read(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + xc = ref->xc; + if (xc->xtype != xtype) + continue; + + ret = load_xattr_cache(c, xc); + if (ret) + goto out; + + if (!strcmp(xname, xc->xname+prlen)) { + ret = xc->value_len; + if (buffer) { + if (size<ret) { + ret = -ERANGE; + } else { + memcpy(buffer, xc->xvalue, xc->value_len); + } + } + goto out; + } + } + ret = -ENODATA; + out: + up_read(&c->xattr_sem); + return ret; +} + +static int do_jffs2_setxattr(struct inode *inode, int xtype, 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_cache *xc; + struct jffs2_xattr_ref *ref; + + int ret, prlen; + + if (unlikely((ret=check_xattr_ref_ilist(c, ic)))) + return ret; + + if ((prlen = jffs2_xattr_xtype_to_length(xtype))<0) + return prlen; + + down_write(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + xc = ref->xc; + BUG_ON(ref->ic!=ic); + if (xc->xtype!=xtype) + continue; + ret = load_xattr_cache(c, xc); + if (ret) + goto out; + if (!strcmp(xc->xname+prlen, xname)) { + /* XATTR Found */ + if (flags & XATTR_CREATE) { + ret = -EEXIST; + goto out; + } + if (buffer) { + ret = create_xattr_cache(c, ic, xtype, xname, buffer, size); + if (ret) + goto out; + } + delete_xattr_ref(c, ref); + goto out; + } + } + /* Not Found */ + if (flags & XATTR_REPLACE) { + ret = -ENODATA; + goto out; + } + if (!buffer) { + ret = -EINVAL; + goto out; + } + /* Create New XATTR Node */ + ret = create_xattr_cache(c, ic, xtype, xname, buffer, size); + out: + up_write(&c->xattr_sem); + + return ret; +} + +int jffs2_garbage_collect_xattr(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw) +{ + /* must be called under down_XXX(alloc_sem) */ + union jffs2_node_union *node; + struct jffs2_raw_node_ref *nraw; + uint32_t phys_ofs, totlen, length, crc; + int ret, nodetype, nlen, vlen; + + BUG_ON(!raw->owner); + + totlen = ref_totlen(c, c->gcblock, raw); + if (totlen < sizeof(struct jffs2_unknown_node)) + return -EINVAL; + + ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length); + if (ret || length<totlen) { + printk(KERN_WARNING "jffs2_reserve_space_gc() = %d " + "request: %d byte reserved: %d byte", + ret, totlen, length); + return ret ? ret : -EBADFD; + } + + node = kmalloc(totlen, GFP_KERNEL); + if (!node) { + printk(KERN_WARNING "kmalloc() failed for XATTR GC processing.\n"); + return -ENOMEM; + } + + ret = jffs2_flash_read(c, ref_offset(raw), totlen, &length, (char *)node); + if (!ret && length != totlen) { + printk(KERN_WARNING "Node read failed at 0x%08x\n", ref_offset(raw)); + kfree(node); + return ret ? ret : -EIO; + } + + ret = -EBADFD; + crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4); + if (je32_to_cpu(node->u.hdr_crc) != crc) { + printk(KERN_WARNING "Header CRC failed on JFFS2_NODETYPE_XATTR/XREF" + " at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc); + goto error; + } + if (totlen != je32_to_cpu(node->u.totlen)) { + printk(KERN_WARNING "Node Length Failed on JFFS2_NODETYPE_XATTR/XREF" + " at 0x%08x: totlen=%d, calculated=%d", + ref_offset(raw), totlen, sizeof(node->u.totlen)); + goto error; + } + + nodetype = je16_to_cpu(node->u.nodetype); + switch(nodetype) { + case JFFS2_NODETYPE_XATTR: + crc = crc32(0, node, sizeof(node->x)-8); + if (je32_to_cpu(node->x.node_crc) != crc) { + printk(KERN_WARNING "Node CRC failed on JFFS2_NODETYPE_XATTR" + " at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->x.node_crc), crc); + goto error; + } + + nlen = JFFS2_XATTR_NAMELEN(je32_to_cpu(node->x.length)); + vlen = JFFS2_XATTR_VALUELEN(je32_to_cpu(node->x.length)); + if (totlen != sizeof(node->x)+PAD(nlen+1)+PAD(vlen)) { + printk(KERN_WARNING "Node Length Failed on JFFS2_NODETYPE_XATTR" + " at 0x%08x: totlen=%d, calculated=%d", + ref_offset(raw), totlen, sizeof(node->x)+PAD(nlen+1)+PAD(vlen)); + goto error; + } + + crc = crc32(0, node->x.data, PAD(nlen+1)+PAD(vlen)); + if (je32_to_cpu(node->x.data_crc) != crc) { + printk(KERN_WARNING "Data CRC failed on JFFS2_NODETYPE_XATTR" + " at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->x.data_crc), crc); + goto error; + } + break; + case JFFS2_NODETYPE_XREF: + crc = crc32(0, node, sizeof(node->r)-4); + if (je32_to_cpu(node->r.node_crc) != crc) { + printk(KERN_WARNING "Node CRC failed on JFFS2_NODETYPE_XREF" + " at 0x%08x: Read 0x%08x, calculated 0x%08x\n", + ref_offset(raw), je32_to_cpu(node->r.node_crc), crc); + goto error; + } + if (totlen != sizeof(node->r)) { + printk(KERN_WARNING "Node Length Failed on JFFS2_NODETYPE_XREF" + " at 0x%08x: totlen=%d, expected=%d", + ref_offset(raw), totlen, sizeof(node->r)); + goto error; + } + break; + default: + printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", + ref_offset(raw), je16_to_cpu(node->u.nodetype)); + goto error; + break; + } + + nraw = jffs2_alloc_raw_node_ref(); + if (!nraw) { + ret = -ENOMEM; + goto error; + } + + nraw->flash_offset = phys_ofs; + nraw->__totlen = totlen; + nraw->next_phys = NULL; + nraw->next_in_ino = NULL; + nraw->owner = raw->owner; + + ret = jffs2_flash_write(c, phys_ofs, totlen, &length, (char *)node); + if (ret || (totlen!=length)) { + printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", + totlen, phys_ofs, ret, length); + if (length) { + nraw->flash_offset |= REF_OBSOLETE; + jffs2_add_physical_node_ref(c, nraw); + jffs2_mark_node_obsolete(c, nraw); + } else { + jffs2_free_raw_node_ref(nraw); + } + ret = ret ? ret : -EIO; + goto error; + } + nraw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, nraw); + + switch (nodetype) { + case JFFS2_NODETYPE_XATTR: + ((struct jffs2_xattr_cache *)raw->owner)->node = nraw; + break; + case JFFS2_NODETYPE_XREF: + ((struct jffs2_xattr_ref *)raw->owner)->node = nraw; + break; + default: + BUG(); + break; + } + jffs2_mark_node_obsolete(c, raw); + ret = 0; + error: + kfree(node); + return ret; +} diff -rpNU3 mtd-0829/include/linux/jffs2.h mtd-0829.xattr/include/linux/jffs2.h --- mtd-0829/include/linux/jffs2.h 2005-07-26 18:00:09.000000000 -0400 +++ mtd-0829.xattr/include/linux/jffs2.h 2005-09-06 10:28:53.000000000 -0400 @@ -59,6 +59,8 @@ #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) #define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) +#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) +#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) // Maybe later... //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) @@ -146,9 +148,39 @@ 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 length; /* upper 12bit = length of name */ + /* lower 20bit = length of value */ + 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 seqno; /* sequencial number */ + jint32_t ino; /* inode number */ + jint32_t xid; /* XATTR identifier number */ + jint32_t node_crc; +} __attribute__((packed)); + 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_unknown_node u; }; diff -rpNU3 mtd-0829/include/linux/jffs2_fs_sb.h mtd-0829.xattr/include/linux/jffs2_fs_sb.h --- mtd-0829/include/linux/jffs2_fs_sb.h 2005-05-19 18:00:14.000000000 -0400 +++ mtd-0829.xattr/include/linux/jffs2_fs_sb.h 2005-09-06 10:28:53.000000000 -0400 @@ -112,6 +112,16 @@ struct jffs2_sb_info { uint32_t fsdata_len; #endif +#ifdef CONFIG_JFFS2_XATTR + uint32_t highest_xid; + uint32_t highest_seqno; + struct list_head *xattrcache; +#define xattr_temp xattrcache[XATTRCACHE_HASHSIZE] + struct rw_semaphore xattr_sem; + + uint32_t xcache_mem_usage; + uint32_t xcache_mem_threshold; +#endif /* OS-private pointer for getting back to master superblock info */ void *os_priv; }; diff -rpNU3 mtd-0829/include/linux/jffs2_fs_x.h mtd-0829.xattr/include/linux/jffs2_fs_x.h --- mtd-0829/include/linux/jffs2_fs_x.h 1969-12-31 19:00:00.000000000 -0500 +++ mtd-0829.xattr/include/linux/jffs2_fs_x.h 2005-09-06 10:28:53.000000000 -0400 @@ -0,0 +1,140 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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 XATTRCACHE_HASHSIZE (57) +#define JFFS2_XATTR_NAMELEN(x) (((x)>>20) & 0x00000fff) +#define JFFS2_XATTR_VALUELEN(x) ( (x) & 0x000fffff) +#define JFFS2_XATTR_LENGTH(n,v) (((n)&0x00000fff)<<20|((v)&0x000fffff)) + +#define XATTR_USER_PREFIX "user." +#define XATTR_TRUSTED_PREFIX "trusted." + +#define JFFS2_XATTRTYPE_USER 1 /* for "user.*" */ +#define JFFS2_XATTRTYPE_SECURITY 2 /* for "security.*" */ +#define JFFS2_XATTRTYPE_ACL_ACCESS 3 /* for "system.posix_acl_access" */ +#define JFFS2_XATTRTYPE_ACL_DEFAULT 4 /* for "system.posix_acl_default" */ +#define JFFS2_XATTRTYPE_TRUSTED 5 /* for "trusted.*" */ +#define JFFS2_XATTRTYPE_INVALID 6 + +#define JFFS2_XCACHE_FLAGS_ACTIVE 0x0001 /* This is HOT cache */ + +struct jffs2_xattr_cache +{ + struct list_head xcache; /* chained to c->xcache */ + struct list_head xlist; /* jffs_xattr_ref is chained here */ + struct jffs2_raw_node_ref *node; + + uint32_t xid; + uint16_t xtype; /* See JFFS2_XATTRTYPE_* */ + uint16_t flags; /* See JFFS2_XCACHE_FLAGS_* */ + uint32_t data_crc; + + uint32_t hashkey; + char *xname; /* XATTR name */ + int name_len; /* length of xname */ + char *xvalue; /* XATTR value */ + int value_len; /* length of xvalue */ +}; + +struct jffs2_inode_cache; /* forward refence */ +struct jffs2_xattr_ref +{ + uint32_t seqno; /* sequencial number */ + struct list_head ilist; /* list from jffs2_inode_cache */ + struct list_head xlist; /* list from jffs2_xattr_cache */ + struct jffs2_xattr_cache *xc; + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *node; +}; + +#ifdef CONFIG_JFFS2_XATTR + +extern int jffs2_init_xattr_caches(struct jffs2_sb_info *c); +extern void jffs2_build_xattr_caches(struct jffs2_sb_info *c); +extern void jffs2_clear_xattr_caches(struct jffs2_sb_info *c); +extern struct jffs2_xattr_cache *jffs2_find_xattr_cache_xid(struct jffs2_sb_info *c, uint32_t xid); +extern void jffs2_attach_xattr_cache(struct jffs2_sb_info *c, struct jffs2_xattr_cache *xc); + +extern char *jffs2_xattr_xtype_to_prefix(int xtype); +extern int jffs2_xattr_prefix_to_xtype(const char *xname, int nlen); +extern int jffs2_xattr_xtype_to_length(int xtype); + +extern void jffs2_xattr_clear_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 struct xattr_handler *jffs2_xattr_handlers[]; + +#define jffs2_permission NULL +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 + +extern int jffs2_garbage_collect_xattr(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); +/*---- Any inline initialize functions ----*/ +#define initxattr_jffs2_inode_cache(x) INIT_LIST_HEAD(&((x)->ilist)) + +static inline void initxattr_jffs2_xattr_cache(struct jffs2_xattr_cache *xc) +{ + memset(xc, 0, sizeof(struct jffs2_xattr_cache)); + INIT_LIST_HEAD(&xc->xcache); + INIT_LIST_HEAD(&xc->xlist); +} + +static inline void initxattr_jffs2_xattr_ref(struct jffs2_xattr_ref *ref) +{ + memset(ref, 0, sizeof(struct jffs2_xattr_ref)); + INIT_LIST_HEAD(&ref->ilist); + INIT_LIST_HEAD(&ref->xlist); +} + +#else + +static inline void jffs2_xattr_write_down(void *c){} +static inline void jffs2_xattr_write_up(void *c){} +static inline void jffs2_xattr_read_down(void *c){} +static inline void jffs2_xattr_read_up(void *c){} + +static inline int jffs2_init_xattr_caches(void *c){ return 0; } +static inline void jffs2_build_xattr_caches(void *c){} +static inline void jffs2_clear_xattr_caches(void *c){} +static inline void *jffs2_find_xattr_cache_xid(void *c, uint32_t x){ BUG(); return NULL; } +static inline void jffs2_attach_xattr_cache(void *c, void *xc){ BUG(); } + +static inline void *jffs2_xattr_xtype_to_prefix(int xtype){ BUG(); } +static inline int jffs2_xattr_prefix_to_xtype(char *xname, int nlen){ BUG(); } +static inline int jffs2_xattr_xtype_to_length(int xtype){ BUG(); } + +static inline void jffs2_xattr_clear_inode(void *c, void *ic){} +static inline void jffs2_xattr_free_inode(void *c, void *ic){} + +static inline void jffs2_unlink_xattr_inode(void *c, void *ic){} +#define jffs2_xattr_handlers NULL + +#define jffs2_permission NULL +#define jffs2_listxattr NULL +#define jffs2_getxattr NULL +#define jffs2_setxattr NULL +#define jffs2_removexattr NULL + +static inline int jffs2_garbage_collect_xattr(void *, void *){ BUG(); } + +#define initxattr_jffs2_inode_cache(x) +static inline void initxattr_jffs2_xattr_cache(struct jffs2_xattr_cache *xc){} +static inline void initxattr_jffs2_xattr_ref(struct jffs2_xattr_ref *ref){} + +#endif /* CONFIG_JFFS2_XATTR */ + +#endif /* _JFFS2_FS_XATTR_H_ */ ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-07 5:14 ` Kaigai Kohei @ 2005-09-08 19:49 ` Jörn Engel 2005-09-08 19:54 ` David Woodhouse 2005-09-09 4:15 ` Kaigai Kohei 0 siblings, 2 replies; 28+ messages in thread From: Jörn Engel @ 2005-09-08 19:49 UTC (permalink / raw) To: Kaigai Kohei Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley, Ma Yun On Wed, 7 September 2005 14:14:19 +0900, Kaigai Kohei wrote: > > This attached patch enables XATTR support in JFFS2. (take-2) > # The contribution has slowed due to the trouble related to GC :-( > > The my first patch posted in Aug 23 is revised at the points as follows. > > - get/setxattr are implemented as a method of generic XATTR handler. > - new methods for "user.*" and "trusted.*" prefix were added. > > - Time to retrieve the duplicate XATTR-cache shortened, when setxattr(). > In first implementation, we must scan all XATTR-cache to find the > duplicate > one, and it's so expensive. Now, we cau use the hash-list of XATTR-cache > which is indexed by own contents. > > - Now, Garbage Collector can deal JFFS2_NODETYPE_XATTR and > JFFS2_NODETYPE_XREF. > It seems to me that GC can mark unconditionally any nodes without inode > as obsolete. But new JFFS2_NODETYPE_XATTR/XREF should be an exception. > GC must not mark them as obsolete, although their "next_in_ino" is NULL. > > ----[ jffs2_garbage_collect_pass() in gc.c ]------------------------ > 249 if (!raw->next_in_ino) { <-- unconditionally mark it as > obsolete. > 250 /* Inode-less node. Clean marker, snapshot or something like > that */ > 251 /* FIXME: If it's something that needs to be copied, including > something > 252 we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we > should do so */ > 253 spin_unlock(&c->erase_completion_lock); > 254 jffs2_mark_node_obsolete(c, raw); > 255 up(&c->alloc_sem); > 256 goto eraseit_lock; > 257 } > -------------------------------------------------------------------- > > I added a new variable 'owner' into jffs2_raw_node_ref to resolve this. > It refers jffs2_xattr_cache or jffs2_xattr_ref object when raw-node > reflect > XATTR-node. Thus, we can distinguish XATTR-node and others by whether > 'owner' > is NULL or not. > > - Consistency has improved. > check_xattr_ref_ilist() checks XATTRs which are binded to same inode. > If multiple XATTR has same name exist, older one is marked as obsolete. > In first implementation, there are possibilities to be created multiple > XATTRs have same name in an inode, when system-crash happens durling > setxattr() Sounds good. It appears as if your patch or something based on it will win. So let's have a look... I notice that you make a couple of mistakes by copying them from existing jffs2 code. You are excused for that, no doubt. But I still don't want copies of old mistakes to go in, just because we have a bad example. Review is just partial. It's late enough already and I need to get some sleep. > diff -rpNU3 mtd-0829/fs/Kconfig mtd-0829.xattr/fs/Kconfig > --- mtd-0829/fs/Kconfig 2005-05-09 18:00:10.000000000 -0400 > +++ mtd-0829.xattr/fs/Kconfig 2005-09-06 10:30:52.000000000 -0400 > @@ -64,6 +64,22 @@ config JFFS2_FS_WRITEBUFFER > - NOR flash with transparent ECC > - DataFlash > > +config JFFS2_XATTR > + bool "JFFS2 XATTR support (EXPERIMENTAL)" > + depends on JFFS2_FS > + default n > + help > + This enables the XATTR support in JFFS2. > + > + It's neccesary for persistent security context in SELinux. > + Now, we can use the following kinds of prefix. > + > + - security.* > + - user.* > + - trust.* > + > + - system.posix_acl_{access|default} are in TODO list now. > + > config JFFS2_COMPRESSION_OPTIONS > bool "Advanced compression options for JFFS2" > default n > diff -rpNU3 mtd-0829/fs/jffs2/Makefile.common mtd-0829.xattr/fs/jffs2/Makefile.common > --- mtd-0829/fs/jffs2/Makefile.common 2005-07-17 18:00:13.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/Makefile.common 2005-09-06 10:31:26.000000000 -0400 > @@ -12,6 +12,7 @@ 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_XATTR) += xattr.o > jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o > jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o > jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o > diff -rpNU3 mtd-0829/fs/jffs2/build.c mtd-0829.xattr/fs/jffs2/build.c > --- mtd-0829/fs/jffs2/build.c 2005-07-30 18:00:11.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/build.c 2005-09-06 10:28:53.000000000 -0400 > @@ -165,6 +165,7 @@ static int jffs2_build_filesystem(struct > ic->scan_dents = NULL; > cond_resched(); > } > + jffs2_build_xattr_caches(c); > c->flags &= ~JFFS2_SB_FLAG_BUILDING; > > D1(printk(KERN_DEBUG "Pass 3 complete\n")); > @@ -184,6 +185,7 @@ exit: > jffs2_free_full_dirent(fd); > } > } > + jffs2_clear_xattr_caches(c); > } > > return ret; > diff -rpNU3 mtd-0829/fs/jffs2/dir.c mtd-0829.xattr/fs/jffs2/dir.c > --- mtd-0829/fs/jffs2/dir.c 2005-08-17 18:00:11.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/dir.c 2005-09-06 10:28:53.000000000 -0400 > @@ -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 > }; > > /***********************************************************************/ > diff -rpNU3 mtd-0829/fs/jffs2/file.c mtd-0829.xattr/fs/jffs2/file.c > --- mtd-0829/fs/jffs2/file.c 2005-07-06 18:00:13.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/file.c 2005-09-06 10:28:53.000000000 -0400 > @@ -57,7 +57,12 @@ struct file_operations jffs2_file_operat > > struct inode_operations jffs2_file_inode_operations = > { > - .setattr = jffs2_setattr > + .permission = jffs2_permission, > + .setattr = jffs2_setattr, > + .setxattr = jffs2_setxattr, > + .getxattr = jffs2_getxattr, > + .listxattr = jffs2_listxattr, > + .removexattr = jffs2_removexattr > }; > > struct address_space_operations jffs2_file_address_operations = > diff -rpNU3 mtd-0829/fs/jffs2/fs.c mtd-0829.xattr/fs/jffs2/fs.c > --- mtd-0829/fs/jffs2/fs.c 2005-08-06 18:00:16.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/fs.c 2005-09-06 10:28:53.000000000 -0400 > @@ -217,6 +217,8 @@ 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)); > > + if (f->inocache && !f->inocache->nlink) > + jffs2_xattr_clear_inode(c, f->inocache); Move the test into jffs2_xattr_clear_inode(). > jffs2_do_clear_inode(c, f); > } > > @@ -504,6 +506,9 @@ int jffs2_do_fill_super(struct super_blo > } > memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); > > + if ((ret = jffs2_init_xattr_caches(c))) > + goto out_inohash; > + ret = jffs2_init_xattr_caches(c); if (ret) goto out_inohash; I agree with gcc, when it warns about assignments in conditions. What I don't agree with is the proposal to use double brackets. > if ((ret = jffs2_do_mount_fs(c))) > goto out_inohash; > > @@ -537,6 +542,7 @@ int jffs2_do_fill_super(struct super_blo > vfree(c->blocks); > else > kfree(c->blocks); > + jffs2_clear_xattr_caches(c); > out_inohash: > kfree(c->inocache_list); > out_wbuf: > diff -rpNU3 mtd-0829/fs/jffs2/gc.c mtd-0829.xattr/fs/jffs2/gc.c > --- mtd-0829/fs/jffs2/gc.c 2005-08-17 18:00:12.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/gc.c 2005-09-06 10:28:53.000000000 -0400 > @@ -251,7 +251,12 @@ int jffs2_garbage_collect_pass(struct jf > /* FIXME: If it's something that needs to be copied, including something > we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ > spin_unlock(&c->erase_completion_lock); > - jffs2_mark_node_obsolete(c, raw); > + if (raw->owner) { > + /* JFFS2_NODETYPE_XATTR or JFFS2_NODETYPE_XREF */ > + ret = jffs2_garbage_collect_xattr(c, raw); > + } else { > + jffs2_mark_node_obsolete(c, raw); > + } > up(&c->alloc_sem); > goto eraseit_lock; > } > diff -rpNU3 mtd-0829/fs/jffs2/malloc.c mtd-0829.xattr/fs/jffs2/malloc.c > --- mtd-0829/fs/jffs2/malloc.c 2005-07-27 18:00:18.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/malloc.c 2005-09-06 10:28:53.000000000 -0400 > @@ -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_XATTR > +static kmem_cache_t *xattr_cache_slab; > +static kmem_cache_t *xattr_ref_slab; > +#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_XATTR > + xattr_cache_slab = kmem_cache_create("jffs2_xattr_cache", > + sizeof(struct jffs2_xattr_cache), > + 0, 0, NULL, NULL); > + if (!xattr_cache_slab) > + goto err; > + > + xattr_ref_slab = kmem_cache_create("jffs2_xattr_ref", > + sizeof(struct jffs2_xattr_ref), > + 0, 0, NULL, NULL); > + if (!xattr_ref_slab) > + 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_XATTR > + if(xattr_cache_slab) > + kmem_cache_destroy(xattr_cache_slab); > + if(xattr_ref_slab) > + kmem_cache_destroy(xattr_ref_slab); > +#endif > } > > struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) > @@ -169,6 +195,7 @@ struct jffs2_raw_node_ref *jffs2_alloc_r > struct jffs2_raw_node_ref *ret; > ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); > JFFS2_DBG_MEMALLOC("%p\n", ret); > + memset(ret, 0, sizeof(*ret)); /* to guarantee owner==NULL */ This could be optimized a little by using the constructor/destructor stuff in slabs. Not too important. > return ret; > } > > @@ -205,3 +232,35 @@ void jffs2_free_inode_cache(struct jffs2 > JFFS2_DBG_MEMALLOC("%p\n", x); > kmem_cache_free(inode_cache_slab, x); > } > + > +#ifdef CONFIG_JFFS2_XATTR > + > +struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void) > +{ > + struct jffs2_xattr_cache *xc; ^^^^^^ The name doesn't make sense. You're referring to a single object, not the complete cache. Quite confusing. > + xc = kmem_cache_alloc(xattr_cache_slab, GFP_KERNEL); ^^^^^ This is the cache. A slab is just part of the cache. So please rename the two. > + JFFS2_DBG_MEMALLOC("%p\n", xc); > + return xc; > +} > + > +void jffs2_free_xattr_cache(struct jffs2_xattr_cache *xc) > +{ > + JFFS2_DBG_MEMALLOC("%p\n", xc); > + kmem_cache_free(xattr_cache_slab, xc); > +} > + > +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) > +{ > + struct jffs2_xattr_ref *ref; This name makes sense. > + ref = kmem_cache_alloc(xattr_ref_slab, GFP_KERNEL); s/slab/cache/ > + JFFS2_DBG_MEMALLOC("%p\n", ref); > + return ref; > +} > + > +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref) > +{ > + JFFS2_DBG_MEMALLOC("%p\n", ref); > + kmem_cache_free(xattr_ref_slab, ref); > +} > + > +#endif > diff -rpNU3 mtd-0829/fs/jffs2/nodelist.c mtd-0829.xattr/fs/jffs2/nodelist.c > --- mtd-0829/fs/jffs2/nodelist.c 2005-08-22 18:00:15.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/nodelist.c 2005-09-06 10:28:53.000000000 -0400 > @@ -934,6 +934,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; > } > diff -rpNU3 mtd-0829/fs/jffs2/nodelist.h mtd-0829.xattr/fs/jffs2/nodelist.h > --- mtd-0829/fs/jffs2/nodelist.h 2005-08-17 18:00:12.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/nodelist.h 2005-09-06 10:28:53.000000000 -0400 > @@ -20,6 +20,7 @@ > #include <linux/jffs2.h> > #include <linux/jffs2_fs_sb.h> > #include <linux/jffs2_fs_i.h> > +#include <linux/jffs2_fs_x.h> I p a l n. I prefer a longer name. jffs2_fs_xattr.h would be fine. jffs2_xattr.h would also, the "_fs" is more redundant that "attr". > > #ifdef __ECOS > #include "os-ecos.h" > @@ -80,6 +81,7 @@ struct jffs2_raw_node_ref > struct jffs2_raw_node_ref *next_phys; > uint32_t flash_offset; > uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ > + void *owner; This grows a node reference from 16/24 bytes to 20/32 bytes for 32/64 bit architectures. I'm not entirely happy about that, but don't a better solution either. > }; > > /* flash_offset & 3 always has to be zero, because nodes are > @@ -111,6 +113,10 @@ struct jffs2_inode_cache { > uint32_t ino; > int nlink; > int state; > +#ifdef CONFIG_JFFS2_XATTR > + struct list_head ilist; > + uint8_t ilist_checked; > +#endif > }; > > /* Inode states for 'state' above. We need the 'GC' state to prevent > @@ -367,6 +373,13 @@ void jffs2_free_node_frag(struct jffs2_n > struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); > void jffs2_free_inode_cache(struct jffs2_inode_cache *); > > +#ifdef CONFIG_JFFS2_XATTR > +struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void); > +void jffs2_free_xattr_cache(struct jffs2_xattr_cache *); > +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void); > +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *); > +#endif /* CONFIG_JFFS2_XATTR */ > + > /* gc.c */ > int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); > > diff -rpNU3 mtd-0829/fs/jffs2/readinode.c mtd-0829.xattr/fs/jffs2/readinode.c > --- mtd-0829/fs/jffs2/readinode.c 2005-08-17 18:00:12.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/readinode.c 2005-09-06 10:28:53.000000000 -0400 > @@ -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; > + initxattr_jffs2_inode_cache(f->inocache); > jffs2_add_ino_cache(c, f->inocache); > } > if (!f->inocache) { > diff -rpNU3 mtd-0829/fs/jffs2/scan.c mtd-0829.xattr/fs/jffs2/scan.c > --- mtd-0829/fs/jffs2/scan.c 2005-07-20 18:00:12.000000000 -0400 > +++ mtd-0829.xattr/fs/jffs2/scan.c 2005-09-06 10:28:53.000000000 -0400 > @@ -57,6 +57,12 @@ static int jffs2_scan_inode_node(struct > struct jffs2_raw_inode *ri, uint32_t ofs); > static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, > struct jffs2_raw_dirent *rd, uint32_t ofs); > +#ifdef CONFIG_JFFS2_XATTR > +static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, > + struct jffs2_raw_xattr *rx, uint32_t ofs); > +static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, > + struct jffs2_raw_xref *rr, uint32_t ofs); > +#endif You don't need the function definitions if you change the order inside the file. > #define BLK_STATE_ALLFF 0 > #define BLK_STATE_CLEAN 1 > @@ -552,7 +558,40 @@ scan_more: > if (err) return err; > ofs += PAD(je32_to_cpu(node->totlen)); > break; > - > +#ifdef CONFIG_JFFS2_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); > + 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); > + if (err) > + return err; > + ofs += PAD(je32_to_cpu(node->totlen)); > + break; > +#endif By itself, this code is not too bad. But it add a little more crap to a function that is aching for a rewrite already. Not sure. > 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) { > @@ -665,6 +704,7 @@ static struct jffs2_inode_cache *jffs2_s > > ic->ino = ino; > ic->nodes = (void *)ic; > + initxattr_jffs2_inode_cache(ic); > jffs2_add_ino_cache(c, ic); > if (ino == 1) > ic->nlink = 1; > @@ -820,6 +860,190 @@ static int jffs2_scan_dirent_node(struct > return 0; > } > > +#ifdef CONFIG_JFFS2_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_raw_node_ref *raw; > + struct jffs2_xattr_cache *xc; > + uint32_t crc; > + int xtype, nlen, vlen; > + > + crc = crc32(0, rx, sizeof(*rx)-8); > + if (crc != je32_to_cpu(rx->node_crc)) { > + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Node CRC failed" > + " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", > + ofs, je32_to_cpu(rx->node_crc), crc); > + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); > + return 0; > + } > + > + nlen = JFFS2_XATTR_NAMELEN(je32_to_cpu(rx->length)); > + vlen = JFFS2_XATTR_VALUELEN(je32_to_cpu(rx->length)); Types don't match. nlen and vlen should become u32. Btw, I agree with Linus. u32 is _much_ nicer than uint32_t, not to speak of the uint32_fast_t insanity. > + if (je32_to_cpu(rx->totlen) != sizeof(*rx)+PAD(nlen+1)+PAD(vlen)) { > + printk(KERN_NOTICE "jffs2_scan_xattr_node(): length mimatch!" > + " totlen=%d length=0x%08x nlen=%d vlen=%d on 0x%08x\n", > + je32_to_cpu(rx->totlen), je32_to_cpu(rx->length), nlen, vlen, ofs); > + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); > + return 0; > + } > + > + crc = crc32(0, rx->data, PAD(nlen+1)+PAD(vlen)); > + if (crc != je32_to_cpu(rx->data_crc)) { > + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Data CRC failed" > + " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", > + ofs, je32_to_cpu(rx->data_crc), crc); > + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); > + return 0; > + } > + > + xc = jffs2_find_xattr_cache_xid(c, je32_to_cpu(rx->xid)); > + if (xc) { > + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Duplicate XID Found" > + " on node at 0x%08x XID=%d Later one ignored\n", ofs, xc->xid); > + DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); > + return 0; > + } How do you deal with xattr reference counting and lifetime? > + xtype = jffs2_xattr_prefix_to_xtype(rx->data, nlen); > + if (JFFS2_XATTRTYPE_INVALID==xtype) { > + printk(KERN_NOTICE "jffs2_scan_xattr_node(): Unsupported xattr type" > + " on node at 0x%08x\n", ofs); > + USED_SPACE(PAD(je32_to_cpu(rx->totlen))); > + return 0; > + } Why is it required that JFFS2 knows the type of the xattr? > + > + raw = jffs2_alloc_raw_node_ref(); > + if (!raw) > + return -ENOMEM; > + > + xc = jffs2_alloc_xattr_cache(); > + if (!xc) { > + jffs2_free_raw_node_ref(raw); > + return -ENOMEM; > + } > + initxattr_jffs2_xattr_cache(xc); > + > + xc->xid = je32_to_cpu(rx->xid); > + if (xc->xid > c->highest_xid) > + c->highest_xid = xc->xid; > + xc->xtype = xtype; > + xc->node = raw; > + xc->name_len = nlen; > + xc->value_len = vlen; > + xc->data_crc = crc; > + > + raw->__totlen = PAD(je32_to_cpu(rx->totlen)); > + raw->flash_offset = ofs | REF_PRISTINE; > + raw->next_phys = NULL; > + raw->next_in_ino = NULL; > + 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))); > + > + jffs2_attach_xattr_cache(c, xc); > + > + D1(printk(KERN_NOTICE "XATTR_CACHE: XID=%d inserted\n", xc->xid)); > + return 0; > +} > + > +static inline int jffs2_scan_xref_node_helper(struct jffs2_sb_info *c, > + struct jffs2_xattr_ref *new, struct jffs2_xattr_ref *old) > +{ > + /* (uint32_t)new->xc == (uint32_t)old->xc has been made sure. */ > + if ((uint32_t)new->ic == (uint32_t)old->ic) { Why the casts? How can this be safe on 64bit machines? > + if (new->seqno > old->seqno) { > + jffs2_mark_node_obsolete(c, old->node); > + old->seqno = new->seqno; > + old->node = new->node; > + } else { > + jffs2_mark_node_obsolete(c, new->node); > + } > + return 1; /* 'new' should be released. */ > + } > + 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_raw_node_ref *raw; > + struct jffs2_xattr_ref *ref, *pos, *pos2; > + uint32_t crc; > + > + crc = crc32(0, rr, sizeof(*rr)-4); > + if (crc != je32_to_cpu(rr->node_crc)) { > + printk(KERN_NOTICE "jffs2_scan_xref_node(): Node CRC failed" > + " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", > + ofs, je32_to_cpu(rr->node_crc), crc); > + DIRTY_SPACE(PAD(je32_to_cpu(rr->totlen))); > + return 0; > + } > + > + ref = jffs2_alloc_xattr_ref(); > + if (!ref) > + return -ENOMEM; > + initxattr_jffs2_xattr_ref(ref); > + > + raw = jffs2_alloc_raw_node_ref(); > + if (!raw) { > + jffs2_free_xattr_ref(ref); > + return -ENOMEM; > + } > + > + ref->seqno = je32_to_cpu(rr->seqno); > + if (ref->seqno > c->highest_seqno) > + c->highest_seqno = ref->seqno; > + ref->ic = (void *)je32_to_cpu(rr->ino); > + ref->xc = (void *)je32_to_cpu(rr->xid); WTF?!? You read a je32 from flash and use that as a pointer?!? Please tell me I'm wrong. Jörn -- Don't worry about people stealing your ideas. If your ideas are any good, you'll have to ram them down people's throats. -- Howard Aiken quoted by Ken Iverson quoted by Jim Horning quoted by Raph Levien, 1979 ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-08 19:49 ` Jörn Engel @ 2005-09-08 19:54 ` David Woodhouse 2005-09-09 4:15 ` Kaigai Kohei 1 sibling, 0 replies; 28+ messages in thread From: David Woodhouse @ 2005-09-08 19:54 UTC (permalink / raw) To: Jörn Engel Cc: James Morris, Ma Yun, linux-mtd, Stephen Smalley, Kaigai Kohei On Thu, 2005-09-08 at 21:49 +0200, Jörn Engel wrote: > I prefer a longer name. jffs2_fs_xattr.h would be fine. > jffs2_xattr.h would also, the "_fs" is more redundant that "attr". Now would probably be a good time to drop jffs2_fs_i.h and jffs2_fs_sb.h anyway. We no longer have the inode and superblock unions; this stuff can be entirely contained within the fs/jffs2/ directory instead of being in include/linux/ -- dwmw2 ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-08 19:49 ` Jörn Engel 2005-09-08 19:54 ` David Woodhouse @ 2005-09-09 4:15 ` Kaigai Kohei 2005-09-09 7:24 ` Jörn Engel 1 sibling, 1 reply; 28+ messages in thread From: Kaigai Kohei @ 2005-09-09 4:15 UTC (permalink / raw) To: Jörn Engel Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley, Ma Yun Hello,Jörn. Thanks for reviewing in spite of so long patch. > Sounds good. > > It appears as if your patch or something based on it will win. So > let's have a look... > > I notice that you make a couple of mistakes by copying them from > existing jffs2 code. You are excused for that, no doubt. But I still > don't want copies of old mistakes to go in, just because we have a bad > example. Is this using uint32_t, xattr scanning code and so on ? > Review is just partial. It's late enough already and I need to get > some sleep. Please take a rest slowly on the weekend. I'll modify my patch during this period. ;-) >>--- mtd-0829/fs/jffs2/fs.c 2005-08-06 18:00:16.000000000 -0400 >>+++ mtd-0829.xattr/fs/jffs2/fs.c 2005-09-06 10:28:53.000000000 -0400 >>@@ -217,6 +217,8 @@ 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)); >> >>+ if (f->inocache && !f->inocache->nlink) >>+ jffs2_xattr_clear_inode(c, f->inocache); > > > Move the test into jffs2_xattr_clear_inode(). OK, I'll modify it. >>@@ -504,6 +506,9 @@ int jffs2_do_fill_super(struct super_blo >> } >> memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); >> >>+ if ((ret = jffs2_init_xattr_caches(c))) >>+ goto out_inohash; >>+ > > > ret = jffs2_init_xattr_caches(c); > if (ret) > goto out_inohash; > > I agree with gcc, when it warns about assignments in conditions. What > I don't agree with is the proposal to use double brackets. OK, I'll modify this and similar codes at different location if exist. >> struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) >>@@ -169,6 +195,7 @@ struct jffs2_raw_node_ref *jffs2_alloc_r >> struct jffs2_raw_node_ref *ret; >> ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); >> JFFS2_DBG_MEMALLOC("%p\n", ret); >>+ memset(ret, 0, sizeof(*ret)); /* to guarantee owner==NULL */ > > > This could be optimized a little by using the constructor/destructor > stuff in slabs. Not too important. I'll prepare a tiny and obvious constructor for jffs2_raw_node_ref. NULL is set in raw->owner by default is important, the method of setting is not so, I think. >>@@ -205,3 +232,35 @@ void jffs2_free_inode_cache(struct jffs2 >> JFFS2_DBG_MEMALLOC("%p\n", x); >> kmem_cache_free(inode_cache_slab, x); >> } >>+ >>+#ifdef CONFIG_JFFS2_XATTR >>+ >>+struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void) >>+{ >>+ struct jffs2_xattr_cache *xc; > > ^^^^^^ > The name doesn't make sense. You're referring to a single object, not > the complete cache. Quite confusing. > > >>+ xc = kmem_cache_alloc(xattr_cache_slab, GFP_KERNEL); > > ^^^^^ > This is the cache. A slab is just part of the cache. So please > rename the two. For example, how about the following names ? "jffs2_xattr_datum" as an alternative of jffs2_xattr_cache. "xattr_datum_cache" as an alternative of xattr_cache_slab. In this case, "xc" defined as jffs2_xattr_cache structure should be renamed to "xd" defined as jffs2_xattr_datum. >>+void jffs2_free_xattr_cache(struct jffs2_xattr_cache *xc) >>+{ >>+ JFFS2_DBG_MEMALLOC("%p\n", xc); >>+ kmem_cache_free(xattr_cache_slab, xc); >>+} >>+ >>+struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) >>+{ >>+ struct jffs2_xattr_ref *ref; > > > This name makes sense. > > >>+ ref = kmem_cache_alloc(xattr_ref_slab, GFP_KERNEL); > > > s/slab/cache/ OK, I'll rename it. >>diff -rpNU3 mtd-0829/fs/jffs2/nodelist.h mtd-0829.xattr/fs/jffs2/nodelist.h >>--- mtd-0829/fs/jffs2/nodelist.h 2005-08-17 18:00:12.000000000 -0400 >>+++ mtd-0829.xattr/fs/jffs2/nodelist.h 2005-09-06 10:28:53.000000000 -0400 >>@@ -20,6 +20,7 @@ >> #include <linux/jffs2.h> >> #include <linux/jffs2_fs_sb.h> >> #include <linux/jffs2_fs_i.h> >>+#include <linux/jffs2_fs_x.h> > > > I p a l n. > > I prefer a longer name. jffs2_fs_xattr.h would be fine. > jffs2_xattr.h would also, the "_fs" is more redundant that "attr". OK, I'll rename "jffs2_fs_x.h" to "jffs2_xattr.h" and deploy it under "fs/jffs2" as David says. >> #ifdef __ECOS >> #include "os-ecos.h" >>@@ -80,6 +81,7 @@ struct jffs2_raw_node_ref >> struct jffs2_raw_node_ref *next_phys; >> uint32_t flash_offset; >> uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ >>+ void *owner; > > > This grows a node reference from 16/24 bytes to 20/32 bytes for 32/64 > bit architectures. I'm not entirely happy about that, but don't a > better solution either. Indeed, it's involved the growing of object size. But we must discriminate XATTR node from other non-inode node and indicate jffs2_xattr_cache/jffs2_xattr_ref object from this. I didn't have a good idea more than this. And, I forgot to enclose the declaration of "void *owner" by #ifdef - #endif. >>diff -rpNU3 mtd-0829/fs/jffs2/scan.c mtd-0829.xattr/fs/jffs2/scan.c >>--- mtd-0829/fs/jffs2/scan.c 2005-07-20 18:00:12.000000000 -0400 >>+++ mtd-0829.xattr/fs/jffs2/scan.c 2005-09-06 10:28:53.000000000 -0400 >>@@ -57,6 +57,12 @@ static int jffs2_scan_inode_node(struct >> struct jffs2_raw_inode *ri, uint32_t ofs); >> static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, >> struct jffs2_raw_dirent *rd, uint32_t ofs); >>+#ifdef CONFIG_JFFS2_XATTR >>+static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, >>+ struct jffs2_raw_xattr *rx, uint32_t ofs); >>+static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, >>+ struct jffs2_raw_xref *rr, uint32_t ofs); >>+#endif > > > You don't need the function definitions if you change the order inside > the file. OK, I'll change the deployment of above functions. >> #define BLK_STATE_ALLFF 0 >> #define BLK_STATE_CLEAN 1 >>@@ -552,7 +558,40 @@ scan_more: >> if (err) return err; >> ofs += PAD(je32_to_cpu(node->totlen)); >> break; >>- >>+#ifdef CONFIG_JFFS2_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); >>+ 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); >>+ if (err) >>+ return err; >>+ ofs += PAD(je32_to_cpu(node->totlen)); >>+ break; >>+#endif > > > By itself, this code is not too bad. But it add a little more crap to > a function that is aching for a rewrite already. Not sure. Hmm,,, I wrote the above part of codes with referring to existing code. If the policy is shown, I'll follow it. How should I do ? >>@@ -820,6 +860,190 @@ static int jffs2_scan_dirent_node(struct >> return 0; >> } >> >>+#ifdef CONFIG_JFFS2_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_raw_node_ref *raw; >>+ struct jffs2_xattr_cache *xc; >>+ uint32_t crc; >>+ int xtype, nlen, vlen; >>+ >>+ crc = crc32(0, rx, sizeof(*rx)-8); >>+ if (crc != je32_to_cpu(rx->node_crc)) { >>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Node CRC failed" >>+ " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", >>+ ofs, je32_to_cpu(rx->node_crc), crc); >>+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); >>+ return 0; >>+ } >>+ >>+ nlen = JFFS2_XATTR_NAMELEN(je32_to_cpu(rx->length)); >>+ vlen = JFFS2_XATTR_VALUELEN(je32_to_cpu(rx->length)); > > > Types don't match. nlen and vlen should become u32. Is it enought for "int"? JFFS2_XATTR_NAMELEN has 12-bit length, and JFFS2_XATTR_VALUELEN has 20-bit length. > Btw, I agree with Linus. u32 is _much_ nicer than uint32_t, not to > speak of the uint32_fast_t insanity. Is is necessary to correct the part where other uint32_t used. >>+ if (je32_to_cpu(rx->totlen) != sizeof(*rx)+PAD(nlen+1)+PAD(vlen)) { >>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): length mimatch!" >>+ " totlen=%d length=0x%08x nlen=%d vlen=%d on 0x%08x\n", >>+ je32_to_cpu(rx->totlen), je32_to_cpu(rx->length), nlen, vlen, ofs); >>+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); >>+ return 0; >>+ } >>+ >>+ crc = crc32(0, rx->data, PAD(nlen+1)+PAD(vlen)); >>+ if (crc != je32_to_cpu(rx->data_crc)) { >>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Data CRC failed" >>+ " on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", >>+ ofs, je32_to_cpu(rx->data_crc), crc); >>+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); >>+ return 0; >>+ } >>+ >>+ xc = jffs2_find_xattr_cache_xid(c, je32_to_cpu(rx->xid)); >>+ if (xc) { >>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Duplicate XID Found" >>+ " on node at 0x%08x XID=%d Later one ignored\n", ofs, xc->xid); >>+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); >>+ return 0; >>+ } > > > How do you deal with xattr reference counting and lifetime? When xc->xlist is empty, jffs2_xattr_cache type object will be released. jffs2_xattr_ref type object is chained to xc->xlist. list_empty(xc->xlist) means nobady refers this XATTR. >>+ xtype = jffs2_xattr_prefix_to_xtype(rx->data, nlen); >>+ if (JFFS2_XATTRTYPE_INVALID==xtype) { >>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Unsupported xattr type" >>+ " on node at 0x%08x\n", ofs); >>+ USED_SPACE(PAD(je32_to_cpu(rx->totlen))); >>+ return 0; >>+ } > > > Why is it required that JFFS2 knows the type of the xattr? Hmm,,, I added this member to discriminate the prefix of xattr without strncmp(). But it might be a bit ugly design. >>+static inline int jffs2_scan_xref_node_helper(struct jffs2_sb_info *c, >>+ struct jffs2_xattr_ref *new, struct jffs2_xattr_ref *old) >>+{ >>+ /* (uint32_t)new->xc == (uint32_t)old->xc has been made sure. */ >>+ if ((uint32_t)new->ic == (uint32_t)old->ic) { > > > Why the casts? How can this be safe on 64bit machines? This function is called before jffs2_build_xattr_cache() while FS-mounting. In this implementation, xid (uint32 value) is stored in jffs2_xattr_ref->xc before jffs2_build_xattr_cache(), and ino (uint32 value) is stored in jffs2_xattr_ref->ic. After calling jffs2_build_xattr_cache(), xc and ic is used as a pointer which indicate to jffs2_xattr_cache/jffs2_inode_cache. It might be bad manner, but is the following implementation preferable ? struct jffs2_xattr_ref { uint32_t seqno; struct list_head ilist; struct list_head xlist; union { struct jffs2_xattr_cache *xc; uint32_t xid; } x; union { struct jffs2_inode_cache *ic; uint32_t ino; } i; struct jffs2_raw_node_ref *node; }; >>+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_raw_node_ref *raw; - snip - >>+ ref->seqno = je32_to_cpu(rr->seqno); >>+ if (ref->seqno > c->highest_seqno) >>+ c->highest_seqno = ref->seqno; >>+ ref->ic = (void *)je32_to_cpu(rr->ino); >>+ ref->xc = (void *)je32_to_cpu(rr->xid); > > > WTF?!? > > You read a je32 from flash and use that as a pointer?!? > > Please tell me I'm wrong. If we use above emample, we can write it as follows: ref->i.ino = je32_to_cpu(rr->ino); ref->x.xid = je32_to_cpu(rr->xid); In jffs2_build_xattr_cache(), we find up the jffs2_inode_cache and jffs2_xattr_cache by using ino/xid as a key. Then the correct pointers are stored in ref->ic and ref->xc after jffs2_build_xattr_cache(). Is it the situation to use union ? Thanks for attentive comments. -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-09 4:15 ` Kaigai Kohei @ 2005-09-09 7:24 ` Jörn Engel 2005-09-10 4:15 ` KaiGai Kohei 0 siblings, 1 reply; 28+ messages in thread From: Jörn Engel @ 2005-09-09 7:24 UTC (permalink / raw) To: Kaigai Kohei Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley, Ma Yun On Fri, 9 September 2005 13:15:54 +0900, Kaigai Kohei wrote: > Thanks for reviewing in spite of so long patch. > > > Sounds good. > > > > It appears as if your patch or something based on it will win. So > > let's have a look... > > > > I notice that you make a couple of mistakes by copying them from > > existing jffs2 code. You are excused for that, no doubt. But I still > > don't want copies of old mistakes to go in, just because we have a bad > > example. > > Is this using uint32_t, xattr scanning code and so on ? Yes. > > Review is just partial. It's late enough already and I need to get > > some sleep. > > Please take a rest slowly on the weekend. > I'll modify my patch during this period. ;-) Thanks. > >> struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) > >>@@ -169,6 +195,7 @@ struct jffs2_raw_node_ref *jffs2_alloc_r > >> struct jffs2_raw_node_ref *ret; > >> ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); > >> JFFS2_DBG_MEMALLOC("%p\n", ret); > >>+ memset(ret, 0, sizeof(*ret)); /* to guarantee owner==NULL */ > > > > > > This could be optimized a little by using the constructor/destructor > > stuff in slabs. Not too important. > > I'll prepare a tiny and obvious constructor for jffs2_raw_node_ref. > NULL is set in raw->owner by default is important, the method of setting > is not so, I think. Right. One of the least important issues I had. > >>@@ -205,3 +232,35 @@ void jffs2_free_inode_cache(struct jffs2 > >> JFFS2_DBG_MEMALLOC("%p\n", x); > >> kmem_cache_free(inode_cache_slab, x); > >> } > >>+ > >>+#ifdef CONFIG_JFFS2_XATTR > >>+ > >>+struct jffs2_xattr_cache *jffs2_alloc_xattr_cache(void) > >>+{ > >>+ struct jffs2_xattr_cache *xc; > > > > ^^^^^^ > > The name doesn't make sense. You're referring to a single object, not > > the complete cache. Quite confusing. > > > > > >>+ xc = kmem_cache_alloc(xattr_cache_slab, GFP_KERNEL); > > > > ^^^^^ > > This is the cache. A slab is just part of the cache. So please > > rename the two. > > For example, how about the following names ? > "jffs2_xattr_datum" as an alternative of jffs2_xattr_cache. > "xattr_datum_cache" as an alternative of xattr_cache_slab. > > In this case, "xc" defined as jffs2_xattr_cache structure > should be renamed to "xd" defined as jffs2_xattr_datum. Would be fine. > >>diff -rpNU3 mtd-0829/fs/jffs2/nodelist.h > mtd-0829.xattr/fs/jffs2/nodelist.h > >>--- mtd-0829/fs/jffs2/nodelist.h 2005-08-17 18:00:12.000000000 -0400 > >>+++ mtd-0829.xattr/fs/jffs2/nodelist.h 2005-09-06 > 10:28:53.000000000 -0400 > >>@@ -20,6 +20,7 @@ > >> #include <linux/jffs2.h> > >> #include <linux/jffs2_fs_sb.h> > >> #include <linux/jffs2_fs_i.h> > >>+#include <linux/jffs2_fs_x.h> > > > > > > I p a l n. > > > > I prefer a longer name. jffs2_fs_xattr.h would be fine. > > jffs2_xattr.h would also, the "_fs" is more redundant that "attr". > > OK, I'll rename "jffs2_fs_x.h" to "jffs2_xattr.h" and deploy it under > "fs/jffs2" as David says. Can you rename it to xattr.h instead? The prefix has lost its meaning in fs/jffs2/. > >> #ifdef __ECOS > >> #include "os-ecos.h" > >>@@ -80,6 +81,7 @@ struct jffs2_raw_node_ref > >> struct jffs2_raw_node_ref *next_phys; > >> uint32_t flash_offset; > >> uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ > >>+ void *owner; > > > > > > This grows a node reference from 16/24 bytes to 20/32 bytes for 32/64 > > bit architectures. I'm not entirely happy about that, but don't a > > better solution either. > > Indeed, it's involved the growing of object size. > But we must discriminate XATTR node from other non-inode node > and indicate jffs2_xattr_cache/jffs2_xattr_ref object from this. > I didn't have a good idea more than this. > > And, I forgot to enclose the declaration of "void *owner" > by #ifdef - #endif. True. Most likely this idea is stupid, but let me have a try anyway. You could give your xattr/xref objects an inode number - either one per object or a special one shared for all xattr/xref objects. That would open a new bag of worms, but keep the size down. Would it be possible to work with such an approach? > > By itself, this code is not too bad. But it add a little more crap to > > a function that is aching for a rewrite already. Not sure. > > Hmm,,, I wrote the above part of codes with referring to existing code. > If the policy is shown, I'll follow it. How should I do ? In a perfect world, you should send a cleanup patch to break jffs2_scan_eraseblock() into several smaller functions. Have the xattr patch depend on the cleanup. I might still have an old patch that I could send you for inspiration. > >>+ > >>+ nlen = JFFS2_XATTR_NAMELEN(je32_to_cpu(rx->length)); > >>+ vlen = JFFS2_XATTR_VALUELEN(je32_to_cpu(rx->length)); > > > > > > Types don't match. nlen and vlen should become u32. > > Is it enought for "int"? JFFS2_XATTR_NAMELEN has 12-bit length, and > JFFS2_XATTR_VALUELEN has 20-bit length. Makes sense. But endianness conversions between je32 and int (which can be anything from 8-64 bit signed/unsigned per c standard) should be avoided. > > Btw, I agree with Linus. u32 is _much_ nicer than uint32_t, not to > > speak of the uint32_fast_t insanity. > > Is is necessary to correct the part where other uint32_t used. Not really. Without the above ("should become u32") I wouldn't even have mentioned it. Really minor. > >>+ xc = jffs2_find_xattr_cache_xid(c, je32_to_cpu(rx->xid)); > >>+ if (xc) { > >>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Duplicate XID > Found" > >>+ " on node at 0x%08x XID=%d Later one ignored\n", ofs, > xc->xid); > >>+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); > >>+ return 0; > >>+ } > > > > > > How do you deal with xattr reference counting and lifetime? > > When xc->xlist is empty, jffs2_xattr_cache type object will be released. > jffs2_xattr_ref type object is chained to xc->xlist. > list_empty(xc->xlist) means nobady refers this XATTR. ...at which time you also remove the xattr from medium, I assume. Ok. Next question: How do you remove the xattr from medium? How is it garbage collected? > >>+ xtype = jffs2_xattr_prefix_to_xtype(rx->data, nlen); > >>+ if (JFFS2_XATTRTYPE_INVALID==xtype) { > >>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Unsupported > xattr type" > >>+ " on node at 0x%08x\n", ofs); > >>+ USED_SPACE(PAD(je32_to_cpu(rx->totlen))); > >>+ return 0; > >>+ } > > > > > > Why is it required that JFFS2 knows the type of the xattr? > > Hmm,,, I added this member to discriminate the prefix of xattr > without strncmp(). But it might be a bit ugly design. Then why is it required that JFFS2 knows the prefix? > >>+static inline int jffs2_scan_xref_node_helper(struct jffs2_sb_info *c, > >>+ struct jffs2_xattr_ref *new, struct jffs2_xattr_ref > *old) > >>+{ > >>+ /* (uint32_t)new->xc == (uint32_t)old->xc has been made sure. */ > >>+ if ((uint32_t)new->ic == (uint32_t)old->ic) { > > > > > > Why the casts? How can this be safe on 64bit machines? > > This function is called before jffs2_build_xattr_cache() while FS-mounting. > In this implementation, xid (uint32 value) is stored in jffs2_xattr_ref->xc > before jffs2_build_xattr_cache(), and ino (uint32 value) is stored in > jffs2_xattr_ref->ic. > > After calling jffs2_build_xattr_cache(), xc and ic is used as a pointer > which indicate to jffs2_xattr_cache/jffs2_inode_cache. Ok. > It might be bad manner, but is the following implementation preferable ? Yes. And you can use unnamed unions, just remove the "x" and "i". > struct jffs2_xattr_ref { > uint32_t seqno; > struct list_head ilist; > struct list_head xlist; > union { > struct jffs2_xattr_cache *xc; > uint32_t xid; > } x; > union { > struct jffs2_inode_cache *ic; > uint32_t ino; > } i; > struct jffs2_raw_node_ref *node; > }; > > > >>+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_raw_node_ref *raw; > > - snip - > > >>+ ref->seqno = je32_to_cpu(rr->seqno); > >>+ if (ref->seqno > c->highest_seqno) > >>+ c->highest_seqno = ref->seqno; > >>+ ref->ic = (void *)je32_to_cpu(rr->ino); > >>+ ref->xc = (void *)je32_to_cpu(rr->xid); > > > > > > WTF?!? > > > > You read a je32 from flash and use that as a pointer?!? > > > > Please tell me I'm wrong. > > If we use above emample, we can write it as follows: > > ref->i.ino = je32_to_cpu(rr->ino); > ref->x.xid = je32_to_cpu(rr->xid); ret->ino = je32_to_cpu(rr->ino); ... > In jffs2_build_xattr_cache(), we find up the jffs2_inode_cache and > jffs2_xattr_cache by using ino/xid as a key. Then the correct pointers > are stored in ref->ic and ref->xc after jffs2_build_xattr_cache(). > > Is it the situation to use union ? Yes. Makes more sense now. Sorry about the tone. Still, you can take it as an indication that your reuse-old-fields trick is not obvious to new readers. A comment explaining this would be useful. Maybe you already have it further down, in the part I didn't review yet. Good stuff. Jörn -- All art is but imitation of nature. -- Lucius Annaeus Seneca ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-09 7:24 ` Jörn Engel @ 2005-09-10 4:15 ` KaiGai Kohei 2005-09-11 11:46 ` Jörn Engel 0 siblings, 1 reply; 28+ messages in thread From: KaiGai Kohei @ 2005-09-10 4:15 UTC (permalink / raw) To: Jörn Engel Cc: James Morris, Kaigai Kohei, Stephen Smalley, linux-mtd, Ma Yun, David Woodhouse Hi, >>>I p a l n. >>> >>>I prefer a longer name. jffs2_fs_xattr.h would be fine. >>>jffs2_xattr.h would also, the "_fs" is more redundant that "attr". >> >>OK, I'll rename "jffs2_fs_x.h" to "jffs2_xattr.h" and deploy it under >>"fs/jffs2" as David says. > > > Can you rename it to xattr.h instead? The prefix has lost its meaning > in fs/jffs2/. Indeed, jffs2_XXX.h under fs/jffs2 is a bit redundant. >>>>#ifdef __ECOS >>>>#include "os-ecos.h" >>>>@@ -80,6 +81,7 @@ struct jffs2_raw_node_ref >>>> struct jffs2_raw_node_ref *next_phys; >>>> uint32_t flash_offset; >>>> uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ >>>>+ void *owner; >>> >>> >>>This grows a node reference from 16/24 bytes to 20/32 bytes for 32/64 >>>bit architectures. I'm not entirely happy about that, but don't a >>>better solution either. >> >>Indeed, it's involved the growing of object size. >>But we must discriminate XATTR node from other non-inode node >>and indicate jffs2_xattr_cache/jffs2_xattr_ref object from this. >>I didn't have a good idea more than this. >> >>And, I forgot to enclose the declaration of "void *owner" >>by #ifdef - #endif. > > > True. > > Most likely this idea is stupid, but let me have a try anyway. You > could give your xattr/xref objects an inode number - either one per > object or a special one shared for all xattr/xref objects. That would > open a new bag of worms, but keep the size down. > > Would it be possible to work with such an approach? It accompanies a so big remodeling, and hope not to do so, if possible. I think it is important here to be able to distinguish three kinds of raw_node by a reasonable method (1. existing raw_node_ref of inode, 2. existing raw_node_ref without inode, 3. new raw_node_ref for XATTR), and to be able to refer from the third type of raw_node to the jffs2_xattr_(cache|ref) objects. I noticed an idea to resolve it. Any objects refered by the last next_in_ino are allocated by slab. If we can use kmem_ptr_validate() function, we can discriminate whether this pointer indicate xattr_cache/ref or not. Then, we can keep the size of jffs2_raw_node_ref. (the member of 'owner' will go anyware.) The issues of this method are as follows: - If there is an adverse effect by existing of non-inode raw_node_ref whose next_in_ino is not NULL, I must resolve this. - It's necessary to discuss in LKML, since this function is not EXPORT_SYMBOL()ed for kernel loadable modules currently. >>>By itself, this code is not too bad. But it add a little more crap to >>>a function that is aching for a rewrite already. Not sure. >> >>Hmm,,, I wrote the above part of codes with referring to existing code. >>If the policy is shown, I'll follow it. How should I do ? > > > In a perfect world, you should send a cleanup patch to break > jffs2_scan_eraseblock() into several smaller functions. Have the > xattr patch depend on the cleanup. I might still have an old patch > that I could send you for inspiration. I have developed this functionality based on mtd-snapshot-20050829. It means I should develop this based on mtd-snapshot-XXXX + your cleanup patches, isn't it? If so, I think my patch should be moditied for new version. Isn't it fundamental changing ? >>>>+ xc = jffs2_find_xattr_cache_xid(c, je32_to_cpu(rx->xid)); >>>>+ if (xc) { >>>>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Duplicate XID >> >>Found" >> >>>>+ " on node at 0x%08x XID=%d Later one ignored\n", ofs, >> >>xc->xid); >> >>>>+ DIRTY_SPACE(PAD(je32_to_cpu(rx->totlen))); >>>>+ return 0; >>>>+ } >>> >>> >>>How do you deal with xattr reference counting and lifetime? >> >>When xc->xlist is empty, jffs2_xattr_cache type object will be released. >>jffs2_xattr_ref type object is chained to xc->xlist. >>list_empty(xc->xlist) means nobady refers this XATTR. > > > ....at which time you also remove the xattr from medium, I assume. Ok. Yes. jffs2_mark_node_obsolete() is called at this timing. > Next question: How do you remove the xattr from medium? How is it > garbage collected? If my understanding is not wrong, an obsolete XATTR or XREF node is garbase collected as raw-node which does not have inode. (Becase next_in_ino of XATTR/XREF node is NULL.) >>>>+ xtype = jffs2_xattr_prefix_to_xtype(rx->data, nlen); >>>>+ if (JFFS2_XATTRTYPE_INVALID==xtype) { >>>>+ printk(KERN_NOTICE "jffs2_scan_xattr_node(): Unsupported >> >>xattr type" >> >>>>+ " on node at 0x%08x\n", ofs); >>>>+ USED_SPACE(PAD(je32_to_cpu(rx->totlen))); >>>>+ return 0; >>>>+ } >>> >>> >>>Why is it required that JFFS2 knows the type of the xattr? >> >>Hmm,,, I added this member to discriminate the prefix of xattr >>without strncmp(). But it might be a bit ugly design. > > > Then why is it required that JFFS2 knows the prefix? A permittion checking policy depends on XATTR's prefix. For example, only an user has CAP_SYS_ADMIN capability can read/write "trusted.*" xattr. In the "security.*" prefix case, this checking was done in LSM module. Therefore, filesystem must know XATTR's prefix. >>>>+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_raw_node_ref *raw; >> >> - snip - >> >> >>>>+ ref->seqno = je32_to_cpu(rr->seqno); >>>>+ if (ref->seqno > c->highest_seqno) >>>>+ c->highest_seqno = ref->seqno; >>>>+ ref->ic = (void *)je32_to_cpu(rr->ino); >>>>+ ref->xc = (void *)je32_to_cpu(rr->xid); >>> >>> >>>WTF?!? >>> >>>You read a je32 from flash and use that as a pointer?!? >>> >>>Please tell me I'm wrong. >> >>If we use above emample, we can write it as follows: >> >> ref->i.ino = je32_to_cpu(rr->ino); >> ref->x.xid = je32_to_cpu(rr->xid); > > > ret->ino = je32_to_cpu(rr->ino); > ... > > >>In jffs2_build_xattr_cache(), we find up the jffs2_inode_cache and >>jffs2_xattr_cache by using ino/xid as a key. Then the correct pointers >>are stored in ref->ic and ref->xc after jffs2_build_xattr_cache(). >> >>Is it the situation to use union ? > > > Yes. Makes more sense now. Sorry about the tone. > > Still, you can take it as an indication that your reuse-old-fields > trick is not obvious to new readers. A comment explaining this would > be useful. Maybe you already have it further down, in the part I > didn't review yet. Sorry for tricky coding without comments. I'll pay attention of such coding style. Thanks, -- KaiGai Kohei <kaigai@kaigai.gr.jp> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-10 4:15 ` KaiGai Kohei @ 2005-09-11 11:46 ` Jörn Engel 2005-09-12 2:17 ` Kaigai Kohei 0 siblings, 1 reply; 28+ messages in thread From: Jörn Engel @ 2005-09-11 11:46 UTC (permalink / raw) To: KaiGai Kohei Cc: James Morris, Ma Yun, linux-mtd, David Woodhouse, Stephen Smalley On Sat, 10 September 2005 13:15:39 +0900, KaiGai Kohei wrote: > > >Most likely this idea is stupid, but let me have a try anyway. You > >could give your xattr/xref objects an inode number - either one per > >object or a special one shared for all xattr/xref objects. That would > >open a new bag of worms, but keep the size down. > > > >Would it be possible to work with such an approach? > > It accompanies a so big remodeling, and hope not to do so, if possible. > > I think it is important here to be able to distinguish three kinds of > raw_node by a reasonable method (1. existing raw_node_ref of inode, > 2. existing raw_node_ref without inode, 3. new raw_node_ref for XATTR), > and to be able to refer from the third type of raw_node to > the jffs2_xattr_(cache|ref) objects. Yes. 1 and 2 are currently distinguished by next_in_ino being either either valid or NULL. > I noticed an idea to resolve it. > Any objects refered by the last next_in_ino are allocated by slab. > If we can use kmem_ptr_validate() function, we can discriminate whether > this pointer indicate xattr_cache/ref or not. Then, we can keep the > size of jffs2_raw_node_ref. > (the member of 'owner' will go anyware.) Sounds scary. But in principle, you are right. In case of either 1 or 3, we attach "something" to next_in_ino. Now we only have to decide whether "something" is an inode, or the new xattr-struff. If you add an extra field to jffs2_inode_cache (which also is grossly misnamed, old mistake), you can do so. And since inodes are a lot less frequent than raw nodes, the memory consumption should have improved quite a bit. Or you could play some magic like reserving ino 0xffffffff for xattr_object, 0xfffffffe for xattr_ref, etc. and simply decide by jffs2_inode_cache->ino having one of the magic numbers. Then you would need the extra field only for xattr_obj or xattr_ref. > The issues of this method are as follows: > - If there is an adverse effect by existing of non-inode raw_node_ref > whose next_in_ino is not NULL, I must resolve this. Yep. > - It's necessary to discuss in LKML, since this function is not > EXPORT_SYMBOL()ed for kernel loadable modules currently. Where it won't receive a lot of love. Do it with some variant of what I outlined above. > >>>By itself, this code is not too bad. But it add a little more crap to > >>>a function that is aching for a rewrite already. Not sure. > >> > >>Hmm,,, I wrote the above part of codes with referring to existing code. > >>If the policy is shown, I'll follow it. How should I do ? > > > > > >In a perfect world, you should send a cleanup patch to break > >jffs2_scan_eraseblock() into several smaller functions. Have the > >xattr patch depend on the cleanup. I might still have an old patch > >that I could send you for inspiration. > > I have developed this functionality based on mtd-snapshot-20050829. > It means I should develop this based on mtd-snapshot-XXXX + your > cleanup patches, isn't it? No. You should develop two patches based on current cvs: [PATCH 1/2] Cleanup of jffs2_scan_eraseblock() [PATCH 2/2] xattr for jffs2 And you may or may not use my previous attempt as a basis, that's your decision. If you simply added further mess to jffs2_scan_eraseblock(), as you do today, people will see horrible code become even worse. Who likes that? If instead you do a cleanup first, people will see that you helped make the function much more readable and maintainable. Anyone objecting your merge will have a tough time arguing now. > >Next question: How do you remove the xattr from medium? How is it > >garbage collected? > > If my understanding is not wrong, an obsolete XATTR or XREF node > is garbase collected as raw-node which does not have inode. > (Becase next_in_ino of XATTR/XREF node is NULL.) Ah, yes. Makes sense now, even to stupid people like me. > >Then why is it required that JFFS2 knows the prefix? > > A permittion checking policy depends on XATTR's prefix. > For example, only an user has CAP_SYS_ADMIN capability > can read/write "trusted.*" xattr. In the "security.*" > prefix case, this checking was done in LSM module. > Therefore, filesystem must know XATTR's prefix. This is just wrong. I agree that something inside the kernel must know whether users need specific capabilities to read/write the xattr. Sure. But why in hell does it have to be JFFS2? How do other filesystems deal with this? ext[23] and reiserfs (v3) all have xattr, I believe. > >Still, you can take it as an indication that your reuse-old-fields > >trick is not obvious to new readers. A comment explaining this would > >be useful. Maybe you already have it further down, in the part I > >didn't review yet. > > Sorry for tricky coding without comments. I'll pay attention of such > coding style. Thanks. This is one of the reasons for reviews. You, being the author, have a bad judgement about what people new to the code can and cannot easily understand. You're not new to the code. ;) Jörn -- Measure. Don't tune for speed until you've measured, and even then don't unless one part of the code overwhelms the rest. -- Rob Pike ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-11 11:46 ` Jörn Engel @ 2005-09-12 2:17 ` Kaigai Kohei 2005-09-12 6:40 ` Jörn Engel 0 siblings, 1 reply; 28+ messages in thread From: Kaigai Kohei @ 2005-09-12 2:17 UTC (permalink / raw) To: Jörn Engel Cc: James Morris, Ma Yun, linux-mtd, David Woodhouse, Stephen Smalley Hi, >>It accompanies a so big remodeling, and hope not to do so, if possible. >> >>I think it is important here to be able to distinguish three kinds of >>raw_node by a reasonable method (1. existing raw_node_ref of inode, >>2. existing raw_node_ref without inode, 3. new raw_node_ref for XATTR), >>and to be able to refer from the third type of raw_node to >>the jffs2_xattr_(cache|ref) objects. > > > Yes. 1 and 2 are currently distinguished by next_in_ino being either > either valid or NULL. > > >>I noticed an idea to resolve it. >>Any objects refered by the last next_in_ino are allocated by slab. >>If we can use kmem_ptr_validate() function, we can discriminate whether >>this pointer indicate xattr_cache/ref or not. Then, we can keep the >>size of jffs2_raw_node_ref. >>(the member of 'owner' will go anyware.) > > > Sounds scary. But in principle, you are right. In case of either 1 > or 3, we attach "something" to next_in_ino. Now we only have to > decide whether "something" is an inode, or the new xattr-struff. > > If you add an extra field to jffs2_inode_cache (which also is grossly > misnamed, old mistake), you can do so. And since inodes are a lot > less frequent than raw nodes, the memory consumption should have > improved quite a bit. I prefer this kind of approach than other one. It's simple solution. I'm afraid of breaking something to be assumed implicitly by reserving some inode-numbers. And, how about such an idea ? Currently, the variable of 'state' in jffs2_inode_cache is defined as int type, but the range of this variable is between 0 and 6. In general case, 'int' has 32-bit width. It's so much for this field. For example, we can redefine the 'state' as 16-bit width variable, and insert a new 16-bit width variable to discriminate own object. If acceptable, it can also keep the memory comsumption for jffs2_inode_cache. In this plan, jffs2_inode_cahce and jffs2_xattr_datum will be defined as follows: struct jffs2_inode_cache { struct jffs2_full_dirent *scan_dents; uint16_t class; /* =0 means jffs2_inode_cache */ uint16_t state; : struct jffs2_xattr_datum { void *dummy; /* always NULL */ uint16_t class; /* =1 means jffs2_xattr_datum */ : >>>In a perfect world, you should send a cleanup patch to break >>>jffs2_scan_eraseblock() into several smaller functions. Have the >>>xattr patch depend on the cleanup. I might still have an old patch >>>that I could send you for inspiration. >> >>I have developed this functionality based on mtd-snapshot-20050829. >>It means I should develop this based on mtd-snapshot-XXXX + your >>cleanup patches, isn't it? > > > No. You should develop two patches based on current cvs: > [PATCH 1/2] Cleanup of jffs2_scan_eraseblock() > [PATCH 2/2] xattr for jffs2 > > And you may or may not use my previous attempt as a basis, that's your > decision. I see, jffs2_scan_eraseblock() is indeed a bit long. Please send your attempt patches at first. I'll try to cleanup it for the basis of XATTR patch. >>>Then why is it required that JFFS2 knows the prefix? >> >>A permittion checking policy depends on XATTR's prefix. >>For example, only an user has CAP_SYS_ADMIN capability >>can read/write "trusted.*" xattr. In the "security.*" >>prefix case, this checking was done in LSM module. >>Therefore, filesystem must know XATTR's prefix. > > > This is just wrong. I agree that something inside the kernel must > know whether users need specific capabilities to read/write the xattr. > Sure. But why in hell does it have to be JFFS2? > > How do other filesystems deal with this? ext[23] and reiserfs (v3) > all have xattr, I believe. Indeed, more upper layer might be able to manage this kind of decision making as you say. But it's transferred to the filesystem currently. All of them (ext2/3, reiser and so on) have own permission checking codes like 'fs/ext2/xattr_trusted.c', and I imitated those implementations. I don't know the reason correctly. Thanks, -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-12 2:17 ` Kaigai Kohei @ 2005-09-12 6:40 ` Jörn Engel 2005-09-12 11:01 ` Kaigai Kohei 0 siblings, 1 reply; 28+ messages in thread From: Jörn Engel @ 2005-09-12 6:40 UTC (permalink / raw) To: Kaigai Kohei Cc: James Morris, Ma Yun, linux-mtd, David Woodhouse, Stephen Smalley On Mon, 12 September 2005 11:17:09 +0900, Kaigai Kohei wrote: > > > >Sounds scary. But in principle, you are right. In case of either 1 > >or 3, we attach "something" to next_in_ino. Now we only have to > >decide whether "something" is an inode, or the new xattr-struff. > > > >If you add an extra field to jffs2_inode_cache (which also is grossly > >misnamed, old mistake), you can do so. And since inodes are a lot > >less frequent than raw nodes, the memory consumption should have > >improved quite a bit. > > I prefer this kind of approach than other one. It's simple solution. > I'm afraid of breaking something to be assumed implicitly by reserving > some inode-numbers. > > And, how about such an idea ? > Currently, the variable of 'state' in jffs2_inode_cache is defined > as int type, but the range of this variable is between 0 and 6. > In general case, 'int' has 32-bit width. It's so much for this field. > > For example, we can redefine the 'state' as 16-bit width variable, > and insert a new 16-bit width variable to discriminate own object. > If acceptable, it can also keep the memory comsumption for > jffs2_inode_cache. Yes, that sounds fine. > In this plan, jffs2_inode_cahce and jffs2_xattr_datum will be defined as > follows: > struct jffs2_inode_cache { > struct jffs2_full_dirent *scan_dents; > uint16_t class; /* =0 means jffs2_inode_cache */ > uint16_t state; > : > > struct jffs2_xattr_datum { > void *dummy; /* always NULL */ > uint16_t class; /* =1 means jffs2_xattr_datum */ > : You could even make the class a u8. For inodes, the remaining 8 bits would just be reserved space for proper alignment. If either of your xattr object could make use of the extra byte, it would be worth it. If not, just ignore my silly idea. > I see, jffs2_scan_eraseblock() is indeed a bit long. > Please send your attempt patches at first. > I'll try to cleanup it for the basis of XATTR patch. It was 34 patches going over all of jffs2, actually. I guess it's easier to send the changed scan.c. The pseudo-random stuff was controversial, so I lost interest after a while. > >This is just wrong. I agree that something inside the kernel must > >know whether users need specific capabilities to read/write the xattr. > >Sure. But why in hell does it have to be JFFS2? > > > >How do other filesystems deal with this? ext[23] and reiserfs (v3) > >all have xattr, I believe. > > Indeed, more upper layer might be able to manage this kind of decision > making as you say. But it's transferred to the filesystem currently. > All of them (ext2/3, reiser and so on) have own permission checking codes > like 'fs/ext2/xattr_trusted.c', and I imitated those implementations. > I don't know the reason correctly. The code is not exactly easy to follow. But it appears as if they used that stuff for compression. If prefix is "trusted.", just use an e_name_index of 4 and drop the prefix. It can be reconstructed on reads. Reiserfs doesn't worry about compression and has should-be-generic code only. I guess it would make sense to copy the compression stuff from ext2, at least the concept. Jörn -- Don't patch bad code, rewrite it. -- Kernigham and Pike, according to Rusty ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-12 6:40 ` Jörn Engel @ 2005-09-12 11:01 ` Kaigai Kohei 2005-09-28 8:44 ` Kaigai Kohei 0 siblings, 1 reply; 28+ messages in thread From: Kaigai Kohei @ 2005-09-12 11:01 UTC (permalink / raw) To: Jörn Engel Cc: James Morris, Ma Yun, linux-mtd, David Woodhouse, Stephen Smalley Hi, >>In this plan, jffs2_inode_cahce and jffs2_xattr_datum will be defined as >>follows: >>struct jffs2_inode_cache { >> struct jffs2_full_dirent *scan_dents; >> uint16_t class; /* =0 means jffs2_inode_cache */ >> uint16_t state; >> : >> >>struct jffs2_xattr_datum { >> void *dummy; /* always NULL */ >> uint16_t class; /* =1 means jffs2_xattr_datum */ >> : > > > You could even make the class a u8. For inodes, the remaining 8 bits > would just be reserved space for proper alignment. If either of your > xattr object could make use of the extra byte, it would be worth it. > If not, just ignore my silly idea. u8 type variable has enough width for discernment, I think. And, is it possible to name the remaining 8-bit 'flags' than 'reserved' or 'unused' in jffs2_inode_cache/jffs2_xattr_datum ? In my implementation, 'uint8_t ilist_checked' is added in jffs2_inode_cache for flag purpose, and 'uint16_t flags' exists in jffs2_xattr_datum(jffs2_xattr_cache in old name). If we can stuff those into the remaining 8-bit, each object size will be reduced by 4-byte, I think. >>I see, jffs2_scan_eraseblock() is indeed a bit long. >>Please send your attempt patches at first. >>I'll try to cleanup it for the basis of XATTR patch. > > > It was 34 patches going over all of jffs2, actually. I guess it's > easier to send the changed scan.c. The pseudo-random stuff was > controversial, so I lost interest after a while. '34 patches over all of jffs2' is over my expectation, orz. I prefer to pay attention on only the changed scan.c, if possible... Please send it to me, >>>This is just wrong. I agree that something inside the kernel must >>>know whether users need specific capabilities to read/write the xattr. >>>Sure. But why in hell does it have to be JFFS2? >>> >>>How do other filesystems deal with this? ext[23] and reiserfs (v3) >>>all have xattr, I believe. >> >>Indeed, more upper layer might be able to manage this kind of decision >>making as you say. But it's transferred to the filesystem currently. >>All of them (ext2/3, reiser and so on) have own permission checking codes >>like 'fs/ext2/xattr_trusted.c', and I imitated those implementations. >>I don't know the reason correctly. > > > The code is not exactly easy to follow. But it appears as if they > used that stuff for compression. If prefix is "trusted.", just use an > e_name_index of 4 and drop the prefix. It can be reconstructed on > reads. Hmm, it's functionality certainly effective for reduction of memory and medium usage. It's not difficult to implement, I think. I'll modify the method of storing xattr in next. Thanks, -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-12 11:01 ` Kaigai Kohei @ 2005-09-28 8:44 ` Kaigai Kohei 2005-09-29 7:45 ` Jörn Engel 2005-10-12 4:25 ` Kaigai Kohei 0 siblings, 2 replies; 28+ messages in thread From: Kaigai Kohei @ 2005-09-28 8:44 UTC (permalink / raw) To: Jörn Engel, linux-mtd Cc: James Morris, Ma Yun, David Woodhouse, Stephen Smalley, Kaigai Kohei [-- Attachment #1: Type: text/plain, Size: 2714 bytes --] Hi, I revised the previous xattr patch in jffs2 based on some suggestions. [1] scanning xattr related node for cleaned-up scan.c. (jffs2_xattr_scan.c-cleanup.patch) [2] enables xattr support in jffs2. (jffs2_xattr_take-3.patch) (*) We can apply jffs2_xattr_take-3.patch for current CVS independently. The following points are updated. - jffs2_scan_xattr_node() and jffs2_scan_xref_node() are implemented with considering the cleaned-up scan.c. (jffs2_xattr_scan.c-cleanup.patch) Since I can't build the cleaned-up source, this patch has not been verified yet. But same code is included in jffs2_xattr_take-3.patch, and it works correctly. - A deadlock problem was detected in take-2 patch. Thus, the order of acquiring semaphore was changed. There was down_write(&c->xattr_sem) under down(&c->alloc_sem) in take-2 patch. Now, I avoid such an implementation. In do_jffs2_setxattr(), xattr_sem is acquired after alloc_sem is done. - "include/linux/jffs2_fs_x.h" was moved to "fs/jffs2/xattr.h". - 'void *owner' in struct jffs2_raw_node_ref was removed. - 'int state' in struct jffs2_inode_cache was removed, and 'u8 class' and 'u8 flags', 'uint16_t state' were added on instead. GC uses raw->next_in_ino for reverse-reference to xattr_datum/xattr_ref from jffs_raw_node_ref. - the declaration of jffs2_xattr_ref was changed. The strange cast was removed, and the variables not-used concurrently are defined as union type. ('uint32_t xid' and 'struct jffs2_xattr_datum *xd' are not used concurrently. 'uint32_t ino' and 'struct jffs2_inode_cache *ic' are not used concurrently.) - A xattr prefixes like 'security.' are represented by 8bit code, and omit it from name-strings. - jffs2_xattr_datum->xlist was removed, and 'uint32_t refcnt' was added. Scanning inodes which share the same xattr_datum is not used. Thus, this list is not necessary and enough in reference counter. - some functions/variables/structures are renamed. struct jffs2_xattr_cache -> struct jffs2_xattr_datum kmem_cache_t *xattr_cache_slab -> xattr_datum_cache kmem_cache_t *xattr_ref_slab -> xattr_ref_cache jffs2_init_xattr_caches() -> jffs2_init_xattr_subsystem() jffs2_build_xattr_caches() -> jffs2_build_xattr_subsystem() jffs2_clear_xattr_caches() -> jffs2_clear_xattr_subsystem() ,,,and so on. The remaining work is as follows: - Additional revising besed on any comments. - Test & Stabilization. - The implementation for POSIX ACL support. - The implementation for XATTR support in mkfs.jffs2. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Thanks, -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: jffs2_xattr_take-3.patch --] [-- Type: text/x-patch, Size: 55162 bytes --] diff -prNU3 mtd-0927/fs/Kconfig mtd-0927.xattr/fs/Kconfig --- mtd-0927/fs/Kconfig 2005-09-07 04:34:52.000000000 -0400 +++ mtd-0927.xattr/fs/Kconfig 2005-09-26 19:17:32.000000000 -0400 @@ -64,6 +64,23 @@ config JFFS2_FS_WRITEBUFFER - NOR flash with transparent ECC - DataFlash +config JFFS2_XATTR + bool "JFFS2 XATTR support (EXPERIMENTAL)" + depends on JFFS2_FS && EXPERIMENTAL + default n + help + This enables the Extended Attributes(XATTR) support in JFFS2. + XATTR is name:value pairs associated with inodes by the kernel + or by users. + It's neccesary for persistent security context in SELinux. + + In addition, the following kinds of prefixes are supported now. + - security.* + - user.* + - trusted.* + + - system.posix_acl_{access|default} are in TODO list now. + config JFFS2_SUMMARY bool "JFFS2 summary support (EXPERIMENTAL)" depends on JFFS2_FS && EXPERIMENTAL diff -prNU3 mtd-0927/fs/jffs2/Makefile.common mtd-0927.xattr/fs/jffs2/Makefile.common --- mtd-0927/fs/jffs2/Makefile.common 2005-09-07 04:34:53.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/Makefile.common 2005-09-26 19:16:05.000000000 -0400 @@ -12,6 +12,7 @@ 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_XATTR) += xattr.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff -prNU3 mtd-0927/fs/jffs2/build.c mtd-0927.xattr/fs/jffs2/build.c --- mtd-0927/fs/jffs2/build.c 2005-09-21 11:52:33.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/build.c 2005-09-26 19:06:31.000000000 -0400 @@ -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; diff -prNU3 mtd-0927/fs/jffs2/dir.c mtd-0927.xattr/fs/jffs2/dir.c --- mtd-0927/fs/jffs2/dir.c 2005-09-07 04:34:54.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/dir.c 2005-09-26 19:06:31.000000000 -0400 @@ -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 }; /***********************************************************************/ diff -prNU3 mtd-0927/fs/jffs2/file.c mtd-0927.xattr/fs/jffs2/file.c --- mtd-0927/fs/jffs2/file.c 2005-09-07 04:34:54.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/file.c 2005-09-26 19:06:31.000000000 -0400 @@ -57,7 +57,12 @@ struct file_operations jffs2_file_operat struct inode_operations jffs2_file_inode_operations = { - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; struct address_space_operations jffs2_file_address_operations = diff -prNU3 mtd-0927/fs/jffs2/fs.c mtd-0927.xattr/fs/jffs2/fs.c --- mtd-0927/fs/jffs2/fs.c 2005-09-07 04:34:54.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/fs.c 2005-09-26 19:06:31.000000000 -0400 @@ -218,6 +218,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); } @@ -490,9 +491,12 @@ int jffs2_do_fill_super(struct super_blo } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); - if ((ret = jffs2_do_mount_fs(c))) + if ((ret = jffs2_init_xattr_subsystem(c))) goto out_inohash; + if ((ret = jffs2_do_mount_fs(c))) + goto out_xattr; + ret = -EINVAL; D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); @@ -523,6 +527,8 @@ int jffs2_do_fill_super(struct super_blo vfree(c->blocks); else kfree(c->blocks); + out_xattr: + jffs2_clear_xattr_subsystem(c); out_inohash: kfree(c->inocache_list); out_wbuf: diff -prNU3 mtd-0927/fs/jffs2/gc.c mtd-0927.xattr/fs/jffs2/gc.c --- mtd-0927/fs/jffs2/gc.c 2005-09-07 04:34:54.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/gc.c 2005-09-26 19:06:31.000000000 -0400 @@ -258,6 +258,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. */ diff -prNU3 mtd-0927/fs/jffs2/malloc.c mtd-0927.xattr/fs/jffs2/malloc.c --- mtd-0927/fs/jffs2/malloc.c 2005-09-20 10:27:34.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/malloc.c 2005-09-26 21:01:14.000000000 -0400 @@ -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_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_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_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,35 @@ void jffs2_free_inode_cache(struct jffs2 dbg_memalloc("%p\n", x); kmem_cache_free(inode_cache_slab, x); } + +#ifdef CONFIG_JFFS2_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); + 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); + 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 diff -prNU3 mtd-0927/fs/jffs2/nodelist.c mtd-0927.xattr/fs/jffs2/nodelist.c --- mtd-0927/fs/jffs2/nodelist.c 2005-09-21 09:28:35.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/nodelist.c 2005-09-26 19:06:31.000000000 -0400 @@ -937,6 +937,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; } diff -prNU3 mtd-0927/fs/jffs2/nodelist.h mtd-0927.xattr/fs/jffs2/nodelist.h --- mtd-0927/fs/jffs2/nodelist.h 2005-09-07 04:34:54.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/nodelist.h 2005-09-26 19:21:53.000000000 -0400 @@ -20,6 +20,7 @@ #include <linux/jffs2.h> #include <linux/jffs2_fs_sb.h> #include <linux/jffs2_fs_i.h> +#include "xattr.h" #include "summary.h" #ifdef __ECOS @@ -107,11 +108,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_XATTR + struct list_head ilist; +#endif }; /* Inode states for 'state' above. We need the 'GC' state to prevent @@ -125,6 +131,8 @@ 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 INOCACHE_HASHSIZE 128 /* @@ -375,6 +383,13 @@ void jffs2_free_node_frag(struct jffs2_n struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); void jffs2_free_inode_cache(struct jffs2_inode_cache *); +#ifdef CONFIG_JFFS2_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 /* CONFIG_JFFS2_XATTR */ + /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); diff -prNU3 mtd-0927/fs/jffs2/readinode.c mtd-0927.xattr/fs/jffs2/readinode.c --- mtd-0927/fs/jffs2/readinode.c 2005-09-20 10:27:34.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/readinode.c 2005-09-26 19:06:31.000000000 -0400 @@ -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) { diff -prNU3 mtd-0927/fs/jffs2/scan.c mtd-0927.xattr/fs/jffs2/scan.c --- mtd-0927/fs/jffs2/scan.c 2005-09-21 09:05:22.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/scan.c 2005-09-26 19:06:31.000000000 -0400 @@ -309,6 +309,159 @@ int jffs2_scan_classify_jeb(struct jffs2 return BLK_STATE_ALLDIRTY; } +#ifdef CONFIG_JFFS2_XATTR + +#if 1 /* In cleaned up scan.c, this section is not necessary. */ +static inline uint32_t dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + uint32_t unpadded_space) +{ + DIRTY_SPACE(PAD(unpadded_space)); + + return PAD(unpadded_space);; +} + +static inline uint32_t used_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + uint32_t unpadded_space) +{ + USED_SPACE(PAD(unpadded_space)); + + return PAD(unpadded_space); +} + +#endif /* In cleaned up scan.c, this section is not necessary. */ + +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_xattr_datum *xd; + struct jffs2_raw_node_ref *raw; + uint32_t crc; + + crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr)-8); + if (crc != je32_to_cpu(rx->node_crc)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_find_xattr_datum(c, je32_to_cpu(rx->xid)); + if (xd) { + printk(KERN_NOTICE "%s() duplicate xid=%u found. " + "on node at 0x%08x, later one is ignored.\n", + __FUNCTION__, je32_to_cpu(rx->xid), ofs); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + crc = crc32(0, rx->data, rx->name_len + 1 + je16_to_cpu(rx->value_len)); + if (crc != je32_to_cpu(rx->data_crc)) { + printk(KERN_NOTICE "%s data CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return -ENOMEM; + init_xattr_datum(xd); + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + jffs2_free_xattr_datum(xd); + return -ENOMEM; + } + + xd->xid = je32_to_cpu(rx->xid); + if (xd->xid > c->highest_xseqno) + c->highest_xseqno = xd->xid; + xd->xprefix = rx->xprefix; /* 8bit width */ + xd->name_len = rx->name_len; /* 8bit width */ + xd->value_len = je16_to_cpu(rx->value_len); + xd->data_crc = je32_to_cpu(rx->data_crc); + xd->node = raw; + + raw->__totlen = PAD(je32_to_cpu(rx->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(c, jeb, je32_to_cpu(rx->totlen)); + + jffs2_attach_xattr_datum(c, xd); + + 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_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)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rr->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rr->totlen)); + return 0; + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return -ENOMEM; + init_xattr_ref(ref); + + 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->seqno = je32_to_cpu(rr->seqno); + if (ref->seqno > c->highest_xseqno) + c->highest_xseqno = ref->seqno; + ref->ino = je32_to_cpu(rr->ino); + ref->xid = je32_to_cpu(rr->xid); + ref->node = raw; + + 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(c, jeb, je32_to_cpu(rr->totlen)); + + list_add_tail(&ref->ilist, &c->xattr_temp); + + 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; @@ -617,6 +770,43 @@ scan_more: ofs += PAD(je32_to_cpu(node->totlen)); break; +#ifdef CONFIG_JFFS2_XATTR + case JFFS2_NODETYPE_XATTR: + if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { + /* FIXME: In cleaned-up scan.c, this If-block is not necessary. */ + 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); + 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)) { + /* FIXME: In cleaned-up scan.c, this If-block is not necessary. */ + 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); + if (err) + return err; + ofs += PAD(je32_to_cpu(node->totlen)); + break; +#endif /* CONFIG_JFFS2_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) { @@ -724,6 +914,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; diff -prNU3 mtd-0927/fs/jffs2/super.c mtd-0927.xattr/fs/jffs2/super.c --- mtd-0927/fs/jffs2/super.c 2005-09-07 04:34:55.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/super.c 2005-09-26 19:06:31.000000000 -0400 @@ -151,6 +151,7 @@ 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; ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); @@ -293,6 +294,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); diff -prNU3 mtd-0927/fs/jffs2/symlink.c mtd-0927.xattr/fs/jffs2/symlink.c --- mtd-0927/fs/jffs2/symlink.c 2005-07-17 07:13:47.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/symlink.c 2005-09-26 19:06:31.000000000 -0400 @@ -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 int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) diff -prNU3 mtd-0927/fs/jffs2/write.c mtd-0927.xattr/fs/jffs2/write.c --- mtd-0927/fs/jffs2/write.c 2005-09-07 04:34:55.000000000 -0400 +++ mtd-0927.xattr/fs/jffs2/write.c 2005-09-26 19:06:31.000000000 -0400 @@ -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)); diff -prNU3 mtd-0927/fs/jffs2/xattr.c mtd-0927.xattr/fs/jffs2/xattr.c --- mtd-0927/fs/jffs2/xattr.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-0927.xattr/fs/jffs2/xattr.c 2005-09-26 19:06:31.000000000 -0400 @@ -0,0 +1,1046 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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" + +/* ---- decralation of internal use functions ----------------- */ +static void reclaim_xattr_datum(struct jffs2_sb_info *c); +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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); +static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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); +static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref); + +static int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, + char *buffer, size_t size); +static int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, + const char *buffer, size_t size, int flags); + +/* ---- XATTR Handler for "user.*" --------------------- */ +static int jffs2_user_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + int ret; + + if (!strcmp(name, "")) + return -EINVAL; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + 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) +{ + int ret; + + if (!strcmp(name, "") == 0) + return -EINVAL; + + if ( !S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + 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) +{ + int retlen = sizeof(XATTR_USER_PREFIX) + name_len; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_USER_PREFIX); + strcpy(list+sizeof(XATTR_USER_PREFIX)-1, name); + } + + return retlen; +} + +static struct xattr_handler jffs2_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .list = jffs2_user_listxattr, + .set = jffs2_user_setxattr, + .get = jffs2_user_getxattr +}; + +/* ---- XATTR Handler for "trusted.*" ------------------ */ +static int jffs2_trusted_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + 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; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + 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) +{ + int retlen = sizeof(XATTR_TRUSTED_PREFIX) + name_len; + + if (!capable(CAP_SYS_ADMIN)) + return 0; /* ignore this entry */ + + if (list && retlen<=list_size) { + strcpy(list, XATTR_TRUSTED_PREFIX); + strcpy(list+sizeof(XATTR_TRUSTED_PREFIX)-1, name); + } + + return retlen; +} + +static struct xattr_handler jffs2_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .list = jffs2_trusted_listxattr, + .set = jffs2_trusted_setxattr, + .get = jffs2_trusted_getxattr +}; + +/* ---- 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 = sizeof(XATTR_SECURITY_PREFIX) + name_len; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_SECURITY_PREFIX); + strcpy(list+sizeof(XATTR_SECURITY_PREFIX)-1, name); + } + + return retlen; +} + +static struct xattr_handler jffs2_security_xattr_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .list = jffs2_security_listxattr, + .set = jffs2_security_setxattr, + .get = jffs2_security_getxattr +}; +/* ----------------------------------------------------- */ +struct xattr_handler *jffs2_xattr_handlers[] = { + &jffs2_user_xattr_handler, + &jffs2_security_xattr_handler, + &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; + case JFFS2_XPREFIX_SECURITY: + ret = &jffs2_security_xattr_handler; + break; + case JFFS2_XPREFIX_TRUSTED: + ret = &jffs2_trusted_xattr_handler; + break; + default: + ret = NULL; + break; + } + return ret; +} + +static uint32_t jffs2_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); +} +/* ---- Build-up and Destruct XATTR-Cache functions ----------- */ +int jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) +{ + int i; + + c->xattrindex = kmalloc((XATTRINDEX_HASHSIZE+1)*sizeof(struct list_head), GFP_KERNEL); + if (!c->xattrindex) + return -ENOMEM; + + init_rwsem(&c->xattr_sem); + + for (i=0; i<=XATTRINDEX_HASHSIZE; i++) + INIT_LIST_HEAD(&c->xattrindex[i]); + + //c->xdatum_mem_threshold = 20 * 1024; /* Default 20KB */ + c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */ + + return 0; +} + +void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_xattr_ref *ref, *_ref; + int i; + + if (!c->xattrindex) + return; /* not initialized */ + + 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); + } + } + + kfree(c->xattrindex); + c->xattrindex = NULL; +} + +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; + + 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); + /* 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) { + printk(KERN_NOTICE "building xref {ino=%u, xid=%u} is not found.\n", + ref->xid, ref->ino); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + jffs2_free_xattr_ref(ref); + continue; + } + ref->xd = xd; + ref->ic = ic; + xd->refcnt++; + list_add_tail(&ref->ilist, &ic->ilist); + D1(printk(KERN_NOTICE "bind XREF{ino=%u, xid=%u}\n", ic->ino, xd->xid)); + } + /* 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) { + printk(KERN_NOTICE "unrefered xattr_datum found xid=%u\n", xd->xid); + delete_xattr_datum(c, xd); + } + } + } + /* build complete */ + printk(KERN_NOTICE "%s complete.\n", __FUNCTION__); +} + +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_attach_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + int i = xd->xid % XATTRINDEX_HASHSIZE; + + /* It's only used in scanning process. */ + BUG_ON(!(c->flags & JFFS2_SB_FLAG_SCANNING)); + + list_add_tail(&xd->xindex, &c->xattrindex[i]); +} + +/* ---- Internal XATTR related functions ------------------------------------ */ +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; + + before = c->xdatum_mem_usage; + target = c->xdatum_mem_usage * 4 / 5; /* 20% reduction */ + while(1) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[index], xindex) { + if (xd->flags & JFFS2_XDATUM_FLAGS_HOT) { + xd->flags &= ~JFFS2_XDATUM_FLAGS_HOT; + } else { + unload_xattr_datum(c, xd); + } + if (c->xdatum_mem_usage <= target) + goto out; + } + index = (index+1) % XATTRINDEX_HASHSIZE; + } + out: + printk(KERN_NOTICE "JFFS2: reclaim_xattr_cache() before: %d Byte after: %d Byte\n", + before, c->xdatum_mem_usage); +} + +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; + + D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + retry: + length = xd->name_len + 1 + xd->value_len; + data = kmalloc(length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + BUG_ON(!xd->node); + ret = jffs2_flash_read(c, ref_offset(xd->node)+sizeof(struct jffs2_raw_xattr), + length, &readlen, data); + + if (ret || length!=readlen) { + printk(KERN_WARNING "jffs2_flash_read()=%d, request: %d, readlen: %d, at 0x%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) { + printk(KERN_WARNING "node CRC failed (JFFS2_NODETYPE_XREF)" + " at 0x%08x, read: 0x%08x calculated: 0x%08x\n", + ref_offset(xd->node), xd->data_crc, crc); + kfree(data); + return -EIO; + } + + xd->flags = JFFS2_XDATUM_FLAGS_HOT; + xd->xname = data; + xd->xvalue = data+xd->name_len+1; + + c->xdatum_mem_usage += length; + + xd->hashkey = jffs2_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 (c->xdatum_mem_usage > c->xdatum_mem_threshold && retry==0) { + reclaim_xattr_datum(c); + if (!xd->xname) { + retry = 1; + goto retry; + } + } + + return 0; +} + +static inline int load_xattr_datum_nolock(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem); */ + if (unlikely(!xd->xname)) + return do_load_xattr_datum(c, xd); + return 0; +} + +static inline int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_read(xattr_sem); */ + int ret; + + if (likely(xd->xname)) + return 0; + + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + ret = load_xattr_datum_nolock(c, xd); + downgrade_write(&c->xattr_sem); + + return ret; +} + +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + + 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 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 ret, totlen; + + ret = load_xattr_datum_nolock(c, xd); + if (ret) + return ret; + + vecs[0].iov_base = ℞ + 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(totlen); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node)-4)); + + rx.xid = cpu_to_je32(xd->xid); + 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)-8)); + + ret = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); + if (ret || totlen!=length) { + printk(KERN_NOTICE "%s:%d Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", + __FILE__, __LINE__, totlen, phys_ofs, ret, length); + 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; + } + /* success */ + raw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, raw); + if (xd->node) { + xd->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, xd->node); + } + xd->node = raw; + + 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, nlen; + char *data; + int i, rc; + + /* Search xattr_datum has same xname/xvalue by index */ + hashkey = jffs2_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 */ + nlen = strlen(xname); + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + init_xattr_datum(xd); /* class = RAWNODE_CLASS_XATTR_DATUM, refcnt = 0 */ + + data = kmalloc(nlen + 1 + xsize, GFP_KERNEL); + if (!data) { + jffs2_free_xattr_datum(xd); + return ERR_PTR(-ENOMEM); + } + strcpy(data, xname); + data[nlen] = '\0'; + memcpy(data + nlen + 1, xvalue, xsize); + + xd->refcnt++; /* refcnt = 1 */ + xd->flags = JFFS2_XDATUM_FLAGS_HOT; + xd->xprefix = xprefix; + xd->xid = ++c->highest_xseqno; + + xd->hashkey = hashkey; + xd->xname = data; + xd->xvalue = data + nlen + 1; + xd->name_len = nlen; + 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; + if (c->xdatum_mem_usage > c->xdatum_mem_threshold) + reclaim_xattr_datum(c); + + return xd; +} + +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) { + xd->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, xd->node); + } + jffs2_free_xattr_datum(xd); +} + +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(sizeof(rr)); + rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node)-4)); + + rr.seqno = cpu_to_je32(ref->seqno); + 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(struct jffs2_raw_xref)-4)); + + ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); + if (ret || sizeof(rr)!=length) { + printk(KERN_NOTICE "%s:%d Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + __FILE__, __LINE__, sizeof(rr), phys_ofs, ret, length); + 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) { + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + } + ref->node = raw; + + 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; + + /* Duplication Check */ + list_for_each_entry(ref, &ic->ilist, ilist) { + if (ref->xd == xd) + return ERR_PTR(-EINVAL); + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return ERR_PTR(-ENOMEM); + init_xattr_ref(ref); /* class = RAWNODE_CLASS_XATTR_REF */ + + ref->seqno = ++c->highest_xseqno; + 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 */ +} + +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); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + + list_del(&ref->ilist); + xd = ref->xd; + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + + jffs2_free_xattr_ref(ref); +} + +void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + 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() */ + 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 do_check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_xattr_ref *ref, *cmp; + int ret = 0; + + down_write(&c->xattr_sem); + retry: + list_for_each_entry(ref, &ic->ilist, ilist) { + ret = load_xattr_datum_nolock(c, ref->xd); + if (ret) + goto out; + cmp = ref; + list_for_each_entry_continue(cmp, &ic->ilist, ilist) { + ret = load_xattr_datum_nolock(c, cmp->xd); + if (ret) + goto out; + if (ref->xd->xprefix == cmp->xd->xprefix + && !strcmp(ref->xd->xname, cmp->xd->xname)) { + delete_xattr_ref(c, ref->seqno > cmp->seqno ? cmp : ref); + goto retry; + } + } + } + out: + up_write(&c->xattr_sem); + + if (!ret) + ic->flags |= INO_FLAGS_XATTR_CHECKED; + + return ret; +} + +static inline int check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED)) + return 0; + return do_check_xattr_ref_ilist(c, ic); +} + +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; + int len, ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + down_read(&c->xattr_sem); + len = 0; + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + xd = ref->xd; + ret = load_xattr_datum(c, xd); + if (ret<0) + goto out; + + xhandle = xprefix_to_handler(xd->xprefix); + if (!xhandle) + continue; + + if (buffer) { + ret = xhandle->list(inode, buffer+len, size-len, xd->xname, xd->name_len); + } else { + ret = xhandle->list(inode, NULL, 0, xd->xname, xd->name_len); + } + if (ret < 0) + goto out; + len += ret; + } + out: + up_read(&c->xattr_sem); + return ret; +} + +static 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 ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + down_read(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + + ret = load_xattr_datum(c, xd); + if (ret) + goto out; + + if (!strcmp(xname, xd->xname)) { + ret = xd->value_len; + if (buffer) { + if (size < ret) { + ret = -ERANGE; + } else { + memcpy(buffer, xd->xvalue, xd->value_len); + } + } + goto out; + } + } + ret = -ENODATA; + out: + up_read(&c->xattr_sem); + return ret; +} + +static 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, *nref; + uint32_t phys_ofs, length, request; + int ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + request = PAD(sizeof(*xd) + strlen(xname) + 1 + size); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", + __FILE__, __LINE__, ret, length, request, phys_ofs); + return ret; + } + + /* Find existing xattr */ + down_write(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + + ret = load_xattr_datum(c, xd); + if (ret) + goto out; + + if (!strcmp(xd->xname, xname)) { + if (flags & XATTR_CREATE) { + ret = -EEXIST; + goto out; + } + goto found; + } + } + /* not found */ + ref = NULL; + if (flags & XATTR_REPLACE) { + ret = -ENODATA; + goto out; + } + if (!buffer) { + ret = -EINVAL; + goto out; + } + found: + xd = create_xattr_datum(c, xprefix, xname, buffer, size, phys_ofs); + if (IS_ERR(xd)) { + ret = PTR_ERR(xd); + goto out; + } + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + /* create xattr_ref */ + request = PAD(sizeof(*nref)); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", + __FILE__, __LINE__, ret, length, request, phys_ofs); + down_write(&c->xattr_sem); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + up_write(&c->xattr_sem); + return ret; + } + down_write(&c->xattr_sem); + nref = create_xattr_ref(c, ic, xd, phys_ofs); + if (IS_ERR(nref)) { + ret = PTR_ERR(nref); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + goto out; + } else if (ref) { + /* If replaced xattr_ref exists */ + delete_xattr_ref(c, ref); + } + out: + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + return ret; +} + +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; + int ret; + + BUG_ON(!xd->node); + + totlen = ref_totlen(c, c->gcblock, xd->node); + if (totlen < sizeof(struct jffs2_raw_xattr)) + return -EINVAL; + + ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret || length < totlen) { + printk(KERN_WARNING "jffs2_reserve_space_gc() = %d " + "request: %d byte reserved: %d byte", + ret, totlen, length); + return ret ? ret : -EBADFD; + } + + ret = load_xattr_datum_nolock(c, xd); + if (!ret) + ret = save_xattr_datum(c, xd, phys_ofs); + + return ret; +} + + +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; + int ret; + + BUG_ON(!ref->node); + + totlen = ref_totlen(c, c->gcblock, ref->node); + if (totlen != sizeof(struct jffs2_raw_xref)) + return -EINVAL; + + ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret || length < totlen) { + printk(KERN_WARNING "jffs2_reserve_space_gc() = %d" + " request: %d byte reserved: %d byte", ret, totlen, length); + return ret ? ret : -EBADFD; + } + + ret = save_xattr_ref(c, ref, phys_ofs); + + return ret; +} + +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; +} diff -prNU3 mtd-0927/fs/jffs2/xattr.h mtd-0927.xattr/fs/jffs2/xattr.h --- mtd-0927/fs/jffs2/xattr.h 1969-12-31 19:00:00.000000000 -0500 +++ mtd-0927.xattr/fs/jffs2/xattr.h 2005-09-26 19:41:26.000000000 -0400 @@ -0,0 +1,137 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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 XATTRINDEX_HASHSIZE (57) +#define XATTR_USER_PREFIX "user." +#define XATTR_TRUSTED_PREFIX "trusted." + +#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_XPREFIX_INVALID 6 + +#define JFFS2_XDATUM_FLAGS_HOT 0x01 /* This is hot xattr_datum */ + +#define RAWNODE_CLASS_INODE_CACHE 0 +#define RAWNODE_CLASS_XATTR_DATUM 1 +#define RAWNODE_CLASS_XATTR_REF 2 + +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 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; + + uint32_t seqno; + 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_XATTR + +extern int 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_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid); +extern void jffs2_attach_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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 struct xattr_handler *jffs2_xattr_handlers[]; +#define jffs2_permission NULL +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)) + +static inline void init_xattr_datum(struct jffs2_xattr_datum *xd) +{ + memset(xd, 0, sizeof(struct jffs2_xattr_datum)); + xd->class = RAWNODE_CLASS_XATTR_DATUM; + INIT_LIST_HEAD(&xd->xindex); +} + +static inline void init_xattr_ref(struct jffs2_xattr_ref *ref) +{ + memset(ref, 0, sizeof(struct jffs2_xattr_ref)); + ref->class = RAWNODE_CLASS_XATTR_REF; + INIT_LIST_HEAD(&ref->ilist); +} + +#else + +static inline int jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) { return 0; } +static inline void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) {} +static inline void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) {} + +static inline void *jffs2_find_xattr_datum(void *c, uint32_t xid) { return NULL; } +static inline void jffs2_attach_xattr_datum(void *c, void *xd) {} + +static inline void jffs2_xattr_delete_inode(void *c, void *ic) {} +static inline void jffs2_xattr_free_inode(void *c, void *ic) {} +static inline int jffs2_garbage_collect_xattr(void *c, void *ic) { return 1; } + +#define jffs2_xattr_handlers NULL +#define jffs2_permission NULL +#define jffs2_listxattr NULL +#define jffs2_getxattr NULL +#define jffs2_setxattr NULL +#define jffs2_removexattr NULL + +#define init_xattr_inode_cache(x) +static inline void init_xattr_datum(void *xd) {} +static inline void init_xattr_ref(void *ref) {} + +#endif /* CONFIG_JFFS2_XATTR */ + +#endif /* _JFFS2_FS_XATTR_H_ */ diff -prNU3 mtd-0927/include/linux/jffs2.h mtd-0927.xattr/include/linux/jffs2.h --- mtd-0927/include/linux/jffs2.h 2005-09-26 07:37:23.000000000 -0400 +++ mtd-0927.xattr/include/linux/jffs2.h 2005-09-26 19:11:10.000000000 -0400 @@ -65,6 +65,9 @@ #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) + // 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 +154,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 */ + 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 seqno; /* sequencial number */ + 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 +198,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-0927/include/linux/jffs2_fs_sb.h mtd-0927.xattr/include/linux/jffs2_fs_sb.h --- mtd-0927/include/linux/jffs2_fs_sb.h 2005-09-21 09:37:34.000000000 -0400 +++ mtd-0927.xattr/include/linux/jffs2_fs_sb.h 2005-09-26 19:06:31.000000000 -0400 @@ -115,6 +115,14 @@ struct jffs2_sb_info { struct jffs2_summary *summary; /* Summary information */ +#ifdef CONFIG_JFFS2_XATTR + uint32_t highest_xseqno; + struct list_head *xattrindex; +#define xattr_temp xattrindex[XATTRINDEX_HASHSIZE] + 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; }; [-- Attachment #3: jffs2_xattr_scan.c-cleanup.patch --] [-- Type: text/x-patch, Size: 4851 bytes --] --- cleanup-scan.c 2005-09-25 03:48:35.000000000 -0400 +++ cleanup-scan.xattr.c 2005-09-25 03:53:16.000000000 -0400 @@ -337,6 +337,139 @@ static int jffs2_scan_dirent_node(struct return 0; } +#ifdef CONFIG_JFFS2_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_xattr_datum *xd; + struct jffs2_raw_node_ref *raw; + uint32_t crc; + + crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr)-8); + if (crc != je32_to_cpu(rx->node_crc)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_find_xattr_datum(c, je32_to_cpu(rx->xid)); + if (xd) { + printk(KERN_NOTICE "%s() duplicate xid=%u found. " + "on node at 0x%08x, later one is ignored.\n", + __FUNCTION__, je32_to_cpu(rx->xid), ofs); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + crc = crc32(0, rx->data, rx->name_len + 1 + je16_to_cpu(rx->value_len)); + if (crc != je32_to_cpu(rx->data_crc)) { + printk(KERN_NOTICE "%s data CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return -ENOMEM; + init_xattr_datum(xd); + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + jffs2_free_xattr_datum(xd); + return -ENOMEM; + } + + xd->xid = je32_to_cpu(rx->xid); + if (xd->xid > c->highest_xseqno) + c->highest_xseqno = xd->xid; + xd->xprefix = rx->xprefix; /* 8bit width */ + xd->name_len = rx->name_len; /* 8bit width */ + xd->value_len = je16_to_cpu(rx->value_len); + xd->data_crc = je32_to_cpu(rx->data_crc); + xd->node = raw; + + raw->__totlen = PAD(je32_to_cpu(rx->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(c, jeb, je32_to_cpu(rx->totlen)); + + jffs2_attach_xattr_datum(c, xd); + + 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_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)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rr->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rr->totlen)); + return 0; + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return -ENOMEM; + init_xattr_ref(ref); + + 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->seqno = je32_to_cpu(rr->seqno); + if (ref->seqno > c->highest_xseqno) + c->highest_xseqno = ref->seqno; + ref->ino = je32_to_cpu(rr->ino); + ref->xid = je32_to_cpu(rr->xid); + ref->node = raw; + + 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(c, jeb, je32_to_cpu(rr->totlen)); + + list_add_tail(&ref->ilist, &c->xattr_temp); + + return 0; +} +#endif /* CONFIG_JFFS2_XATTR */ + static int cleanmarkerfound; static int jffs2_nand_initial_scan(struct jffs2_sb *c, @@ -510,6 +643,20 @@ static int jffs2_scan_eraseblock(struct return ret; ofs += PAD(je32_to_cpu(node->totlen)); break; +#ifdef CONFIG_JFFS2_XATTR + case JFFS2_NODETYPE_XATTR: + ret = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs); + if (ret) + return ret; + ofs += PAD(je32_to_cpu(node->totlen)); + break; + case JFFS2_NODETYPE_XREF: + ret = jffs2_scan_xref_node(c, jeb, (void *)node, ofs); + if (ret) + return ret; + ofs += PAD(je32_to_cpu(node->totlen)); + break; +#endif /* CONFIG_JFFS2_XATR */ case JFFS2_NODETYPE_PADDING: ofs += dirty_space(c, jeb, je32_to_cpu(node->totlen)); break; ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-28 8:44 ` Kaigai Kohei @ 2005-09-29 7:45 ` Jörn Engel 2005-10-03 1:01 ` E-mail with attached file has not delivered yet. (Re: [PATCH] XATTR issues on JFFS2) Kaigai Kohei 2005-10-19 13:18 ` [PATCH] XATTR issues on JFFS2 Kaigai Kohei 2005-10-12 4:25 ` Kaigai Kohei 1 sibling, 2 replies; 28+ messages in thread From: Jörn Engel @ 2005-09-29 7:45 UTC (permalink / raw) To: Kaigai Kohei Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley, Ma Yun On Wed, 28 September 2005 17:44:08 +0900, Kaigai Kohei wrote: > > I revised the previous xattr patch in jffs2 based on some suggestions. > > [1] scanning xattr related node for cleaned-up scan.c. > (jffs2_xattr_scan.c-cleanup.patch) > [2] enables xattr support in jffs2. > (jffs2_xattr_take-3.patch) > (*) We can apply jffs2_xattr_take-3.patch for current CVS independently. Looks quite interesting. I'll take a closer look over the weekend. Thanks! Jörn -- The strong give up and move away, while the weak give up and stay. -- unknown ^ permalink raw reply [flat|nested] 28+ messages in thread
* E-mail with attached file has not delivered yet. (Re: [PATCH] XATTR issues on JFFS2) 2005-09-29 7:45 ` Jörn Engel @ 2005-10-03 1:01 ` Kaigai Kohei 2005-10-19 13:18 ` [PATCH] XATTR issues on JFFS2 Kaigai Kohei 1 sibling, 0 replies; 28+ messages in thread From: Kaigai Kohei @ 2005-10-03 1:01 UTC (permalink / raw) To: linux-mtd Hi, The quoted E-mail seems not to have been delivered by some reasons yet. (No E-mail with attached files has been distributed on the list since 27-Sep.) http://www.kaigai.gr.jp/patch/jffs2_xattr_take-3.patch The above patch was what I posted on 28-Sep, but it has not been delivered yet. (Today is 03-Oct JST!) Are there any other E-mails which has not been delivered yet ? Thanks, Jörn Engel wrote: > On Wed, 28 September 2005 17:44:08 +0900, Kaigai Kohei wrote: > >>I revised the previous xattr patch in jffs2 based on some suggestions. >> >>[1] scanning xattr related node for cleaned-up scan.c. >> (jffs2_xattr_scan.c-cleanup.patch) >>[2] enables xattr support in jffs2. >> (jffs2_xattr_take-3.patch) >>(*) We can apply jffs2_xattr_take-3.patch for current CVS independently. > > > Looks quite interesting. I'll take a closer look over the weekend. > > Thanks! > > Jörn -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-29 7:45 ` Jörn Engel 2005-10-03 1:01 ` E-mail with attached file has not delivered yet. (Re: [PATCH] XATTR issues on JFFS2) Kaigai Kohei @ 2005-10-19 13:18 ` Kaigai Kohei 2005-10-19 14:24 ` Jörn Engel 1 sibling, 1 reply; 28+ messages in thread From: Kaigai Kohei @ 2005-10-19 13:18 UTC (permalink / raw) To: Jörn Engel; +Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley Hi Jörn, What status is the XATTR patch of JFFS2 now ?? I hope to merge it, if this patch has no problem. Of course, I'll correct any inconvenient points which we found. # I droped a Cc: for Ma, since his address seems to be invalid. Jörn Engel wrote: > On Wed, 28 September 2005 17:44:08 +0900, Kaigai Kohei wrote: > >>I revised the previous xattr patch in jffs2 based on some suggestions. >> >>[1] scanning xattr related node for cleaned-up scan.c. >> (jffs2_xattr_scan.c-cleanup.patch) >>[2] enables xattr support in jffs2. >> (jffs2_xattr_take-3.patch) >>(*) We can apply jffs2_xattr_take-3.patch for current CVS independently. > > > Looks quite interesting. I'll take a closer look over the weekend. > > Thanks! > > Jörn -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-10-19 13:18 ` [PATCH] XATTR issues on JFFS2 Kaigai Kohei @ 2005-10-19 14:24 ` Jörn Engel 2005-10-20 2:01 ` Kaigai Kohei 2005-11-27 6:58 ` KaiGai Kohei 0 siblings, 2 replies; 28+ messages in thread From: Jörn Engel @ 2005-10-19 14:24 UTC (permalink / raw) To: Kaigai Kohei; +Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley On Wed, 19 October 2005 22:18:32 +0900, Kaigai Kohei wrote: > > What status is the XATTR patch of JFFS2 now ?? > > I hope to merge it, if this patch has no problem. > Of course, I'll correct any inconvenient points which we found. Lack of time. I still have your patch and some from Forrest lingering in my inbox. They all should receive a thorough line-by-line review - not necessarily by me, but by someone with enough knowledge. If it helps you, your patches are already in a relatively good state. I cannot easily find problems with it. > # I droped a Cc: for Ma, since his address seems to be invalid. Fair enough. Jörn -- "Security vulnerabilities are here to stay." -- Scott Culp, Manager of the Microsoft Security Response Center, 2001 ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-10-19 14:24 ` Jörn Engel @ 2005-10-20 2:01 ` Kaigai Kohei 2005-11-27 6:58 ` KaiGai Kohei 1 sibling, 0 replies; 28+ messages in thread From: Kaigai Kohei @ 2005-10-20 2:01 UTC (permalink / raw) To: Jörn Engel; +Cc: James Morris, linux-mtd, David Woodhouse, Stephen Smalley Hi, > Lack of time. I still have your patch and some from Forrest lingering > in my inbox. They all should receive a thorough line-by-line review - > not necessarily by me, but by someone with enough knowledge. Sorry for hastening. > If it helps you, your patches are already in a relatively good state. > I cannot easily find problems with it. Thanks, I was hesitating whether I should begin to develop the ACL features based on XATTR. If XATTR would be in a good state, I'm willing to begin the design and implementation. -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-10-19 14:24 ` Jörn Engel 2005-10-20 2:01 ` Kaigai Kohei @ 2005-11-27 6:58 ` KaiGai Kohei 2005-11-27 9:43 ` KaiGai Kohei 1 sibling, 1 reply; 28+ messages in thread From: KaiGai Kohei @ 2005-11-27 6:58 UTC (permalink / raw) To: Jörn Engel Cc: James Morris, Kaigai Kohei, Stephen Smalley, linux-mtd, lorenzohgh, David Woodhouse, Andreas Gruenbacher [-- Attachment #1: Type: text/plain, Size: 2050 bytes --] Hi, This attached patches are enables XATTR support on JFFS2. some new functionalities are added from take-3 patches. # Sorry for slowing update. [NOTICE] We can test this patch with ONLY 2.6.14 kernel. The previous versions of kernel do not support an new LSM hook. [1/2] jffs2_xattr_take-4.patch * CONFIG_JFFS2_XATTR was renamed to CONFIG_JFFS2_FS_XATTR The XATTR configuration switch are named CONFIG_<fsname>_FS_XATTR on another some filesystems which support XATTR. I also follow this manner. * CONFIG_JFFS2_FS_POSIX_ACL and JFFS2_FS_SECURITY are selectable. this follows another FS's manner. * POSIX-ACL support was added. This functionality is implemented in acl.c, acl.h. I implemented the most part of this functionalities by referring the Ext2/3's implementation of POSIX-ACL. If I took bad manner or misunderstanding for implementation, notice me please. * jffs2_init_security() was added for inode initialization. At 2.6.14, new LSM hook(security_inode_init_security) was added. Because of this, any filesystems obtain security attribtues must call this this hook to set the security label for new inode. It's deployed in jffs2_create(), jffs2_symlink(), jffs2_mkdir() and jffs2_mknod(). * Some patch conflicts were fixed. We can apply this patches for latest MTD-CVS without incident. * An obvious bug was fixed. In jffs2_build_xattr_subsystem(), NULL checking was omitted. [2/2] mkfs.jffs2-xattr.patch * --with-{xattr|selinux|posix-acl} options are added. --with-xattr enables to pack all xattr-entries into jffs2 image file. --with-selinux enables to pack security related xattr-entries which are named as 'security.*' into jffs2 image file. --with-posix-acl enables to pack ACL related xattr-entries which are names as 'system.posix_acl_access' or 'system.posix_acl_default'. I hope to merge those functionalities into MTD's CVS tree. Thanks, -- KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: mkfs.jffs2-xattr.patch --] [-- Type: text/plain, Size: 13502 bytes --] diff -rpNU3 mtd-20051127.xattr/util/mkfs.jffs2.1 mtd-20051127.mkfs/util/mkfs.jffs2.1 --- mtd-20051127.xattr/util/mkfs.jffs2.1 2005-11-27 00:00:13.000000000 -0500 +++ mtd-20051127.mkfs/util/mkfs.jffs2.1 2005-11-27 00:00:36.000000000 -0500 @@ -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 diff -rpNU3 mtd-20051127.xattr/util/mkfs.jffs2.c mtd-20051127.mkfs/util/mkfs.jffs2.c --- mtd-20051127.xattr/util/mkfs.jffs2.c 2005-11-27 00:00:13.000000000 -0500 +++ mtd-20051127.mkfs/util/mkfs.jffs2.c 2005-11-27 00:00:36.000000000 -0500 @@ -7,6 +7,7 @@ * 2002 Axis Communications AB * 2001, 2002 Erik Andersen <andersen@codepoet.org> * 2004 University of Szeged, Hungary + * 2005 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 <attr/xattr.h> +#include <sys/acl.h> #include <byteswap.h> #define crc32 __complete_crap #include <zlib.h> @@ -1018,6 +1021,282 @@ static void write_special_file(struct fi padword(); } +#include "../fs/jffs2/acl.h" +#define XATTR_BUFFER_SIZE 65536 +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; + +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 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)) + +static uint32_t enable_xattr = 0; +static uint32_t xseqno = 0; + +static struct { + int xprefix; + char *string; + int strict; +} xprefix_tbl[] = { + { JFFS2_XPREFIX_USER, "user.", 0 }, + { JFFS2_XPREFIX_SECURITY, "security.", 0 }, + { JFFS2_XPREFIX_ACL_ACCESS, "system.posix_acl_access", 1 }, + { JFFS2_XPREFIX_ACL_DEFAULT, "system.posix_acl_default", 1 }, + { JFFS2_XPREFIX_TRUSTED, "trusted.", 0 }, + { 0, NULL, 0 } +}; + +static void write_xattr_normal(xattr_entry_t *xe) +{ + struct jffs2_raw_xattr rx; + + 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(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.xprefix = xe->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) - 8)); + + 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(); +} + +static void write_xattr_acl(xattr_entry_t *xe) +{ + struct jffs2_raw_xattr rx; + posix_acl_xattr_header *header; + posix_acl_xattr_entry *entry, *end; + jffs2_acl_header *jheader; + jffs2_acl_entry *jent; + jffs2_acl_entry_short *jent_s; + char buffer[XATTR_BUFFER_SIZE]; + int i, offset = 0; + + header = (posix_acl_xattr_header *)xe->xvalue; + entry = header->a_entries; + end = (posix_acl_xattr_entry *)(xe->xvalue + xe->value_len); + + buffer[offset++] = '\0'; /* termination char of xname */ + jheader = (jffs2_acl_header *)(buffer + offset); + jheader->a_version = cpu_to_je32(JFFS2_ACL_VERSION); + offset += sizeof(jffs2_acl_header); + + for (i=0; i < xe->value_len; i+=sizeof(unsigned long)) { + unsigned long *value = (unsigned long *)(xe->xvalue + i); + printf(" %08lx", *value); + } + putchar('\n'); + + while (entry < end) { + switch (le16_to_cpu(entry->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(entry->e_tag)); + jent_s->e_perm = cpu_to_je16(le16_to_cpu(entry->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(entry->e_tag)); + jent->e_perm = cpu_to_je16(le16_to_cpu(entry->e_perm)); + jent->e_id = cpu_to_je32(le32_to_cpu(entry->e_id)); + break; + default: + printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(entry->e_tag)); + } + entry++; + } + + 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(sizeof(rx) + 1 + offset); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + + rx.xid = cpu_to_je32(xe->xid); + rx.xprefix = xe->xprefix; + rx.name_len = 0; + rx.value_len = cpu_to_je16(offset - 1); + rx.data_crc = cpu_to_je32(crc32(0, buffer, offset)); + rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 8)); + + pad_block_if_less_than(sizeof(rx) + offset); + full_write(out_fd, &rx, sizeof(rx)); + full_write(out_fd, buffer, offset); + padword(); + + for (i=0; i < offset; i+= sizeof(unsigned long)) { + unsigned long *value = (unsigned long *)(buffer + 1 + i); + printf(" %08lx", *value); + } + putchar('\n'); + printf("wrote-a: xid = %u xprefix = %d dlen = %d hcrc = %08x dcrc = %08x\n", xe->xid, xe->xprefix, offset, je32_to_cpu(rx.node_crc), je32_to_cpu(rx.data_crc)); +} + +static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ + xattr_entry_t *xe; + int name_len; + + name_len = strlen(xname); + xe = xcalloc(1, sizeof(struct xattr_entry) + name_len + 1 + value_len); + xe->xid = ++xseqno; + xe->xprefix = xprefix; + xe->xname = (char *)(xe + 1); + 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); + + switch (xprefix) { + case JFFS2_XPREFIX_ACL_ACCESS: + case JFFS2_XPREFIX_ACL_DEFAULT: + write_xattr_acl(xe); + break; + default: + write_xattr_normal(xe); + break; + } + return xe; +} + +#define XATTRENTRY_HASHSIZE 37 +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 name_len, index; + + if ((enable_xattr & (1 << xprefix)) == 0) + return NULL; + + /* find or create xattr entry */ + if (!xentry_hash) + xentry_hash = xcalloc(1, sizeof(struct xattr_entry *) * XATTRENTRY_HASHSIZE); + + name_len = strlen(xname) + 1; + 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; + 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) { + int i, xprefix, prefix_len; + char *prefix_str; + + 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 = strlen(prefix_str); + + if (xprefix_tbl[i].strict) { + if (!strcmp(xname, prefix_str)) + break; + } else { + if (!strncmp(xname, prefix_str, prefix_len)) + break; + } + } + if (!xprefix) { + if (verbose) + printf("%s : XATTR '%s' was not supported.\n", e->hostname, xname); + 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.seqno = cpu_to_je32(++xseqno); + 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; @@ -1025,6 +1304,8 @@ static void recursive_populate_directory if (verbose) { printf("%s\n", dir->fullname); } + write_xattr_entry(dir); /* for '/' */ + e = dir->files; while (e) { @@ -1037,6 +1318,7 @@ static void recursive_populate_directory e->name); } write_pipe(e); + write_xattr_entry(e); break; case S_IFSOCK: if (verbose) { @@ -1045,6 +1327,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) { @@ -1053,6 +1336,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) { @@ -1062,6 +1346,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) { @@ -1071,6 +1356,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) { @@ -1080,6 +1366,7 @@ static void recursive_populate_directory e->link); } write_symlink(e); + write_xattr_entry(e); break; case S_IFREG: if (verbose) { @@ -1088,6 +1375,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, @@ -1172,6 +1460,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} }; @@ -1204,6 +1495,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" @@ -1531,6 +1825,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) { [-- Attachment #3: jffs2_xattr_take-4.patch --] [-- Type: text/plain, Size: 95284 bytes --] diff -prNU3 mtd-20051127/fs/jffs2/acl.c mtd-20051127.xattr/fs/jffs2/acl.c --- mtd-20051127/fs/jffs2/acl.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/acl.c 2005-11-27 00:00:13.000000000 -0500 @@ -0,0 +1,484 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/acl.c + * POSIX ACL support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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/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; + int i, count; + + if (!value) + return NULL; + if (size < sizeof(jffs2_acl_header)) + return ERR_PTR(-EINVAL); + if (je32_to_cpu(((jffs2_acl_header *)value)->a_version) != JFFS2_ACL_VERSION) { + D1(printk(KERN_NOTICE "%s:%d invalid ACL version.\n", __FUNCTION__, __LINE__)); + 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; + char *value = NULL; + int rc, xprefix; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + switch (type) { + case ACL_TYPE_ACCESS: + xprefix = JFFS2_XPREFIX_ACL_ACCESS; + if (acl) { + mode_t mode = inode->i_mode; + rc = posix_acl_equiv_mode(acl, &mode); + if (rc < 0) + return rc; + if (inode->i_mode != mode) { + inode->i_mode = mode; + jffs2_dirty_inode(inode); + } + if (rc == 0) + acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: + xprefix = JFFS2_XPREFIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + default: + return -EINVAL; + } + if (acl) { + value = jffs2_acl_to_medium(acl, &size); + if (IS_ERR(value)) + return PTR_ERR(value); + } + + rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0); + + if (value) + kfree(value); + if (!rc) { + switch(type) { + case ACL_TYPE_ACCESS: + jffs2_iset_acl(inode, &f->i_acl_access, acl); + break; + case ACL_TYPE_DEFAULT: + jffs2_iset_acl(inode, &f->i_acl_default, acl); + break; + } + } + return rc; +} + +static int jffs2_check_acl(struct inode *inode, int mask) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_permission(inode, acl, mask); + posix_acl_release(acl); + return rc; + } + return -EAGAIN; +} + +int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, jffs2_check_acl); +} + +int jffs2_init_acl(struct inode *inode, struct inode *dir) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct posix_acl *acl = NULL, *clone; + mode_t mode; + int rc = 0; + + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + if (!S_ISLNK(inode->i_mode)) { + acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + inode->i_mode &= ~current->fs->umask; + } + if (acl) { + if (S_ISDIR(inode->i_mode)) { + rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (rc) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + rc = -ENOMEM; + if (!clone) + goto cleanup; + mode = inode->i_mode; + rc = posix_acl_create_masq(clone, &mode); + if (rc >= 0) { + inode->i_mode = mode; + if (rc > 0) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + } + posix_acl_release(clone); + } + cleanup: + posix_acl_release(acl); + return rc; +} + +void jffs2_clear_acl(struct inode *inode) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + + if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_access); + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + } + if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_default); + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + } +} + +int jffs2_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int rc; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + rc = posix_acl_chmod_masq(clone, inode->i_mode); + if (!rc) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return rc; +} + +static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_ACCESS); + return retlen; +} + +static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_DEFAULT); + return retlen; +} + +static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + return -ENODATA; + rc = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return rc; +} + +static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size) +{ + struct posix_acl *acl; + int rc; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_valid(acl); + if (rc) + goto out; + } + } else { + acl = NULL; + } + rc = jffs2_set_acl(inode, type, acl); + out: + posix_acl_release(acl); + return rc; +} + +static int jffs2_acl_access_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +struct xattr_handler jffs2_acl_access_xattr_handler = { + .prefix = POSIX_ACL_XATTR_ACCESS, + .list = jffs2_acl_access_listxattr, + .get = jffs2_acl_access_getxattr, + .set = jffs2_acl_access_setxattr, +}; + +struct xattr_handler jffs2_acl_default_xattr_handler = { + .prefix = POSIX_ACL_XATTR_DEFAULT, + .list = jffs2_acl_default_listxattr, + .get = jffs2_acl_default_getxattr, + .set = jffs2_acl_default_setxattr, +}; diff -prNU3 mtd-20051127/fs/jffs2/acl.h mtd-20051127.xattr/fs/jffs2/acl.h --- mtd-20051127/fs/jffs2/acl.h 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/acl.h 2005-11-27 00:00:13.000000000 -0500 @@ -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) 2005 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 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 +static inline int jffs2_acl_chmod(struct inode *inode) { return 0; } +static inline int jffs2_init_acl(struct inode *inode, struct inode *dir) { return 0; } +static inline void jffs2_clear_acl(struct inode *inode) {} + +#endif + + diff -prNU3 mtd-20051127/fs/jffs2/build.c mtd-20051127.xattr/fs/jffs2/build.c --- mtd-20051127/fs/jffs2/build.c 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/build.c 2005-11-27 00:00:13.000000000 -0500 @@ -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; diff -prNU3 mtd-20051127/fs/jffs2/dir.c mtd-20051127.xattr/fs/jffs2/dir.c --- mtd-20051127/fs/jffs2/dir.c 2005-11-07 20:02:14.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/dir.c 2005-11-27 00:00:13.000000000 -0500 @@ -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 }; /***********************************************************************/ @@ -206,12 +211,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)); @@ -221,6 +229,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; } /***********************************************************************/ @@ -371,6 +385,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) { @@ -501,6 +527,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) { @@ -657,6 +695,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) { diff -prNU3 mtd-20051127/fs/jffs2/file.c mtd-20051127.xattr/fs/jffs2/file.c --- mtd-20051127/fs/jffs2/file.c 2005-11-07 20:02:14.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/file.c 2005-11-27 00:00:13.000000000 -0500 @@ -54,7 +54,12 @@ struct file_operations jffs2_file_operat struct inode_operations jffs2_file_inode_operations = { - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; struct address_space_operations jffs2_file_address_operations = diff -prNU3 mtd-20051127/fs/jffs2/fs.c mtd-20051127.xattr/fs/jffs2/fs.c --- mtd-20051127/fs/jffs2/fs.c 2005-11-24 20:02:10.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/fs.c 2005-11-27 00:00:13.000000000 -0500 @@ -179,7 +179,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) + rc = jffs2_acl_chmod(dentry->d_inode); + return rc; } int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) @@ -218,6 +223,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); } @@ -492,9 +498,12 @@ int jffs2_do_fill_super(struct super_blo } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); - if ((ret = jffs2_do_mount_fs(c))) + if ((ret = jffs2_init_xattr_subsystem(c))) goto out_inohash; + if ((ret = jffs2_do_mount_fs(c))) + goto out_xattr; + ret = -EINVAL; D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); @@ -522,6 +531,8 @@ int jffs2_do_fill_super(struct super_blo jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); jffs2_free_eraseblocks(c); + out_xattr: + jffs2_clear_xattr_subsystem(c); out_inohash: kfree(c->inocache_list); out_wbuf: diff -prNU3 mtd-20051127/fs/jffs2/fs.c.orig mtd-20051127.xattr/fs/jffs2/fs.c.orig --- mtd-20051127/fs/jffs2/fs.c.orig 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/fs.c.orig 2005-11-27 00:00:13.000000000 -0500 @@ -0,0 +1,683 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse <dwmw2@infradead.org> + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: fs.c,v 1.70 2005/11/24 16:13:24 dedekind Exp $ + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/mtd/mtd.h> +#include <linux/pagemap.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/vfs.h> +#include <linux/crc32.h> +#include "nodelist.h" + +static int jffs2_flash_setup(struct jffs2_sb_info *c); + +static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) +{ + struct jffs2_full_dnode *old_metadata, *new_metadata; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_raw_inode *ri; + unsigned short dev; + unsigned char *mdata = NULL; + int mdatalen = 0; + unsigned int ivalid; + uint32_t phys_ofs, alloclen; + int ret; + D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); + ret = inode_change_ok(inode, iattr); + if (ret) + return ret; + + /* Special cases - we don't want more than one data node + for these types on the medium at any time. So setattr + must read the original data associated with the node + (i.e. the device numbers or the target name) and write + it out again with the appropriate data attached */ + if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { + /* For these, we don't actually need to read the old node */ + dev = old_encode_dev(inode->i_rdev); + mdata = (char *)&dev; + mdatalen = sizeof(dev); + D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); + } else if (S_ISLNK(inode->i_mode)) { + mdatalen = f->metadata->size; + mdata = kmalloc(f->metadata->size, GFP_USER); + if (!mdata) + return -ENOMEM; + ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); + if (ret) { + kfree(mdata); + return ret; + } + D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); + } + + ri = jffs2_alloc_raw_inode(); + if (!ri) { + if (S_ISLNK(inode->i_mode)) + kfree(mdata); + return -ENOMEM; + } + + ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + if (ret) { + jffs2_free_raw_inode(ri); + if (S_ISLNK(inode->i_mode & S_IFMT)) + kfree(mdata); + return ret; + } + down(&f->sem); + ivalid = iattr->ia_valid; + + ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); + ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen); + ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); + + ri->ino = cpu_to_je32(inode->i_ino); + ri->version = cpu_to_je32(++f->highest_version); + + ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid); + ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid); + + if (ivalid & ATTR_MODE) + if (iattr->ia_mode & S_ISGID && + !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID)) + ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID); + else + ri->mode = cpu_to_jemode(iattr->ia_mode); + else + ri->mode = cpu_to_jemode(inode->i_mode); + + + ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size); + ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime)); + ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime)); + ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime)); + + ri->offset = cpu_to_je32(0); + ri->csize = ri->dsize = cpu_to_je32(mdatalen); + ri->compr = JFFS2_COMPR_NONE; + if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { + /* It's an extension. Make it a hole node */ + ri->compr = JFFS2_COMPR_ZERO; + ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); + ri->offset = cpu_to_je32(inode->i_size); + } + ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + if (mdatalen) + ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); + else + ri->data_crc = cpu_to_je32(0); + + new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL); + if (S_ISLNK(inode->i_mode)) + kfree(mdata); + + if (IS_ERR(new_metadata)) { + jffs2_complete_reservation(c); + jffs2_free_raw_inode(ri); + up(&f->sem); + return PTR_ERR(new_metadata); + } + /* It worked. Update the inode */ + inode->i_atime = ITIME(je32_to_cpu(ri->atime)); + inode->i_ctime = ITIME(je32_to_cpu(ri->ctime)); + inode->i_mtime = ITIME(je32_to_cpu(ri->mtime)); + inode->i_mode = jemode_to_cpu(ri->mode); + inode->i_uid = je16_to_cpu(ri->uid); + inode->i_gid = je16_to_cpu(ri->gid); + + + old_metadata = f->metadata; + + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) + jffs2_truncate_fragtree (c, &f->fragtree, iattr->ia_size); + + if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { + jffs2_add_full_dnode_to_inode(c, f, new_metadata); + inode->i_size = iattr->ia_size; + f->metadata = NULL; + } else { + f->metadata = new_metadata; + } + if (old_metadata) { + jffs2_mark_node_obsolete(c, old_metadata->raw); + jffs2_free_full_dnode(old_metadata); + } + jffs2_free_raw_inode(ri); + + up(&f->sem); + jffs2_complete_reservation(c); + + /* We have to do the vmtruncate() without f->sem held, since + some pages may be locked and waiting for it in readpage(). + We are protected from a simultaneous write() extending i_size + back past iattr->ia_size, because do_truncate() holds the + generic inode semaphore. */ + if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) + vmtruncate(inode, iattr->ia_size); + + return 0; +} + +int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) +{ + return jffs2_do_setattr(dentry->d_inode, iattr); +} + +int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + unsigned long avail; + + buf->f_type = JFFS2_SUPER_MAGIC; + buf->f_bsize = 1 << PAGE_SHIFT; + buf->f_blocks = c->flash_size >> PAGE_SHIFT; + buf->f_files = 0; + buf->f_ffree = 0; + buf->f_namelen = JFFS2_MAX_NAME_LEN; + + spin_lock(&c->erase_completion_lock); + avail = c->dirty_size + c->free_size; + if (avail > c->sector_size * c->resv_blocks_write) + avail -= c->sector_size * c->resv_blocks_write; + else + avail = 0; + spin_unlock(&c->erase_completion_lock); + + buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; + + return 0; +} + + +void jffs2_clear_inode (struct inode *inode) +{ + /* We can forget about this inode for now - drop all + * the nodelists associated with it, etc. + */ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + + D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); + + jffs2_do_clear_inode(c, f); +} + +void jffs2_read_inode (struct inode *inode) +{ + struct jffs2_inode_info *f; + struct jffs2_sb_info *c; + struct jffs2_raw_inode latest_node; + int ret; + + D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); + + f = JFFS2_INODE_INFO(inode); + c = JFFS2_SB_INFO(inode->i_sb); + + jffs2_init_inode_info(f); + down(&f->sem); + + ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); + + if (ret) { + make_bad_inode(inode); + up(&f->sem); + return; + } + inode->i_mode = jemode_to_cpu(latest_node.mode); + inode->i_uid = je16_to_cpu(latest_node.uid); + inode->i_gid = je16_to_cpu(latest_node.gid); + inode->i_size = je32_to_cpu(latest_node.isize); + inode->i_atime = ITIME(je32_to_cpu(latest_node.atime)); + inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime)); + inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime)); + + inode->i_nlink = f->inocache->nlink; + + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = (inode->i_size + 511) >> 9; + + switch (inode->i_mode & S_IFMT) { + jint16_t rdev; + + case S_IFLNK: + inode->i_op = &jffs2_symlink_inode_operations; + break; + + case S_IFDIR: + { + struct jffs2_full_dirent *fd; + + for (fd=f->dents; fd; fd = fd->next) { + if (fd->type == DT_DIR && fd->ino) + inode->i_nlink++; + } + /* and '..' */ + inode->i_nlink++; + /* Root dir gets i_nlink 3 for some reason */ + if (inode->i_ino == 1) + inode->i_nlink++; + + inode->i_op = &jffs2_dir_inode_operations; + inode->i_fop = &jffs2_dir_operations; + break; + } + case S_IFREG: + inode->i_op = &jffs2_file_inode_operations; + inode->i_fop = &jffs2_file_operations; + inode->i_mapping->a_ops = &jffs2_file_address_operations; + inode->i_mapping->nrpages = 0; + break; + + case S_IFBLK: + case S_IFCHR: + /* Read the device numbers from the media */ + D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); + if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { + /* Eep */ + printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); + up(&f->sem); + jffs2_do_clear_inode(c, f); + make_bad_inode(inode); + return; + } + + case S_IFSOCK: + case S_IFIFO: + inode->i_op = &jffs2_file_inode_operations; + init_special_inode(inode, inode->i_mode, + old_decode_dev((je16_to_cpu(rdev)))); + break; + + default: + printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); + } + + up(&f->sem); + + D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); +} + +void jffs2_dirty_inode(struct inode *inode) +{ + struct iattr iattr; + + if (!(inode->i_state & I_DIRTY_DATASYNC)) { + D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); + return; + } + + D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino)); + + iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME; + iattr.ia_mode = inode->i_mode; + iattr.ia_uid = inode->i_uid; + iattr.ia_gid = inode->i_gid; + iattr.ia_atime = inode->i_atime; + iattr.ia_mtime = inode->i_mtime; + iattr.ia_ctime = inode->i_ctime; + + jffs2_do_setattr(inode, &iattr); +} + +int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + + if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) + return -EROFS; + + /* We stop if it was running, then restart if it needs to. + This also catches the case where it was stopped and this + is just a remount to restart it. + Flush the writebuffer, if neccecary, else we loose it */ + if (!(sb->s_flags & MS_RDONLY)) { + jffs2_stop_garbage_collect_thread(c); + down(&c->alloc_sem); + jffs2_flush_wbuf_pad(c); + up(&c->alloc_sem); + } + + if (!(*flags & MS_RDONLY)) + jffs2_start_garbage_collect_thread(c); + + *flags |= MS_NOATIME; + + return 0; +} + +void jffs2_write_super (struct super_block *sb) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + sb->s_dirt = 0; + + if (sb->s_flags & MS_RDONLY) + return; + + D1(printk(KERN_DEBUG "jffs2_write_super()\n")); + jffs2_garbage_collect_trigger(c); + jffs2_erase_pending_blocks(c, 0); + jffs2_flush_wbuf_gc(c, 0); +} + + +/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, + fill in the raw_inode while you're at it. */ +struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) +{ + struct inode *inode; + struct super_block *sb = dir_i->i_sb; + struct jffs2_sb_info *c; + struct jffs2_inode_info *f; + int ret; + + D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); + + c = JFFS2_SB_INFO(sb); + + inode = new_inode(sb); + + if (!inode) + return ERR_PTR(-ENOMEM); + + f = JFFS2_INODE_INFO(inode); + jffs2_init_inode_info(f); + down(&f->sem); + + memset(ri, 0, sizeof(*ri)); + /* Set OS-specific defaults for new inodes */ + ri->uid = cpu_to_je16(current->fsuid); + + if (dir_i->i_mode & S_ISGID) { + ri->gid = cpu_to_je16(dir_i->i_gid); + if (S_ISDIR(mode)) + mode |= S_ISGID; + } else { + ri->gid = cpu_to_je16(current->fsgid); + } + ri->mode = cpu_to_jemode(mode); + ret = jffs2_do_new_inode (c, f, mode, ri); + if (ret) { + make_bad_inode(inode); + iput(inode); + return ERR_PTR(ret); + } + inode->i_nlink = 1; + inode->i_ino = je32_to_cpu(ri->ino); + inode->i_mode = jemode_to_cpu(ri->mode); + inode->i_gid = je16_to_cpu(ri->gid); + inode->i_uid = je16_to_cpu(ri->uid); + inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); + + inode->i_blksize = PAGE_SIZE; + inode->i_blocks = 0; + inode->i_size = 0; + + insert_inode_hash(inode); + + return inode; +} + + +int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) +{ + struct jffs2_sb_info *c; + struct inode *root_i; + int ret; + size_t blocks; + + c = JFFS2_SB_INFO(sb); + +#ifndef CONFIG_JFFS2_FS_WRITEBUFFER + if (c->mtd->type == MTD_NANDFLASH) { + printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n"); + return -EINVAL; + } + if (c->mtd->type == MTD_DATAFLASH) { + printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n"); + return -EINVAL; + } +#endif + + c->flash_size = c->mtd->size; + c->sector_size = c->mtd->erasesize; + blocks = c->flash_size / c->sector_size; + + /* + * Size alignment check + */ + if ((c->sector_size * blocks) != c->flash_size) { + c->flash_size = c->sector_size * blocks; + printk(KERN_INFO "jffs2: Flash size not aligned to erasesize, reducing to %dKiB\n", + c->flash_size / 1024); + } + + if (c->flash_size < 5*c->sector_size) { + printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size); + return -EINVAL; + } + + c->cleanmarker_size = sizeof(struct jffs2_unknown_node); + c->ebh_size = PAD(sizeof(struct jffs2_raw_ebh)); + + /* NAND (or other bizarre) flash... do setup accordingly */ + ret = jffs2_flash_setup(c); + if (ret) + return ret; + + c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); + if (!c->inocache_list) { + ret = -ENOMEM; + goto out_wbuf; + } + memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + + if ((ret = jffs2_do_mount_fs(c))) + goto out_inohash; + + ret = -EINVAL; + + D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); + root_i = iget(sb, 1); + if (is_bad_inode(root_i)) { + D1(printk(KERN_WARNING "get root inode failed\n")); + goto out_root_i; + } + + D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); + sb->s_root = d_alloc_root(root_i); + if (!sb->s_root) + goto out_root_i; + + sb->s_maxbytes = 0xFFFFFFFF; + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = JFFS2_SUPER_MAGIC; + if (!(sb->s_flags & MS_RDONLY)) + jffs2_start_garbage_collect_thread(c); + return 0; + + out_root_i: + iput(root_i); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + jffs2_free_eraseblocks(c); + out_inohash: + kfree(c->inocache_list); + out_wbuf: + jffs2_flash_cleanup(c); + + return ret; +} + +void jffs2_gc_release_inode(struct jffs2_sb_info *c, + struct jffs2_inode_info *f) +{ + iput(OFNI_EDONI_2SFFJ(f)); +} + +struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, + int inum, int nlink) +{ + struct inode *inode; + struct jffs2_inode_cache *ic; + if (!nlink) { + /* The inode has zero nlink but its nodes weren't yet marked + obsolete. This has to be because we're still waiting for + the final (close() and) iput() to happen. + + There's a possibility that the final iput() could have + happened while we were contemplating. In order to ensure + that we don't cause a new read_inode() (which would fail) + for the inode in question, we use ilookup() in this case + instead of iget(). + + The nlink can't _become_ zero at this point because we're + holding the alloc_sem, and jffs2_do_unlink() would also + need that while decrementing nlink on any inode. + */ + inode = ilookup(OFNI_BS_2SFFJ(c), inum); + if (!inode) { + D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", + inum)); + + spin_lock(&c->inocache_lock); + ic = jffs2_get_ino_cache(c, inum); + if (!ic) { + D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); + spin_unlock(&c->inocache_lock); + return NULL; + } + if (ic->state != INO_STATE_CHECKEDABSENT) { + /* Wait for progress. Don't just loop */ + D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", + ic->ino, ic->state)); + sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); + } else { + spin_unlock(&c->inocache_lock); + } + + return NULL; + } + } else { + /* Inode has links to it still; they're not going away because + jffs2_do_unlink() would need the alloc_sem and we have it. + Just iget() it, and if read_inode() is necessary that's OK. + */ + inode = iget(OFNI_BS_2SFFJ(c), inum); + if (!inode) + return ERR_PTR(-ENOMEM); + } + if (is_bad_inode(inode)) { + printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", + inum, nlink); + /* NB. This will happen again. We need to do something appropriate here. */ + iput(inode); + return ERR_PTR(-EIO); + } + + return JFFS2_INODE_INFO(inode); +} + +unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + unsigned long offset, + unsigned long *priv) +{ + struct inode *inode = OFNI_EDONI_2SFFJ(f); + struct page *pg; + + pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, + (void *)jffs2_do_readpage_unlock, inode); + if (IS_ERR(pg)) + return (void *)pg; + + *priv = (unsigned long)pg; + return kmap(pg); +} + +void jffs2_gc_release_page(struct jffs2_sb_info *c, + unsigned char *ptr, + unsigned long *priv) +{ + struct page *pg = (void *)*priv; + + kunmap(pg); + page_cache_release(pg); +} + +static int jffs2_flash_setup(struct jffs2_sb_info *c) { + int ret = 0; + + if (jffs2_ebh_oob(c)) { + /* NAND flash... do setup accordingly */ + ret = jffs2_nand_flash_setup(c); + if (ret) + return ret; + } + + /* add setups for other bizarre flashes here... */ + if (jffs2_nor_ecc(c)) { + ret = jffs2_nor_ecc_flash_setup(c); + if (ret) + return ret; + } + + /* and Dataflash */ + if (jffs2_dataflash(c)) { + ret = jffs2_dataflash_setup(c); + if (ret) + return ret; + } + + /* and Intel "Sibley" flash */ + if (jffs2_nor_wbuf_flash(c)) { + ret = jffs2_nor_wbuf_flash_setup(c); + if (ret) + return ret; + } + + return ret; +} + +void jffs2_flash_cleanup(struct jffs2_sb_info *c) { + + if (jffs2_ebh_oob(c)) { + jffs2_nand_flash_cleanup(c); + } + + /* add cleanups for other bizarre flashes here... */ + if (jffs2_nor_ecc(c)) { + jffs2_nor_ecc_flash_cleanup(c); + } + + /* and DataFlash */ + if (jffs2_dataflash(c)) { + jffs2_dataflash_cleanup(c); + } + + /* and Intel "Sibley" flash */ + if (jffs2_nor_wbuf_flash(c)) { + jffs2_nor_wbuf_flash_cleanup(c); + } +} diff -prNU3 mtd-20051127/fs/jffs2/gc.c mtd-20051127.xattr/fs/jffs2/gc.c --- mtd-20051127/fs/jffs2/gc.c 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/gc.c 2005-11-27 00:00:13.000000000 -0500 @@ -312,6 +312,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. */ diff -prNU3 mtd-20051127/fs/jffs2/Makefile.common mtd-20051127.xattr/fs/jffs2/Makefile.common --- mtd-20051127/fs/jffs2/Makefile.common 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/Makefile.common 2005-11-27 00:00:13.000000000 -0500 @@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o bac jffs2-y += super.o debug.o wear_leveling.o jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o +jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o +jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o +jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff -prNU3 mtd-20051127/fs/jffs2/malloc.c mtd-20051127.xattr/fs/jffs2/malloc.c --- mtd-20051127/fs/jffs2/malloc.c 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/malloc.c 2005-11-27 00:00:13.000000000 -0500 @@ -28,6 +28,10 @@ static kmem_cache_t *raw_node_ref_slab; static kmem_cache_t *node_frag_slab; static kmem_cache_t *inode_cache_slab; static kmem_cache_t *eraseblock_slab; +#ifdef CONFIG_JFFS2_FS_XATTR +static kmem_cache_t *xattr_datum_cache; +static kmem_cache_t *xattr_ref_cache; +#endif static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c) { @@ -81,8 +85,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; @@ -106,6 +126,12 @@ void jffs2_destroy_slab_caches(void) kmem_cache_destroy(inode_cache_slab); if (eraseblock_slab) kmem_cache_destroy(eraseblock_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) @@ -275,3 +301,33 @@ void jffs2_free_eraseblocks(struct jffs2 kfree(c->blocks); } +#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); + 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); + 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 diff -prNU3 mtd-20051127/fs/jffs2/nodelist.c mtd-20051127.xattr/fs/jffs2/nodelist.c --- mtd-20051127/fs/jffs2/nodelist.c 2005-11-13 20:03:03.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/nodelist.c 2005-11-27 00:00:13.000000000 -0500 @@ -919,6 +919,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; } diff -prNU3 mtd-20051127/fs/jffs2/nodelist.h mtd-20051127.xattr/fs/jffs2/nodelist.h --- mtd-20051127/fs/jffs2/nodelist.h 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/nodelist.h 2005-11-27 00:00:13.000000000 -0500 @@ -20,6 +20,8 @@ #include <linux/jffs2.h> #include <linux/jffs2_fs_sb.h> #include <linux/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,8 @@ 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 INOCACHE_HASHSIZE 128 /* @@ -388,6 +397,13 @@ struct jffs2_inode_cache *jffs2_alloc_in void jffs2_free_inode_cache(struct jffs2_inode_cache *); int jffs2_alloc_eraseblocks(struct jffs2_sb_info *c); void jffs2_free_eraseblocks(struct jffs2_sb_info *c); +#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 /* CONFIG_JFFS2_FS_XATTR */ + /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); diff -prNU3 mtd-20051127/fs/jffs2/os-linux.h mtd-20051127.xattr/fs/jffs2/os-linux.h --- mtd-20051127/fs/jffs2/os-linux.h 2005-11-11 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/os-linux.h 2005-11-27 00:00:13.000000000 -0500 @@ -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 } diff -prNU3 mtd-20051127/fs/jffs2/readinode.c mtd-20051127.xattr/fs/jffs2/readinode.c --- mtd-20051127/fs/jffs2/readinode.c 2005-11-11 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/readinode.c 2005-11-27 00:00:13.000000000 -0500 @@ -891,6 +891,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) { diff -prNU3 mtd-20051127/fs/jffs2/scan.c mtd-20051127.xattr/fs/jffs2/scan.c --- mtd-20051127/fs/jffs2/scan.c 2005-11-18 20:02:13.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/scan.c 2005-11-27 00:11:16.000000000 -0500 @@ -299,6 +299,159 @@ int jffs2_scan_classify_jeb(struct jffs2 return BLK_STATE_ALLDIRTY; } +#ifdef CONFIG_JFFS2_FS_XATTR + +#if 1 /* In cleaned up scan.c, this section is not necessary. */ +static inline uint32_t dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + uint32_t unpadded_space) +{ + DIRTY_SPACE(PAD(unpadded_space)); + + return PAD(unpadded_space);; +} + +static inline uint32_t used_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + uint32_t unpadded_space) +{ + USED_SPACE(PAD(unpadded_space)); + + return PAD(unpadded_space); +} + +#endif /* In cleaned up scan.c, this section is not necessary. */ + +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 crc; + + crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr)-8); + if (crc != je32_to_cpu(rx->node_crc)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_find_xattr_datum(c, je32_to_cpu(rx->xid)); + if (xd) { + printk(KERN_NOTICE "%s() duplicate xid=%u found. " + "on node at 0x%08x, later one is ignored.\n", + __FUNCTION__, je32_to_cpu(rx->xid), ofs); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + crc = crc32(0, rx->data, rx->name_len + 1 + je16_to_cpu(rx->value_len)); + if (crc != je32_to_cpu(rx->data_crc)) { + printk(KERN_NOTICE "%s data CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->data_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return -ENOMEM; + init_xattr_datum(xd); + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + jffs2_free_xattr_datum(xd); + return -ENOMEM; + } + + xd->xid = je32_to_cpu(rx->xid); + if (xd->xid > c->highest_xseqno) + c->highest_xseqno = xd->xid; + xd->xprefix = rx->xprefix; /* 8bit width */ + xd->name_len = rx->name_len; /* 8bit width */ + xd->value_len = je16_to_cpu(rx->value_len); + xd->data_crc = je32_to_cpu(rx->data_crc); + xd->node = raw; + + raw->__totlen = PAD(je32_to_cpu(rx->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(c, jeb, je32_to_cpu(rx->totlen)); + + jffs2_attach_xattr_datum(c, xd); + + 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)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rr->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rr->totlen)); + return 0; + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return -ENOMEM; + init_xattr_ref(ref); + + 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->seqno = je32_to_cpu(rr->seqno); + if (ref->seqno > c->highest_xseqno) + c->highest_xseqno = ref->seqno; + ref->ino = je32_to_cpu(rr->ino); + ref->xid = je32_to_cpu(rr->xid); + ref->node = raw; + + 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(c, jeb, je32_to_cpu(rr->totlen)); + + list_add_tail(&ref->ilist, &c->xattr_temp); + + 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 +767,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)) { + /* FIXME: In cleaned-up scan.c, this If-block is not necessary. */ + 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_flash_read_safe(c, buf_ofs, buf_len, buf); + 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)) { + /* FIXME: In cleaned-up scan.c, this If-block is not necessary. */ + 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_flash_read_safe(c, buf_ofs, buf_len, buf); + 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) { @@ -741,6 +931,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; diff -prNU3 mtd-20051127/fs/jffs2/security.c mtd-20051127.xattr/fs/jffs2/security.c --- mtd-20051127/fs/jffs2/security.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/security.c 2005-11-27 00:00:13.000000000 -0500 @@ -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) 2005 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 = sizeof(XATTR_SECURITY_PREFIX) + name_len; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_SECURITY_PREFIX); + strcpy(list+sizeof(XATTR_SECURITY_PREFIX)-1, 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 +}; diff -prNU3 mtd-20051127/fs/jffs2/super.c mtd-20051127.xattr/fs/jffs2/super.c --- mtd-20051127/fs/jffs2/super.c 2005-11-24 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/super.c 2005-11-27 00:00:13.000000000 -0500 @@ -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_VERBOSE)?1:0); if (ret) { @@ -290,6 +293,7 @@ static void jffs2_put_super (struct supe jffs2_free_eraseblocks(c); jffs2_flash_cleanup(c); kfree(c->inocache_list); + jffs2_clear_xattr_subsystem(c); if (c->mtd->sync) c->mtd->sync(c->mtd); diff -prNU3 mtd-20051127/fs/jffs2/symlink.c mtd-20051127.xattr/fs/jffs2/symlink.c --- mtd-20051127/fs/jffs2/symlink.c 2005-11-07 20:02:15.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/symlink.c 2005-11-27 00:00:13.000000000 -0500 @@ -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) diff -prNU3 mtd-20051127/fs/jffs2/write.c mtd-20051127.xattr/fs/jffs2/write.c --- mtd-20051127/fs/jffs2/write.c 2005-11-11 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/write.c 2005-11-27 00:00:13.000000000 -0500 @@ -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)); diff -prNU3 mtd-20051127/fs/jffs2/xattr.c mtd-20051127.xattr/fs/jffs2/xattr.c --- mtd-20051127/fs/jffs2/xattr.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/xattr.c 2005-11-27 00:00:13.000000000 -0500 @@ -0,0 +1,1021 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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" + +/* ---- decralation of internal use functions ----------------- */ +static void reclaim_xattr_datum(struct jffs2_sb_info *c); +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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); +static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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); +static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref); + +/* ---- XATTR Handler for "user.*" --------------------- */ +#define XATTR_USER_PREFIX "user." +static int jffs2_user_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + int ret; + + if (!strcmp(name, "")) + return -EINVAL; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + 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) +{ + int ret; + + if (!strcmp(name, "") == 0) + return -EINVAL; + + if ( !S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + 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) +{ + int retlen = sizeof(XATTR_USER_PREFIX) + name_len; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_USER_PREFIX); + strcpy(list+sizeof(XATTR_USER_PREFIX)-1, name); + } + + return retlen; +} + +static struct xattr_handler jffs2_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .list = jffs2_user_listxattr, + .set = jffs2_user_setxattr, + .get = jffs2_user_getxattr +}; + +/* ---- XATTR Handler for "trusted.*" ------------------ */ +#define XATTR_TRUSTED_PREFIX "trusted." +static int jffs2_trusted_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + 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; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + 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) +{ + int retlen = sizeof(XATTR_TRUSTED_PREFIX) + name_len; + + if (!capable(CAP_SYS_ADMIN)) + return 0; /* ignore this entry */ + + if (list && retlen<=list_size) { + strcpy(list, XATTR_TRUSTED_PREFIX); + strcpy(list+sizeof(XATTR_TRUSTED_PREFIX)-1, name); + } + + return retlen; +} + +static struct xattr_handler jffs2_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .list = jffs2_trusted_listxattr, + .set = jffs2_trusted_setxattr, + .get = jffs2_trusted_getxattr +}; + +/* ----------------------------------------------------- */ +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; +} + +static uint32_t jffs2_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); +} +/* ---- Build-up and Destruct XATTR-Cache functions ----------- */ +int jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) +{ + int i; + + c->xattrindex = kmalloc((XATTRINDEX_HASHSIZE+1)*sizeof(struct list_head), GFP_KERNEL); + if (!c->xattrindex) + return -ENOMEM; + + init_rwsem(&c->xattr_sem); + + for (i=0; i<=XATTRINDEX_HASHSIZE; i++) + INIT_LIST_HEAD(&c->xattrindex[i]); + + //c->xdatum_mem_threshold = 20 * 1024; /* Default 20KB */ + c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */ + + return 0; +} + +void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_xattr_ref *ref, *_ref; + int i; + + if (!c->xattrindex) + return; /* not initialized */ + + 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); + } + } + + kfree(c->xattrindex); + c->xattrindex = NULL; +} + +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; + + 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); + /* 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) { + printk(KERN_NOTICE "building xref {ino=%u, xid=%u} is not found.\n", + ref->xid, ref->ino); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + jffs2_free_xattr_ref(ref); + continue; + } + ref->xd = xd; + ref->ic = ic; + xd->refcnt++; + list_add_tail(&ref->ilist, &ic->ilist); + D1(printk(KERN_NOTICE "bind XREF{ino=%u, xid=%u}\n", ic->ino, xd->xid)); + } + /* 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) { + printk(KERN_NOTICE "unrefered xattr_datum found xid=%u\n", xd->xid); + delete_xattr_datum(c, xd); + } + } + } + /* build complete */ + D1(printk(KERN_NOTICE "%s complete.\n", __FUNCTION__)); +} + +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_attach_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + int i = xd->xid % XATTRINDEX_HASHSIZE; + + /* It's only used in scanning process. */ + BUG_ON(!(c->flags & JFFS2_SB_FLAG_SCANNING)); + + list_add_tail(&xd->xindex, &c->xattrindex[i]); +} + +/* ---- Internal XATTR related functions ------------------------------------ */ +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; + + before = c->xdatum_mem_usage; + target = c->xdatum_mem_usage * 4 / 5; /* 20% reduction */ + while(1) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[index], xindex) { + if (xd->flags & JFFS2_XDATUM_FLAGS_HOT) { + xd->flags &= ~JFFS2_XDATUM_FLAGS_HOT; + } else { + unload_xattr_datum(c, xd); + } + if (c->xdatum_mem_usage <= target) + goto out; + } + index = (index+1) % XATTRINDEX_HASHSIZE; + } + out: + printk(KERN_NOTICE "JFFS2: reclaim_xattr_cache() before: %d Byte after: %d Byte\n", + before, c->xdatum_mem_usage); +} + +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; + + D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + retry: + length = xd->name_len + 1 + xd->value_len; + data = kmalloc(length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + BUG_ON(!xd->node); + ret = jffs2_flash_read(c, ref_offset(xd->node)+sizeof(struct jffs2_raw_xattr), + length, &readlen, data); + + if (ret || length!=readlen) { + printk(KERN_WARNING "jffs2_flash_read()=%d, request: %d, readlen: %d, at 0x%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) { + printk(KERN_WARNING "node CRC failed (JFFS2_NODETYPE_XREF)" + " at 0x%08x, read: 0x%08x calculated: 0x%08x\n", + ref_offset(xd->node), xd->data_crc, crc); + kfree(data); + return -EIO; + } + + xd->flags = JFFS2_XDATUM_FLAGS_HOT; + xd->xname = data; + xd->xvalue = data+xd->name_len+1; + + c->xdatum_mem_usage += length; + + xd->hashkey = jffs2_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 (c->xdatum_mem_usage > c->xdatum_mem_threshold && retry==0) { + reclaim_xattr_datum(c); + if (!xd->xname) { + retry = 1; + goto retry; + } + } + + return 0; +} + +static inline int load_xattr_datum_nolock(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem); */ + if (unlikely(!xd->xname)) + return do_load_xattr_datum(c, xd); + return 0; +} + +static inline int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_read(xattr_sem); */ + int ret; + + if (likely(xd->xname)) + return 0; + + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + ret = load_xattr_datum_nolock(c, xd); + downgrade_write(&c->xattr_sem); + + return ret; +} + +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + + 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 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 ret, totlen; + + ret = load_xattr_datum_nolock(c, xd); + if (ret) + return ret; + + vecs[0].iov_base = ℞ + 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(totlen); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node)-4)); + + rx.xid = cpu_to_je32(xd->xid); + 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)-8)); + + ret = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); + if (ret || totlen!=length) { + printk(KERN_NOTICE "%s:%d Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", + __FILE__, __LINE__, totlen, phys_ofs, ret, length); + 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; + } + /* success */ + raw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, raw); + if (xd->node) { + xd->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, xd->node); + } + xd->node = raw; + + 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, nlen; + char *data; + int i, rc; + + /* Search xattr_datum has same xname/xvalue by index */ + hashkey = jffs2_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 */ + nlen = strlen(xname); + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + init_xattr_datum(xd); /* class = RAWNODE_CLASS_XATTR_DATUM, refcnt = 0 */ + + data = kmalloc(nlen + 1 + xsize, GFP_KERNEL); + if (!data) { + jffs2_free_xattr_datum(xd); + return ERR_PTR(-ENOMEM); + } + strcpy(data, xname); + data[nlen] = '\0'; + memcpy(data + nlen + 1, xvalue, xsize); + + xd->refcnt++; /* refcnt = 1 */ + xd->flags = JFFS2_XDATUM_FLAGS_HOT; + xd->xprefix = xprefix; + xd->xid = ++c->highest_xseqno; + + xd->hashkey = hashkey; + xd->xname = data; + xd->xvalue = data + nlen + 1; + xd->name_len = nlen; + 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; + if (c->xdatum_mem_usage > c->xdatum_mem_threshold) + reclaim_xattr_datum(c); + + return xd; +} + +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) { + xd->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, xd->node); + } + jffs2_free_xattr_datum(xd); +} + +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(sizeof(rr)); + rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node)-4)); + + rr.seqno = cpu_to_je32(ref->seqno); + 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(struct jffs2_raw_xref)-4)); + + ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); + if (ret || sizeof(rr)!=length) { + printk(KERN_NOTICE "%s:%d Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + __FILE__, __LINE__, sizeof(rr), phys_ofs, ret, length); + 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) { + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + } + ref->node = raw; + + 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; + + /* Duplication Check */ + list_for_each_entry(ref, &ic->ilist, ilist) { + if (ref->xd == xd) + return ERR_PTR(-EINVAL); + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return ERR_PTR(-ENOMEM); + init_xattr_ref(ref); /* class = RAWNODE_CLASS_XATTR_REF */ + + ref->seqno = ++c->highest_xseqno; + 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 */ +} + +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); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + + list_del(&ref->ilist); + xd = ref->xd; + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + + jffs2_free_xattr_ref(ref); +} + +void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + 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() */ + 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 do_check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_xattr_ref *ref, *cmp; + int ret = 0; + + down_write(&c->xattr_sem); + retry: + list_for_each_entry(ref, &ic->ilist, ilist) { + ret = load_xattr_datum_nolock(c, ref->xd); + if (ret) + goto out; + cmp = ref; + list_for_each_entry_continue(cmp, &ic->ilist, ilist) { + ret = load_xattr_datum_nolock(c, cmp->xd); + if (ret) + goto out; + if (ref->xd->xprefix == cmp->xd->xprefix + && !strcmp(ref->xd->xname, cmp->xd->xname)) { + delete_xattr_ref(c, ref->seqno > cmp->seqno ? cmp : ref); + goto retry; + } + } + } + out: + up_write(&c->xattr_sem); + + if (!ret) + ic->flags |= INO_FLAGS_XATTR_CHECKED; + + return ret; +} + +static inline int check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED)) + return 0; + return do_check_xattr_ref_ilist(c, ic); +} + +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; + int len, ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + down_read(&c->xattr_sem); + len = 0; + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + xd = ref->xd; + ret = load_xattr_datum(c, xd); + if (ret<0) + goto out; + + xhandle = xprefix_to_handler(xd->xprefix); + if (!xhandle) + continue; + + if (buffer) { + ret = xhandle->list(inode, buffer+len, size-len, xd->xname, xd->name_len); + } else { + ret = xhandle->list(inode, NULL, 0, xd->xname, xd->name_len); + } + if (ret < 0) + goto out; + len += ret; + } + out: + up_read(&c->xattr_sem); + return ret; +} + +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 ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + down_read(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + + ret = load_xattr_datum(c, xd); + if (ret) + goto out; + + if (!strcmp(xname, xd->xname)) { + ret = xd->value_len; + if (buffer) { + if (size < ret) { + ret = -ERANGE; + } else { + memcpy(buffer, xd->xvalue, xd->value_len); + } + } + goto out; + } + } + ret = -ENODATA; + out: + up_read(&c->xattr_sem); + return ret; +} + +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, *nref; + uint32_t phys_ofs, length, request; + int ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + request = PAD(sizeof(*xd) + strlen(xname) + 1 + size); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", + __FILE__, __LINE__, ret, length, request, phys_ofs); + return ret; + } + + /* Find existing xattr */ + down_write(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + + ret = load_xattr_datum(c, xd); + if (ret) + goto out; + + if (!strcmp(xd->xname, xname)) { + if (flags & XATTR_CREATE) { + ret = -EEXIST; + goto out; + } + goto found; + } + } + /* not found */ + ref = NULL; + if (flags & XATTR_REPLACE) { + ret = -ENODATA; + goto out; + } + if (!buffer) { + ret = -EINVAL; + goto out; + } + found: + xd = create_xattr_datum(c, xprefix, xname, buffer, size, phys_ofs); + if (IS_ERR(xd)) { + ret = PTR_ERR(xd); + goto out; + } + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + /* create xattr_ref */ + request = PAD(sizeof(*nref)); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", + __FILE__, __LINE__, ret, length, request, phys_ofs); + down_write(&c->xattr_sem); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + up_write(&c->xattr_sem); + return ret; + } + down_write(&c->xattr_sem); + nref = create_xattr_ref(c, ic, xd, phys_ofs); + if (IS_ERR(nref)) { + ret = PTR_ERR(nref); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + goto out; + } else if (ref) { + /* If replaced xattr_ref exists */ + delete_xattr_ref(c, ref); + } + out: + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + return ret; +} + +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; + int ret; + + BUG_ON(!xd->node); + + totlen = ref_totlen(c, c->gcblock, xd->node); + if (totlen < sizeof(struct jffs2_raw_xattr)) + return -EINVAL; + + ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret || length < totlen) { + printk(KERN_WARNING "jffs2_reserve_space_gc() = %d " + "request: %d byte reserved: %d byte", + ret, totlen, length); + return ret ? ret : -EBADFD; + } + + ret = load_xattr_datum_nolock(c, xd); + if (!ret) + ret = save_xattr_datum(c, xd, phys_ofs); + + return ret; +} + + +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; + int ret; + + BUG_ON(!ref->node); + + totlen = ref_totlen(c, c->gcblock, ref->node); + if (totlen != sizeof(struct jffs2_raw_xref)) + return -EINVAL; + + ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret || length < totlen) { + printk(KERN_WARNING "jffs2_reserve_space_gc() = %d" + " request: %d byte reserved: %d byte", ret, totlen, length); + return ret ? ret : -EBADFD; + } + + ret = save_xattr_ref(c, ref, phys_ofs); + + return ret; +} + +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; +} diff -prNU3 mtd-20051127/fs/jffs2/xattr.h mtd-20051127.xattr/fs/jffs2/xattr.h --- mtd-20051127/fs/jffs2/xattr.h 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/xattr.h 2005-11-27 00:00:13.000000000 -0500 @@ -0,0 +1,144 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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 XATTRINDEX_HASHSIZE (57) +#define JFFS2_XDATUM_FLAGS_HOT 0x01 /* This is hot xattr_datum */ + +#define RAWNODE_CLASS_INODE_CACHE 0 +#define RAWNODE_CLASS_XATTR_DATUM 1 +#define RAWNODE_CLASS_XATTR_REF 2 + +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 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; + + uint32_t seqno; + 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 int 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_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid); +extern void jffs2_attach_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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 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 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)) + +static inline void init_xattr_datum(struct jffs2_xattr_datum *xd) +{ + memset(xd, 0, sizeof(struct jffs2_xattr_datum)); + xd->class = RAWNODE_CLASS_XATTR_DATUM; + INIT_LIST_HEAD(&xd->xindex); +} + +static inline void init_xattr_ref(struct jffs2_xattr_ref *ref) +{ + memset(ref, 0, sizeof(struct jffs2_xattr_ref)); + ref->class = RAWNODE_CLASS_XATTR_REF; + INIT_LIST_HEAD(&ref->ilist); +} + +#else + +static inline int jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) { return 0; } +static inline void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) {} +static inline void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) {} + +static inline void *jffs2_find_xattr_datum(void *c, uint32_t xid) { return NULL; } +static inline void jffs2_attach_xattr_datum(void *c, void *xd) {} + +static inline void jffs2_xattr_delete_inode(void *c, void *ic) {} +static inline void jffs2_xattr_free_inode(void *c, void *ic) {} +static inline int jffs2_garbage_collect_xattr(void *c, void *ic) { return 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) +static inline void init_xattr_datum(void *xd) {} +static inline void init_xattr_ref(void *ref) {} + +#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 + +static inline int jffs2_init_security(struct inode *inode, struct inode *dir) +{ + return 0; +} + +#endif /* CONFIG_JFFS2_FS_SECURITY */ + +#endif /* _JFFS2_FS_XATTR_H_ */ diff -prNU3 mtd-20051127/fs/Kconfig mtd-20051127.xattr/fs/Kconfig --- mtd-20051127/fs/Kconfig 2005-11-26 23:48:43.000000000 -0500 +++ mtd-20051127.xattr/fs/Kconfig 2005-11-27 00:27:08.000000000 -0500 @@ -65,8 +65,8 @@ config JFFS2_FS_WRITEBUFFER - DataFlash config JFFS2_FS_XATTR - bool "JFFS2 XATTR support (EXPERIMENTAL)" - depends on JFFS2_FS && EXPERIMENTAL + bool "JFFS2 XATTR support" + depends on JFFS2_FS default n help Extended attributes are name:value pairs associated with inodes by diff -prNU3 mtd-20051127/include/linux/jffs2_fs_i.h mtd-20051127.xattr/include/linux/jffs2_fs_i.h --- mtd-20051127/include/linux/jffs2_fs_i.h 2005-11-07 20:02:17.000000000 -0500 +++ mtd-20051127.xattr/include/linux/jffs2_fs_i.h 2005-11-27 00:00:13.000000000 -0500 @@ -5,6 +5,7 @@ #include <linux/version.h> #include <linux/rbtree.h> +#include <linux/posix_acl.h> #include <asm/semaphore.h> struct jffs2_inode_info { @@ -45,6 +46,10 @@ struct jffs2_inode_info { struct inode vfs_inode; #endif #endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + struct posix_acl *i_acl_access; + struct posix_acl *i_acl_default; +#endif }; #endif /* _JFFS2_FS_I */ diff -prNU3 mtd-20051127/include/linux/jffs2_fs_sb.h mtd-20051127.xattr/include/linux/jffs2_fs_sb.h --- mtd-20051127/include/linux/jffs2_fs_sb.h 2005-11-18 20:02:14.000000000 -0500 +++ mtd-20051127.xattr/include/linux/jffs2_fs_sb.h 2005-11-27 00:00:13.000000000 -0500 @@ -140,6 +140,14 @@ struct jffs2_sb_info { struct jffs2_blocks_bucket used_blocks[HASH_SIZE]; /* The hash table for both dirty and clean erase blocks */ struct jffs2_blocks_bucket free_blocks[HASH_SIZE]; /* The hash table for free erase blocks */ +#ifdef CONFIG_JFFS2_FS_XATTR + uint32_t highest_xseqno; + struct list_head *xattrindex; +#define xattr_temp xattrindex[XATTRINDEX_HASHSIZE] + 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; }; diff -prNU3 mtd-20051127/include/linux/jffs2.h mtd-20051127.xattr/include/linux/jffs2.h --- mtd-20051127/include/linux/jffs2.h 2005-11-07 20:02:17.000000000 -0500 +++ mtd-20051127.xattr/include/linux/jffs2.h 2005-11-27 00:00:13.000000000 -0500 @@ -71,6 +71,18 @@ #define JFFS2_NODETYPE_ERASEBLOCK_HEADER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5) #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) @@ -157,6 +169,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 */ + 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 seqno; /* sequencial number */ + jint32_t ino; /* inode number */ + jint32_t xid; /* XATTR identifier number */ + jint32_t node_crc; +} __attribute__((packed)); + struct jffs2_raw_summary { jint16_t magic; @@ -191,6 +229,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_raw_ebh eh; struct jffs2_unknown_node u; ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-11-27 6:58 ` KaiGai Kohei @ 2005-11-27 9:43 ` KaiGai Kohei 2005-11-27 15:45 ` Artem B. Bityutskiy 0 siblings, 1 reply; 28+ messages in thread From: KaiGai Kohei @ 2005-11-27 9:43 UTC (permalink / raw) To: Jörn Engel Cc: James Morris, KaiGai Kohei, Stephen Smalley, linux-mtd, lorenzohgh, David Woodhouse, Andreas Gruenbacher [-- Attachment #1: Type: text/plain, Size: 2274 bytes --] Sorry, some garbages mixed into my previous mail. Please use those patches which are attached this mail, and discard previous one. KaiGai Kohei wrote: > Hi, > > This attached patches are enables XATTR support on JFFS2. > some new functionalities are added from take-3 patches. > > # Sorry for slowing update. > > [NOTICE] > We can test this patch with ONLY 2.6.14 kernel. > The previous versions of kernel do not support an new LSM hook. > > [1/2] jffs2_xattr_take-4.patch > * CONFIG_JFFS2_XATTR was renamed to CONFIG_JFFS2_FS_XATTR > The XATTR configuration switch are named CONFIG_<fsname>_FS_XATTR > on another some filesystems which support XATTR. > I also follow this manner. > > * CONFIG_JFFS2_FS_POSIX_ACL and JFFS2_FS_SECURITY are selectable. > this follows another FS's manner. > > * POSIX-ACL support was added. > This functionality is implemented in acl.c, acl.h. > I implemented the most part of this functionalities by referring > the Ext2/3's implementation of POSIX-ACL. > If I took bad manner or misunderstanding for implementation, > notice me please. > > * jffs2_init_security() was added for inode initialization. > At 2.6.14, new LSM hook(security_inode_init_security) was added. > Because of this, any filesystems obtain security attribtues must > call this this hook to set the security label for new inode. > It's deployed in jffs2_create(), jffs2_symlink(), jffs2_mkdir() > and jffs2_mknod(). > > * Some patch conflicts were fixed. > We can apply this patches for latest MTD-CVS without incident. > > * An obvious bug was fixed. > In jffs2_build_xattr_subsystem(), NULL checking was omitted. > > [2/2] mkfs.jffs2-xattr.patch > * --with-{xattr|selinux|posix-acl} options are added. > --with-xattr enables to pack all xattr-entries into jffs2 image file. > --with-selinux enables to pack security related xattr-entries which > are named as 'security.*' into jffs2 image file. > --with-posix-acl enables to pack ACL related xattr-entries which > are names as 'system.posix_acl_access' or 'system.posix_acl_default'. > > I hope to merge those functionalities into MTD's CVS tree. > Thanks, -- KaiGai Kohei <kaigai@kaigai.gr.jp> [-- Attachment #2: mkfs.jffs2-xattr.patch --] [-- Type: text/plain, Size: 13502 bytes --] diff -rpNU3 mtd-20051127.xattr/util/mkfs.jffs2.1 mtd-20051127.mkfs/util/mkfs.jffs2.1 --- mtd-20051127.xattr/util/mkfs.jffs2.1 2005-11-27 00:00:13.000000000 -0500 +++ mtd-20051127.mkfs/util/mkfs.jffs2.1 2005-11-27 00:00:36.000000000 -0500 @@ -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 diff -rpNU3 mtd-20051127.xattr/util/mkfs.jffs2.c mtd-20051127.mkfs/util/mkfs.jffs2.c --- mtd-20051127.xattr/util/mkfs.jffs2.c 2005-11-27 00:00:13.000000000 -0500 +++ mtd-20051127.mkfs/util/mkfs.jffs2.c 2005-11-27 00:00:36.000000000 -0500 @@ -7,6 +7,7 @@ * 2002 Axis Communications AB * 2001, 2002 Erik Andersen <andersen@codepoet.org> * 2004 University of Szeged, Hungary + * 2005 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 <attr/xattr.h> +#include <sys/acl.h> #include <byteswap.h> #define crc32 __complete_crap #include <zlib.h> @@ -1018,6 +1021,282 @@ static void write_special_file(struct fi padword(); } +#include "../fs/jffs2/acl.h" +#define XATTR_BUFFER_SIZE 65536 +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; + +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 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)) + +static uint32_t enable_xattr = 0; +static uint32_t xseqno = 0; + +static struct { + int xprefix; + char *string; + int strict; +} xprefix_tbl[] = { + { JFFS2_XPREFIX_USER, "user.", 0 }, + { JFFS2_XPREFIX_SECURITY, "security.", 0 }, + { JFFS2_XPREFIX_ACL_ACCESS, "system.posix_acl_access", 1 }, + { JFFS2_XPREFIX_ACL_DEFAULT, "system.posix_acl_default", 1 }, + { JFFS2_XPREFIX_TRUSTED, "trusted.", 0 }, + { 0, NULL, 0 } +}; + +static void write_xattr_normal(xattr_entry_t *xe) +{ + struct jffs2_raw_xattr rx; + + 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(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.xprefix = xe->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) - 8)); + + 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(); +} + +static void write_xattr_acl(xattr_entry_t *xe) +{ + struct jffs2_raw_xattr rx; + posix_acl_xattr_header *header; + posix_acl_xattr_entry *entry, *end; + jffs2_acl_header *jheader; + jffs2_acl_entry *jent; + jffs2_acl_entry_short *jent_s; + char buffer[XATTR_BUFFER_SIZE]; + int i, offset = 0; + + header = (posix_acl_xattr_header *)xe->xvalue; + entry = header->a_entries; + end = (posix_acl_xattr_entry *)(xe->xvalue + xe->value_len); + + buffer[offset++] = '\0'; /* termination char of xname */ + jheader = (jffs2_acl_header *)(buffer + offset); + jheader->a_version = cpu_to_je32(JFFS2_ACL_VERSION); + offset += sizeof(jffs2_acl_header); + + for (i=0; i < xe->value_len; i+=sizeof(unsigned long)) { + unsigned long *value = (unsigned long *)(xe->xvalue + i); + printf(" %08lx", *value); + } + putchar('\n'); + + while (entry < end) { + switch (le16_to_cpu(entry->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(entry->e_tag)); + jent_s->e_perm = cpu_to_je16(le16_to_cpu(entry->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(entry->e_tag)); + jent->e_perm = cpu_to_je16(le16_to_cpu(entry->e_perm)); + jent->e_id = cpu_to_je32(le32_to_cpu(entry->e_id)); + break; + default: + printf("%04x : Unknown XATTR entry tag.\n", le16_to_cpu(entry->e_tag)); + } + entry++; + } + + 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(sizeof(rx) + 1 + offset); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + + rx.xid = cpu_to_je32(xe->xid); + rx.xprefix = xe->xprefix; + rx.name_len = 0; + rx.value_len = cpu_to_je16(offset - 1); + rx.data_crc = cpu_to_je32(crc32(0, buffer, offset)); + rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 8)); + + pad_block_if_less_than(sizeof(rx) + offset); + full_write(out_fd, &rx, sizeof(rx)); + full_write(out_fd, buffer, offset); + padword(); + + for (i=0; i < offset; i+= sizeof(unsigned long)) { + unsigned long *value = (unsigned long *)(buffer + 1 + i); + printf(" %08lx", *value); + } + putchar('\n'); + printf("wrote-a: xid = %u xprefix = %d dlen = %d hcrc = %08x dcrc = %08x\n", xe->xid, xe->xprefix, offset, je32_to_cpu(rx.node_crc), je32_to_cpu(rx.data_crc)); +} + +static xattr_entry_t *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ + xattr_entry_t *xe; + int name_len; + + name_len = strlen(xname); + xe = xcalloc(1, sizeof(struct xattr_entry) + name_len + 1 + value_len); + xe->xid = ++xseqno; + xe->xprefix = xprefix; + xe->xname = (char *)(xe + 1); + 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); + + switch (xprefix) { + case JFFS2_XPREFIX_ACL_ACCESS: + case JFFS2_XPREFIX_ACL_DEFAULT: + write_xattr_acl(xe); + break; + default: + write_xattr_normal(xe); + break; + } + return xe; +} + +#define XATTRENTRY_HASHSIZE 37 +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 name_len, index; + + if ((enable_xattr & (1 << xprefix)) == 0) + return NULL; + + /* find or create xattr entry */ + if (!xentry_hash) + xentry_hash = xcalloc(1, sizeof(struct xattr_entry *) * XATTRENTRY_HASHSIZE); + + name_len = strlen(xname) + 1; + 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; + 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) { + int i, xprefix, prefix_len; + char *prefix_str; + + 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 = strlen(prefix_str); + + if (xprefix_tbl[i].strict) { + if (!strcmp(xname, prefix_str)) + break; + } else { + if (!strncmp(xname, prefix_str, prefix_len)) + break; + } + } + if (!xprefix) { + if (verbose) + printf("%s : XATTR '%s' was not supported.\n", e->hostname, xname); + 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.seqno = cpu_to_je32(++xseqno); + 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; @@ -1025,6 +1304,8 @@ static void recursive_populate_directory if (verbose) { printf("%s\n", dir->fullname); } + write_xattr_entry(dir); /* for '/' */ + e = dir->files; while (e) { @@ -1037,6 +1318,7 @@ static void recursive_populate_directory e->name); } write_pipe(e); + write_xattr_entry(e); break; case S_IFSOCK: if (verbose) { @@ -1045,6 +1327,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) { @@ -1053,6 +1336,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) { @@ -1062,6 +1346,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) { @@ -1071,6 +1356,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) { @@ -1080,6 +1366,7 @@ static void recursive_populate_directory e->link); } write_symlink(e); + write_xattr_entry(e); break; case S_IFREG: if (verbose) { @@ -1088,6 +1375,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, @@ -1172,6 +1460,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} }; @@ -1204,6 +1495,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" @@ -1531,6 +1825,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) { [-- Attachment #3: jffs2_xattr_take-4.patch --] [-- Type: text/plain, Size: 76996 bytes --] diff -prNU3 mtd-20051127/fs/jffs2/acl.c mtd-20051127.xattr/fs/jffs2/acl.c --- mtd-20051127/fs/jffs2/acl.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/acl.c 2005-11-27 04:28:00.000000000 -0500 @@ -0,0 +1,484 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/acl.c + * POSIX ACL support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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/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; + int i, count; + + if (!value) + return NULL; + if (size < sizeof(jffs2_acl_header)) + return ERR_PTR(-EINVAL); + if (je32_to_cpu(((jffs2_acl_header *)value)->a_version) != JFFS2_ACL_VERSION) { + D1(printk(KERN_NOTICE "%s:%d invalid ACL version.\n", __FUNCTION__, __LINE__)); + 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; + char *value = NULL; + int rc, xprefix; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + switch (type) { + case ACL_TYPE_ACCESS: + xprefix = JFFS2_XPREFIX_ACL_ACCESS; + if (acl) { + mode_t mode = inode->i_mode; + rc = posix_acl_equiv_mode(acl, &mode); + if (rc < 0) + return rc; + if (inode->i_mode != mode) { + inode->i_mode = mode; + jffs2_dirty_inode(inode); + } + if (rc == 0) + acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: + xprefix = JFFS2_XPREFIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + default: + return -EINVAL; + } + if (acl) { + value = jffs2_acl_to_medium(acl, &size); + if (IS_ERR(value)) + return PTR_ERR(value); + } + + rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0); + + if (value) + kfree(value); + if (!rc) { + switch(type) { + case ACL_TYPE_ACCESS: + jffs2_iset_acl(inode, &f->i_acl_access, acl); + break; + case ACL_TYPE_DEFAULT: + jffs2_iset_acl(inode, &f->i_acl_default, acl); + break; + } + } + return rc; +} + +static int jffs2_check_acl(struct inode *inode, int mask) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_permission(inode, acl, mask); + posix_acl_release(acl); + return rc; + } + return -EAGAIN; +} + +int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, jffs2_check_acl); +} + +int jffs2_init_acl(struct inode *inode, struct inode *dir) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct posix_acl *acl = NULL, *clone; + mode_t mode; + int rc = 0; + + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + if (!S_ISLNK(inode->i_mode)) { + acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + inode->i_mode &= ~current->fs->umask; + } + if (acl) { + if (S_ISDIR(inode->i_mode)) { + rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (rc) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + rc = -ENOMEM; + if (!clone) + goto cleanup; + mode = inode->i_mode; + rc = posix_acl_create_masq(clone, &mode); + if (rc >= 0) { + inode->i_mode = mode; + if (rc > 0) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + } + posix_acl_release(clone); + } + cleanup: + posix_acl_release(acl); + return rc; +} + +void jffs2_clear_acl(struct inode *inode) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + + if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_access); + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + } + if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_default); + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + } +} + +int jffs2_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int rc; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + rc = posix_acl_chmod_masq(clone, inode->i_mode); + if (!rc) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return rc; +} + +static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_ACCESS); + return retlen; +} + +static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_DEFAULT); + return retlen; +} + +static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + return -ENODATA; + rc = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return rc; +} + +static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size) +{ + struct posix_acl *acl; + int rc; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_valid(acl); + if (rc) + goto out; + } + } else { + acl = NULL; + } + rc = jffs2_set_acl(inode, type, acl); + out: + posix_acl_release(acl); + return rc; +} + +static int jffs2_acl_access_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +struct xattr_handler jffs2_acl_access_xattr_handler = { + .prefix = POSIX_ACL_XATTR_ACCESS, + .list = jffs2_acl_access_listxattr, + .get = jffs2_acl_access_getxattr, + .set = jffs2_acl_access_setxattr, +}; + +struct xattr_handler jffs2_acl_default_xattr_handler = { + .prefix = POSIX_ACL_XATTR_DEFAULT, + .list = jffs2_acl_default_listxattr, + .get = jffs2_acl_default_getxattr, + .set = jffs2_acl_default_setxattr, +}; diff -prNU3 mtd-20051127/fs/jffs2/acl.h mtd-20051127.xattr/fs/jffs2/acl.h --- mtd-20051127/fs/jffs2/acl.h 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/acl.h 2005-11-27 04:28:00.000000000 -0500 @@ -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) 2005 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 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 +static inline int jffs2_acl_chmod(struct inode *inode) { return 0; } +static inline int jffs2_init_acl(struct inode *inode, struct inode *dir) { return 0; } +static inline void jffs2_clear_acl(struct inode *inode) {} + +#endif + + diff -prNU3 mtd-20051127/fs/jffs2/build.c mtd-20051127.xattr/fs/jffs2/build.c --- mtd-20051127/fs/jffs2/build.c 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/build.c 2005-11-27 04:28:00.000000000 -0500 @@ -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; diff -prNU3 mtd-20051127/fs/jffs2/dir.c mtd-20051127.xattr/fs/jffs2/dir.c --- mtd-20051127/fs/jffs2/dir.c 2005-11-07 20:02:14.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/dir.c 2005-11-27 04:28:00.000000000 -0500 @@ -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 }; /***********************************************************************/ @@ -206,12 +211,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)); @@ -221,6 +229,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; } /***********************************************************************/ @@ -371,6 +385,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) { @@ -501,6 +527,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) { @@ -657,6 +695,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) { diff -prNU3 mtd-20051127/fs/jffs2/file.c mtd-20051127.xattr/fs/jffs2/file.c --- mtd-20051127/fs/jffs2/file.c 2005-11-07 20:02:14.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/file.c 2005-11-27 04:28:00.000000000 -0500 @@ -54,7 +54,12 @@ struct file_operations jffs2_file_operat struct inode_operations jffs2_file_inode_operations = { - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; struct address_space_operations jffs2_file_address_operations = diff -prNU3 mtd-20051127/fs/jffs2/fs.c mtd-20051127.xattr/fs/jffs2/fs.c --- mtd-20051127/fs/jffs2/fs.c 2005-11-24 20:02:10.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/fs.c 2005-11-27 04:28:00.000000000 -0500 @@ -179,7 +179,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) + rc = jffs2_acl_chmod(dentry->d_inode); + return rc; } int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) @@ -218,6 +223,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); } @@ -492,9 +498,12 @@ int jffs2_do_fill_super(struct super_blo } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); - if ((ret = jffs2_do_mount_fs(c))) + if ((ret = jffs2_init_xattr_subsystem(c))) goto out_inohash; + if ((ret = jffs2_do_mount_fs(c))) + goto out_xattr; + ret = -EINVAL; D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); @@ -522,6 +531,8 @@ int jffs2_do_fill_super(struct super_blo jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); jffs2_free_eraseblocks(c); + out_xattr: + jffs2_clear_xattr_subsystem(c); out_inohash: kfree(c->inocache_list); out_wbuf: diff -prNU3 mtd-20051127/fs/jffs2/gc.c mtd-20051127.xattr/fs/jffs2/gc.c --- mtd-20051127/fs/jffs2/gc.c 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/gc.c 2005-11-27 04:28:00.000000000 -0500 @@ -312,6 +312,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. */ diff -prNU3 mtd-20051127/fs/jffs2/Makefile.common mtd-20051127.xattr/fs/jffs2/Makefile.common --- mtd-20051127/fs/jffs2/Makefile.common 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/Makefile.common 2005-11-27 04:28:00.000000000 -0500 @@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o bac jffs2-y += super.o debug.o wear_leveling.o jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o +jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o +jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o +jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff -prNU3 mtd-20051127/fs/jffs2/malloc.c mtd-20051127.xattr/fs/jffs2/malloc.c --- mtd-20051127/fs/jffs2/malloc.c 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/malloc.c 2005-11-27 04:28:00.000000000 -0500 @@ -28,6 +28,10 @@ static kmem_cache_t *raw_node_ref_slab; static kmem_cache_t *node_frag_slab; static kmem_cache_t *inode_cache_slab; static kmem_cache_t *eraseblock_slab; +#ifdef CONFIG_JFFS2_FS_XATTR +static kmem_cache_t *xattr_datum_cache; +static kmem_cache_t *xattr_ref_cache; +#endif static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c) { @@ -81,8 +85,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; @@ -106,6 +126,12 @@ void jffs2_destroy_slab_caches(void) kmem_cache_destroy(inode_cache_slab); if (eraseblock_slab) kmem_cache_destroy(eraseblock_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) @@ -275,3 +301,33 @@ void jffs2_free_eraseblocks(struct jffs2 kfree(c->blocks); } +#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); + 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); + 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 diff -prNU3 mtd-20051127/fs/jffs2/nodelist.c mtd-20051127.xattr/fs/jffs2/nodelist.c --- mtd-20051127/fs/jffs2/nodelist.c 2005-11-13 20:03:03.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/nodelist.c 2005-11-27 04:28:00.000000000 -0500 @@ -919,6 +919,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; } diff -prNU3 mtd-20051127/fs/jffs2/nodelist.h mtd-20051127.xattr/fs/jffs2/nodelist.h --- mtd-20051127/fs/jffs2/nodelist.h 2005-11-18 20:02:12.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/nodelist.h 2005-11-27 04:28:00.000000000 -0500 @@ -20,6 +20,8 @@ #include <linux/jffs2.h> #include <linux/jffs2_fs_sb.h> #include <linux/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,8 @@ 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 INOCACHE_HASHSIZE 128 /* @@ -388,6 +397,13 @@ struct jffs2_inode_cache *jffs2_alloc_in void jffs2_free_inode_cache(struct jffs2_inode_cache *); int jffs2_alloc_eraseblocks(struct jffs2_sb_info *c); void jffs2_free_eraseblocks(struct jffs2_sb_info *c); +#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 /* CONFIG_JFFS2_FS_XATTR */ + /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); diff -prNU3 mtd-20051127/fs/jffs2/os-linux.h mtd-20051127.xattr/fs/jffs2/os-linux.h --- mtd-20051127/fs/jffs2/os-linux.h 2005-11-11 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/os-linux.h 2005-11-27 04:28:00.000000000 -0500 @@ -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 } diff -prNU3 mtd-20051127/fs/jffs2/readinode.c mtd-20051127.xattr/fs/jffs2/readinode.c --- mtd-20051127/fs/jffs2/readinode.c 2005-11-11 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/readinode.c 2005-11-27 04:28:00.000000000 -0500 @@ -891,6 +891,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) { diff -prNU3 mtd-20051127/fs/jffs2/scan.c mtd-20051127.xattr/fs/jffs2/scan.c --- mtd-20051127/fs/jffs2/scan.c 2005-11-18 20:02:13.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/scan.c 2005-11-27 04:28:00.000000000 -0500 @@ -299,6 +299,159 @@ int jffs2_scan_classify_jeb(struct jffs2 return BLK_STATE_ALLDIRTY; } +#ifdef CONFIG_JFFS2_FS_XATTR + +#if 1 /* In cleaned up scan.c, this section is not necessary. */ +static inline uint32_t dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + uint32_t unpadded_space) +{ + DIRTY_SPACE(PAD(unpadded_space)); + + return PAD(unpadded_space);; +} + +static inline uint32_t used_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + uint32_t unpadded_space) +{ + USED_SPACE(PAD(unpadded_space)); + + return PAD(unpadded_space); +} + +#endif /* In cleaned up scan.c, this section is not necessary. */ + +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 crc; + + crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr)-8); + if (crc != je32_to_cpu(rx->node_crc)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_find_xattr_datum(c, je32_to_cpu(rx->xid)); + if (xd) { + printk(KERN_NOTICE "%s() duplicate xid=%u found. " + "on node at 0x%08x, later one is ignored.\n", + __FUNCTION__, je32_to_cpu(rx->xid), ofs); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + crc = crc32(0, rx->data, rx->name_len + 1 + je16_to_cpu(rx->value_len)); + if (crc != je32_to_cpu(rx->data_crc)) { + printk(KERN_NOTICE "%s data CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rx->data_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rx->totlen)); + return 0; + } + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return -ENOMEM; + init_xattr_datum(xd); + + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + jffs2_free_xattr_datum(xd); + return -ENOMEM; + } + + xd->xid = je32_to_cpu(rx->xid); + if (xd->xid > c->highest_xseqno) + c->highest_xseqno = xd->xid; + xd->xprefix = rx->xprefix; /* 8bit width */ + xd->name_len = rx->name_len; /* 8bit width */ + xd->value_len = je16_to_cpu(rx->value_len); + xd->data_crc = je32_to_cpu(rx->data_crc); + xd->node = raw; + + raw->__totlen = PAD(je32_to_cpu(rx->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(c, jeb, je32_to_cpu(rx->totlen)); + + jffs2_attach_xattr_datum(c, xd); + + 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)) { + printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + __FUNCTION__, ofs, je32_to_cpu(rr->node_crc), crc); + dirty_space(c, jeb, je32_to_cpu(rr->totlen)); + return 0; + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return -ENOMEM; + init_xattr_ref(ref); + + 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->seqno = je32_to_cpu(rr->seqno); + if (ref->seqno > c->highest_xseqno) + c->highest_xseqno = ref->seqno; + ref->ino = je32_to_cpu(rr->ino); + ref->xid = je32_to_cpu(rr->xid); + ref->node = raw; + + 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(c, jeb, je32_to_cpu(rr->totlen)); + + list_add_tail(&ref->ilist, &c->xattr_temp); + + 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 +767,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)) { + /* FIXME: In cleaned-up scan.c, this If-block is not necessary. */ + 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_flash_read_safe(c, buf_ofs, buf_len, buf); + 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)) { + /* FIXME: In cleaned-up scan.c, this If-block is not necessary. */ + 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_flash_read_safe(c, buf_ofs, buf_len, buf); + 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) { @@ -741,6 +931,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; diff -prNU3 mtd-20051127/fs/jffs2/security.c mtd-20051127.xattr/fs/jffs2/security.c --- mtd-20051127/fs/jffs2/security.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/security.c 2005-11-27 04:28:00.000000000 -0500 @@ -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) 2005 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 = sizeof(XATTR_SECURITY_PREFIX) + name_len; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_SECURITY_PREFIX); + strcpy(list+sizeof(XATTR_SECURITY_PREFIX)-1, 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 +}; diff -prNU3 mtd-20051127/fs/jffs2/super.c mtd-20051127.xattr/fs/jffs2/super.c --- mtd-20051127/fs/jffs2/super.c 2005-11-24 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/super.c 2005-11-27 04:28:00.000000000 -0500 @@ -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_VERBOSE)?1:0); if (ret) { @@ -290,6 +293,7 @@ static void jffs2_put_super (struct supe jffs2_free_eraseblocks(c); jffs2_flash_cleanup(c); kfree(c->inocache_list); + jffs2_clear_xattr_subsystem(c); if (c->mtd->sync) c->mtd->sync(c->mtd); diff -prNU3 mtd-20051127/fs/jffs2/symlink.c mtd-20051127.xattr/fs/jffs2/symlink.c --- mtd-20051127/fs/jffs2/symlink.c 2005-11-07 20:02:15.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/symlink.c 2005-11-27 04:28:00.000000000 -0500 @@ -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) diff -prNU3 mtd-20051127/fs/jffs2/write.c mtd-20051127.xattr/fs/jffs2/write.c --- mtd-20051127/fs/jffs2/write.c 2005-11-11 20:02:11.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/write.c 2005-11-27 04:28:00.000000000 -0500 @@ -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)); diff -prNU3 mtd-20051127/fs/jffs2/xattr.c mtd-20051127.xattr/fs/jffs2/xattr.c --- mtd-20051127/fs/jffs2/xattr.c 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/xattr.c 2005-11-27 04:28:00.000000000 -0500 @@ -0,0 +1,1021 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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" + +/* ---- decralation of internal use functions ----------------- */ +static void reclaim_xattr_datum(struct jffs2_sb_info *c); +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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); +static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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); +static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref); + +/* ---- XATTR Handler for "user.*" --------------------- */ +#define XATTR_USER_PREFIX "user." +static int jffs2_user_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + int ret; + + if (!strcmp(name, "")) + return -EINVAL; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + 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) +{ + int ret; + + if (!strcmp(name, "") == 0) + return -EINVAL; + + if ( !S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return -EPERM; + + ret = permission(inode, MAY_WRITE, NULL); + if (ret) + return ret; + + 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) +{ + int retlen = sizeof(XATTR_USER_PREFIX) + name_len; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_USER_PREFIX); + strcpy(list+sizeof(XATTR_USER_PREFIX)-1, name); + } + + return retlen; +} + +static struct xattr_handler jffs2_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .list = jffs2_user_listxattr, + .set = jffs2_user_setxattr, + .get = jffs2_user_getxattr +}; + +/* ---- XATTR Handler for "trusted.*" ------------------ */ +#define XATTR_TRUSTED_PREFIX "trusted." +static int jffs2_trusted_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + 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; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + 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) +{ + int retlen = sizeof(XATTR_TRUSTED_PREFIX) + name_len; + + if (!capable(CAP_SYS_ADMIN)) + return 0; /* ignore this entry */ + + if (list && retlen<=list_size) { + strcpy(list, XATTR_TRUSTED_PREFIX); + strcpy(list+sizeof(XATTR_TRUSTED_PREFIX)-1, name); + } + + return retlen; +} + +static struct xattr_handler jffs2_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .list = jffs2_trusted_listxattr, + .set = jffs2_trusted_setxattr, + .get = jffs2_trusted_getxattr +}; + +/* ----------------------------------------------------- */ +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; +} + +static uint32_t jffs2_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); +} +/* ---- Build-up and Destruct XATTR-Cache functions ----------- */ +int jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) +{ + int i; + + c->xattrindex = kmalloc((XATTRINDEX_HASHSIZE+1)*sizeof(struct list_head), GFP_KERNEL); + if (!c->xattrindex) + return -ENOMEM; + + init_rwsem(&c->xattr_sem); + + for (i=0; i<=XATTRINDEX_HASHSIZE; i++) + INIT_LIST_HEAD(&c->xattrindex[i]); + + //c->xdatum_mem_threshold = 20 * 1024; /* Default 20KB */ + c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */ + + return 0; +} + +void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_xattr_ref *ref, *_ref; + int i; + + if (!c->xattrindex) + return; /* not initialized */ + + 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); + } + } + + kfree(c->xattrindex); + c->xattrindex = NULL; +} + +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; + + 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); + /* 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) { + printk(KERN_NOTICE "building xref {ino=%u, xid=%u} is not found.\n", + ref->xid, ref->ino); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + jffs2_free_xattr_ref(ref); + continue; + } + ref->xd = xd; + ref->ic = ic; + xd->refcnt++; + list_add_tail(&ref->ilist, &ic->ilist); + D1(printk(KERN_NOTICE "bind XREF{ino=%u, xid=%u}\n", ic->ino, xd->xid)); + } + /* 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) { + printk(KERN_NOTICE "unrefered xattr_datum found xid=%u\n", xd->xid); + delete_xattr_datum(c, xd); + } + } + } + /* build complete */ + D1(printk(KERN_NOTICE "%s complete.\n", __FUNCTION__)); +} + +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_attach_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + int i = xd->xid % XATTRINDEX_HASHSIZE; + + /* It's only used in scanning process. */ + BUG_ON(!(c->flags & JFFS2_SB_FLAG_SCANNING)); + + list_add_tail(&xd->xindex, &c->xattrindex[i]); +} + +/* ---- Internal XATTR related functions ------------------------------------ */ +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; + + before = c->xdatum_mem_usage; + target = c->xdatum_mem_usage * 4 / 5; /* 20% reduction */ + while(1) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[index], xindex) { + if (xd->flags & JFFS2_XDATUM_FLAGS_HOT) { + xd->flags &= ~JFFS2_XDATUM_FLAGS_HOT; + } else { + unload_xattr_datum(c, xd); + } + if (c->xdatum_mem_usage <= target) + goto out; + } + index = (index+1) % XATTRINDEX_HASHSIZE; + } + out: + printk(KERN_NOTICE "JFFS2: reclaim_xattr_cache() before: %d Byte after: %d Byte\n", + before, c->xdatum_mem_usage); +} + +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; + + D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + retry: + length = xd->name_len + 1 + xd->value_len; + data = kmalloc(length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + BUG_ON(!xd->node); + ret = jffs2_flash_read(c, ref_offset(xd->node)+sizeof(struct jffs2_raw_xattr), + length, &readlen, data); + + if (ret || length!=readlen) { + printk(KERN_WARNING "jffs2_flash_read()=%d, request: %d, readlen: %d, at 0x%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) { + printk(KERN_WARNING "node CRC failed (JFFS2_NODETYPE_XREF)" + " at 0x%08x, read: 0x%08x calculated: 0x%08x\n", + ref_offset(xd->node), xd->data_crc, crc); + kfree(data); + return -EIO; + } + + xd->flags = JFFS2_XDATUM_FLAGS_HOT; + xd->xname = data; + xd->xvalue = data+xd->name_len+1; + + c->xdatum_mem_usage += length; + + xd->hashkey = jffs2_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 (c->xdatum_mem_usage > c->xdatum_mem_threshold && retry==0) { + reclaim_xattr_datum(c); + if (!xd->xname) { + retry = 1; + goto retry; + } + } + + return 0; +} + +static inline int load_xattr_datum_nolock(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem); */ + if (unlikely(!xd->xname)) + return do_load_xattr_datum(c, xd); + return 0; +} + +static inline int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_read(xattr_sem); */ + int ret; + + if (likely(xd->xname)) + return 0; + + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + ret = load_xattr_datum_nolock(c, xd); + downgrade_write(&c->xattr_sem); + + return ret; +} + +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + + 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 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 ret, totlen; + + ret = load_xattr_datum_nolock(c, xd); + if (ret) + return ret; + + vecs[0].iov_base = ℞ + 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(totlen); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node)-4)); + + rx.xid = cpu_to_je32(xd->xid); + 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)-8)); + + ret = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); + if (ret || totlen!=length) { + printk(KERN_NOTICE "%s:%d Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", + __FILE__, __LINE__, totlen, phys_ofs, ret, length); + 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; + } + /* success */ + raw->flash_offset |= REF_PRISTINE; + jffs2_add_physical_node_ref(c, raw); + if (xd->node) { + xd->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, xd->node); + } + xd->node = raw; + + 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, nlen; + char *data; + int i, rc; + + /* Search xattr_datum has same xname/xvalue by index */ + hashkey = jffs2_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 */ + nlen = strlen(xname); + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + init_xattr_datum(xd); /* class = RAWNODE_CLASS_XATTR_DATUM, refcnt = 0 */ + + data = kmalloc(nlen + 1 + xsize, GFP_KERNEL); + if (!data) { + jffs2_free_xattr_datum(xd); + return ERR_PTR(-ENOMEM); + } + strcpy(data, xname); + data[nlen] = '\0'; + memcpy(data + nlen + 1, xvalue, xsize); + + xd->refcnt++; /* refcnt = 1 */ + xd->flags = JFFS2_XDATUM_FLAGS_HOT; + xd->xprefix = xprefix; + xd->xid = ++c->highest_xseqno; + + xd->hashkey = hashkey; + xd->xname = data; + xd->xvalue = data + nlen + 1; + xd->name_len = nlen; + 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; + if (c->xdatum_mem_usage > c->xdatum_mem_threshold) + reclaim_xattr_datum(c); + + return xd; +} + +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) { + xd->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, xd->node); + } + jffs2_free_xattr_datum(xd); +} + +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(sizeof(rr)); + rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node)-4)); + + rr.seqno = cpu_to_je32(ref->seqno); + 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(struct jffs2_raw_xref)-4)); + + ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); + if (ret || sizeof(rr)!=length) { + printk(KERN_NOTICE "%s:%d Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + __FILE__, __LINE__, sizeof(rr), phys_ofs, ret, length); + 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) { + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + } + ref->node = raw; + + 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; + + /* Duplication Check */ + list_for_each_entry(ref, &ic->ilist, ilist) { + if (ref->xd == xd) + return ERR_PTR(-EINVAL); + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return ERR_PTR(-ENOMEM); + init_xattr_ref(ref); /* class = RAWNODE_CLASS_XATTR_REF */ + + ref->seqno = ++c->highest_xseqno; + 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 */ +} + +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); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + + list_del(&ref->ilist); + xd = ref->xd; + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + + jffs2_free_xattr_ref(ref); +} + +void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + 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() */ + 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 do_check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + struct jffs2_xattr_ref *ref, *cmp; + int ret = 0; + + down_write(&c->xattr_sem); + retry: + list_for_each_entry(ref, &ic->ilist, ilist) { + ret = load_xattr_datum_nolock(c, ref->xd); + if (ret) + goto out; + cmp = ref; + list_for_each_entry_continue(cmp, &ic->ilist, ilist) { + ret = load_xattr_datum_nolock(c, cmp->xd); + if (ret) + goto out; + if (ref->xd->xprefix == cmp->xd->xprefix + && !strcmp(ref->xd->xname, cmp->xd->xname)) { + delete_xattr_ref(c, ref->seqno > cmp->seqno ? cmp : ref); + goto retry; + } + } + } + out: + up_write(&c->xattr_sem); + + if (!ret) + ic->flags |= INO_FLAGS_XATTR_CHECKED; + + return ret; +} + +static inline int check_xattr_ref_ilist(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED)) + return 0; + return do_check_xattr_ref_ilist(c, ic); +} + +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; + int len, ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + down_read(&c->xattr_sem); + len = 0; + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + xd = ref->xd; + ret = load_xattr_datum(c, xd); + if (ret<0) + goto out; + + xhandle = xprefix_to_handler(xd->xprefix); + if (!xhandle) + continue; + + if (buffer) { + ret = xhandle->list(inode, buffer+len, size-len, xd->xname, xd->name_len); + } else { + ret = xhandle->list(inode, NULL, 0, xd->xname, xd->name_len); + } + if (ret < 0) + goto out; + len += ret; + } + out: + up_read(&c->xattr_sem); + return ret; +} + +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 ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + down_read(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + BUG_ON(ref->ic!=ic); + + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + + ret = load_xattr_datum(c, xd); + if (ret) + goto out; + + if (!strcmp(xname, xd->xname)) { + ret = xd->value_len; + if (buffer) { + if (size < ret) { + ret = -ERANGE; + } else { + memcpy(buffer, xd->xvalue, xd->value_len); + } + } + goto out; + } + } + ret = -ENODATA; + out: + up_read(&c->xattr_sem); + return ret; +} + +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, *nref; + uint32_t phys_ofs, length, request; + int ret; + + ret = check_xattr_ref_ilist(c, ic); + if (unlikely(ret)) + return ret; + + request = PAD(sizeof(*xd) + strlen(xname) + 1 + size); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", + __FILE__, __LINE__, ret, length, request, phys_ofs); + return ret; + } + + /* Find existing xattr */ + down_write(&c->xattr_sem); + list_for_each_entry(ref, &ic->ilist, ilist) { + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + + ret = load_xattr_datum(c, xd); + if (ret) + goto out; + + if (!strcmp(xd->xname, xname)) { + if (flags & XATTR_CREATE) { + ret = -EEXIST; + goto out; + } + goto found; + } + } + /* not found */ + ref = NULL; + if (flags & XATTR_REPLACE) { + ret = -ENODATA; + goto out; + } + if (!buffer) { + ret = -EINVAL; + goto out; + } + found: + xd = create_xattr_datum(c, xprefix, xname, buffer, size, phys_ofs); + if (IS_ERR(xd)) { + ret = PTR_ERR(xd); + goto out; + } + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + /* create xattr_ref */ + request = PAD(sizeof(*nref)); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret) { + printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", + __FILE__, __LINE__, ret, length, request, phys_ofs); + down_write(&c->xattr_sem); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + up_write(&c->xattr_sem); + return ret; + } + down_write(&c->xattr_sem); + nref = create_xattr_ref(c, ic, xd, phys_ofs); + if (IS_ERR(nref)) { + ret = PTR_ERR(nref); + xd->refcnt--; + if (!xd->refcnt) + delete_xattr_datum(c, xd); + goto out; + } else if (ref) { + /* If replaced xattr_ref exists */ + delete_xattr_ref(c, ref); + } + out: + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + return ret; +} + +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; + int ret; + + BUG_ON(!xd->node); + + totlen = ref_totlen(c, c->gcblock, xd->node); + if (totlen < sizeof(struct jffs2_raw_xattr)) + return -EINVAL; + + ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret || length < totlen) { + printk(KERN_WARNING "jffs2_reserve_space_gc() = %d " + "request: %d byte reserved: %d byte", + ret, totlen, length); + return ret ? ret : -EBADFD; + } + + ret = load_xattr_datum_nolock(c, xd); + if (!ret) + ret = save_xattr_datum(c, xd, phys_ofs); + + return ret; +} + + +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; + int ret; + + BUG_ON(!ref->node); + + totlen = ref_totlen(c, c->gcblock, ref->node); + if (totlen != sizeof(struct jffs2_raw_xref)) + return -EINVAL; + + ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); + if (ret || length < totlen) { + printk(KERN_WARNING "jffs2_reserve_space_gc() = %d" + " request: %d byte reserved: %d byte", ret, totlen, length); + return ret ? ret : -EBADFD; + } + + ret = save_xattr_ref(c, ref, phys_ofs); + + return ret; +} + +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; +} diff -prNU3 mtd-20051127/fs/jffs2/xattr.h mtd-20051127.xattr/fs/jffs2/xattr.h --- mtd-20051127/fs/jffs2/xattr.h 1969-12-31 19:00:00.000000000 -0500 +++ mtd-20051127.xattr/fs/jffs2/xattr.h 2005-11-27 04:28:00.000000000 -0500 @@ -0,0 +1,144 @@ +/*-------------------------------------------------------------------------* + * File: fs/jffs2/xattr.c + * XATTR support on JFFS2 FileSystem + * + * Implemented by KaiGai Kohei <kaigai@ak.jp.nec.com> + * Copyright (C) 2005 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 XATTRINDEX_HASHSIZE (57) +#define JFFS2_XDATUM_FLAGS_HOT 0x01 /* This is hot xattr_datum */ + +#define RAWNODE_CLASS_INODE_CACHE 0 +#define RAWNODE_CLASS_XATTR_DATUM 1 +#define RAWNODE_CLASS_XATTR_REF 2 + +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 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; + + uint32_t seqno; + 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 int 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_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid); +extern void jffs2_attach_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); + +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 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 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)) + +static inline void init_xattr_datum(struct jffs2_xattr_datum *xd) +{ + memset(xd, 0, sizeof(struct jffs2_xattr_datum)); + xd->class = RAWNODE_CLASS_XATTR_DATUM; + INIT_LIST_HEAD(&xd->xindex); +} + +static inline void init_xattr_ref(struct jffs2_xattr_ref *ref) +{ + memset(ref, 0, sizeof(struct jffs2_xattr_ref)); + ref->class = RAWNODE_CLASS_XATTR_REF; + INIT_LIST_HEAD(&ref->ilist); +} + +#else + +static inline int jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) { return 0; } +static inline void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) {} +static inline void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) {} + +static inline void *jffs2_find_xattr_datum(void *c, uint32_t xid) { return NULL; } +static inline void jffs2_attach_xattr_datum(void *c, void *xd) {} + +static inline void jffs2_xattr_delete_inode(void *c, void *ic) {} +static inline void jffs2_xattr_free_inode(void *c, void *ic) {} +static inline int jffs2_garbage_collect_xattr(void *c, void *ic) { return 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) +static inline void init_xattr_datum(void *xd) {} +static inline void init_xattr_ref(void *ref) {} + +#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 + +static inline int jffs2_init_security(struct inode *inode, struct inode *dir) +{ + return 0; +} + +#endif /* CONFIG_JFFS2_FS_SECURITY */ + +#endif /* _JFFS2_FS_XATTR_H_ */ diff -prNU3 mtd-20051127/fs/Kconfig mtd-20051127.xattr/fs/Kconfig --- mtd-20051127/fs/Kconfig 2005-11-07 20:02:13.000000000 -0500 +++ mtd-20051127.xattr/fs/Kconfig 2005-11-27 04:31:16.000000000 -0500 @@ -51,6 +51,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 (EXPERIMENTAL)" + depends on JFFS2_FS && EXPERIMENTAL + default n + help + Extended attributes are name:value pairs associated with inodes by + the kernel or by users (see the attr(5) manual page, or visit + <http://acl.bestbits.at/> for details). + + If unsure, say N. + +config JFFS2_FS_POSIX_ACL + bool "JFFS2 POSIX Access Control Lists" + depends on JFFS2_FS_XATTR + default y + select FS_POSIX_ACL + help + Posix Access Control Lists (ACLs) support permissions for users and + groups beyond the owner/group/world scheme. + + To learn more about Access Control Lists, visit the Posix ACLs for + Linux website <http://acl.bestbits.at/>. + + If you don't know what Access Control Lists are, say N + +config JFFS2_FS_SECURITY + bool "JFFS2 Security Labels" + depends on JFFS2_FS_XATTR + default y + help + Security labels support alternative access control models + implemented by security modules like SELinux. This option + enables an extended attribute handler for file security + labels in the jffs2 filesystem. + + If you are not using a security module that requires using + extended attributes for file security labels, say N. + config JFFS2_FS_WRITEBUFFER bool "JFFS2 write-buffering support" depends on JFFS2_FS diff -prNU3 mtd-20051127/include/linux/jffs2_fs_i.h mtd-20051127.xattr/include/linux/jffs2_fs_i.h --- mtd-20051127/include/linux/jffs2_fs_i.h 2005-11-07 20:02:17.000000000 -0500 +++ mtd-20051127.xattr/include/linux/jffs2_fs_i.h 2005-11-27 04:28:00.000000000 -0500 @@ -5,6 +5,7 @@ #include <linux/version.h> #include <linux/rbtree.h> +#include <linux/posix_acl.h> #include <asm/semaphore.h> struct jffs2_inode_info { @@ -45,6 +46,10 @@ struct jffs2_inode_info { struct inode vfs_inode; #endif #endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + struct posix_acl *i_acl_access; + struct posix_acl *i_acl_default; +#endif }; #endif /* _JFFS2_FS_I */ diff -prNU3 mtd-20051127/include/linux/jffs2_fs_sb.h mtd-20051127.xattr/include/linux/jffs2_fs_sb.h --- mtd-20051127/include/linux/jffs2_fs_sb.h 2005-11-18 20:02:14.000000000 -0500 +++ mtd-20051127.xattr/include/linux/jffs2_fs_sb.h 2005-11-27 04:28:00.000000000 -0500 @@ -140,6 +140,14 @@ struct jffs2_sb_info { struct jffs2_blocks_bucket used_blocks[HASH_SIZE]; /* The hash table for both dirty and clean erase blocks */ struct jffs2_blocks_bucket free_blocks[HASH_SIZE]; /* The hash table for free erase blocks */ +#ifdef CONFIG_JFFS2_FS_XATTR + uint32_t highest_xseqno; + struct list_head *xattrindex; +#define xattr_temp xattrindex[XATTRINDEX_HASHSIZE] + 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; }; diff -prNU3 mtd-20051127/include/linux/jffs2.h mtd-20051127.xattr/include/linux/jffs2.h --- mtd-20051127/include/linux/jffs2.h 2005-11-07 20:02:17.000000000 -0500 +++ mtd-20051127.xattr/include/linux/jffs2.h 2005-11-27 04:28:00.000000000 -0500 @@ -71,6 +71,18 @@ #define JFFS2_NODETYPE_ERASEBLOCK_HEADER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 5) #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) @@ -157,6 +169,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 */ + 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 seqno; /* sequencial number */ + jint32_t ino; /* inode number */ + jint32_t xid; /* XATTR identifier number */ + jint32_t node_crc; +} __attribute__((packed)); + struct jffs2_raw_summary { jint16_t magic; @@ -191,6 +229,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_raw_ebh eh; struct jffs2_unknown_node u; ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-11-27 9:43 ` KaiGai Kohei @ 2005-11-27 15:45 ` Artem B. Bityutskiy 2005-11-28 4:13 ` Kaigai Kohei 2005-12-03 4:38 ` KaiGai Kohei 0 siblings, 2 replies; 28+ messages in thread From: Artem B. Bityutskiy @ 2005-11-27 15:45 UTC (permalink / raw) To: KaiGai Kohei Cc: James Morris, Stephen Smalley, David Woodhouse, linux-mtd, lorenzohgh, Andreas Gruenbacher Hello KaiGai, to ease JFFS2 debugging I split debugging messages by subsystems and one may enable/disable debugging messages from each subsystem separately. I wonder, if it is possible to add DBG_XATTR() or the like instead of using D1()? It really makes bugs hunting easier. Please, glance at debug.h. Thanks. -- Best Regards, Artem B. Bityutskiy, St.-Petersburg, Russia. ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-11-27 15:45 ` Artem B. Bityutskiy @ 2005-11-28 4:13 ` Kaigai Kohei 2005-12-03 4:38 ` KaiGai Kohei 1 sibling, 0 replies; 28+ messages in thread From: Kaigai Kohei @ 2005-11-28 4:13 UTC (permalink / raw) To: Artem B. Bityutskiy Cc: James Morris, Stephen Smalley, David Woodhouse, linux-mtd, lorenzohgh, Andreas Gruenbacher Hello Artem, > to ease JFFS2 debugging I split debugging messages by subsystems and one > may enable/disable debugging messages from each subsystem separately. I > wonder, if it is possible to add DBG_XATTR() or the like instead of > using D1()? It really makes bugs hunting easier. Please, glance at debug.h. I agree what you say. There is especially no reason why such a stub macro is not used. In the next patch, I'll use some stub macros as are defined in debug.h. The following definition will be added in debug.h, and I'll use JFFS2_NOTICE() and so on instead of raw-printk(). /* Watch the XATTR subsystem */ #ifdef JFFS2_DBG_XATTR_MESSAGES #define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) #else #define dbg_xattr(fmt, ...) #endif Thanks, -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-11-27 15:45 ` Artem B. Bityutskiy 2005-11-28 4:13 ` Kaigai Kohei @ 2005-12-03 4:38 ` KaiGai Kohei 1 sibling, 0 replies; 28+ messages in thread From: KaiGai Kohei @ 2005-12-03 4:38 UTC (permalink / raw) To: Artem B. Bityutskiy Cc: James Morris, KaiGai Kohei, David Woodhouse, linux-mtd, lorenzohgh, Stephen Smalley, Andreas Gruenbacher [-- Attachment #1: Type: text/plain, Size: 1260 bytes --] Hello, Aterm. This patch replaces any warning messages by raw-printk() with JFFS2_WARNING()/JFFS2_NOTICE() macros, and any debugging messages are replaced by dbg_xattr() which is newly defined in debug.h. Please apply this patch after 'jffs2_xattr_take-4.patch' which I posted at 27-Nov 2005. Since the whole of those implementation is a bit massive, I post only difference with last time. If you hope to get the whole of this implementation already patched, please visit http://www.kaigai.gr.jp/ and download 'jffs2_xattr_take-4.1.patch'. [JFFS2] Provide XATTR and POSIX ACL support on JFFS2 - http://www.kaigai.gr.jp/patch/jffs2_xattr_take-4.1.patch [JFFS2] Provide --with-{xattr|selinux|posix-acl} options for mkfs.jffs2 - http://www.kaigai.gr.jp/patch/mkfs.jffs2-xattr.patch Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Thanks, Artem B. Bityutskiy wrote: > Hello KaiGai, > > to ease JFFS2 debugging I split debugging messages by subsystems and one > may enable/disable debugging messages from each subsystem separately. I > wonder, if it is possible to add DBG_XATTR() or the like instead of > using D1()? It really makes bugs hunting easier. Please, glance at debug.h. > > Thanks. -- KaiGai Kohei <kaigai@kaigai.gr.jp> [-- Attachment #2: jffs2_xattr_dbgmsg.patch --] [-- Type: text/plain, Size: 12393 bytes --] diff -prNU3 mtd-20051127.xattr/fs/jffs2/acl.c mtd-20051127.xattr-dbg/fs/jffs2/acl.c --- mtd-20051127.xattr/fs/jffs2/acl.c 2005-12-02 12:14:28.000000000 -0500 +++ mtd-20051127.xattr-dbg/fs/jffs2/acl.c 2005-11-29 10:42:16.000000000 -0500 @@ -53,14 +53,16 @@ static struct posix_acl *jffs2_acl_from_ { 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); - if (je32_to_cpu(((jffs2_acl_header *)value)->a_version) != JFFS2_ACL_VERSION) { - D1(printk(KERN_NOTICE "%s:%d invalid ACL version.\n", __FUNCTION__, __LINE__)); + 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); } diff -prNU3 mtd-20051127.xattr/fs/jffs2/debug.h mtd-20051127.xattr-dbg/fs/jffs2/debug.h --- mtd-20051127.xattr/fs/jffs2/debug.h 2005-11-07 20:02:14.000000000 -0500 +++ mtd-20051127.xattr-dbg/fs/jffs2/debug.h 2005-12-02 12:20:43.000000000 -0500 @@ -171,6 +171,12 @@ #define dbg_memalloc(fmt, ...) #endif +/* Watch the XATTR subsystem */ +#ifdef JFFS2_DBG_XATTR_MESSAGES +#define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_xattr(fmt, ...) +#endif /* "Sanity" checks */ void diff -prNU3 mtd-20051127.xattr/fs/jffs2/scan.c mtd-20051127.xattr-dbg/fs/jffs2/scan.c --- mtd-20051127.xattr/fs/jffs2/scan.c 2005-12-02 12:14:28.000000000 -0500 +++ mtd-20051127.xattr-dbg/fs/jffs2/scan.c 2005-12-02 10:38:13.000000000 -0500 @@ -329,27 +329,27 @@ static int jffs2_scan_xattr_node(struct crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr)-8); if (crc != je32_to_cpu(rx->node_crc)) { - printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " - "Read 0x%08x, calculated 0x%08x\n", - __FUNCTION__, ofs, je32_to_cpu(rx->node_crc), crc); + JFFS2_WARNING("node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rx->node_crc), crc); dirty_space(c, jeb, je32_to_cpu(rx->totlen)); return 0; } xd = jffs2_find_xattr_datum(c, je32_to_cpu(rx->xid)); if (xd) { - printk(KERN_NOTICE "%s() duplicate xid=%u found. " - "on node at 0x%08x, later one is ignored.\n", - __FUNCTION__, je32_to_cpu(rx->xid), ofs); + JFFS2_WARNING("duplicate xid=%u found. " + "on node at 0x%08x, later one is ignored.\n", + je32_to_cpu(rx->xid), ofs); dirty_space(c, jeb, je32_to_cpu(rx->totlen)); return 0; } crc = crc32(0, rx->data, rx->name_len + 1 + je16_to_cpu(rx->value_len)); if (crc != je32_to_cpu(rx->data_crc)) { - printk(KERN_NOTICE "%s data CRC failed on node at 0x%08x: " - "Read 0x%08x, calculated 0x%08x\n", - __FUNCTION__, ofs, je32_to_cpu(rx->data_crc), crc); + JFFS2_WARNING("data CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rx->data_crc), crc); dirty_space(c, jeb, je32_to_cpu(rx->totlen)); return 0; } @@ -388,6 +388,9 @@ static int jffs2_scan_xattr_node(struct jffs2_attach_xattr_datum(c, xd); + D2(dbg_xattr("scan JFFS2_NODETYPE_XATTR at %#08x (xid=%u, xprefix=%d)\n", + ofs, xd->xid, xd->xprefix)); + return 0; } @@ -400,9 +403,9 @@ static int jffs2_scan_xref_node(struct j crc = crc32(0, rr, sizeof(*rr)-4); if (crc != je32_to_cpu(rr->node_crc)) { - printk(KERN_NOTICE "%s node CRC failed on node at 0x%08x: " - "Read 0x%08x, calculated 0x%08x\n", - __FUNCTION__, ofs, je32_to_cpu(rr->node_crc), crc); + JFFS2_WARNING("node CRC failed on node at 0x%08x: " + "Read 0x%08x, calculated 0x%08x\n", + ofs, je32_to_cpu(rr->node_crc), crc); dirty_space(c, jeb, je32_to_cpu(rr->totlen)); return 0; } @@ -448,6 +451,9 @@ static int jffs2_scan_xref_node(struct j list_add_tail(&ref->ilist, &c->xattr_temp); + D2(dbg_xattr("scan JFFS2_NODETYPE_XREF at %#08x (xid=%u, ino=%u)\n", + ofs, ref->xid, ref->ino)); + return 0; } #endif diff -prNU3 mtd-20051127.xattr/fs/jffs2/xattr.c mtd-20051127.xattr-dbg/fs/jffs2/xattr.c --- mtd-20051127.xattr/fs/jffs2/xattr.c 2005-12-02 12:14:28.000000000 -0500 +++ mtd-20051127.xattr-dbg/fs/jffs2/xattr.c 2005-11-29 10:39:44.000000000 -0500 @@ -239,7 +239,7 @@ void jffs2_build_xattr_subsystem(struct struct jffs2_xattr_ref *ref, *_ref; struct jffs2_xattr_datum *xd, *_xd; struct jffs2_inode_cache *ic; - int i; + int i, count = 0; BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING)); @@ -251,8 +251,8 @@ void jffs2_build_xattr_subsystem(struct xd = jffs2_find_xattr_datum(c, ref->xid); ic = jffs2_get_ino_cache(c, ref->ino); if (!xd || !ic) { - printk(KERN_NOTICE "building xref {ino=%u, xid=%u} is not found.\n", - ref->xid, ref->ino); + JFFS2_WARNING("can not bind xref(ino=%u, xid=%u), xattr or inode " + "was not found\n", ref->xid, ref->ino); ref->node->next_in_ino = NULL; jffs2_mark_node_obsolete(c, ref->node); jffs2_free_xattr_ref(ref); @@ -262,7 +262,8 @@ void jffs2_build_xattr_subsystem(struct ref->ic = ic; xd->refcnt++; list_add_tail(&ref->ilist, &ic->ilist); - D1(printk(KERN_NOTICE "bind XREF{ino=%u, xid=%u}\n", ic->ino, xd->xid)); + count++; + D1(dbg_xattr("bind xref(ino=%u, xid=%u)\n", ic->ino, xd->xid)); } /* After this, ref->xid/ino are never used. */ @@ -271,13 +272,13 @@ void jffs2_build_xattr_subsystem(struct list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { list_del_init(&xd->xindex); if (!xd->refcnt) { - printk(KERN_NOTICE "unrefered xattr_datum found xid=%u\n", xd->xid); + JFFS2_WARNING("unrefered xattr found xid=%u\n", xd->xid); delete_xattr_datum(c, xd); } } } /* build complete */ - D1(printk(KERN_NOTICE "%s complete.\n", __FUNCTION__)); + D1(dbg_xattr("complete binding %d of JFFS2_NODETYPE_XREFs\n", count)); } struct jffs2_xattr_datum *jffs2_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid) @@ -328,8 +329,8 @@ static void reclaim_xattr_datum(struct j index = (index+1) % XATTRINDEX_HASHSIZE; } out: - printk(KERN_NOTICE "JFFS2: reclaim_xattr_cache() before: %d Byte after: %d Byte\n", - before, c->xdatum_mem_usage); + 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 int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) @@ -340,7 +341,7 @@ static int do_load_xattr_datum(struct jf uint32_t crc, length; int i, ret, retry = 0; - D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + D1(dbg_xattr("xid=%u xprefix=%d\n", xd->xid, xd->xprefix)); retry: length = xd->name_len + 1 + xd->value_len; data = kmalloc(length, GFP_KERNEL); @@ -352,8 +353,8 @@ static int do_load_xattr_datum(struct jf length, &readlen, data); if (ret || length!=readlen) { - printk(KERN_WARNING "jffs2_flash_read()=%d, request: %d, readlen: %d, at 0x%08x\n", - ret, length, readlen, ref_offset(xd->node)); + 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; } @@ -361,9 +362,9 @@ static int do_load_xattr_datum(struct jf data[xd->name_len] = '\0'; crc = crc32(0, data, length); if (crc != xd->data_crc) { - printk(KERN_WARNING "node CRC failed (JFFS2_NODETYPE_XREF)" - " at 0x%08x, read: 0x%08x calculated: 0x%08x\n", - ref_offset(xd->node), xd->data_crc, 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; } @@ -416,7 +417,7 @@ static inline int load_xattr_datum(struc static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under down_write(xattr_sem) */ - D1(printk(KERN_NOTICE "%s xid=%u xprefix=%d\n", __FUNCTION__, xd->xid, xd->xprefix)); + D1(dbg_xattr("xid=%u xprefix=%d\n", xd->xid, xd->xprefix)); if (xd->xname) { c->xdatum_mem_usage -= (xd->name_len + 1 + xd->value_len); @@ -471,8 +472,8 @@ static int save_xattr_datum(struct jffs2 ret = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); if (ret || totlen!=length) { - printk(KERN_NOTICE "%s:%d Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", - __FILE__, __LINE__, totlen, phys_ofs, ret, length); + JFFS2_WARNING("jffs2_flash_writev() returned %d, totlen=%u, retlen=%u, at %#08x\n", + ret, totlen, length, phys_ofs); ret = ret ? ret : -EIO; if (length) { raw->flash_offset |= REF_OBSOLETE; @@ -609,8 +610,8 @@ static int save_xattr_ref(struct jffs2_s ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); if (ret || sizeof(rr)!=length) { - printk(KERN_NOTICE "%s:%d Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", - __FILE__, __LINE__, sizeof(rr), phys_ofs, ret, 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; @@ -688,6 +689,8 @@ static void delete_xattr_ref(struct jffs 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) @@ -701,7 +704,7 @@ void jffs2_xattr_delete_inode(struct jff void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { - /* It's called from jffs2_free_ino_caches() */ + /* It's called from jffs2_free_ino_caches() until unmounting FS. */ struct jffs2_xattr_datum *xd; struct jffs2_xattr_ref *ref, *_ref; @@ -860,10 +863,11 @@ int do_jffs2_setxattr(struct inode *inod return ret; request = PAD(sizeof(*xd) + strlen(xname) + 1 + size); - ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, + ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); if (ret) { - printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", - __FILE__, __LINE__, ret, length, request, phys_ofs); + JFFS2_WARNING("jffs2_reserve_space() returned %d, request=%u, length=%u at %#08x\n", + ret, request, length, phys_ofs); return ret; } @@ -907,10 +911,11 @@ int do_jffs2_setxattr(struct inode *inod /* create xattr_ref */ request = PAD(sizeof(*nref)); - ret = jffs2_reserve_space(c, request, &phys_ofs, &length, ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); + ret = jffs2_reserve_space(c, request, &phys_ofs, &length, + ALLOC_NORMAL, JFFS2_SUMMARY_NOSUM_SIZE); if (ret) { - printk(KERN_NOTICE "%s:%d jffs2_reserve_space()=%d length=%d request=%d at 0x%08x\n", - __FILE__, __LINE__, ret, length, request, phys_ofs); + JFFS2_WARNING("jffs2_reserve_space() returned %d, request=%u, length=%u at %#08x\n", + ret, request, length, phys_ofs); down_write(&c->xattr_sem); xd->refcnt--; if (!xd->refcnt) @@ -951,9 +956,8 @@ static int jffs2_garbage_collect_xattr_d ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); if (ret || length < totlen) { - printk(KERN_WARNING "jffs2_reserve_space_gc() = %d " - "request: %d byte reserved: %d byte", - ret, totlen, length); + JFFS2_WARNING("jffs2_reserve_space_gc()=%d, request=%u, reserved=%u, at %#08x\n", + ret, totlen, length, phys_ofs); return ret ? ret : -EBADFD; } @@ -979,8 +983,8 @@ static int jffs2_garbage_collect_xattr_r ret = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_NOSUM_SIZE); if (ret || length < totlen) { - printk(KERN_WARNING "jffs2_reserve_space_gc() = %d" - " request: %d byte reserved: %d byte", ret, totlen, length); + JFFS2_WARNING("jffs2_reserve_space_gc()=%d, request=%u, reserved=%u, at %#08x\n", + ret, totlen, length, phys_ofs); return ret ? ret : -EBADFD; } ^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] XATTR issues on JFFS2 2005-09-28 8:44 ` Kaigai Kohei 2005-09-29 7:45 ` Jörn Engel @ 2005-10-12 4:25 ` Kaigai Kohei 1 sibling, 0 replies; 28+ messages in thread From: Kaigai Kohei @ 2005-10-12 4:25 UTC (permalink / raw) To: Jörn Engel, linux-mtd Cc: James Morris, Ma Yun, David Woodhouse, Stephen Smalley, Kaigai Kohei [-- Attachment #1: Type: text/plain, Size: 987 bytes --] Hi, The attached patch adds an functionality for mkfs.jffs2. '-A' or '--with-xattr' enables to stuff up any xattr-imforamations for each files into JFFS2 image file. This patch depends on 'jffs2_xattr_take-3.patch' I posted on 28-Sep. In addition, some constants of JFFS2_XPREFIX_* are moved from fs/jffs2/xattr.h to include/linux/jffs2.h, because I want to use those constants in mkfs.jffs2. This change has no effects in kernel-side. Perhaps, another style like '--with-selinux' or '--with-acl' might be better than '--with-xattr'. Any comments please, Thanks. Kaigai Kohei wrote: > Hi, > > I revised the previous xattr patch in jffs2 based on some suggestions. > > [1] scanning xattr related node for cleaned-up scan.c. > (jffs2_xattr_scan.c-cleanup.patch) > [2] enables xattr support in jffs2. > (jffs2_xattr_take-3.patch) > (*) We can apply jffs2_xattr_take-3.patch for current CVS independently. -- Linux Promotion Center, NEC KaiGai Kohei <kaigai@ak.jp.nec.com> [-- Attachment #2: mkfs.jffs2-xattr.patch --] [-- Type: text/x-patch, Size: 12246 bytes --] diff -prNU3 mtd-051011.xattr/fs/jffs2/xattr.c mtd-051011.xattr-mkfs/fs/jffs2/xattr.c --- mtd-051011.xattr/fs/jffs2/xattr.c 2005-10-12 11:33:37.000000000 +0900 +++ mtd-051011.xattr-mkfs/fs/jffs2/xattr.c 2005-10-12 11:34:39.000000000 +0900 @@ -42,6 +42,7 @@ static int do_jffs2_setxattr(struct inod const char *buffer, size_t size, int flags); /* ---- XATTR Handler for "user.*" --------------------- */ +#define XATTR_USER_PREFIX "user." static int jffs2_user_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) { @@ -97,6 +98,7 @@ static struct xattr_handler jffs2_user_x }; /* ---- XATTR Handler for "trusted.*" ------------------ */ +#define XATTR_TRUSTED_PREFIX "trusted." static int jffs2_trusted_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) { diff -prNU3 mtd-051011.xattr/fs/jffs2/xattr.h mtd-051011.xattr-mkfs/fs/jffs2/xattr.h --- mtd-051011.xattr/fs/jffs2/xattr.h 2005-10-12 11:33:37.000000000 +0900 +++ mtd-051011.xattr-mkfs/fs/jffs2/xattr.h 2005-10-12 11:34:39.000000000 +0900 @@ -14,16 +14,6 @@ #include <linux/xattr.h> #define XATTRINDEX_HASHSIZE (57) -#define XATTR_USER_PREFIX "user." -#define XATTR_TRUSTED_PREFIX "trusted." - -#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_XPREFIX_INVALID 6 - #define JFFS2_XDATUM_FLAGS_HOT 0x01 /* This is hot xattr_datum */ #define RAWNODE_CLASS_INODE_CACHE 0 diff -prNU3 mtd-051011.xattr/include/linux/jffs2.h mtd-051011.xattr-mkfs/include/linux/jffs2.h --- mtd-051011.xattr/include/linux/jffs2.h 2005-10-12 11:33:37.000000000 +0900 +++ mtd-051011.xattr-mkfs/include/linux/jffs2.h 2005-10-12 11:34:39.000000000 +0900 @@ -68,6 +68,13 @@ #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.*" */ + // 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) diff -prNU3 mtd-051011.xattr/util/mkfs.jffs2.1 mtd-051011.xattr-mkfs/util/mkfs.jffs2.1 --- mtd-051011.xattr/util/mkfs.jffs2.1 2005-09-15 21:18:43.000000000 +0900 +++ mtd-051011.xattr-mkfs/util/mkfs.jffs2.1 2005-10-12 11:34:39.000000000 +0900 @@ -49,6 +49,9 @@ mkfs.jffs2 \- Create a JFFS2 file system .B -P,--squash-perms ] [ +.B -A,--with-xattr +] +[ .B -m,--compression-mode=MODE ] [ @@ -178,6 +181,9 @@ Squash owners making all files be owned .B -P, --squash-perms Squash permissions, removing write permission for \'group\' and \'other\'. .TP +.B -A, --with-xattr +Include XATTR meta information. It's necessary for SELinux or ACL support. +.TP .B -m, --compression-mode=MODE Set the default compression mode. The default mode is .B priority diff -prNU3 mtd-051011.xattr/util/mkfs.jffs2.c mtd-051011.xattr-mkfs/util/mkfs.jffs2.c --- mtd-051011.xattr/util/mkfs.jffs2.c 2005-09-15 21:18:43.000000000 +0900 +++ mtd-051011.xattr-mkfs/util/mkfs.jffs2.c 2005-10-12 11:55:09.000000000 +0900 @@ -7,6 +7,7 @@ * 2002 Axis Communications AB * 2001, 2002 Erik Andersen <andersen@codepoet.org> * 2004 University of Szeged, Hungary + * 2005 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,7 @@ #include <ctype.h> #include <time.h> #include <getopt.h> +#include <attr/xattr.h> #include <byteswap.h> #define crc32 __complete_crap #include <zlib.h> @@ -1018,6 +1020,166 @@ static void write_special_file(struct fi padword(); } +struct xattr_entry { + struct xattr_entry *next; + uint32_t xid; + int xprefix; + char *xname; + char *xvalue; + int value_len; +}; +static int enable_xattr = 0; +static uint32_t xseqno = 0; +static struct { + int code; + char *string; + int length; +} xprefix_tbl[] = { + { JFFS2_XPREFIX_USER, "user.", sizeof("user.")-1 }, + { JFFS2_XPREFIX_SECURITY, "security.", sizeof("security.")-1 }, +// { JFFS2_XPREFIX_ACL_ACCESS, "system.posix_acl_access", sizeof("system.posix_acl_access")-1 }, +// { JFFS2_XPREFIX_ACL_DEFAULT, "system.posix_acl_default", sizeof("system.posix_acl_default")-1 }, + { JFFS2_XPREFIX_TRUSTED, "trusted.", sizeof("trusted")-1 }, + { 0, NULL, 0 } +}; + +static struct xattr_entry *create_xattr_entry(int xprefix, char *xname, char *xvalue, int value_len) +{ + struct xattr_entry *xe; + struct jffs2_raw_xattr rx; + int name_len, data_len; + + name_len = strlen(xname); + data_len = name_len + 1 + value_len; + xe = xcalloc(1, sizeof(struct xattr_entry) + data_len); + xe->xid = ++xseqno; + xe->xprefix = xprefix; + xe->xname = (char *)(xe + 1); + xe->xvalue = xe->xname + name_len + 1; + xe->value_len = value_len; + strcpy(xe->xname, xname); + memcpy(xe->xvalue, xvalue, value_len); + + 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(sizeof(rx) + data_len); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + + rx.xid = cpu_to_je32(xe->xid); + rx.xprefix = xprefix; /* 8bit width */ + rx.name_len = name_len; /* 8bit width */ + rx.value_len = cpu_to_je16(value_len); + rx.data_crc = cpu_to_je32(crc32(0, xe->xname, data_len)); + rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(rx) - 8)); + + pad_block_if_less_than(sizeof(rx) + data_len); + full_write(out_fd, &rx, sizeof(rx)); + full_write(out_fd, xe->xname, data_len); + padword(); + + return xe; +} + +#define XATTRENTRY_HASHSIZE (256) +static struct xattr_entry *find_xattr_entry(char *xname, char *xvalue, int value_len) +{ + static struct xattr_entry **xentry_hash = NULL; + struct xattr_entry *xe; + int i, name_len, xprefix, index; + + /* -- lookup xprefix -- */ + for (xprefix=0, i=0; xprefix_tbl[i].string; i++) { + char *prstr = xprefix_tbl[i].string; + int prlen = xprefix_tbl[i].length; + if (!strncmp(xname, prstr, prlen)) { + xprefix = xprefix_tbl[i].code; + xname += prlen; + break; + } + } + if (xprefix == 0) + return NULL; + + /* -- find or create xattr entry -- */ + if (!xentry_hash) + xentry_hash = xcalloc(1, sizeof(struct xattr_entry *) * XATTRENTRY_HASHSIZE); + + name_len = strlen(xname) + 1; + 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; +} + +#define XATTR_BUFSZ 65536 +static void write_xattr_entry(struct filesystem_entry *e) +{ + struct jffs2_raw_xref ref; + struct xattr_entry *xe; + char xlist[XATTR_BUFSZ], xvalue[XATTR_BUFSZ], filepath[PATH_MAX+1]; + char *xname; + int list_sz, offset, name_len, value_len; + + if (!enable_xattr) + return; + + sprintf(filepath, "%s%s", rootdir, e->fullname); + + list_sz = llistxattr(filepath, xlist, XATTR_BUFSZ); + if (list_sz < 0) { + if (verbose) + printf("llistxattr(\"%s\") returned %d:%s\n", + e->fullname, errno, strerror(errno)); + return; + } + + for (offset = 0; offset < list_sz; offset += name_len) { + xname = xlist+offset; + name_len = strlen(xname) + 1; + + value_len = lgetxattr(filepath, xname, xvalue, XATTR_BUFSZ); + if (value_len < 0) { + if (verbose) + printf("lgetxattr(\"%s\", \"%s\") returned %d:%s\n", + e->fullname, xname, errno, strerror(errno)); + continue; + } + + xe = find_xattr_entry(xname, xvalue, value_len); + if (!xe) { + if (verbose) + printf("XATTR \"%s\" -- Not supported on \"%s\"\n", xname, e->fullname); + 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.seqno = cpu_to_je32(++xseqno); + 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; @@ -1025,9 +1187,10 @@ static void recursive_populate_directory if (verbose) { printf("%s\n", dir->fullname); } + write_xattr_entry(dir); /* for '/' */ + e = dir->files; while (e) { - switch (e->sb.st_mode & S_IFMT) { case S_IFDIR: if (verbose) { @@ -1037,6 +1200,7 @@ static void recursive_populate_directory e->name); } write_pipe(e); + write_xattr_entry(e); break; case S_IFSOCK: if (verbose) { @@ -1045,6 +1209,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) { @@ -1053,6 +1218,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) { @@ -1062,6 +1228,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) { @@ -1071,6 +1238,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) { @@ -1080,6 +1248,7 @@ static void recursive_populate_directory e->link); } write_symlink(e); + write_xattr_entry(e); break; case S_IFREG: if (verbose) { @@ -1088,6 +1257,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, @@ -1165,6 +1335,7 @@ static struct option long_options[] = { {"test-compression", 0, NULL, 't'}, {"compressor-priority", 1, NULL, 'y'}, {"incremental", 1, NULL, 'i'}, + {"with-xattr", 0, NULL, 'A'}, {NULL, 0, NULL, 0} }; @@ -1200,7 +1371,8 @@ static char *helptext = " -h, --help Display this help text\n" " -v, --verbose Verbose operation\n" " -V, --version Display version information\n" - " -i, --incremental=FILE Parse FILE and generate appendage output for it\n\n"; + " -i, --incremental=FILE Parse FILE and generate appendage output for it\n" + " -A, --with-xattr include XATTR meta informatin.\n\n"; static char *revtext = "$Revision: 1.45 $"; @@ -1353,7 +1525,7 @@ int main(int argc, char **argv) jffs2_compressors_init(); while ((opt = getopt_long(argc, argv, - "D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:", long_options, &c)) >= 0) + "D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lty:i:A", long_options, &c)) >= 0) { switch (opt) { case 'D': @@ -1515,6 +1687,9 @@ int main(int argc, char **argv) perror_msg_and_die("cannot open (incremental) file"); } break; + case 'A': + enable_xattr = 1; + break; } } if (out_fd == -1) { ^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2005-12-03 4:41 UTC | newest] Thread overview: 28+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2005-08-23 10:24 [PATCH] XATTR issues on JFFS2 Kaigai Kohei 2005-08-23 12:46 ` Jörn Engel 2005-08-23 12:52 ` David Woodhouse 2005-08-24 9:49 ` Kaigai Kohei 2005-08-25 10:28 ` Kaigai Kohei 2005-08-25 14:12 ` Jörn Engel 2005-09-07 5:14 ` Kaigai Kohei 2005-09-08 19:49 ` Jörn Engel 2005-09-08 19:54 ` David Woodhouse 2005-09-09 4:15 ` Kaigai Kohei 2005-09-09 7:24 ` Jörn Engel 2005-09-10 4:15 ` KaiGai Kohei 2005-09-11 11:46 ` Jörn Engel 2005-09-12 2:17 ` Kaigai Kohei 2005-09-12 6:40 ` Jörn Engel 2005-09-12 11:01 ` Kaigai Kohei 2005-09-28 8:44 ` Kaigai Kohei 2005-09-29 7:45 ` Jörn Engel 2005-10-03 1:01 ` E-mail with attached file has not delivered yet. (Re: [PATCH] XATTR issues on JFFS2) Kaigai Kohei 2005-10-19 13:18 ` [PATCH] XATTR issues on JFFS2 Kaigai Kohei 2005-10-19 14:24 ` Jörn Engel 2005-10-20 2:01 ` Kaigai Kohei 2005-11-27 6:58 ` KaiGai Kohei 2005-11-27 9:43 ` KaiGai Kohei 2005-11-27 15:45 ` Artem B. Bityutskiy 2005-11-28 4:13 ` Kaigai Kohei 2005-12-03 4:38 ` KaiGai Kohei 2005-10-12 4:25 ` Kaigai Kohei
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox