Re: JFFS2/xattr problems.

KaiGai Kohei
2006-06-12T02:19:52Z

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

Hi, David.

I fixed the problem when xdatum/xref was removed, and so on.

[1/2] jffs2-xattr-v6-01-delete_marker.patch
[2/2] jffs2-xattr-v6-02-fix_posixacl_bug.patch

This correction uses 'delete marker' instead of filling an
existing node by 0xff. It is written when we remvoe xdatum/xref.

Any nodes indicates xdatum/xref includes 'delete marker' are
chained with xd->node or ref->node.
# In the previous implementation, xd->node and ref->node have
# only one node.
Then the garbage collector tries to remove those nodes except
the latest one.

If the latest one is delete marker and any other nodes are not chained
this list, the garbage collector remove the delete marker and in-memory
object (xattr_datum, xattr_ref).
# In previous implementation, deletion of nodes and in-memory objects
# are done immediately

- A xdatum with 'version' equals 0xffffffff means delete marker.
- A xref with odd-numbered 'xseqno' means delete marker.
It increases 4-byte additional consumption per xref. :(

And the second patch fixes posix-acl bug when we try to replace
null-xattr by null-acl.
Those have been commited into jffs2-xattr-2.6.git.

Thanks,

KaiGai Kohei wrote:
> Hello, David.
>
> David Woodhouse wrote:
>> I created a user xattr on JFFS2, then attempted to remove it. On NAND
>> flash I get the following BUG():
>>
>> jffs2_flash_writev(): Non-contiguous write to 004ca000
>> wbuf was previously 004ca000-004ca040
>> ------------[ cut here ]------------
>> kernel BUG at /root/jffs2/wbuf.c:672!
>>
>> Call Trace:
>> [<c8222aad>] jffs2_flash_writev+0x616/0x6ac [jffs2]
>> [<c823eadb>] nand_read_ecc+0x29/0x2f [nand] [<c823eadb>] nand_read_ecc+0x29/0x2f [nand]
>> [<c8221a6a>] jffs2_flash_read+0x8a/0x22b [jffs2] [<c8222b8b>] jffs2_flash_write+0x48/0x51 [jffs2]
>> [<c822364d>] delete_xattr_datum_node+0xcc/0x144 [jffs2] [<c8223dea>] delete_xattr_datum+0x2c/0x3c [jffs2]
>> [<c8223ed2>] delete_xattr_ref+0x32/0x3b [jffs2] [<c8224e37>] do_jffs2_setxattr+0x1a9/0x5aa [jffs2]
>> [<c01c1462>] _atomic_dec_and_lock+0x22/0x2c [<c82254fc>] jffs2_user_setxattr+0x3c/0x47 [jffs2]
>> [<c016e9e8>] generic_removexattr+0x37/0x3d [<c016ef78>] vfs_removexattr+0x78/0xc8
>>
>> What is delete_xattr_datum_node() trying to do? It seems to be writing
>> for a second time to an area of flash which has _already_ been written.
>> You can't do that.
>
> delete_xattr_datum_node() try to write invalid data to ensure the target
> node was already deleted. I was worried about that a xdatum already removed
> may be reused as a effective xdatum on next mounting.
> But it's purely design problem that should be changed.
>
> Alternatively, I plan to write a delete marker on new node allocated by
> jffs2_reserve_space() when xdatum is removed.
> I intend to be dealt with a xdatum with 0xffffffff of version number
> as a delete marker for xdatum. This node will be ignored on next mounting.
>
> There is a same issue on deletion of xattr_ref, I also plan to implement
> with same method.
>
> Some days will be necessary to change it.
> Would you wait for a while?
>
>> Also, just 'cp -av /lib/libc.so.6 /mnt/jffs2' is failing to set the
>> POSIX ACL, with -EINVAL. This happens because posix_acl_equiv_mode()
>> returns zero, because the ACL is entirely equivalent to normal modes. So
>> the 'value' passed to do_jffs2_setxattr() is NULL, which should _delete_
>> the corresponding xattr.
>>
>> But because the xattr is not found, the code returns -EINVAL. Shouldn't
>> that error be -ENOATTR? And shouldn't the ACL code then convert it to a
>> more appropriate return value?
>
> I agree, I'll fix it.
>
> Thanks,
--
Open Source Software Promotion Center, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>

[-- Attachment #2: jffs2-xattr-v6-01-delete_marker.patch --]
[-- Type: text/plain, Size: 53138 bytes --]

diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h
index c6f7066..c9c7607 100644
--- a/include/linux/jffs2.h
+++ b/include/linux/jffs2.h
@@ -186,6 +186,7 @@ struct jffs2_raw_xref
jint32_t hdr_crc;
jint32_t ino; /* inode number */
jint32_t xid; /* XATTR identifier number */
+ jint32_t xseqno; /* xref sequencial number */
jint32_t node_crc;
} __attribute__((packed));

diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
index 1862e8b..1644e34 100644
--- a/fs/jffs2/erase.c
+++ b/fs/jffs2/erase.c
@@ -230,7 +230,6 @@ static inline void jffs2_remove_node_ref
at the end of the linked list. Stash it and continue
from the beginning of the list */
ic = (struct jffs2_inode_cache *)(*prev);
- BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
prev = &ic->nodes;
continue;
}
@@ -254,7 +253,8 @@ static inline void jffs2_remove_node_ref

/* PARANOIA */
if (!ic) {
- printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n");
+ JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref"
+ " not found in remove_node_refs()!!\n");
return;
}

@@ -279,8 +279,19 @@ static inline void jffs2_remove_node_ref
printk("\n");
});

- if (ic->nodes == (void *)ic && ic->nlink == 0)
- jffs2_del_ino_cache(c, ic);
+ switch (ic->class) {
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case RAWNODE_CLASS_XATTR_DATUM:
+ jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+ break;
+ case RAWNODE_CLASS_XATTR_REF:
+ jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+ break;
+#endif
+ default:
+ if (ic->nodes == (void *)ic && ic->nlink == 0)
+ jffs2_del_ino_cache(c, ic);
+ }
}

void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
index 477c526..f59b147 100644
--- a/fs/jffs2/gc.c
+++ b/fs/jffs2/gc.c
@@ -275,13 +275,12 @@ #ifdef CONFIG_JFFS2_FS_XATTR
* We can decide whether this node is inode or xattr by ic->class. */
if (ic->class == RAWNODE_CLASS_XATTR_DATUM
|| ic->class == RAWNODE_CLASS_XATTR_REF) {
- BUG_ON(raw->next_in_ino != (void *)ic);
spin_unlock(&c->erase_completion_lock);

if (ic->class == RAWNODE_CLASS_XATTR_DATUM) {
- ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+ ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic, raw);
} else {
- ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+ ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
}
goto release_sem;
}
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index 935fec1..b985949 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -119,8 +119,11 @@ #endif
#ifdef CONFIG_JFFS2_FS_XATTR
#define XATTRINDEX_HASHSIZE (57)
uint32_t highest_xid;
+ uint32_t highest_xseqno;
struct list_head xattrindex[XATTRINDEX_HASHSIZE];
struct list_head xattr_unchecked;
+ struct list_head xattr_dead_list;
+ struct jffs2_xattr_ref *xref_dead_list;
struct jffs2_xattr_ref *xref_temp;
struct rw_semaphore xattr_sem;
uint32_t xdatum_mem_usage;
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c
index 4889d07..8310c95 100644
--- a/fs/jffs2/malloc.c
+++ b/fs/jffs2/malloc.c
@@ -291,6 +291,7 @@ struct jffs2_xattr_datum *jffs2_alloc_xa

memset(xd, 0, sizeof(struct jffs2_xattr_datum));
xd->class = RAWNODE_CLASS_XATTR_DATUM;
+ xd->node = (void *)xd;
INIT_LIST_HEAD(&xd->xindex);
return xd;
}
@@ -309,6 +310,7 @@ struct jffs2_xattr_ref *jffs2_alloc_xatt

memset(ref, 0, sizeof(struct jffs2_xattr_ref));
ref->class = RAWNODE_CLASS_XATTR_REF;
+ ref->node = (void *)ref;
return ref;
}

diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index 71d1630..d822bea 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -666,19 +666,26 @@ void jffs2_mark_node_obsolete(struct jff
spin_lock(&c->erase_completion_lock);

ic = jffs2_raw_ref_to_ic(ref);
- /* It seems we should never call jffs2_mark_node_obsolete() for
- XATTR nodes.... yet. Make sure we notice if/when we change
- that :) */
- BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
;

*p = ref->next_in_ino;
ref->next_in_ino = NULL;

- if (ic->nodes == (void *)ic && ic->nlink == 0)
- jffs2_del_ino_cache(c, ic);
-
+ switch (ic->class) {
+#ifdef CONFIG_JFFS2_FS_XATTR
+ case RAWNODE_CLASS_XATTR_DATUM:
+ jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
+ break;
+ case RAWNODE_CLASS_XATTR_REF:
+ jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
+ break;
+#endif
+ default:
+ if (ic->nodes == (void *)ic && ic->nlink == 0)
+ jffs2_del_ino_cache(c, ic);
+ break;
+ }
spin_unlock(&c->erase_completion_lock);
}

diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
index 6161808..79638f5 100644
--- a/fs/jffs2/scan.c
+++ b/fs/jffs2/scan.c
@@ -317,20 +317,25 @@ static int jffs2_scan_xattr_node(struct
struct jffs2_summary *s)
{
struct jffs2_xattr_datum *xd;
- uint32_t totlen, crc;
+ uint32_t xid, version, totlen, crc;
int err;

crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4);
if (crc != je32_to_cpu(rx->node_crc)) {
- if (je32_to_cpu(rx->node_crc) != 0xffffffff)
- JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ofs, je32_to_cpu(rx->node_crc), crc);
+ JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ofs, je32_to_cpu(rx->node_crc), crc);
if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
return err;
return 0;
}

- totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len));
+ xid = je32_to_cpu(rx->xid);
+ version = je32_to_cpu(rx->version);
+
+ totlen = sizeof(struct jffs2_raw_xattr);
+ if (version != XDATUM_DELETE_MARKER)
+ totlen += rx->name_len + 1 + je16_to_cpu(rx->value_len);
+ totlen = PAD(totlen);
if (totlen != je32_to_cpu(rx->totlen)) {
JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
ofs, je32_to_cpu(rx->totlen), totlen);
@@ -339,22 +344,24 @@ static int jffs2_scan_xattr_node(struct
return 0;
}

- xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version));
- if (IS_ERR(xd)) {
- if (PTR_ERR(xd) == -EEXIST) {
- if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rx->totlen)))))
- return err;
- return 0;
- }
+ xd = jffs2_setup_xattr_datum(c, xid, version);
+ if (IS_ERR(xd))
return PTR_ERR(xd);
- }
- xd->xprefix = rx->xprefix;
- xd->name_len = rx->name_len;
- xd->value_len = je16_to_cpu(rx->value_len);
- xd->data_crc = je32_to_cpu(rx->data_crc);

- xd->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL);
- /* FIXME */ xd->node->next_in_ino = (void *)xd;
+ if (xd->version > version) {
+ struct jffs2_raw_node_ref *raw
+ = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL);
+ raw->next_in_ino = xd->node->next_in_ino;
+ xd->node->next_in_ino = raw;
+ } else {
+ xd->version = version;
+ xd->xprefix = rx->xprefix;
+ xd->name_len = rx->name_len;
+ xd->value_len = je16_to_cpu(rx->value_len);
+ xd->data_crc = je32_to_cpu(rx->data_crc);
+
+ jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, (void *)xd);
+ }

if (jffs2_sum_active())
jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
@@ -373,9 +380,8 @@ static int jffs2_scan_xref_node(struct j

crc = crc32(0, rr, sizeof(*rr) - 4);
if (crc != je32_to_cpu(rr->node_crc)) {
- if (je32_to_cpu(rr->node_crc) != 0xffffffff)
- JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ofs, je32_to_cpu(rr->node_crc), crc);
+ JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ ofs, je32_to_cpu(rr->node_crc), crc);
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen)))))
return err;
return 0;
@@ -395,6 +401,7 @@ static int jffs2_scan_xref_node(struct j
return -ENOMEM;

/* BEFORE jffs2_build_xattr_subsystem() called,
+ * and AFTER xattr_ref is marked as a dead xref,
* 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
@@ -404,11 +411,13 @@ static int jffs2_scan_xref_node(struct j
*/
ref->ino = je32_to_cpu(rr->ino);
ref->xid = je32_to_cpu(rr->xid);
+ ref->xseqno = je32_to_cpu(rr->xseqno);
+ if (ref->xseqno > c->highest_xseqno)
+ c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER);
ref->next = c->xref_temp;
c->xref_temp = ref;

- ref->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), NULL);
- /* FIXME */ ref->node->next_in_ino = (void *)ref;
+ jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), (void *)ref);

if (jffs2_sum_active())
jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset);
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c
index 51bf165..d60784a 100644
--- a/fs/jffs2/summary.c
+++ b/fs/jffs2/summary.c
@@ -310,8 +310,6 @@ int jffs2_sum_add_kvec(struct jffs2_sb_i
#ifdef CONFIG_JFFS2_FS_XATTR
case JFFS2_NODETYPE_XATTR: {
struct jffs2_sum_xattr_mem *temp;
- if (je32_to_cpu(node->x.version) == 0xffffffff)
- return 0;
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
if (!temp)
goto no_mem;
@@ -327,10 +325,6 @@ #ifdef CONFIG_JFFS2_FS_XATTR
}
case JFFS2_NODETYPE_XREF: {
struct jffs2_sum_xref_mem *temp;
-
- if (je32_to_cpu(node->r.ino) == 0xffffffff
- && je32_to_cpu(node->r.xid) == 0xffffffff)
- return 0;
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
if (!temp)
goto no_mem;
@@ -483,22 +477,20 @@ #ifdef CONFIG_JFFS2_FS_XATTR

xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
je32_to_cpu(spx->version));
- if (IS_ERR(xd)) {
- if (PTR_ERR(xd) == -EEXIST) {
- /* a newer version of xd exists */
- if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(spx->totlen))))
- return err;
- sp += JFFS2_SUMMARY_XATTR_SIZE;
- break;
- }
- JFFS2_NOTICE("allocation of xattr_datum failed\n");
+ if (IS_ERR(xd))
return PTR_ERR(xd);
+ if (xd->version > je32_to_cpu(spx->version)) {
+ /* node is not the newest one */
+ struct jffs2_raw_node_ref *raw
+ = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spx->totlen)), NULL);
+ raw->next_in_ino = xd->node->next_in_ino;
+ xd->node->next_in_ino = raw;
+ } else {
+ xd->version = je32_to_cpu(spx->version);
+ sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
+ PAD(je32_to_cpu(spx->totlen)), (void *)xd);
}
-
- xd->node = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
- PAD(je32_to_cpu(spx->totlen)), NULL);
- /* FIXME */ xd->node->next_in_ino = (void *)xd;
-
*pseudo_random += je32_to_cpu(spx->xid);
sp += JFFS2_SUMMARY_XATTR_SIZE;

@@ -519,14 +511,11 @@ #ifdef CONFIG_JFFS2_FS_XATTR
JFFS2_NOTICE("allocation of xattr_datum failed\n");
return -ENOMEM;
}
- ref->ino = 0xfffffffe;
- ref->xid = 0xfffffffd;
ref->next = c->xref_temp;
c->xref_temp = ref;

- ref->node = sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
- PAD(sizeof(struct jffs2_raw_xref)), NULL);
- /* FIXME */ ref->node->next_in_ino = (void *)ref;
+ sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
+ PAD(sizeof(struct jffs2_raw_xref)), (void *)ref);

*pseudo_random += ref->node->flash_offset;
sp += JFFS2_SUMMARY_XREF_SIZE;
diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 2d82e25..03871ab 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -23,18 +23,15 @@ #include "nodelist.h"
* xattr_datum_hashkey(xprefix, xname, xvalue, xsize)
* is used to calcurate xdatum hashkey. The reminder of hashkey into XATTRINDEX_HASHSIZE is
* the index of the xattr name/value pair cache (c->xattrindex).
+ * is_xattr_datum_unchecked(c, xd)
+ * returns 1, if xdatum contains any unchecked raw nodes. if all raw nodes are not
+ * unchecked, it returns 0.
* unload_xattr_datum(c, xd)
* is used to release xattr name/value pair and detach from c->xattrindex.
* reclaim_xattr_datum(c)
* is used to reclaim xattr name/value pairs on the xattr name/value pair cache when
* memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold
* is hard coded as 32KiB.
- * delete_xattr_datum_node(c, xd)
- * is used to delete a jffs2 node is dominated by xdatum. When EBS(Erase Block Summary) is
- * enabled, it overwrites the obsolete node by myself.
- * delete_xattr_datum(c, xd)
- * is used to delete jffs2_xattr_datum object. It must be called with 0-value of reference
- * counter. (It means how many jffs2_xattr_ref object refers this xdatum.)
* do_verify_xattr_datum(c, xd)
* is used to load the xdatum informations without name/value pair from the medium.
* It's necessary once, because those informations are not collected during mounting
@@ -53,8 +50,13 @@ #include "nodelist.h"
* is used to write xdatum to medium. xd->version will be incremented.
* create_xattr_datum(c, xprefix, xname, xvalue, xsize)
* is used to create new xdatum and write to medium.
+ * delete_xattr_datum_delay(c, xd)
+ * is used to delete a xdatum without 'delete marker'. It has a possibility to detect
+ * orphan xdatum on next mounting.
+ * delete_xattr_datum(c, xd)
+ * is used to delete a xdatum with 'delete marker'. Calling jffs2_reserve_space() is
+ * necessary before this function.
* -------------------------------------------------- */
-
static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize)
{
int name_len = strlen(xname);
@@ -62,6 +64,22 @@ static uint32_t xattr_datum_hashkey(int
return crc32(xprefix, xname, name_len) ^ crc32(xprefix, xvalue, xsize);
}

+static int is_xattr_datum_unchecked(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ struct jffs2_raw_node_ref *raw;
+ int rc = 0;
+
+ spin_lock(&c->erase_completion_lock);
+ for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) {
+ if (ref_flags(raw) == REF_UNCHECKED) {
+ rc = 1;
+ break;
+ }
+ }
+ spin_unlock(&c->erase_completion_lock);
+ return rc;
+}
+
static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
@@ -107,80 +125,39 @@ static void reclaim_xattr_datum(struct j
before, c->xdatum_mem_usage, before - c->xdatum_mem_usage);
}

-static void delete_xattr_datum_node(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
-{
- /* must be called under down_write(xattr_sem) */
- struct jffs2_raw_xattr rx;
- size_t length;
- int rc;
-
- if (!xd->node) {
- JFFS2_WARNING("xdatum (xid=%u) is removed twice.\n", xd->xid);
- return;
- }
- if (jffs2_sum_active()) {
- memset(&rx, 0xff, sizeof(struct jffs2_raw_xattr));
- rc = jffs2_flash_read(c, ref_offset(xd->node),
- sizeof(struct jffs2_unknown_node),
- &length, (char *)&rx);
- if (rc || length != sizeof(struct jffs2_unknown_node)) {
- JFFS2_ERROR("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n",
- rc, sizeof(struct jffs2_unknown_node),
- length, ref_offset(xd->node));
- }
- rc = jffs2_flash_write(c, ref_offset(xd->node), sizeof(rx),
- &length, (char *)&rx);
- if (rc || length != sizeof(struct jffs2_raw_xattr)) {
- JFFS2_ERROR("jffs2_flash_write()=%d, req=%zu, wrote=%zu ar %#08x\n",
- rc, sizeof(rx), length, ref_offset(xd->node));
- }
- }
- spin_lock(&c->erase_completion_lock);
- xd->node->next_in_ino = NULL;
- spin_unlock(&c->erase_completion_lock);
- jffs2_mark_node_obsolete(c, xd->node);
- xd->node = NULL;
-}
-
-static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
-{
- /* must be called under down_write(xattr_sem) */
- BUG_ON(xd->refcnt);
-
- unload_xattr_datum(c, xd);
- if (xd->node) {
- delete_xattr_datum_node(c, xd);
- xd->node = NULL;
- }
- jffs2_free_xattr_datum(xd);
-}
-
static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xattr rx;
size_t readlen;
- uint32_t crc, totlen;
+ uint32_t crc, offset, totlen;
int rc;

- BUG_ON(!xd->node);
- BUG_ON(ref_flags(xd->node) != REF_UNCHECKED);
+ spin_lock(&c->erase_completion_lock);
+ offset = ref_offset(xd->node);
+ if (ref_flags(xd->node) == REF_PRISTINE)
+ goto complete;
+ spin_unlock(&c->erase_completion_lock);

- rc = jffs2_flash_read(c, ref_offset(xd->node), sizeof(rx), &readlen, (char *)&rx);
+ rc = jffs2_flash_read(c, offset, sizeof(rx), &readlen, (char *)&rx);
if (rc || readlen != sizeof(rx)) {
JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n",
- rc, sizeof(rx), readlen, ref_offset(xd->node));
+ rc, sizeof(rx), readlen, offset);
return rc ? rc : -EIO;
}
crc = crc32(0, &rx, sizeof(rx) - 4);
if (crc != je32_to_cpu(rx.node_crc)) {
- if (je32_to_cpu(rx.node_crc) != 0xffffffff)
- JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ref_offset(xd->node), je32_to_cpu(rx.hdr_crc), crc);
+ JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ offset, je32_to_cpu(rx.hdr_crc), crc);
+ xd->flags |= JFFS2_XFLAGS_INVALID;
return EIO;
}
- totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len));
+ totlen = sizeof(rx);
+ if (xd->version != XDATUM_DELETE_MARKER)
+ totlen += rx.name_len + 1 + je16_to_cpu(rx.value_len);
+ totlen = PAD(totlen);
if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK
|| je16_to_cpu(rx.nodetype) != JFFS2_NODETYPE_XATTR
|| je32_to_cpu(rx.totlen) != totlen
@@ -188,11 +165,12 @@ static int do_verify_xattr_datum(struct
|| je32_to_cpu(rx.version) != xd->version) {
JFFS2_ERROR("inconsistent xdatum at %#08x, magic=%#04x/%#04x, "
"nodetype=%#04x/%#04x, totlen=%u/%u, xid=%u/%u, version=%u/%u\n",
- ref_offset(xd->node), je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK,
+ offset, je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK,
je16_to_cpu(rx.nodetype), JFFS2_NODETYPE_XATTR,
je32_to_cpu(rx.totlen), totlen,
je32_to_cpu(rx.xid), xd->xid,
je32_to_cpu(rx.version), xd->version);
+ xd->flags |= JFFS2_XFLAGS_INVALID;
return EIO;
}
xd->xprefix = rx.xprefix;
@@ -200,14 +178,17 @@ static int do_verify_xattr_datum(struct
xd->value_len = je16_to_cpu(rx.value_len);
xd->data_crc = je32_to_cpu(rx.data_crc);

- /* This JFFS2_NODETYPE_XATTR node is checked */
- jeb = &c->blocks[ref_offset(xd->node) / c->sector_size];
- totlen = PAD(je32_to_cpu(rx.totlen));
-
spin_lock(&c->erase_completion_lock);
- c->unchecked_size -= totlen; c->used_size += totlen;
- jeb->unchecked_size -= totlen; jeb->used_size += totlen;
- xd->node->flash_offset = ref_offset(xd->node) | REF_PRISTINE;
+ complete:
+ for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) {
+ jeb = &c->blocks[ref_offset(raw) / c->sector_size];
+ totlen = PAD(ref_totlen(c, jeb, raw));
+ if (ref_flags(raw) == REF_UNCHECKED) {
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ }
+ raw->flash_offset = ref_offset(raw) | ((xd->node==raw) ? REF_PRISTINE : REF_NORMAL);
+ }
spin_unlock(&c->erase_completion_lock);

/* unchecked xdatum is chained with c->xattr_unchecked */
@@ -227,7 +208,6 @@ static int do_load_xattr_datum(struct jf
uint32_t crc, length;
int i, ret, retry = 0;

- BUG_ON(!xd->node);
BUG_ON(ref_flags(xd->node) != REF_PRISTINE);
BUG_ON(!list_empty(&xd->xindex));
retry:
@@ -253,6 +233,7 @@ static int do_load_xattr_datum(struct jf
" at %#08x, read: 0x%08x calculated: 0x%08x\n",
ref_offset(xd->node), xd->data_crc, crc);
kfree(data);
+ xd->flags |= JFFS2_XFLAGS_INVALID;
return EIO;
}

@@ -286,16 +267,13 @@ static int load_xattr_datum(struct jffs2
* rc > 0 : Unrecoverable error, this node should be deleted.
*/
int rc = 0;
- BUG_ON(xd->xname);
- if (!xd->node)
+
+ if (xd->xname)
+ return 0;
+ if (xd->flags & JFFS2_XFLAGS_INVALID)
return EIO;
- if (unlikely(ref_flags(xd->node) != REF_PRISTINE)) {
+ if (unlikely(is_xattr_datum_unchecked(c, xd)))
rc = do_verify_xattr_datum(c, xd);
- if (rc > 0) {
- list_del_init(&xd->xindex);
- delete_xattr_datum_node(c, xd);
- }
- }
if (!rc)
rc = do_load_xattr_datum(c, xd);
return rc;
@@ -304,36 +282,43 @@ static int load_xattr_datum(struct jffs2
static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
- struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xattr rx;
struct kvec vecs[2];
size_t length;
- int rc, totlen;
+ int rc, totlen, nvecs = 1;
uint32_t phys_ofs = write_ofs(c);

- BUG_ON(!xd->xname);
+ BUG_ON(is_xattr_datum_dead(xd) || (xd->flags & JFFS2_XFLAGS_INVALID)
+ ? !!xd->xname : !xd->xname);

vecs[0].iov_base = &rx;
- vecs[0].iov_len = PAD(sizeof(rx));
- vecs[1].iov_base = xd->xname;
- vecs[1].iov_len = xd->name_len + 1 + xd->value_len;
- totlen = vecs[0].iov_len + vecs[1].iov_len;
-
+ vecs[0].iov_len = totlen = sizeof(rx);
+ if (!is_xattr_datum_dead(xd) && !(xd->flags & JFFS2_XFLAGS_INVALID)) {
+ nvecs++;
+ vecs[1].iov_base = xd->xname;
+ vecs[1].iov_len = xd->name_len + 1 + xd->value_len;
+ totlen += vecs[1].iov_len;
+ }
/* Setup raw-xattr */
+ memset(&rx, 0, sizeof(rx));
rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
rx.totlen = cpu_to_je32(PAD(totlen));
rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4));

rx.xid = cpu_to_je32(xd->xid);
- rx.version = cpu_to_je32(++xd->version);
- rx.xprefix = xd->xprefix;
- rx.name_len = xd->name_len;
- rx.value_len = cpu_to_je16(xd->value_len);
- rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len));
+ if (!is_xattr_datum_dead(xd) && !(xd->flags & JFFS2_XFLAGS_INVALID)) {
+ rx.version = cpu_to_je32(++xd->version);
+ rx.xprefix = xd->xprefix;
+ rx.name_len = xd->name_len;
+ rx.value_len = cpu_to_je16(xd->value_len);
+ rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len));
+ } else {
+ rx.version = cpu_to_je32(XDATUM_DELETE_MARKER);
+ }
rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr) - 4));

- rc = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0);
+ rc = jffs2_flash_writev(c, vecs, nvecs, phys_ofs, &length, 0);
if (rc || totlen != length) {
JFFS2_WARNING("jffs2_flash_writev()=%d, req=%u, wrote=%zu, at %#08x\n",
rc, totlen, length, phys_ofs);
@@ -343,14 +328,8 @@ static int save_xattr_datum(struct jffs2

return rc;
}
-
/* success */
- raw = jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), NULL);
- /* FIXME */ raw->next_in_ino = (void *)xd;
-
- if (xd->node)
- delete_xattr_datum_node(c, xd);
- xd->node = raw;
+ jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), (void *)xd);

dbg_xattr("success on saving xdatum (xid=%u, version=%u, xprefix=%u, xname='%s')\n",
xd->xid, xd->version, xd->xprefix, xd->xname);
@@ -426,6 +405,39 @@ static struct jffs2_xattr_datum *create_
return xd;
}

+static void delete_xattr_datum_delay(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);
+ set_xattr_datum_dead(xd);
+ spin_lock(&c->erase_completion_lock);
+ list_add(&xd->xindex, &c->xattr_dead_list);
+ spin_unlock(&c->erase_completion_lock);
+ JFFS2_NOTICE("xdatum(xid=%u) was removed without delete marker. "
+ "An orphan xdatum may be detected on next mounting.\n", xd->xid);
+}
+
+static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under jffs2_reserve_space() and down_write(xattr_sem) */
+ int rc;
+ BUG_ON(xd->refcnt);
+
+ unload_xattr_datum(c, xd);
+ set_xattr_datum_dead(xd);
+ rc = save_xattr_datum(c, xd);
+ if (rc) {
+ JFFS2_NOTICE("xdatum(xid=%u) was removed without delete marker. "
+ "An orphan xdatum may be detected on next mounting.\n",
+ xd->xid);
+ }
+ spin_lock(&c->erase_completion_lock);
+ list_add(&xd->xindex, &c->xattr_dead_list);
+ spin_unlock(&c->erase_completion_lock);
+}
+
/* -------- xref related functions ------------------
* verify_xattr_ref(c, ref)
* is used to load xref information from medium. Because summary data does not
@@ -450,25 +462,29 @@ static struct jffs2_xattr_datum *create_
static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xref rr;
size_t readlen;
- uint32_t crc, totlen;
+ uint32_t crc, offset, totlen;
int rc;

- BUG_ON(ref_flags(ref->node) != REF_UNCHECKED);
+ spin_lock(&c->erase_completion_lock);
+ if (ref_flags(ref->node) != REF_UNCHECKED)
+ goto complete;
+ offset = ref_offset(ref->node);
+ spin_unlock(&c->erase_completion_lock);

- rc = jffs2_flash_read(c, ref_offset(ref->node), sizeof(rr), &readlen, (char *)&rr);
+ rc = jffs2_flash_read(c, offset, sizeof(rr), &readlen, (char *)&rr);
if (rc || sizeof(rr) != readlen) {
JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu, at %#08x\n",
- rc, sizeof(rr), readlen, ref_offset(ref->node));
+ rc, sizeof(rr), readlen, offset);
return rc ? rc : -EIO;
}
/* obsolete node */
crc = crc32(0, &rr, sizeof(rr) - 4);
if (crc != je32_to_cpu(rr.node_crc)) {
- if (je32_to_cpu(rr.node_crc) != 0xffffffff)
- JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
- ref_offset(ref->node), je32_to_cpu(rr.node_crc), crc);
+ JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
+ offset, je32_to_cpu(rr.node_crc), crc);
return EIO;
}
if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK
@@ -476,22 +492,28 @@ static int verify_xattr_ref(struct jffs2
|| je32_to_cpu(rr.totlen) != PAD(sizeof(rr))) {
JFFS2_ERROR("inconsistent xref at %#08x, magic=%#04x/%#04x, "
"nodetype=%#04x/%#04x, totlen=%u/%zu\n",
- ref_offset(ref->node), je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK,
+ offset, je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK,
je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF,
je32_to_cpu(rr.totlen), PAD(sizeof(rr)));
return EIO;
}
ref->ino = je32_to_cpu(rr.ino);
ref->xid = je32_to_cpu(rr.xid);
-
- /* fixup superblock/eraseblock info */
- jeb = &c->blocks[ref_offset(ref->node) / c->sector_size];
- totlen = PAD(sizeof(rr));
+ ref->xseqno = je32_to_cpu(rr.xseqno);
+ if (ref->xseqno > c->highest_xseqno)
+ c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER);

spin_lock(&c->erase_completion_lock);
- c->unchecked_size -= totlen; c->used_size += totlen;
- jeb->unchecked_size -= totlen; jeb->used_size += totlen;
- ref->node->flash_offset = ref_offset(ref->node) | REF_PRISTINE;
+ complete:
+ for (raw=ref->node; raw != (void *)ref; raw=raw->next_in_ino) {
+ jeb = &c->blocks[ref_offset(raw) / c->sector_size];
+ totlen = PAD(ref_totlen(c, jeb, raw));
+ if (ref_flags(raw) == REF_UNCHECKED) {
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ }
+ raw->flash_offset = ref_offset(raw) | ((ref->node==raw) ? REF_PRISTINE : REF_NORMAL);
+ }
spin_unlock(&c->erase_completion_lock);

dbg_xattr("success on verifying xref (ino=%u, xid=%u) at %#08x\n",
@@ -499,58 +521,12 @@ static int verify_xattr_ref(struct jffs2
return 0;
}

-static void delete_xattr_ref_node(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
-{
- struct jffs2_raw_xref rr;
- size_t length;
- int rc;
-
- if (jffs2_sum_active()) {
- memset(&rr, 0xff, sizeof(rr));
- rc = jffs2_flash_read(c, ref_offset(ref->node),
- sizeof(struct jffs2_unknown_node),
- &length, (char *)&rr);
- if (rc || length != sizeof(struct jffs2_unknown_node)) {
- JFFS2_ERROR("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n",
- rc, sizeof(struct jffs2_unknown_node),
- length, ref_offset(ref->node));
- }
- rc = jffs2_flash_write(c, ref_offset(ref->node), sizeof(rr),
- &length, (char *)&rr);
- if (rc || length != sizeof(struct jffs2_raw_xref)) {
- JFFS2_ERROR("jffs2_flash_write()=%d, req=%zu, wrote=%zu at %#08x\n",
- rc, sizeof(rr), length, ref_offset(ref->node));
- }
- }
- spin_lock(&c->erase_completion_lock);
- ref->node->next_in_ino = NULL;
- spin_unlock(&c->erase_completion_lock);
- jffs2_mark_node_obsolete(c, ref->node);
- ref->node = NULL;
-}
-
-static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
-{
- /* must be called under down_write(xattr_sem) */
- struct jffs2_xattr_datum *xd;
-
- BUG_ON(!ref->node);
- delete_xattr_ref_node(c, ref);
-
- xd = ref->xd;
- xd->refcnt--;
- if (!xd->refcnt)
- delete_xattr_datum(c, xd);
- jffs2_free_xattr_ref(ref);
-}
-
static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
/* must be called under down_write(xattr_sem) */
- struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xref rr;
size_t length;
- uint32_t phys_ofs = write_ofs(c);
+ uint32_t xseqno, phys_ofs = write_ofs(c);
int ret;

rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
@@ -558,8 +534,16 @@ static int save_xattr_ref(struct jffs2_s
rr.totlen = cpu_to_je32(PAD(sizeof(rr)));
rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node) - 4));

- rr.ino = cpu_to_je32(ref->ic->ino);
- rr.xid = cpu_to_je32(ref->xd->xid);
+ xseqno = (c->highest_xseqno += 2);
+ if (is_xattr_ref_dead(ref)) {
+ xseqno |= XREF_DELETE_MARKER;
+ rr.ino = cpu_to_je32(ref->ino);
+ rr.xid = cpu_to_je32(ref->xid);
+ } else {
+ rr.ino = cpu_to_je32(ref->ic->ino);
+ rr.xid = cpu_to_je32(ref->xd->xid);
+ }
+ rr.xseqno = cpu_to_je32(xseqno);
rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(rr) - 4));

ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr);
@@ -572,12 +556,9 @@ static int save_xattr_ref(struct jffs2_s

return ret;
}
-
- raw = jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(sizeof(rr)), NULL);
- /* FIXME */ raw->next_in_ino = (void *)ref;
- if (ref->node)
- delete_xattr_ref_node(c, ref);
- ref->node = raw;
+ /* success */
+ ref->xseqno = xseqno;
+ jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(sizeof(rr)), (void *)ref);

dbg_xattr("success on saving xref (ino=%u, xid=%u)\n", ref->ic->ino, ref->xd->xid);

@@ -610,22 +591,116 @@ static struct jffs2_xattr_ref *create_xa
return ref; /* success */
}

+static void delete_xattr_ref_delay(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ /* must be called under down_write(xattr_sem) */
+ struct jffs2_xattr_datum *xd;
+
+ set_xattr_ref_dead(ref);
+ xd = ref->xd;
+ ref->ino = ref->ic->ino;
+ ref->xid = ref->xd->xid;
+ spin_lock(&c->erase_completion_lock);
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ spin_unlock(&c->erase_completion_lock);
+
+ JFFS2_NOTICE("xref(ino=%u, xid=%u) was removed without delete marker. "
+ "An orphan xref may be detected on next mounting.\n",
+ ref->ino, ref->xid);
+
+ if (!--xd->refcnt)
+ delete_xattr_datum_delay(c, xd);
+}
+
+static int delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, int enforce)
+{
+ /* must be called under jffs2_reserve_space() and down_write(xattr_sem) */
+ struct jffs2_inode_cache *ic;
+ struct jffs2_xattr_datum *xd;
+ uint32_t length;
+ int rc;
+
+ set_xattr_ref_dead(ref);
+ ic = ref->ic;
+ xd = ref->xd;
+ ref->ino = ic->ino;
+ ref->xid = xd->xid;
+ rc = save_xattr_ref(c, ref);
+ if (rc) {
+ if (!enforce) {
+ clr_xattr_ref_dead(ref);
+ ref->ic = ic;
+ ref->xd = xd;
+ return rc;
+ }
+ JFFS2_WARNING("could not write delete marker of xref(ino=%u, xid=%u). "
+ "An orphan xref may be detected on next mounting.\n",
+ ref->ic->ino, ref->xd->xid);
+ }
+ spin_lock(&c->erase_completion_lock);
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ spin_unlock(&c->erase_completion_lock);
+
+ xd->refcnt--;
+ if (xd->refcnt)
+ return 0;
+
+ /* delete xdatum */
+ unload_xattr_datum(c, xd);
+ up_write(&c->xattr_sem);
+ jffs2_complete_reservation(c);
+
+ rc = jffs2_reserve_space(c, PAD(sizeof(struct jffs2_raw_xattr)), &length,
+ ALLOC_DELETION, JFFS2_SUMMARY_XATTR_SIZE);
+ if (rc) {
+ down(&c->alloc_sem);
+ down_write(&c->xattr_sem);
+ delete_xattr_datum_delay(c, xd);
+ } else {
+ down_write(&c->xattr_sem);
+ delete_xattr_datum(c, xd);
+ }
+ return 0;
+}
+
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;
+ struct jffs2_xattr_ref *ref;
+ uint32_t length;
+ int rc, retry;

if (!ic || ic->nlink > 0)
return;

+ down_read(&c->xattr_sem);
+ if (!ic->xref) {
+ up_read(&c->xattr_sem);
+ return;
+ }
+ up_read(&c->xattr_sem);
+ retry:
+ rc = jffs2_reserve_space(c, PAD(sizeof(struct jffs2_raw_xref)), &length,
+ ALLOC_DELETION, JFFS2_SUMMARY_XREF_SIZE);
down_write(&c->xattr_sem);
- for (ref = ic->xref; ref; ref = _ref) {
- _ref = ref->next;
- delete_xattr_ref(c, ref);
+ if (ic->xref) {
+ ref = ic->xref;
+ ic->xref = ref->next;
+ if (rc) {
+ delete_xattr_ref_delay(c, ref);
+ } else {
+ delete_xattr_ref(c, ref, 1);
+ }
}
- ic->xref = NULL;
+ retry = ic->xref ? 1 : 0;
up_write(&c->xattr_sem);
+ if (!rc)
+ jffs2_complete_reservation(c);
+ if (retry)
+ goto retry;
}

void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
@@ -655,7 +730,7 @@ static int check_xattr_ref_inode(struct
* duplicate name/value pairs. If duplicate name/value pair would be found,
* one will be removed.
*/
- struct jffs2_xattr_ref *ref, *cmp, **pref;
+ struct jffs2_xattr_ref *ref, *cmp, **pref, **pcmp;
int rc = 0;

if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED))
@@ -668,27 +743,32 @@ static int check_xattr_ref_inode(struct
rc = load_xattr_datum(c, ref->xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
- delete_xattr_ref(c, ref);
+ delete_xattr_ref_delay(c, ref);
goto retry;
} else if (unlikely(rc < 0))
goto out;
}
- for (cmp=ref->next, pref=&ref->next; cmp; pref=&cmp->next, cmp=cmp->next) {
+ for (cmp=ref->next, pcmp=&ref->next; cmp; pcmp=&cmp->next, cmp=cmp->next) {
if (!cmp->xd->xname) {
ref->xd->flags |= JFFS2_XFLAGS_BIND;
rc = load_xattr_datum(c, cmp->xd);
ref->xd->flags &= ~JFFS2_XFLAGS_BIND;
if (unlikely(rc > 0)) {
- *pref = cmp->next;
- delete_xattr_ref(c, cmp);
+ *pcmp = cmp->next;
+ delete_xattr_ref_delay(c, cmp);
goto retry;
} else if (unlikely(rc < 0))
goto out;
}
if (ref->xd->xprefix == cmp->xd->xprefix
&& !strcmp(ref->xd->xname, cmp->xd->xname)) {
- *pref = cmp->next;
- delete_xattr_ref(c, cmp);
+ if (ref->xseqno > cmp->xseqno) {
+ *pcmp = cmp->next;
+ delete_xattr_ref_delay(c, cmp);
+ } else {
+ *pref = ref->next;
+ delete_xattr_ref_delay(c, ref);
+ }
goto retry;
}
}
@@ -719,9 +799,13 @@ void jffs2_init_xattr_subsystem(struct j
for (i=0; i < XATTRINDEX_HASHSIZE; i++)
INIT_LIST_HEAD(&c->xattrindex[i]);
INIT_LIST_HEAD(&c->xattr_unchecked);
+ INIT_LIST_HEAD(&c->xattr_dead_list);
+ c->xref_dead_list = NULL;
c->xref_temp = NULL;

init_rwsem(&c->xattr_sem);
+ c->highest_xid = 0;
+ c->highest_xseqno = 0;
c->xdatum_mem_usage = 0;
c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */
}
@@ -751,7 +835,11 @@ void jffs2_clear_xattr_subsystem(struct
_ref = ref->next;
jffs2_free_xattr_ref(ref);
}
- c->xref_temp = NULL;
+
+ for (ref=c->xref_dead_list; ref; ref = _ref) {
+ _ref = ref->next;
+ jffs2_free_xattr_ref(ref);
+ }

for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
@@ -761,64 +849,120 @@ void jffs2_clear_xattr_subsystem(struct
jffs2_free_xattr_datum(xd);
}
}
+
+ list_for_each_entry_safe(xd, _xd, &c->xattr_dead_list, xindex) {
+ list_del(&xd->xindex);
+ jffs2_free_xattr_datum(xd);
+ }
}

+#define XREF_TMPHASH_SIZE (128)
void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
{
struct jffs2_xattr_ref *ref, *_ref;
+ struct jffs2_xattr_ref *xref_tmphash[XREF_TMPHASH_SIZE];
struct jffs2_xattr_datum *xd, *_xd;
struct jffs2_inode_cache *ic;
- int i, xdatum_count =0, xdatum_unchecked_count = 0, xref_count = 0;
+ struct jffs2_raw_node_ref *raw;
+ int i, xdatum_count = 0, xdatum_unchecked_count = 0, xref_count = 0;

BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING));
+ /* Phase.1 : Drop dead xdatum */
+ for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
+ list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
+ BUG_ON(xd->node == (void *)xd);
+ if (is_xattr_datum_dead(xd)) {
+ list_del_init(&xd->xindex);
+ list_add(&xd->xindex, &c->xattr_unchecked);
+ }
+ }
+ }

- /* Phase.1 */
+ /* Phase.2 : Merge same xref */
+ for (i=0; i < XREF_TMPHASH_SIZE; i++)
+ xref_tmphash[i] = NULL;
for (ref=c->xref_temp; ref; ref=_ref) {
+ struct jffs2_xattr_ref *tmp;
+
_ref = ref->next;
- /* checking REF_UNCHECKED nodes */
if (ref_flags(ref->node) != REF_PRISTINE) {
if (verify_xattr_ref(c, ref)) {
- delete_xattr_ref_node(c, ref);
+ BUG_ON(ref->node->next_in_ino != (void *)ref);
+ ref->node->next_in_ino = NULL;
+ jffs2_mark_node_obsolete(c, ref->node);
jffs2_free_xattr_ref(ref);
continue;
}
}
- /* At this point, ref->xid and ref->ino contain XID and inode number.
- ref->xd and ref->ic are not valid yet. */
- xd = jffs2_find_xattr_datum(c, ref->xid);
- ic = jffs2_get_ino_cache(c, ref->ino);
- if (!xd || !ic) {
- if (ref_flags(ref->node) != REF_UNCHECKED)
- JFFS2_WARNING("xref(ino=%u, xid=%u) is orphan. \n",
- ref->ino, ref->xid);
- delete_xattr_ref_node(c, ref);
+
+ i = (ref->ino ^ ref->xid) % XREF_TMPHASH_SIZE;
+ for (tmp=xref_tmphash[i]; tmp; tmp=tmp->next) {
+ if (tmp->ino == ref->ino && tmp->xid == ref->xid)
+ break;
+ }
+ if (tmp) {
+ raw = ref->node;
+ if (ref->xseqno > tmp->xseqno) {
+ tmp->xseqno = ref->xseqno;
+ raw->next_in_ino = tmp->node;
+ tmp->node = raw;
+ } else {
+ raw->next_in_ino = tmp->node->next_in_ino;
+ tmp->node->next_in_ino = raw;
+ }
jffs2_free_xattr_ref(ref);
continue;
+ } else {
+ ref->next = xref_tmphash[i];
+ xref_tmphash[i] = ref;
}
- ref->xd = xd;
- ref->ic = ic;
- xd->refcnt++;
- ref->next = ic->xref;
- ic->xref = ref;
- xref_count++;
}
c->xref_temp = NULL;
- /* After this, ref->xid/ino are NEVER used. */

- /* Phase.2 */
+ /* Phase.3 : Bind xref with inode_cache and xattr_datum */
+ for (i=0; i < XREF_TMPHASH_SIZE; i++) {
+ for (ref=xref_tmphash[i]; ref; ref=_ref) {
+ _ref = ref->next;
+ if (is_xattr_ref_dead(ref)) {
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ continue;
+ }
+ /* At this point, ref->xid and ref->ino contain XID and inode number.
+ ref->xd and ref->ic are not valid yet. */
+ xd = jffs2_find_xattr_datum(c, ref->xid);
+ ic = jffs2_get_ino_cache(c, ref->ino);
+ if (!xd || !ic) {
+ JFFS2_WARNING("xref(ino=%u, xid=%u, xseqno=%u) is orphan. \n",
+ ref->ino, ref->xid, ref->xseqno);
+ set_xattr_ref_dead(ref);
+ ref->next = c->xref_dead_list;
+ c->xref_dead_list = ref;
+ continue;
+ }
+ ref->xd = xd;
+ ref->ic = ic;
+ xd->refcnt++;
+ ref->next = ic->xref;
+ ic->xref = ref;
+ xref_count++;
+ }
+ }
+
+ /* Phase.4 : Link unchecked xdatum to xattr_unchecked list */
for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
list_del_init(&xd->xindex);
if (!xd->refcnt) {
- if (ref_flags(xd->node) != REF_UNCHECKED)
- JFFS2_WARNING("orphan xdatum(xid=%u, version=%u) at %#08x\n",
- xd->xid, xd->version, ref_offset(xd->node));
- delete_xattr_datum(c, xd);
+ JFFS2_WARNING("orphan xdatum(xid=%u, version=%u)\n",
+ xd->xid, xd->version);
+ set_xattr_datum_dead(xd);
+ list_add(&xd->xindex, &c->xattr_unchecked);
continue;
}
- if (ref_flags(xd->node) != REF_PRISTINE) {
- dbg_xattr("unchecked xdatum(xid=%u) at %#08x\n",
- xd->xid, ref_offset(xd->node));
+ if (is_xattr_datum_unchecked(c, xd)) {
+ dbg_xattr("unchecked xdatum(xid=%u, version=%u)\n",
+ xd->xid, xd->version);
list_add(&xd->xindex, &c->xattr_unchecked);
xdatum_unchecked_count++;
}
@@ -833,28 +977,18 @@ void jffs2_build_xattr_subsystem(struct
struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
uint32_t xid, uint32_t version)
{
- struct jffs2_xattr_datum *xd, *_xd;
+ struct jffs2_xattr_datum *xd;

- _xd = jffs2_find_xattr_datum(c, xid);
- if (_xd) {
- dbg_xattr("duplicate xdatum (xid=%u, version=%u/%u) at %#08x\n",
- xid, version, _xd->version, ref_offset(_xd->node));
- if (version < _xd->version)
- return ERR_PTR(-EEXIST);
- }
- xd = jffs2_alloc_xattr_datum();
- if (!xd)
- return ERR_PTR(-ENOMEM);
- xd->xid = xid;
- xd->version = version;
- if (xd->xid > c->highest_xid)
- c->highest_xid = xd->xid;
- list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]);
-
- if (_xd) {
- list_del_init(&_xd->xindex);
- delete_xattr_datum_node(c, _xd);
- jffs2_free_xattr_datum(_xd);
+ xd = jffs2_find_xattr_datum(c, xid);
+ if (!xd) {
+ xd = jffs2_alloc_xattr_datum();
+ if (!xd)
+ return ERR_PTR(-ENOMEM);
+ xd->xid = xid;
+ xd->version = version;
+ if (xd->xid > c->highest_xid)
+ c->highest_xid = xd->xid;
+ list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]);
}
return xd;
}
@@ -945,7 +1079,7 @@ ssize_t jffs2_listxattr(struct dentry *d
rc = load_xattr_datum(c, xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
- delete_xattr_ref(c, ref);
+ delete_xattr_ref_delay(c, ref);
goto retry;
} else if (unlikely(rc < 0))
goto out;
@@ -1006,7 +1140,7 @@ int do_jffs2_getxattr(struct inode *inod
rc = load_xattr_datum(c, xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
- delete_xattr_ref(c, ref);
+ delete_xattr_ref_delay(c, ref);
goto retry;
} else if (unlikely(rc < 0)) {
goto out;
@@ -1069,7 +1203,7 @@ int do_jffs2_setxattr(struct inode *inod
rc = load_xattr_datum(c, xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
- delete_xattr_ref(c, ref);
+ delete_xattr_ref_delay(c, ref);
goto retry;
} else if (unlikely(rc < 0))
goto out;
@@ -1081,8 +1215,7 @@ int do_jffs2_setxattr(struct inode *inod
}
if (!buffer) {
*pref = ref->next;
- delete_xattr_ref(c, ref);
- rc = 0;
+ rc = delete_xattr_ref(c, ref, 0);
goto out;
}
goto found;
@@ -1094,7 +1227,7 @@ int do_jffs2_setxattr(struct inode *inod
goto out;
}
if (!buffer) {
- rc = -EINVAL;
+ rc = -ENODATA;
goto out;
}
found:
@@ -1110,16 +1243,15 @@ int do_jffs2_setxattr(struct inode *inod
request = PAD(sizeof(struct jffs2_raw_xref));
rc = jffs2_reserve_space(c, request, &length,
ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE);
+ down_write(&c->xattr_sem);
if (rc) {
JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
- down_write(&c->xattr_sem);
xd->refcnt--;
if (!xd->refcnt)
- delete_xattr_datum(c, xd);
+ delete_xattr_datum_delay(c, xd);
up_write(&c->xattr_sem);
return rc;
}
- down_write(&c->xattr_sem);
if (ref)
*pref = ref->next;
newref = create_xattr_ref(c, ic, xd);
@@ -1131,9 +1263,21 @@ int do_jffs2_setxattr(struct inode *inod
rc = PTR_ERR(newref);
xd->refcnt--;
if (!xd->refcnt)
- delete_xattr_datum(c, xd);
+ delete_xattr_datum_delay(c, xd);
} else if (ref) {
- delete_xattr_ref(c, ref);
+ up_write(&c->xattr_sem);
+ jffs2_complete_reservation(c);
+
+ rc = jffs2_reserve_space(c, request, &length,
+ ALLOC_DELETION, JFFS2_SUMMARY_XREF_SIZE);
+ down_write(&c->xattr_sem);
+ if (rc) {
+ JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
+ delete_xattr_ref_delay(c, ref);
+ up_write(&c->xattr_sem);
+ return 0;
+ }
+ delete_xattr_ref(c, ref, 1);
}
out:
up_write(&c->xattr_sem);
@@ -1142,38 +1286,37 @@ int do_jffs2_setxattr(struct inode *inod
}

/* -------- garbage collector functions -------------
- * jffs2_garbage_collect_xattr_datum(c, xd)
+ * jffs2_garbage_collect_xattr_datum(c, xd, raw)
* is used to move xdatum into new node.
- * jffs2_garbage_collect_xattr_ref(c, ref)
+ * jffs2_garbage_collect_xattr_ref(c, ref, raw)
* is used to move xref into new node.
* jffs2_verify_xattr(c)
* is used to call do_verify_xattr_datum() before garbage collecting.
* -------------------------------------------------- */
-int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd,
+ struct jffs2_raw_node_ref *raw)
{
uint32_t totlen, length, old_ofs;
- int rc = -EINVAL;
+ int rc = 0;

down_write(&c->xattr_sem);
- BUG_ON(!xd->node);
+ if (xd->node != raw)
+ goto out;
+ if (is_xattr_datum_dead(xd) && (raw->next_in_ino == (void *)xd))
+ goto out;

old_ofs = ref_offset(xd->node);
totlen = ref_totlen(c, c->gcblock, xd->node);
- if (totlen < sizeof(struct jffs2_raw_xattr))
- goto out;

- if (!xd->xname) {
+ if (!is_xattr_datum_dead(xd)) {
rc = load_xattr_datum(c, xd);
- if (unlikely(rc > 0)) {
- delete_xattr_datum_node(c, xd);
- rc = 0;
- goto out;
- } else if (unlikely(rc < 0))
+ if (unlikely(rc < 0))
goto out;
}
+
rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XATTR_SIZE);
- if (rc || length < totlen) {
- JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, totlen);
+ if (rc) {
+ JFFS2_WARNING("jffs2_reserve_space_gc()=%d, request=%u\n", rc, totlen);
rc = rc ? rc : -EBADFD;
goto out;
}
@@ -1182,27 +1325,33 @@ int jffs2_garbage_collect_xattr_datum(st
dbg_xattr("xdatum (xid=%u, version=%u) GC'ed from %#08x to %08x\n",
xd->xid, xd->version, old_ofs, ref_offset(xd->node));
out:
+ if (!rc)
+ jffs2_mark_node_obsolete(c, raw);
up_write(&c->xattr_sem);
return rc;
}

-int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref,
+ struct jffs2_raw_node_ref *raw)
{
uint32_t totlen, length, old_ofs;
- int rc = -EINVAL;
+ int rc = 0;

down_write(&c->xattr_sem);
BUG_ON(!ref->node);

+ if (ref->node != raw)
+ goto out;
+ if (is_xattr_ref_dead(ref) && (raw->next_in_ino == (void *)ref))
+ goto out;
+
old_ofs = ref_offset(ref->node);
totlen = ref_totlen(c, c->gcblock, ref->node);
- if (totlen != sizeof(struct jffs2_raw_xref))
- goto out;

rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XREF_SIZE);
- if (rc || length < totlen) {
- JFFS2_WARNING("%s: jffs2_reserve_space() = %d, request = %u\n",
+ if (rc) {
+ JFFS2_WARNING("%s: jffs2_reserve_space_gc() = %d, request = %u\n",
__FUNCTION__, rc, totlen);
rc = rc ? rc : -EBADFD;
goto out;
@@ -1212,6 +1361,8 @@ int jffs2_garbage_collect_xattr_ref(stru
dbg_xattr("xref (ino=%u, xid=%u) GC'ed from %#08x to %08x\n",
ref->ic->ino, ref->xd->xid, old_ofs, ref_offset(ref->node));
out:
+ if (!rc)
+ jffs2_mark_node_obsolete(c, raw);
up_write(&c->xattr_sem);
return rc;
}
@@ -1219,20 +1370,59 @@ int jffs2_garbage_collect_xattr_ref(stru
int jffs2_verify_xattr(struct jffs2_sb_info *c)
{
struct jffs2_xattr_datum *xd, *_xd;
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *raw;
+ uint32_t totlen;
int rc;

down_write(&c->xattr_sem);
list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) {
rc = do_verify_xattr_datum(c, xd);
- if (rc == 0) {
- list_del_init(&xd->xindex);
- break;
- } else if (rc > 0) {
- list_del_init(&xd->xindex);
- delete_xattr_datum_node(c, xd);
+ if (rc < 0)
+ continue;
+ list_del_init(&xd->xindex);
+ spin_lock(&c->erase_completion_lock);
+ for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) {
+ if (ref_flags(raw) != REF_UNCHECKED)
+ continue;
+ jeb = &c->blocks[ref_offset(raw) / c->sector_size];
+ totlen = PAD(ref_totlen(c, jeb, raw));
+ c->unchecked_size -= totlen; c->used_size += totlen;
+ jeb->unchecked_size -= totlen; jeb->used_size += totlen;
+ raw->flash_offset = ref_offset(raw)
+ | ((xd->node == (void *)raw) ? REF_PRISTINE : REF_NORMAL);
}
+ if (is_xattr_datum_dead(xd))
+ list_add(&xd->xindex, &c->xattr_dead_list);
+ spin_unlock(&c->erase_completion_lock);
}
up_write(&c->xattr_sem);
-
return list_empty(&c->xattr_unchecked) ? 1 : 0;
}
+
+void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
+{
+ /* must be called under spin_lock(&c->erase_completion_lock) */
+ if (xd->node != (void *)xd)
+ return;
+
+ list_del(&xd->xindex);
+ jffs2_free_xattr_datum(xd);
+}
+
+void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
+{
+ /* must be called under spin_lock(&c->erase_completion_lock) */
+ struct jffs2_xattr_ref *tmp, **ptmp;
+
+ if (ref->node != (void *)ref)
+ return;
+
+ for (tmp=c->xref_dead_list, ptmp=&c->xref_dead_list; tmp; ptmp=&tmp->next, tmp=tmp->next) {
+ if (ref == tmp) {
+ *ptmp = tmp->next;
+ jffs2_free_xattr_ref(ref);
+ break;
+ }
+ }
+}
diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h
index 2c19985..06ab7b8 100644
--- a/fs/jffs2/xattr.h
+++ b/fs/jffs2/xattr.h
@@ -16,6 +16,7 @@ #include <linux/list.h>

#define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */
#define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */
+#define JFFS2_XFLAGS_INVALID (0x80) /* This datum contains crc error */

struct jffs2_xattr_datum
{
@@ -23,7 +24,7 @@ struct jffs2_xattr_datum
struct jffs2_raw_node_ref *node;
uint8_t class;
uint8_t flags;
- uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */
+ uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */

struct list_head xindex; /* chained from c->xattrindex[n] */
uint32_t refcnt; /* # of xattr_ref refers this */
@@ -47,6 +48,7 @@ struct jffs2_xattr_ref
uint8_t flags; /* Currently unused */
u16 unused;

+ uint32_t xseqno;
union {
struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */
uint32_t ino; /* only used in scanning/building */
@@ -58,6 +60,34 @@ struct jffs2_xattr_ref
struct jffs2_xattr_ref *next; /* chained from ic->xref_list */
};

+#define XDATUM_DELETE_MARKER (0xffffffff)
+#define XREF_DELETE_MARKER (0x00000001)
+static inline int is_xattr_datum_dead(struct jffs2_xattr_datum *xd)
+{
+ return (xd->version == XDATUM_DELETE_MARKER);
+}
+
+static inline void set_xattr_datum_dead(struct jffs2_xattr_datum *xd)
+{
+ xd->version = XDATUM_DELETE_MARKER;
+}
+
+static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref)
+{
+ return ((ref->xseqno & XREF_DELETE_MARKER) != 0);
+}
+
+static inline void set_xattr_ref_dead(struct jffs2_xattr_ref *ref)
+{
+ ref->xseqno |= XREF_DELETE_MARKER;
+}
+
+static inline void clr_xattr_ref_dead(struct jffs2_xattr_ref *ref)
+{
+ ref->xseqno &= ~XREF_DELETE_MARKER;
+}
+
+
#ifdef CONFIG_JFFS2_FS_XATTR

extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c);
@@ -70,9 +100,13 @@ extern struct jffs2_xattr_datum *jffs2_s
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_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd);
-extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);
+extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd,
+ struct jffs2_raw_node_ref *raw);
+extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref,
+ struct jffs2_raw_node_ref *raw);
extern int jffs2_verify_xattr(struct jffs2_sb_info *c);
+extern void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd);
+extern void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);

extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
char *buffer, size_t size);

[-- Attachment #3: jffs2-xattr-v6-02-fix_posixacl_bug.patch --]
[-- Type: text/plain, Size: 332 bytes --]

diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c
index 320dd48..9c2077e 100644
--- a/fs/jffs2/acl.c
+++ b/fs/jffs2/acl.c
@@ -267,6 +267,8 @@ static int jffs2_set_acl(struct inode *i
}

rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
+ if (!value && rc == -ENODATA)
+ rc = 0;
if (value)
kfree(value);
if (!rc) {