From 15953580e79b58caefb107e77f218e009b9992e6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 1 Nov 2007 16:25:56 -0400 Subject: [JFFS2] Improve getdents vs. f_pos handling on NOR flash. Commit a491486a2087ac3dfc00efb4f838c8d684afaf54 started obliterating dirents directly on the medium, when jffs2_can_mark_obsolete(). Removing them immediately from the f->dents list, however, screws up handling of f_pos within a directory -- because the offset is equivalent to the number of entries through the list we are, and the existence of deletion dirents served to provide 'placeholders' for unlinked entries. Now, 'rm -r' doesn't even manage to unlink everything in the directory. Revert to keeping 'deletion' dirents in the list, at least in memory even though we no longer write anything to the medium. Spotted, debugged and mostly fixed by Joakim Tjernlund Signed-off-by: David Woodhouse --- fs/jffs2/nodelist.c | 9 ++++++--- fs/jffs2/write.c | 27 +++++++++++++++------------ 2 files changed, 21 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 4bf86088b3ae..87c6f555e1a0 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -32,15 +32,18 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) { /* Duplicate. Free one */ if (new->version < (*prev)->version) { - dbg_dentlist("Eep! Marking new dirent node is obsolete, old is \"%s\", ino #%u\n", + dbg_dentlist("Eep! Marking new dirent node obsolete, old is \"%s\", ino #%u\n", (*prev)->name, (*prev)->ino); jffs2_mark_node_obsolete(c, new->raw); jffs2_free_full_dirent(new); } else { - dbg_dentlist("marking old dirent \"%s\", ino #%u bsolete\n", + dbg_dentlist("marking old dirent \"%s\", ino #%u obsolete\n", (*prev)->name, (*prev)->ino); new->next = (*prev)->next; - jffs2_mark_node_obsolete(c, ((*prev)->raw)); + /* It may have been a 'placeholder' deletion dirent, + if jffs2_can_mark_obsolete() (see jffs2_do_unlink()) */ + if ((*prev)->raw) + jffs2_mark_node_obsolete(c, ((*prev)->raw)); jffs2_free_full_dirent(*prev); *prev = new; } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 147e2cbee9e4..611012f7c8ae 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -582,7 +582,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); } else { - struct jffs2_full_dirent **prev = &dir_f->dents; + struct jffs2_full_dirent *fd = dir_f->dents; uint32_t nhash = full_name_hash(name, namelen); /* We don't actually want to reserve any space, but we do @@ -590,18 +590,20 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, down(&c->alloc_sem); down(&dir_f->sem); - while ((*prev) && (*prev)->nhash <= nhash) { - if ((*prev)->nhash == nhash && - !memcmp((*prev)->name, name, namelen) && - !(*prev)->name[namelen]) { - struct jffs2_full_dirent *this = *prev; + for (fd = dir_f->dents; fd; fd = fd->next) { + if (fd->nhash == nhash && + !memcmp(fd->name, name, namelen) && + !fd->name[namelen]) { D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n", - this->ino, ref_offset(this->raw))); - - *prev = this->next; - jffs2_mark_node_obsolete(c, (this->raw)); - jffs2_free_full_dirent(this); + fd->ino, ref_offset(fd->raw))); + jffs2_mark_node_obsolete(c, fd->raw); + /* We don't want to remove it from the list immediately, + because that screws up getdents()/seek() semantics even + more than they're screwed already. Turn it into a + node-less deletion dirent instead -- a placeholder */ + fd->raw = NULL; + fd->ino = 0; break; } prev = &((*prev)->next); @@ -630,7 +632,8 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); } - jffs2_mark_node_obsolete(c, fd->raw); + if (fd->raw) + jffs2_mark_node_obsolete(c, fd->raw); jffs2_free_full_dirent(fd); } } -- cgit v1.2.3 From 857013b87b6e0fea776c1f0b365dbce3d6eba8c6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 1 Nov 2007 16:27:38 -0400 Subject: [JFFS2] Don't strip sgid bit from inode permissions dwmw2: anyway, removing sgid from directories or from files without S_IXGRP is a plain and simple bug these days you don't need that logics at all - simply remove it Signed-off-by: David Woodhouse --- fs/jffs2/fs.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index d2e06f7ea96f..ee192af0b8b0 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -97,11 +97,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) 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); + ri->mode = cpu_to_jemode(iattr->ia_mode); else ri->mode = cpu_to_jemode(inode->i_mode); -- cgit v1.2.3 From 6d88202e3985afc5ac62733b7673c7e815cda698 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 6 Nov 2007 08:29:59 +0000 Subject: [JFFS2] Fix misapplied patch causing compile breakage Somehow, the patch in commit 15953580e79b58caefb107e77f218e009b9992e6 was misapplied and part of the old list-traversal remained. Remove it. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/write.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 611012f7c8ae..ecdf18d0486f 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -606,7 +606,6 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, fd->ino = 0; break; } - prev = &((*prev)->next); } up(&dir_f->sem); } -- cgit v1.2.3 From 050416e93354158b025360387746fb7257d7ce07 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 6 Nov 2007 08:36:49 +0000 Subject: [JFFS2] make jffs2_get_acl() static jffs2_get_acl() can now become static again. Signed-off-by: Adrian Bunk Acked-by: KaiGai Kohei Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/acl.c | 2 +- fs/jffs2/acl.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 77fc5838609c..993ddfce0318 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -176,7 +176,7 @@ static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct spin_unlock(&inode->i_lock); } -struct posix_acl *jffs2_get_acl(struct inode *inode, int type) +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; diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index 76c6ebd1acd9..0bb7f003fd80 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -28,7 +28,6 @@ struct jffs2_acl_header { #define JFFS2_ACL_NOT_CACHED ((void *)-1) -extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type); extern int jffs2_permission(struct inode *, int, struct nameidata *); extern int jffs2_acl_chmod(struct inode *); extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *); @@ -40,7 +39,6 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler; #else -#define jffs2_get_acl(inode, type) (NULL) #define jffs2_permission (NULL) #define jffs2_acl_chmod(inode) (0) #define jffs2_init_acl_pre(dir_i,inode,mode) (0) -- cgit v1.2.3 From 92525726df0c30e080b0fce9b0eb699c622d261e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 21 Nov 2007 12:08:16 -0500 Subject: [JFFS2] Fix data CRC checking on NOR flash. We were failing to check the data CRC on data nodes on non-writebuffered flash, which led to "interesting" behaviour on unclean shutdowns. Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 2eae5d2dbebe..da22da954597 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -37,23 +37,24 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info BUG_ON(tn->csize == 0); - if (!jffs2_is_writebuffered(c)) - goto adj_acc; - /* Calculate how many bytes were already checked */ ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode); - len = ofs % c->wbuf_pagesize; - if (likely(len)) - len = c->wbuf_pagesize - len; - - if (len >= tn->csize) { - dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", - ref_offset(ref), tn->csize, ofs); - goto adj_acc; - } + len = tn->csize; + + if (jffs2_is_writebuffered(c)) { + int adj = ofs % c->wbuf_pagesize; + if (likely(adj)) + adj = c->wbuf_pagesize - adj; + + if (adj >= tn->csize) { + dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", + ref_offset(ref), tn->csize, ofs); + goto adj_acc; + } - ofs += len; - len = tn->csize - len; + ofs += adj; + len -= adj; + } dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n", ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len); -- cgit v1.2.3 From c2056e1e1ddcca8d43e89543e1795e4457f5d1e9 Mon Sep 17 00:00:00 2001 From: Alexey Korolev Date: Tue, 27 Nov 2007 11:25:10 +0000 Subject: [JFFS2] Fix return value check for mtd->point() in check_node_data() If we ask it to map 'len' bytes of the device, don't compare against some other number and whine that it's different. That's a little silly. Signed-off-by: Alexey Korolev Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index da22da954597..fb89ab5e1d50 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -64,7 +64,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info * adding and jffs2_flash_read_end() interface. */ if (c->mtd->point) { err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); - if (!err && retlen < tn->csize) { + if (!err && retlen < len) { JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); c->mtd->unpoint(c->mtd, buffer, ofs, retlen); } else if (err) -- cgit v1.2.3 From 36f97bc617e2c31d16b74e89cd2406de4d24ede5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 6 Jan 2008 17:50:34 +0100 Subject: [JFFS2] Add missing call to posix_acl_release posix_acl_clone does a memory allocation and sets a reference count, so posix_acl_release is needed afterwards to free it. The problem was fixed using the following semantic patch. (http://www.emn.fr/x-info/coccinelle/) // @@ type T; identifier E; expression E1, E2; int ret; statement S; @@ T E; <+... ( E = \(posix_acl_clone\|posix_acl_alloc\|posix_acl_dup\)(...); if (E == NULL) S | if ((E = \(posix_acl_clone\|posix_acl_alloc\|posix_acl_dup\)(...)) == NULL) S ) ... when != E2 = E when strict ( posix_acl_release(E); | E1 = E; | + posix_acl_release(E); return; | + posix_acl_release(E); return ret; ) ...+> // Signed-off-by: Julia Lawall Acked-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/acl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 993ddfce0318..4c80404a9aba 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -345,8 +345,10 @@ int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, int *i_mode) if (!clone) return -ENOMEM; rc = posix_acl_create_masq(clone, (mode_t *)i_mode); - if (rc < 0) + if (rc < 0) { + posix_acl_release(clone); return rc; + } if (rc > 0) jffs2_iset_acl(inode, &f->i_acl_access, clone); -- cgit v1.2.3